diff --git a/DEPS b/DEPS
index 93479a6..b3345a7 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '71b762f2aca1c8bda51f68ee31157d2af41aa34e',
+  'skia_revision': 'dad2923b8ec9270d810c1e8e76da8e6768d8f9dd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '4e01f2a8efd8cbf3ee2142f0fbeaa221e0c6dc9c',
+  'v8_revision': 'd34f038fd58bf112c755ab35b798c78f644ee24d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '8a463c5a1a2252b5fdbff3b5ab07d50273bbc391',
+  'pdfium_revision': 'd7ecb5f272de6cdd88ecef4c7b4d0dbee4355610',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -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': 'da3aa694af9729a870e1837d7cbeebb38bd64063',
+  'catapult_revision': '760530e4994ad2d4a6d68dceead76e588fdb9a04',
   # 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/common/metrics/pointer_metrics_recorder_unittest.cc b/ash/common/metrics/pointer_metrics_recorder_unittest.cc
index 5399f4ea..396cb17 100644
--- a/ash/common/metrics/pointer_metrics_recorder_unittest.cc
+++ b/ash/common/metrics/pointer_metrics_recorder_unittest.cc
@@ -181,11 +181,6 @@
   pointer_metrics_recorder_->OnPointerEventObserved(pointer_event, gfx::Point(),
                                                     target.get());
   histogram_tester_->ExpectBucketCount(kDestinationHistogramName, 3, 1);
-
-  window->SetAppType(static_cast<int>(AppType::DEFAULT_NOTE_TAKING_APP));
-  pointer_metrics_recorder_->OnPointerEventObserved(pointer_event, gfx::Point(),
-                                                    target.get());
-  histogram_tester_->ExpectBucketCount(kDestinationHistogramName, 4, 1);
 }
 
 }  // namespace ash
diff --git a/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc b/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
index 38b1c0ad..b10258c 100644
--- a/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
+++ b/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
@@ -340,8 +340,6 @@
   // In the material design, we will add a title item with a separator on the
   // top of the IME menu.
   if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
-    bubble_view->SetLayoutManager(
-        new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
     bubble_view->AddChildView(
         new ImeTitleView(!ShouldShowEmojiHandwritingVoiceButtons()));
   } else {
diff --git a/ash/common/system/tray/system_tray_bubble.cc b/ash/common/system/tray/system_tray_bubble.cc
index 19ad267..c08e00e6 100644
--- a/ash/common/system/tray/system_tray_bubble.cc
+++ b/ash/common/system/tray/system_tray_bubble.cc
@@ -45,6 +45,9 @@
 // detailed view or vice versa.
 const int kSwipeDelayMS = 150;
 
+// Extra bottom padding when showing the BUBBLE_TYPE_DEFAULT view.
+const int kDefaultViewBottomPadding = 4;
+
 // Implicit animation observer that deletes itself and the layer at the end of
 // the animation.
 class AnimationObserverDeleteLayer : public ui::ImplicitAnimationObserver {
@@ -150,6 +153,7 @@
     return;
   }
 
+  UpdateBottomPadding();
   bubble_view_->GetWidget()->GetContentsView()->Layout();
   // Make sure that the bubble is large enough for the default view.
   if (bubble_type_ == BUBBLE_TYPE_DEFAULT) {
@@ -197,6 +201,7 @@
   }
 
   bubble_view_ = TrayBubbleView::Create(anchor, tray_, init_params);
+  UpdateBottomPadding();
   bubble_view_->set_adjust_if_offscreen(false);
   CreateItemViews(login_status);
 
@@ -302,6 +307,15 @@
   }
 }
 
+void SystemTrayBubble::UpdateBottomPadding() {
+  if (bubble_type_ == BUBBLE_TYPE_DEFAULT &&
+      MaterialDesignController::IsSystemTrayMenuMaterial()) {
+    bubble_view_->SetBottomPadding(kDefaultViewBottomPadding);
+  } else {
+    bubble_view_->SetBottomPadding(0);
+  }
+}
+
 void SystemTrayBubble::CreateItemViews(LoginStatus login_status) {
   tray_item_view_map_.clear();
 
diff --git a/ash/common/system/tray/system_tray_bubble.h b/ash/common/system/tray/system_tray_bubble.h
index 1fed3521..aeb8722 100644
--- a/ash/common/system/tray/system_tray_bubble.h
+++ b/ash/common/system/tray/system_tray_bubble.h
@@ -66,6 +66,9 @@
   void RecordVisibleRowMetrics();
 
  private:
+  // Updates the bottom padding of the |bubble_view_| based on the
+  // |bubble_type_|.
+  void UpdateBottomPadding();
   void CreateItemViews(LoginStatus login_status);
 
   ash::SystemTray* tray_;
diff --git a/ash/common/system/web_notification/web_notification_tray.cc b/ash/common/system/web_notification/web_notification_tray.cc
index b6bd17a..351fd441 100644
--- a/ash/common/system/web_notification/web_notification_tray.cc
+++ b/ash/common/system/web_notification/web_notification_tray.cc
@@ -9,6 +9,7 @@
 #include "ash/common/shelf/shelf_constants.h"
 #include "ash/common/shelf/wm_shelf.h"
 #include "ash/common/shelf/wm_shelf_util.h"
+#include "ash/common/system/status_area_widget.h"
 #include "ash/common/system/tray/system_tray.h"
 #include "ash/common/system/tray/system_tray_delegate.h"
 #include "ash/common/system/tray/tray_bubble_wrapper.h"
@@ -96,6 +97,7 @@
  public:
   // Takes ownership of |bubble| and creates |bubble_wrapper_|.
   WebNotificationBubbleWrapper(WebNotificationTray* tray,
+                               TrayBackgroundView* anchor_tray,
                                message_center::MessageBubbleBase* bubble) {
     bubble_.reset(bubble);
     views::TrayBubbleView::AnchorAlignment anchor_alignment =
@@ -103,8 +105,8 @@
     views::TrayBubbleView::InitParams init_params =
         bubble->GetInitParams(anchor_alignment);
     views::TrayBubbleView* bubble_view = views::TrayBubbleView::Create(
-        tray->GetBubbleAnchor(), tray, &init_params);
-    bubble_view->set_anchor_view_insets(tray->GetBubbleAnchorInsets());
+        anchor_tray->GetBubbleAnchor(), tray, &init_params);
+    bubble_view->set_anchor_view_insets(anchor_tray->GetBubbleAnchorInsets());
     bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_view));
     bubble->InitializeContents(bubble_view);
   }
@@ -373,8 +375,18 @@
       std::max(0, max_height - GetTrayConstant(TRAY_SPACING)));
   if (show_settings)
     message_center_bubble->SetSettingsVisible();
-  message_center_bubble_.reset(
-      new WebNotificationBubbleWrapper(this, message_center_bubble));
+
+  // For vertical shelf alignments, anchor to the WebNotificationTray, but for
+  // horizontal (i.e. bottom) shelves, anchor to the system tray.
+  TrayBackgroundView* anchor_tray = this;
+  if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) {
+    anchor_tray = WmShelf::ForWindow(status_area_window_)
+                      ->GetStatusAreaWidget()
+                      ->system_tray();
+  }
+
+  message_center_bubble_.reset(new WebNotificationBubbleWrapper(
+      this, anchor_tray, message_center_bubble));
 
   system_tray_->SetHideNotifications(true);
   shelf()->UpdateAutoHideState();
diff --git a/ash/common/wm/default_state.cc b/ash/common/wm/default_state.cc
index 2b5a5893..d7e49d9 100644
--- a/ash/common/wm/default_state.cc
+++ b/ash/common/wm/default_state.cc
@@ -682,8 +682,13 @@
         bounds_in_parent = window->GetBounds();
       }
       // Make sure that part of the window is always visible.
-      wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent,
-                                                      &bounds_in_parent);
+      if (!window_state->is_dragged()) {
+        // Avoid doing this while the window is being dragged as its root
+        // window hasn't been updated yet in the case of dragging to another
+        // display. crbug.com/666836.
+        wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent,
+                                                        &bounds_in_parent);
+      }
       break;
     }
     case WINDOW_STATE_TYPE_MAXIMIZED:
diff --git a/ash/common/wm/workspace/workspace_window_resizer.cc b/ash/common/wm/workspace/workspace_window_resizer.cc
index 8dd745f..e17da97 100644
--- a/ash/common/wm/workspace/workspace_window_resizer.cc
+++ b/ash/common/wm/workspace/workspace_window_resizer.cc
@@ -439,12 +439,8 @@
                                        GetTarget()->GetBounds())) {
         // Set the window to WINDOW_STATE_TYPE_NORMAL but keep the
         // window at the bounds that the user has moved/resized the
-        // window to. ClearRestoreBounds() is used instead of
-        // SaveCurrentBoundsForRestore() because most of the restore
-        // logic is skipped because we are still in the middle of a
-        // drag.  TODO(pkotwicz): Fix this and use
-        // SaveCurrentBoundsForRestore().
-        window_state()->ClearRestoreBounds();
+        // window to.
+        window_state()->SaveCurrentBoundsForRestore();
         window_state()->Restore();
       }
     } else if (!dock_layout_->is_dragged_window_docked()) {
diff --git a/ash/mus/accelerators/accelerator_controller_delegate_mus.cc b/ash/mus/accelerators/accelerator_controller_delegate_mus.cc
index f1664b58..b92ada3b 100644
--- a/ash/mus/accelerators/accelerator_controller_delegate_mus.cc
+++ b/ash/mus/accelerators/accelerator_controller_delegate_mus.cc
@@ -11,6 +11,7 @@
 #include "services/ui/public/interfaces/constants.mojom.h"
 #include "services/ui/public/interfaces/display/display_controller.mojom.h"
 #include "services/ui/public/interfaces/display/test_display_controller.mojom.h"
+#include "ui/display/screen.h"
 
 namespace ash {
 namespace mus {
@@ -28,12 +29,18 @@
 AcceleratorControllerDelegateMus::~AcceleratorControllerDelegateMus() {}
 
 bool AcceleratorControllerDelegateMus::HandlesAction(AcceleratorAction action) {
-  // This is the list of actions that are not ported from aura. The actions are
-  // replicated here to make sure we don't forget any. This list should
-  // eventually be empty. If there are any actions that don't make sense for
-  // mus, then they should be removed from AcceleratorAction.
+  // Accelerators that return true need to work differently in mash. These
+  // should have implementations in CanPerformAction() and PerformAction().
+  // Accelerators that return false have not been ported to work with mash yet.
+  // If the behavior between cash and mash can be unified then the accelerator
+  // should be moved to accelerator_controller.cc/h. See
   // http://crbug.com/612331.
   switch (action) {
+    case SCALE_UI_DOWN:
+    case SCALE_UI_RESET:
+    case SCALE_UI_UP:
+    case ROTATE_SCREEN:
+      return true;
     case DEBUG_TOGGLE_DEVICE_SCALE_FACTOR:
     case DEV_TOGGLE_ROOT_WINDOW_FULL_SCREEN:
     case DEBUG_TOGGLE_SHOW_DEBUG_BORDERS:
@@ -41,11 +48,7 @@
     case DEBUG_TOGGLE_SHOW_PAINT_RECTS:
     case MAGNIFY_SCREEN_ZOOM_IN:
     case MAGNIFY_SCREEN_ZOOM_OUT:
-    case ROTATE_SCREEN:
     case ROTATE_WINDOW:
-    case SCALE_UI_DOWN:
-    case SCALE_UI_RESET:
-    case SCALE_UI_UP:
     case SHOW_SYSTEM_TRAY_BUBBLE:
     case TAKE_PARTIAL_SCREENSHOT:
     case TAKE_SCREENSHOT:
@@ -58,13 +61,13 @@
     case DEV_ADD_REMOVE_DISPLAY:
     case DEV_TOGGLE_UNIFIED_DESKTOP:
     case SWAP_PRIMARY_DISPLAY:
+    case TOGGLE_MIRROR_MODE:
     case TOUCH_HUD_PROJECTION_TOGGLE:
       return true;
     case LOCK_PRESSED:
     case LOCK_RELEASED:
     case POWER_PRESSED:
     case POWER_RELEASED:
-    case TOGGLE_MIRROR_MODE:
     case TOUCH_HUD_CLEAR:
     case TOUCH_HUD_MODE_CHANGE:
       NOTIMPLEMENTED();
@@ -81,25 +84,49 @@
     AcceleratorAction action,
     const ui::Accelerator& accelerator,
     const ui::Accelerator& previous_accelerator) {
-#if defined(OS_CHROMEOS)
   switch (action) {
+    case ROTATE_SCREEN:
+    case SCALE_UI_DOWN:
+    case SCALE_UI_RESET:
+    case SCALE_UI_UP:
+      return true;
+#if defined(OS_CHROMEOS)
     case DEV_ADD_REMOVE_DISPLAY:
     case DEV_TOGGLE_UNIFIED_DESKTOP:
+      return true;
     case SWAP_PRIMARY_DISPLAY:
+      return display::Screen::GetScreen()->GetNumDisplays() > 1;
+    case TOGGLE_MIRROR_MODE:
     case TOUCH_HUD_PROJECTION_TOGGLE:
       return true;
+#endif
     default:
       break;
   }
-#endif
   return false;
 }
 
 void AcceleratorControllerDelegateMus::PerformAction(
     AcceleratorAction action,
     const ui::Accelerator& accelerator) {
-#if defined(OS_CHROMEOS)
   switch (action) {
+    case ROTATE_SCREEN: {
+      window_manager_->GetDisplayController()->RotateCurrentDisplayCW();
+      break;
+    }
+    case SCALE_UI_DOWN: {
+      window_manager_->GetDisplayController()->DecreaseInternalDisplayZoom();
+      break;
+    }
+    case SCALE_UI_RESET: {
+      window_manager_->GetDisplayController()->ResetInternalDisplayZoom();
+      break;
+    }
+    case SCALE_UI_UP: {
+      window_manager_->GetDisplayController()->IncreaseInternalDisplayZoom();
+      break;
+    }
+#if defined(OS_CHROMEOS)
     case DEV_ADD_REMOVE_DISPLAY: {
       display::mojom::TestDisplayControllerPtr test_display_controller;
       window_manager_->connector()->ConnectToInterface(
@@ -120,6 +147,10 @@
       window_manager_->GetDisplayController()->SwapPrimaryDisplay();
       break;
     }
+    case TOGGLE_MIRROR_MODE: {
+      window_manager_->GetDisplayController()->ToggleMirrorMode();
+      break;
+    }
     case TOUCH_HUD_PROJECTION_TOGGLE: {
       mash::mojom::LaunchablePtr launchable;
       window_manager_->connector()->ConnectToInterface("touch_hud",
@@ -128,12 +159,10 @@
                          mash::mojom::LaunchMode::DEFAULT);
       break;
     }
+#endif
     default:
       NOTREACHED();
   }
-#else
-  NOTREACHED();
-#endif
 }
 
 void AcceleratorControllerDelegateMus::ShowDeprecatedAcceleratorNotification(
diff --git a/ash/mus/accelerators/accelerator_controller_delegate_mus.h b/ash/mus/accelerators/accelerator_controller_delegate_mus.h
index 4d14750..79a30fee 100644
--- a/ash/mus/accelerators/accelerator_controller_delegate_mus.h
+++ b/ash/mus/accelerators/accelerator_controller_delegate_mus.h
@@ -13,6 +13,7 @@
 
 class WindowManager;
 
+// Controls accelerators that are specific to mash.
 class AcceleratorControllerDelegateMus : public AcceleratorControllerDelegate {
  public:
   explicit AcceleratorControllerDelegateMus(WindowManager* window_manager);
diff --git a/ash/shared/app_types.h b/ash/shared/app_types.h
index 0cebf8d3..a1df588 100644
--- a/ash/shared/app_types.h
+++ b/ash/shared/app_types.h
@@ -14,8 +14,8 @@
   BROWSER,
   CHROME_APP,
   ARC_APP,
-  DEFAULT_NOTE_TAKING_APP,
-  APP_TYPE_LAST = DEFAULT_NOTE_TAKING_APP,
+  DEPRECATED_DEFAULT_NOTE_TAKING_APP,  // Use CHROME_APP or ARC_APP instead.
+  APP_TYPE_LAST = DEPRECATED_DEFAULT_NOTE_TAKING_APP,
 };
 
 const int kAppCount = static_cast<int>(AppType::APP_TYPE_LAST) + 1;
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index 067e238..cfc8ebf 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -14,6 +14,7 @@
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_tracker.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/hit_test.h"
@@ -94,13 +95,16 @@
   wm::WindowState* window_state = wm::GetWindowState(source);
   const bool window_position_managed = window_state->window_position_managed();
   window_state->set_window_position_managed(false);
+  aura::WindowTracker tracker({source});
 
   run_loop.Run();
 
   if (!weak_ptr)
     return aura::client::MOVE_CANCELED;
 
-  window_state->set_window_position_managed(window_position_managed);
+  // Make sure the window hasn't been deleted.
+  if (tracker.Contains(source))
+    window_state->set_window_position_managed(window_position_managed);
 
   in_move_loop_ = false;
   return result == wm::WmToplevelWindowEventHandler::DragResult::SUCCESS
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index ba89155..573686c 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -5,6 +5,7 @@
 #include "ash/wm/toplevel_window_event_handler.h"
 
 #include "ash/common/wm/window_state.h"
+#include "ash/common/wm/wm_event.h"
 #include "ash/common/wm/workspace_controller.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
@@ -24,6 +25,8 @@
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/hit_test.h"
+#include "ui/display/display_layout_builder.h"
+#include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/test/event_generator.h"
@@ -827,6 +830,46 @@
                                      aura::client::WINDOW_MOVE_SOURCE_TOUCH));
 }
 
+// Tests that dragging a snapped window to another display updates the window's
+// bounds correctly.
+TEST_F(ToplevelWindowEventHandlerTest, DragSnappedWindowToExternalDisplay) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  UpdateDisplay("940x550,940x550");
+  int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+  int64_t secondary_id = display_manager()->GetSecondaryDisplay().id();
+  display::DisplayLayoutBuilder builder(primary_id);
+  builder.SetSecondaryPlacement(secondary_id, display::DisplayPlacement::TOP,
+                                0);
+  display_manager()->SetLayoutForCurrentDisplays(builder.Build());
+
+  const gfx::Size initial_window_size(330, 230);
+  std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegateAndType(
+      new TestWindowDelegate(HTCAPTION), ui::wm::WINDOW_TYPE_NORMAL, 0,
+      gfx::Rect(initial_window_size)));
+
+  // Snap the window to the right.
+  wm::WindowState* window_state = wm::GetWindowState(w1.get());
+  ASSERT_TRUE(window_state->CanSnap());
+  const wm::WMEvent event(wm::WM_EVENT_CYCLE_SNAP_DOCK_RIGHT);
+  window_state->OnWMEvent(&event);
+  ASSERT_TRUE(window_state->IsSnapped());
+
+  // Drag the window to the secondary display.
+  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), w1.get());
+  generator.DragMouseTo(472, -462);
+
+  // Expect the window is no longer snapped and its size was restored to the
+  // initial size.
+  EXPECT_FALSE(window_state->IsSnapped());
+  EXPECT_EQ(initial_window_size.ToString(), w1->bounds().size().ToString());
+
+  // The window is now fully contained in the secondary display.
+  EXPECT_TRUE(display_manager()->GetSecondaryDisplay().bounds().Contains(
+      w1->GetBoundsInScreen()));
+}
+
 // Showing the resize shadows when the mouse is over the window edges is tested
 // in resize_shadow_and_cursor_test.cc
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index bbdf073..cd6fd2e 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -823,6 +823,8 @@
     "task_scheduler/scheduler_worker_pool_params.h",
     "task_scheduler/scheduler_worker_stack.cc",
     "task_scheduler/scheduler_worker_stack.h",
+    "task_scheduler/scoped_set_task_priority_for_current_thread.cc",
+    "task_scheduler/scoped_set_task_priority_for_current_thread.h",
     "task_scheduler/sequence.cc",
     "task_scheduler/sequence.h",
     "task_scheduler/sequence_sort_key.cc",
@@ -1984,6 +1986,7 @@
     "task_scheduler/scheduler_worker_pool_impl_unittest.cc",
     "task_scheduler/scheduler_worker_stack_unittest.cc",
     "task_scheduler/scheduler_worker_unittest.cc",
+    "task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc",
     "task_scheduler/sequence_sort_key_unittest.cc",
     "task_scheduler/sequence_unittest.cc",
     "task_scheduler/task_scheduler_impl_unittest.cc",
diff --git a/base/task_scheduler/scoped_set_task_priority_for_current_thread.cc b/base/task_scheduler/scoped_set_task_priority_for_current_thread.cc
new file mode 100644
index 0000000..a163863
--- /dev/null
+++ b/base/task_scheduler/scoped_set_task_priority_for_current_thread.cc
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+LazyInstance<ThreadLocalPointer<const TaskPriority>>::Leaky
+    tls_task_priority_for_current_thread = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+ScopedSetTaskPriorityForCurrentThread::ScopedSetTaskPriorityForCurrentThread(
+    TaskPriority priority)
+    : priority_(priority) {
+  DCHECK(!tls_task_priority_for_current_thread.Get().Get());
+  tls_task_priority_for_current_thread.Get().Set(&priority_);
+}
+
+ScopedSetTaskPriorityForCurrentThread::
+    ~ScopedSetTaskPriorityForCurrentThread() {
+  DCHECK_EQ(&priority_, tls_task_priority_for_current_thread.Get().Get());
+  tls_task_priority_for_current_thread.Get().Set(nullptr);
+}
+
+TaskPriority GetTaskPriorityForCurrentThread() {
+  const TaskPriority* priority =
+      tls_task_priority_for_current_thread.Get().Get();
+  return priority ? *priority : TaskPriority::USER_VISIBLE;
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/scoped_set_task_priority_for_current_thread.h b/base/task_scheduler/scoped_set_task_priority_for_current_thread.h
new file mode 100644
index 0000000..4508911d
--- /dev/null
+++ b/base/task_scheduler/scoped_set_task_priority_for_current_thread.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_SCHEDULER_SCOPED_SET_TASK_PRIORITY_FOR_CURRENT_THREAD_H_
+#define BASE_TASK_SCHEDULER_SCOPED_SET_TASK_PRIORITY_FOR_CURRENT_THREAD_H_
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/task_scheduler/task_traits.h"
+
+namespace base {
+namespace internal {
+
+class BASE_EXPORT ScopedSetTaskPriorityForCurrentThread {
+ public:
+  // Within the scope of this object, GetTaskPriorityForCurrentThread() will
+  // return |priority|.
+  ScopedSetTaskPriorityForCurrentThread(TaskPriority priority);
+  ~ScopedSetTaskPriorityForCurrentThread();
+
+ private:
+  const TaskPriority priority_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedSetTaskPriorityForCurrentThread);
+};
+
+// Returns the priority of the TaskScheduler task running on the current thread,
+// or TaskPriority::USER_VISIBLE if no TaskScheduler task is running on the
+// current thread.
+BASE_EXPORT TaskPriority GetTaskPriorityForCurrentThread();
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_SCHEDULER_SCOPED_SET_TASK_PRIORITY_FOR_CURRENT_THREAD_H_
diff --git a/base/task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc b/base/task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc
new file mode 100644
index 0000000..c497af6
--- /dev/null
+++ b/base/task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h"
+
+#include "base/task_scheduler/task_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+TEST(TaskSchedulerScopedSetTaskPriorityForCurrentThreadTest,
+     ScopedSetTaskPriorityForCurrentThread) {
+  EXPECT_EQ(TaskPriority::USER_VISIBLE, GetTaskPriorityForCurrentThread());
+  {
+    ScopedSetTaskPriorityForCurrentThread
+        scoped_set_task_priority_for_current_thread(
+            TaskPriority::USER_BLOCKING);
+    EXPECT_EQ(TaskPriority::USER_BLOCKING, GetTaskPriorityForCurrentThread());
+  }
+  EXPECT_EQ(TaskPriority::USER_VISIBLE, GetTaskPriorityForCurrentThread());
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
index 03f161e..818ded7 100644
--- a/base/task_scheduler/task_tracker.cc
+++ b/base/task_scheduler/task_tracker.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/sequence_token.h"
 #include "base/synchronization/condition_variable.h"
+#include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -230,9 +231,10 @@
         ThreadRestrictions::SetWaitAllowed(task->traits.with_wait());
 
     {
-      // Set up SequenceToken as expected for the scope of the task.
       ScopedSetSequenceTokenForCurrentThread
           scoped_set_sequence_token_for_current_thread(sequence_token);
+      ScopedSetTaskPriorityForCurrentThread
+          scoped_set_task_priority_for_current_thread(task->traits.priority());
 
       // Set up TaskRunnerHandle as expected for the scope of the task.
       std::unique_ptr<SequencedTaskRunnerHandle> sequenced_task_runner_handle;
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index cc05f58e..67b1e06 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -315,7 +315,7 @@
     <ignore regexp="blimp/client/app/android/java/res/values/dimens.xml"/>
     <ignore regexp="blimp/client/app/android/java/res/xml/about_blimp_preferences.xml"/>
     <ignore regexp="blimp/client/app/android/java/res/xml/blimp_preferences.xml"/>
-    <ignore regexp="blimp/client/core/resources/android/java/res/*" />    
+    <ignore regexp="blimp/client/core/resources/android/java/res/*" />
     <!-- TODO(crbug.com/635567): Fix this properly. -->
     <ignore regexp="chrome/android/java/res/drawable-hdpi/*"/>
     <ignore regexp="chrome/android/java/res/drawable-hdpi/amex_card.png"/>
@@ -460,6 +460,10 @@
     <!-- TODO(crbug.com/635567): Fix this properly. -->
     <ignore regexp="content/shell/android/shell_apk/res/values/strings.xml"/>
   </issue>
+  <!-- TODO(crbug.com/669629): Remove this when the Chromecast dependency on old layout code is removed. -->
+  <issue id="UseCompoundDrawables">
+    <ignore regexp="chromecast/internal/android/prebuilt/settings/res/layout-v17/setup_activity_progress.xml"/>
+  </issue>
   <issue id="UselessParent">
     <ignore regexp="android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_browser.xml"/>
     <ignore regexp="chrome/android/java/res/layout/data_reduction_promo_screen.xml"/>
diff --git a/build/linux/sysroot_scripts/install-sysroot.py b/build/linux/sysroot_scripts/install-sysroot.py
index d1e392f..109d553 100755
--- a/build/linux/sysroot_scripts/install-sysroot.py
+++ b/build/linux/sysroot_scripts/install-sysroot.py
@@ -25,6 +25,7 @@
 import shutil
 import subprocess
 import sys
+import urllib
 
 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 sys.path.append(os.path.dirname(os.path.dirname(SCRIPT_DIR)))
@@ -245,8 +246,14 @@
   print 'Downloading %s' % url
   sys.stdout.flush()
   sys.stderr.flush()
-  subprocess.check_call(
-      ['wget', '--quiet', '-t', '3', '-O', tarball, url])
+  for _ in range(3):
+    try:
+      urllib.urlretrieve(url, tarball)
+      break
+    except:
+      pass
+  else:
+    raise Error('Failed to download %s' % url)
   sha1sum = GetSha1(tarball)
   if sha1sum != tarball_sha1sum:
     raise Error('Tarball sha1sum is wrong.'
diff --git a/build/secondary/third_party/android_tools/BUILD.gn b/build/secondary/third_party/android_tools/BUILD.gn
index 213109a..f35977d2 100644
--- a/build/secondary/third_party/android_tools/BUILD.gn
+++ b/build/secondary/third_party/android_tools/BUILD.gn
@@ -30,7 +30,7 @@
   ]
 }
 
-lib_version = "24.1.1"
+lib_version = "25.0.1"
 lib_path = "$android_sdk_root/extras/android/m2repository/com/android/support"
 
 android_java_prebuilt("android_gcm_java") {
@@ -60,8 +60,39 @@
   jar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.jar"
 }
 
-android_aar_prebuilt("android_support_v4_java") {
-  _lib_name = "support-v4"
+java_group("android_support_v4_java") {
+  deps = [
+    ":android_support_compat_java",
+    ":android_support_core_ui_java",
+    ":android_support_core_utils_java",
+    ":android_support_fragment_java",
+    ":android_support_media_compat_java",
+  ]
+}
+
+android_aar_prebuilt("android_support_compat_java") {
+  _lib_name = "support-compat"
+  aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+  ignore_aidl = true  # We don't appear to need these currently.
+}
+
+android_aar_prebuilt("android_support_core_ui_java") {
+  _lib_name = "support-core-ui"
+  aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+}
+
+android_aar_prebuilt("android_support_core_utils_java") {
+  _lib_name = "support-core-utils"
+  aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+}
+
+android_aar_prebuilt("android_support_fragment_java") {
+  _lib_name = "support-fragment"
+  aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
+}
+
+android_aar_prebuilt("android_support_media_compat_java") {
+  _lib_name = "support-media-compat"
   aar_path = "$lib_path/$_lib_name/$lib_version/$_lib_name-$lib_version.aar"
   ignore_aidl = true  # We don't appear to need these currently.
 }
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 0435b84..fdb91d6d 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -211,6 +211,7 @@
     "//third_party/jsr-305:jsr_305_javalib",
     "//third_party/leakcanary:leakcanary_java",
     "//ui/android:ui_java",
+    "//ui/gfx/geometry/mojo:mojo_java",
     "//url/mojo:url_mojom_gurl_java",
     google_play_services_library,
   ]
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index f1f4805f..c7223de7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1720,13 +1720,7 @@
                 builder.show();
             }
         } else if (id == R.id.help_id) {
-            // Since reading back the compositor is asynchronous, we need to do the readback
-            // before starting the GoogleHelp.
-            String helpContextId = HelpAndFeedback.getHelpContextIdFromUrl(
-                    this, currentTab.getUrl(), getCurrentTabModel().isIncognito());
-            HelpAndFeedback.getInstance(this)
-                    .show(this, helpContextId, currentTab.getProfile(), currentTab.getUrl());
-            RecordUserAction.record("MobileMenuFeedback");
+            startHelpAndFeedback(currentTab, "MobileMenuFeedback");
         } else {
             return false;
         }
@@ -1734,6 +1728,21 @@
     }
 
     /**
+     * Shows HelpAndFeedback and records the user action as well.
+     * @param currentTab The tab that the user is currently on.
+     * @param recordAction The user action to record.
+     */
+    public void startHelpAndFeedback(Tab currentTab, String recordAction) {
+        // Since reading back the compositor is asynchronous, we need to do the readback
+        // before starting the GoogleHelp.
+        String helpContextId = HelpAndFeedback.getHelpContextIdFromUrl(
+                this, currentTab.getUrl(), getCurrentTabModel().isIncognito());
+        HelpAndFeedback.getInstance(this)
+                .show(this, helpContextId, currentTab.getProfile(), currentTab.getUrl());
+        RecordUserAction.record(recordAction);
+    }
+
+    /**
      * Tests if VR Shell (the mode displaying browser UI and tab contents in VR) is currently
      * enabled.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
index 7dced043e3..ffe957b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
@@ -293,12 +293,13 @@
     }
 
     /**
-     * Sets whether navigation info should be recorded and shared for the session.
+     * Sets whether navigation info should be recorded and shared for the current navigation in this
+     * session.
      */
     public synchronized void setSendNavigationInfoForSession(
-            CustomTabsSessionToken session, boolean save) {
+            CustomTabsSessionToken session, boolean send) {
         SessionParams params = mSessionParams.get(session);
-        if (params != null) params.mShouldSendNavigationInfo = save;
+        if (params != null) params.mShouldSendNavigationInfo = send;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
index b4ad78c..9313416 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
@@ -95,6 +95,7 @@
             mPageLoadStartedTimestamp = SystemClock.elapsedRealtime();
         }
         if (mCustomTabsConnection != null) {
+            mCustomTabsConnection.setSendNavigationInfoForSession(mSession, false);
             mCustomTabsConnection.notifyNavigationEvent(
                     mSession, CustomTabsCallback.NAVIGATION_STARTED);
             mScreenshotTakenForCurrentNavigation = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index 13219d9..a9585d87 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -662,6 +662,13 @@
     }
 
     /**
+     * See {@link ClientManager#setSendNavigationInfoForSession(CustomTabsSessionToken, boolean)}.
+     */
+    void setSendNavigationInfoForSession(CustomTabsSessionToken session, boolean send) {
+        mClientManager.setSendNavigationInfoForSession(session, send);
+    }
+
+    /**
      * Extracts the creator package name from the intent.
      * @param intent The intent to get the package name from.
      * @return the package name which can be null.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java
index dec67a4a2..9eb7542 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java
@@ -22,6 +22,7 @@
 
 import java.io.File;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.RejectedExecutionException;
 
 /** A View that manages the display of space used by the downloads. */
 public class SpaceDisplay extends RecyclerView.AdapterDataObserver {
@@ -108,6 +109,7 @@
     private TextView mSpaceUsedByOtherAppsTextView;
     private TextView mSpaceFreeTextView;
     private ProgressBar mSpaceBar;
+    private long mFreeBytes;
 
     SpaceDisplay(final ViewGroup parent, DownloadHistoryAdapter historyAdapter) {
         mHistoryAdapter = historyAdapter;
@@ -131,13 +133,22 @@
         }
 
         // Determine how much space is free now, then update the display.
-        mFreeBytesTask = new StorageSizeTask(false) {
-            @Override
-            protected void onPostExecute(Long bytes) {
-                update();
+        if (mFreeBytesTask == null) {
+            mFreeBytesTask = new StorageSizeTask(false) {
+                @Override
+                protected void onPostExecute(Long bytes) {
+                    mFreeBytes = bytes.longValue();
+                    mFreeBytesTask = null;
+                    update();
+                }
+            };
+
+            try {
+                mFreeBytesTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+            } catch (RejectedExecutionException e) {
+                mFreeBytesTask = null;
             }
-        };
-        mFreeBytesTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        }
     }
 
     @VisibleForTesting
@@ -147,17 +158,15 @@
 
     private void update() {
         long fileSystemBytes = 0;
-        long freeBytes = 0;
 
         try {
             fileSystemBytes = mFileSystemBytesTask.get();
-            freeBytes = mFreeBytesTask.get();
         } catch (ExecutionException | InterruptedException e) {
             // Can't do anything here.
         }
 
         // Indicate how much space has been used by everything on the device via the progress bar.
-        long bytesUsedTotal = Math.max(0, fileSystemBytes - freeBytes);
+        long bytesUsedTotal = Math.max(0, fileSystemBytes - mFreeBytes);
         long bytesUsedByDownloads = Math.max(0, mHistoryAdapter.getTotalDownloadSize());
         long bytesUsedByOtherApps = Math.max(0, bytesUsedTotal - bytesUsedByDownloads);
 
@@ -166,7 +175,7 @@
                 getStringForBytes(USED_STRINGS, bytesUsedByDownloads));
         mSpaceUsedByOtherAppsTextView.setText(
                 getStringForBytes(OTHER_STRINGS, bytesUsedByOtherApps));
-        mSpaceFreeTextView.setText(getStringForBytes(FREE_STRINGS, freeBytes));
+        mSpaceFreeTextView.setText(getStringForBytes(FREE_STRINGS, mFreeBytes));
 
         // Set a minimum size for the download size so that it shows up in the progress bar.
         long onePercentOfSystem = fileSystemBytes == 0 ? 0 : fileSystemBytes / 100;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java b/chrome/android/java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java
index 0442a6f..ac04f445 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java
@@ -5,7 +5,9 @@
 package org.chromium.chrome.browser.mojo;
 
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.blink.mojom.BarcodeDetection;
 import org.chromium.chrome.browser.payments.PaymentRequestFactory;
+import org.chromium.chrome.browser.shapedetection.BarcodeDetectionFactory;
 import org.chromium.chrome.browser.webshare.ShareServiceImplementationFactory;
 import org.chromium.content_public.browser.InterfaceRegistrar;
 import org.chromium.content_public.browser.WebContents;
@@ -28,5 +30,7 @@
         registry.addInterface(PaymentRequest.MANAGER, new PaymentRequestFactory(webContents));
         registry.addInterface(
                 ShareService.MANAGER, new ShareServiceImplementationFactory(webContents));
+        registry.addInterface(
+                BarcodeDetection.MANAGER, new BarcodeDetectionFactory(webContents));
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionFactory.java
new file mode 100644
index 0000000..c3c3e564
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionFactory.java
@@ -0,0 +1,39 @@
+// 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.
+
+package org.chromium.chrome.browser.shapedetection;
+
+import android.app.Activity;
+
+import org.chromium.blink.mojom.BarcodeDetection;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.services.service_manager.InterfaceFactory;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * Factory class registered to create BarcodeDetections upon request.
+ */
+public class BarcodeDetectionFactory implements InterfaceFactory<BarcodeDetection> {
+    private final WebContents mWebContents;
+
+    public BarcodeDetectionFactory(WebContents webContents) {
+        mWebContents = webContents;
+    }
+
+    @Override
+    public BarcodeDetection createImpl() {
+        // Get android.content.Context out of |mWebContents|.
+        final ContentViewCore contentViewCore = ContentViewCore.fromWebContents(mWebContents);
+        if (contentViewCore == null) return null;
+
+        final WindowAndroid window = contentViewCore.getWindowAndroid();
+        if (window == null) return null;
+
+        final Activity context = window.getActivity().get();
+        if (context == null) return null;
+
+        return new BarcodeDetectionImpl(context);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionImpl.java
new file mode 100644
index 0000000..ec9e4f5
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionImpl.java
@@ -0,0 +1,118 @@
+// 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.
+
+package org.chromium.chrome.browser.shapedetection;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.util.SparseArray;
+
+import com.google.android.gms.vision.Frame;
+import com.google.android.gms.vision.barcode.Barcode;
+import com.google.android.gms.vision.barcode.BarcodeDetector;
+
+import org.chromium.base.Log;
+import org.chromium.blink.mojom.BarcodeDetection;
+import org.chromium.blink.mojom.BarcodeDetectionResult;
+import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
+import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
+import org.chromium.gfx.mojom.RectF;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implementation of mojo BarcodeDetection, using Google Play Services vision package.
+ */
+public class BarcodeDetectionImpl implements BarcodeDetection {
+    private static final String TAG = "BarcodeDetectionImpl";
+
+    private final Context mContext;
+    private BarcodeDetector mBarcodeDetector;
+
+    public BarcodeDetectionImpl(Context context) {
+        Log.d(TAG, "BarcodeDetectionImpl ctor()");
+        mContext = context;
+        mBarcodeDetector = new BarcodeDetector.Builder(mContext).build();
+    }
+
+    @Override
+    public void detect(
+            SharedBufferHandle frameData, int width, int height, DetectResponse callback) {
+        if (!ExternalAuthUtils.getInstance().canUseGooglePlayServices(
+                    mContext, new UserRecoverableErrorHandler.Silent())) {
+            Log.e(TAG, "Google Play Services not available");
+            callback.call(new BarcodeDetectionResult[0]);
+            return;
+        }
+        // The vision library will be downloaded the first time the API is used
+        // on the device; this happens "fast", but it might have not completed,
+        // bail in this case. Also, the API was disabled between and v.9.0 and
+        // v.9.2, see https://developers.google.com/android/guides/releases.
+        if (!mBarcodeDetector.isOperational()) {
+            Log.e(TAG, "BarcodeDetector is not operational");
+            callback.call(new BarcodeDetectionResult[0]);
+            return;
+        }
+
+        final long numPixels = (long) width * height;
+        // TODO(mcasas): https://crbug.com/670028 homogeneize overflow checking.
+        if (!frameData.isValid() || width <= 0 || height <= 0 || numPixels > (Long.MAX_VALUE / 4)) {
+            callback.call(new BarcodeDetectionResult[0]);
+            return;
+        }
+
+        // Mapping |frameData| will fail if the intended mapped size is larger
+        // than its actual capacity, which is limited by the appropriate
+        // mojo::edk::Configuration entry.
+        ByteBuffer imageBuffer = frameData.map(0, numPixels * 4, MapFlags.none());
+        if (imageBuffer.capacity() <= 0) {
+            callback.call(new BarcodeDetectionResult[0]);
+            return;
+        }
+
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        bitmap.copyPixelsFromBuffer(imageBuffer);
+
+        Frame frame = null;
+        try {
+            // This constructor implies a pixel format conversion to YUV.
+            frame = new Frame.Builder().setBitmap(bitmap).build();
+        } catch (IllegalArgumentException | IllegalStateException ex) {
+            Log.e(TAG, "Frame.Builder().setBitmap() or build(): " + ex);
+            callback.call(new BarcodeDetectionResult[0]);
+            return;
+        }
+
+        final SparseArray<Barcode> barcodes = mBarcodeDetector.detect(frame);
+
+        BarcodeDetectionResult[] barcodeArray = new BarcodeDetectionResult[barcodes.size()];
+        for (int i = 0; i < barcodes.size(); i++) {
+            barcodeArray[i] = new BarcodeDetectionResult();
+            final Barcode barcode = barcodes.valueAt(i);
+            barcodeArray[i].rawValue = barcode.rawValue;
+            final Rect rect = barcode.getBoundingBox();
+            barcodeArray[i].boundingBox = new RectF();
+            barcodeArray[i].boundingBox.x = rect.left;
+            barcodeArray[i].boundingBox.y = rect.top;
+            barcodeArray[i].boundingBox.width = rect.width();
+            barcodeArray[i].boundingBox.height = rect.height();
+        }
+        callback.call(barcodeArray);
+    }
+
+    @Override
+    public void close() {
+        mBarcodeDetector.release();
+    }
+
+    @Override
+    public void onConnectionError(MojoException e) {
+        close();
+    }
+
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTabViewFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTabViewFactory.java
index 704b8e18..71204a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTabViewFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTabViewFactory.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.tab;
 
 import android.content.Context;
+import android.support.annotation.StringRes;
 import android.text.method.LinkMovementMethod;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -21,16 +22,18 @@
  * A factory class for creating the "Sad Tab" view, which is shown in place of a crashed renderer.
  */
 public class SadTabViewFactory {
+
     /**
      * @param context Context of the resulting Sad Tab view.
      * @param suggestionAction Action to be executed when user clicks "try these suggestions".
      * @param reloadButtonAction Action to be executed when Reload button is pressed.
      *                           (e.g., refreshing the page)
+     * @param buttonTextId The string resource for the button text label.
      * @return A "Sad Tab" view instance which is used in place of a crashed renderer.
      */
     public static View createSadTabView(
             Context context, final OnClickListener suggestionAction,
-            OnClickListener reloadButtonAction) {
+            OnClickListener reloadButtonAction, @StringRes int buttonTextId) {
         // Inflate Sad tab and initialize.
         LayoutInflater inflater = (LayoutInflater) context.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
@@ -41,6 +44,7 @@
         messageText.setMovementMethod(LinkMovementMethod.getInstance());
 
         Button reloadButton = (Button) sadTabView.findViewById(R.id.sad_tab_reload_button);
+        reloadButton.setText(buttonTextId);
         reloadButton.setOnClickListener(reloadButtonAction);
 
         return sadTabView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 466a003..d1d9bcc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -365,6 +365,12 @@
      */
     private View mSadTabView;
 
+    /**
+     * Counts the number of successive refreshes on the sad tab page. The count is is reset after a
+     * successful page load.
+     */
+    private int mSadTabSuccessiveRefreshCounter;
+
     private final int mDefaultThemeColor;
     private int mThemeColor;
 
@@ -1669,6 +1675,9 @@
                     "Navigation.IsMobileOptimized", mContentViewCore.getIsMobileOptimizedHint());
         }
 
+        // Reset the succressiveRefresh counter after successfully loading a page.
+        mSadTabSuccessiveRefreshCounter = 0;
+
         if (mTabUma != null) mTabUma.onPageLoadFinished();
 
         for (TabObserver observer : mObservers) observer.onPageLoadFinished(this);
@@ -1876,18 +1885,29 @@
                             Profile.getLastUsedProfile(), null);
                 }
             };
-            OnClickListener reloadButtonAction = new OnClickListener() {
+
+            // If the tab has crashed twice in a row change the button to "Send Feedback" and
+            // change the onClickListener.
+            final boolean showSendFeedbackButton = mSadTabSuccessiveRefreshCounter >= 1;
+            OnClickListener buttonAction = new OnClickListener() {
+
                 @Override
-                public void onClick(View view) {
-                    reload();
+                public void onClick(View v) {
+                    if (showSendFeedbackButton) {
+                        getActivity().startHelpAndFeedback(Tab.this, "MobileSadTabFeedback");
+                    } else {
+                        reload();
+                    }
                 }
             };
 
             // Make sure we are not adding the "Aw, snap" view over an existing one.
             assert mSadTabView == null;
-            mSadTabView = SadTabViewFactory.createSadTabView(
-                    mThemedApplicationContext, suggestionAction, reloadButtonAction);
 
+            mSadTabView = SadTabViewFactory.createSadTabView(mThemedApplicationContext,
+                    suggestionAction, buttonAction, showSendFeedbackButton
+                            ? R.string.sad_tab_send_feedback_label : R.string.sad_tab_reload_label);
+            mSadTabSuccessiveRefreshCounter++;
             // Show the sad tab inside ContentView.
             getContentViewCore().getContainerView().addView(
                     mSadTabView, new FrameLayout.LayoutParams(
@@ -1912,6 +1932,16 @@
     }
 
     /**
+     * Removes any existing sad tab view and shows it again. This "reloads" the tab without
+     * going through any formal loading logic.
+     */
+    @VisibleForTesting
+    public void reloadSadTabForTesting() {
+        removeSadTabIfPresent();
+        showSadTab();
+    }
+
+    /**
      * @return Whether or not the sad tab is showing.
      */
     public boolean isShowingSadTab() {
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 1e8dc4d..29e6868 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -863,6 +863,8 @@
   "java/src/org/chromium/chrome/browser/services/gcm/GcmUma.java",
   "java/src/org/chromium/chrome/browser/services/gcm/InvalidationGcmUpstreamSender.java",
   "java/src/org/chromium/chrome/browser/sessions/SessionTabHelper.java",
+  "java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionFactory.java",
+  "java/src/org/chromium/chrome/browser/shapedetection/BarcodeDetectionImpl.java",
   "java/src/org/chromium/chrome/browser/share/ShareDialogAdapter.java",
   "java/src/org/chromium/chrome/browser/share/ShareHelper.java",
   "java/src/org/chromium/chrome/browser/signin/AccountAdder.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
index 3ef355a..e4b69bd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
@@ -290,9 +290,10 @@
                 mAdapter.onDownloadItemCreated(item8);
             }
         });
-        mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount, 2);
 
-        // This first check is a Criteria because initialization of the Adapter is asynchronous.
+        // The criteria is needed because an AsyncTask is fired to update the space display, which
+        // can result in either 1 or 2 updates.
+        mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
         CriteriaHelper.pollUiThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -367,9 +368,10 @@
                 mAdapter.onDownloadItemCreated(item8);
             }
         });
-        mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount, 2);
 
-        // This first check is a Criteria because initialization of the Adapter is asynchronous.
+        // The criteria is needed because an AsyncTask is fired to update the space display, which
+        // can result in either 1 or 2 updates.
+        mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
         CriteriaHelper.pollUiThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
index 98802ab6..37b4647 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
@@ -5,10 +5,12 @@
 package org.chromium.chrome.browser.tab;
 
 import android.test.suitebuilder.annotation.SmallTest;
+import android.widget.Button;
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.test.ChromeActivityTestCaseBase;
 
@@ -36,14 +38,7 @@
         final Tab tab = getActivity().getActivityTab();
 
         assertFalse(tab.isShowingSadTab());
-
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                tab.simulateRendererKilledForTesting(true);
-            }
-        });
-
+        simulateRendererKilled(tab, true);
         assertTrue(tab.isShowingSadTab());
     }
 
@@ -57,14 +52,91 @@
         final Tab tab = getActivity().getActivityTab();
 
         assertFalse(tab.isShowingSadTab());
+        simulateRendererKilled(tab, false);
+        assertFalse(tab.isShowingSadTab());
+    }
 
+    /**
+     * Confirm that after a successive refresh of a failed tab that failed to load, change the
+     * button from "Reload" to "Send Feedback".
+     */
+    @SmallTest
+    @Feature({"SadTab"})
+    public void testChangeSadButtonToFeedbackAfterFailedRefresh() {
+        final Tab tab = getActivity().getActivityTab();
+
+        assertFalse(tab.isShowingSadTab());
+        simulateRendererKilled(tab, true);
+        assertTrue(tab.isShowingSadTab());
+        String actualText = getSadTabButton(tab).getText().toString();
+        assertEquals("Expected the sad tab button to have the reload label",
+                getActivity().getString(R.string.sad_tab_reload_label), actualText);
+
+        reloadSadTab(tab);
+        assertTrue(tab.isShowingSadTab());
+        actualText = getSadTabButton(tab).getText().toString();
+        assertEquals(
+                "Expected the sad tab button to have the feedback label after the tab button "
+                + "crashes twice in a row.",
+                getActivity().getString(R.string.sad_tab_send_feedback_label), actualText);
+    }
+
+    /**
+     * Confirm after two failures, if we refresh a third time and it's successful, and then we
+     * crash again, we do not show the "Send Feedback" button and instead show the "Reload" tab.
+     */
+    @SmallTest
+    @Feature({"SadTab"})
+    public void testSadButtonRevertsBackToReloadAfterSuccessfulLoad() {
+        final Tab tab = getActivity().getActivityTab();
+
+        simulateRendererKilled(tab, true);
+        reloadSadTab(tab);
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                tab.simulateRendererKilledForTesting(false);
+                tab.reload(); // Erases the sad tab page
+                tab.didFinishPageLoad(); // Resets the tab counter to 0
             }
         });
-
-        assertFalse(tab.isShowingSadTab());
+        simulateRendererKilled(tab, true);
+        String actualText = getSadTabButton(tab).getText().toString();
+        assertEquals(getActivity().getString(R.string.sad_tab_reload_label), actualText);
     }
+
+    /**
+     * Helper method that kills the renderer on a UI thread.
+     */
+    private void simulateRendererKilled(final Tab tab, final boolean wasOomProtected) {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                tab.simulateRendererKilledForTesting(wasOomProtected);
+            }
+        });
+    }
+
+    /**
+     * Helper method that reloads a tab with a SadTabView currently displayed.
+     */
+    private void reloadSadTab(final Tab tab) {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                tab.reloadSadTabForTesting();
+            }
+        });
+    }
+
+    /**
+     * If there is a SadTabView, this method will get the button for the sad tab.
+     * @param tab The tab that needs to contain a SadTabView.
+     * @return Returns the button that is on the SadTabView, null if SadTabView.
+     *         doesn't exist.
+     */
+    private Button getSadTabButton(Tab tab) {
+        return (Button) tab.getContentViewCore().getContainerView()
+                .findViewById(R.id.sad_tab_reload_button);
+    }
+
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4779a4b..6aae3e3d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2172,6 +2172,9 @@
       "download/notification/download_notification.h",
       "download/notification/download_notification_manager.cc",
       "download/notification/download_notification_manager.h",
+      "memory/memory_kills_histogram.h",
+      "memory/memory_kills_monitor.cc",
+      "memory/memory_kills_monitor.h",
       "metrics/chromeos_metrics_provider.cc",
       "metrics/chromeos_metrics_provider.h",
       "metrics/leak_detector/leak_detector_controller.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index d3568a1..6b96566 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -107,6 +107,7 @@
   "+third_party/WebKit/public/platform/modules/remoteplayback/WebRemotePlaybackAvailability.h",
   "+third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationLockType.h",
   "+third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h",
+  "+third_party/WebKit/public/platform/modules/shapedetection/barcodedetection.mojom.h",
   "+third_party/WebKit/public/platform/modules/webshare/webshare.mojom.h",
   "+third_party/WebKit/public/public_features.h",
   "+third_party/WebKit/public/web/WebCache.h",
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1c7ce3b..2cab771 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -193,6 +193,7 @@
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "storage/browser/fileapi/external_mount_points.h"
+#include "third_party/WebKit/public/platform/modules/shapedetection/barcodedetection.mojom.h"
 #include "third_party/WebKit/public/platform/modules/webshare/webshare.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -1440,7 +1441,8 @@
   return true;
 }
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN) || \
+    defined(OS_ANDROID)
 bool AreExperimentalWebPlatformFeaturesEnabled() {
   const base::CommandLine& browser_command_line =
       *base::CommandLine::ForCurrentProcess();
@@ -3027,6 +3029,19 @@
     }
   }
 #endif
+
+#if defined(OS_ANDROID)
+  if (AreExperimentalWebPlatformFeaturesEnabled()) {
+    content::WebContents* web_contents =
+        content::WebContents::FromRenderFrameHost(render_frame_host);
+    if (web_contents) {
+      registry->AddInterface(
+          web_contents->GetJavaInterfaces()
+              ->CreateInterfaceFactory<blink::mojom::BarcodeDetection>());
+    }
+  }
+#endif
+
 }
 
 void ChromeContentBrowserClient::ExposeInterfacesToGpuProcess(
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index eef80fdb..7bce362b 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -44,6 +44,7 @@
         "renderer": [
           "autofill::mojom::AutofillDriver",
           "autofill::mojom::PasswordManagerDriver",
+          "blink::mojom::BarcodeDetection",
           "blink::mojom::ShareService",
           "bluetooth::mojom::AdapterFactory",
           "device::usb::ChooserService",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index fc3ef093..19f18b12 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1389,6 +1389,7 @@
     "../extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc",
     "../extensions/api/log_private/syslog_parser_unittest.cc",
     "../extensions/updater/local_extension_cache_unittest.cc",
+    "../memory/memory_kills_monitor_unittest.cc",
     "../metrics/chromeos_metrics_provider_unittest.cc",
     "../metrics/leak_detector/leak_detector_controller_unittest.cc",
     "../metrics/perf/cpu_identity_unittest.cc",
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
index 7ff06f5..7b28aa97 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
@@ -31,7 +31,8 @@
 
 void ArcKioskAppLauncher::OnTaskCreated(int32_t task_id,
                                         const std::string& package_name,
-                                        const std::string& activity) {
+                                        const std::string& activity,
+                                        const std::string& intent) {
   std::unique_ptr<ArcAppListPrefs::AppInfo> app = prefs_->GetApp(app_id_);
   if (!app || app->package_name != package_name || app->activity != activity)
     return;
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.h b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.h
index 5ea5feb..f854a70 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.h
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.h
@@ -28,7 +28,8 @@
   // ArcAppListPrefs::Observer overrides.
   void OnTaskCreated(int32_t task_id,
                      const std::string& package_name,
-                     const std::string& activity) override;
+                     const std::string& activity,
+                     const std::string& intent) override;
 
   // aura::EnvObserver overrides.
   void OnWindowInitialized(aura::Window* window) override;
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc
index b943504..59611d3a 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc
@@ -50,7 +50,8 @@
 
 void ArcKioskAppService::OnTaskCreated(int32_t task_id,
                                        const std::string& package_name,
-                                       const std::string& activity) {
+                                       const std::string& activity,
+                                       const std::string& intent) {
   // Store task id of the app to stop it later when needed.
   if (app_info_ && package_name == app_info_->package_name &&
       activity == app_info_->activity) {
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h
index 11ea62d9..79ed66a 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h
@@ -35,7 +35,8 @@
   void OnAppReadyChanged(const std::string& id, bool ready) override;
   void OnTaskCreated(int32_t task_id,
                      const std::string& package_name,
-                     const std::string& activity) override;
+                     const std::string& activity,
+                     const std::string& intent) override;
   void OnTaskDestroyed(int32_t task_id) override;
   void OnPackageListInitialRefreshed() override;
 
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 8cd1f92..b9b4a980 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -354,6 +354,10 @@
     chrome::SetChannel(channel);
 #endif
 
+  // Start monitoring OOM kills.
+  memory_kills_monitor_ = base::MakeUnique<memory::MemoryKillsMonitor::Handle>(
+      memory::MemoryKillsMonitor::StartMonitoring());
+
   ChromeBrowserMainPartsLinux::PreEarlyInitialization();
 }
 
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index fb33160..cea135d 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -11,6 +11,7 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "chrome/browser/chrome_browser_main_linux.h"
 #include "chrome/browser/chromeos/external_metrics.h"
+#include "chrome/browser/memory/memory_kills_monitor.h"
 #include "chromeos/system/version_loader.h"
 
 namespace session_manager {
@@ -95,6 +96,8 @@
   std::unique_ptr<LowDiskNotification> low_disk_notification_;
   std::unique_ptr<ArcKioskAppManager> arc_kiosk_app_manager_;
 
+  std::unique_ptr<memory::MemoryKillsMonitor::Handle> memory_kills_monitor_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainPartsChromeos);
 };
 
diff --git a/chrome/browser/chromeos/login/users/affiliation.cc b/chrome/browser/chromeos/login/users/affiliation.cc
index 4330afc..6d9b38d 100644
--- a/chrome/browser/chromeos/login/users/affiliation.cc
+++ b/chrome/browser/chromeos/login/users/affiliation.cc
@@ -4,7 +4,9 @@
 
 #include "chrome/browser/chromeos/login/users/affiliation.h"
 
+#include "base/command_line.h"
 #include "chrome/browser/chromeos/policy/device_local_account.h"
+#include "components/policy/core/common/policy_switches.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
 namespace chromeos {
@@ -40,6 +42,14 @@
     return true;
   }
 
+  // Not all test servers correctly support affiliation ids so far, so
+  // this is a work-around.
+  // TODO(antrim): remove this once all test servers support affiliation ids.
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(policy::switches::kUserAlwaysAffiliated)) {
+    return true;
+  }
+
   if (!device_affiliation_ids.empty() && !user_affiliation_ids.empty()) {
     return HaveCommonElement(user_affiliation_ids, device_affiliation_ids);
   }
diff --git a/chrome/browser/chromeos/note_taking_app_utils.cc b/chrome/browser/chromeos/note_taking_app_utils.cc
index d68cd41..7f12a8d 100644
--- a/chrome/browser/chromeos/note_taking_app_utils.cc
+++ b/chrome/browser/chromeos/note_taking_app_utils.cc
@@ -15,7 +15,6 @@
 #include "base/strings/string_split.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/chromeos_switches.h"
-#include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/api/app_runtime.h"
 #include "extensions/common/extension.h"
@@ -87,11 +86,4 @@
   apps::LaunchPlatformAppWithAction(profile, app, std::move(action_data), path);
 }
 
-bool IsNoteTakingAppWindow(extensions::AppWindow* app_window,
-                           Profile* profile) {
-  DCHECK(app_window && profile);
-  const extensions::Extension* app = GetApp(profile);
-  return app && (app->id() == app_window->extension_id());
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/note_taking_app_utils.h b/chrome/browser/chromeos/note_taking_app_utils.h
index 8968dd8..1f32610 100644
--- a/chrome/browser/chromeos/note_taking_app_utils.h
+++ b/chrome/browser/chromeos/note_taking_app_utils.h
@@ -11,10 +11,6 @@
 class FilePath;
 }  // namespace base
 
-namespace extensions {
-class AppWindow;
-}  // namespace extensions
-
 namespace chromeos {
 
 // Returns true if an app that can be used to take notes is available.
@@ -26,9 +22,6 @@
 void LaunchNoteTakingAppForNewNote(Profile* profile,
                                    const base::FilePath& path);
 
-// Returns true if |app_window| belongs to the default note-taking app.
-bool IsNoteTakingAppWindow(extensions::AppWindow* app_window, Profile* profile);
-
 }  // namespace chromeos
 
 #endif  // CHROME_BROWSER_CHROMEOS_NOTE_TAKING_APP_UTILS_H_
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
index 366e5d4..cd1785d 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
@@ -334,8 +334,10 @@
       // Add a new recorder to the render frame map to replace the deleted one.
       DataUseRecorderEntry entry = data_use_recorders_.emplace(
           data_use_recorders_.end());
-      render_frame_data_use_map_.insert(std::make_pair(
-          RenderFrameHostID(render_process_id, render_frame_id), entry));
+      std::pair<int, int> frame_key =
+          RenderFrameHostID(render_process_id, render_frame_id);
+      entry->set_main_frame_id(frame_key);
+      render_frame_data_use_map_.insert(std::make_pair(frame_key, entry));
     }
     return;
   }
diff --git a/chrome/browser/memory/memory_kills_histogram.h b/chrome/browser/memory/memory_kills_histogram.h
new file mode 100644
index 0000000..67f1641
--- /dev/null
+++ b/chrome/browser/memory/memory_kills_histogram.h
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEMORY_MEMORY_KILLS_HISTOGRAM_H_
+#define CHROME_BROWSER_MEMORY_MEMORY_KILLS_HISTOGRAM_H_
+
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+
+namespace memory {
+
+constexpr base::TimeDelta kMaxMemoryKillTimeDelta =
+        base::TimeDelta::FromSeconds(30);
+
+}  // namespace memory
+
+// Use this macro to report elapsed time since last Memory kill event.
+// Must be a macro as the underlying HISTOGRAM macro creates static variables.
+#define UMA_HISTOGRAM_MEMORY_KILL_TIME_INTERVAL(name, sample)  \
+  UMA_HISTOGRAM_CUSTOM_TIMES(                               \
+      name, sample, base::TimeDelta::FromMilliseconds(1),   \
+      ::memory::kMaxMemoryKillTimeDelta, 50)
+
+#endif  // CHROME_BROWSER_MEMORY_MEMORY_KILLS_HISTOGRAM_H_
diff --git a/chrome/browser/memory/memory_kills_monitor.cc b/chrome/browser/memory/memory_kills_monitor.cc
new file mode 100644
index 0000000..9e6da116
--- /dev/null
+++ b/chrome/browser/memory/memory_kills_monitor.cc
@@ -0,0 +1,218 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/memory/memory_kills_monitor.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/leak_annotations.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/posix/safe_strerror.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/time/time.h"
+#include "chrome/browser/memory/memory_kills_histogram.h"
+#include "third_party/re2/src/re2/re2.h"
+
+namespace memory {
+
+using base::SequencedWorkerPool;
+using base::TimeDelta;
+
+namespace {
+
+int64_t GetTimestamp(const std::string& line) {
+  std::vector<std::string> fields = base::SplitString(
+      line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+  int64_t timestamp = -1;
+  // Timestamp is the third field in a line of /dev/kmsg.
+  if (fields.size() < 3 || !base::StringToInt64(fields[2], &timestamp))
+    return -1;
+  return timestamp;
+}
+
+void LogEvent(const base::Time& time_stamp, const std::string& event) {
+  VLOG(1) << time_stamp.ToJavaTime() << ", " << event;
+}
+
+void LogOOMKill(int64_t time_stamp, int oom_badness) {
+  static int64_t last_kill_time = -1;
+  static int oom_kills = 0;
+
+  // Ideally the timestamp should be parsed from /dev/kmsg, but the timestamp
+  // there is the elapsed time since system boot. So the timestamp |now| used
+  // here is a bit delayed.
+  base::Time now = base::Time::Now();
+  LogEvent(now, "OOM_KILL");
+
+  ++oom_kills;
+  // Report the cumulative count of killed process in one login session.
+  // For example if there are 3 processes killed, it would report 1 for the
+  // first kill, 2 for the second kill, then 3 for the final kill.
+  // It doesn't report a final count at the end of a user session because
+  // the code runs in a dedicated thread and never ends until browser shutdown
+  // (or logout on Chrome OS). And on browser shutdown the thread may be
+  // terminated brutally so there's no chance to execute a "final" block.
+  // More specifically, code outside the main loop of MemoryKillsMonitor::Run()
+  // are not guaranteed to be executed.
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Arc.OOMKills.Count", oom_kills, 1, 1000, 1001);
+
+  // In practice most process has oom_badness < 1000, but
+  // strictly speaking the number could be [1, 2000]. What it really
+  // means is the baseline, proportion of memory used (normalized to
+  // [0, 1000]), plus an adjustment score oom_score_adj [-1000, 1000],
+  // truncated to 1 if negative (0 means never kill).
+  // Ref: https://lwn.net/Articles/396552/
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Arc.OOMKills.Score", oom_badness, 1, 2000, 2001);
+
+  if (time_stamp > 0) {
+    // Sets to |kMaxMemoryKillTimeDelta| for the first kill event.
+    const TimeDelta time_delta =
+        last_kill_time < 0 ? kMaxMemoryKillTimeDelta:
+        TimeDelta::FromMicroseconds(time_stamp - last_kill_time);
+
+    last_kill_time = time_stamp;
+
+    UMA_HISTOGRAM_MEMORY_KILL_TIME_INTERVAL(
+        "Arc.OOMKills.TimeDelta", time_delta);
+  }
+}
+
+}  // namespace
+
+MemoryKillsMonitor::Handle::Handle(MemoryKillsMonitor* outer) : outer_(outer) {
+  DCHECK(outer_);
+}
+
+MemoryKillsMonitor::Handle::Handle(MemoryKillsMonitor::Handle&& other)
+    : outer_(nullptr) {
+  outer_ = other.outer_;
+  other.outer_ = nullptr;
+}
+
+MemoryKillsMonitor::Handle::~Handle() {
+  if (outer_) {
+    VLOG(2) << "Chrome is shutting down" << outer_;
+    outer_->is_shutting_down_.Set();
+  }
+}
+
+MemoryKillsMonitor::MemoryKillsMonitor() {
+  base::SimpleThread::Options non_joinable_options;
+  non_joinable_options.joinable = false;
+  non_joinable_worker_thread_ = base::MakeUnique<base::DelegateSimpleThread>(
+      this, "memory_kills_monitor", non_joinable_options);
+  non_joinable_worker_thread_->Start();
+}
+
+MemoryKillsMonitor::~MemoryKillsMonitor() {
+  // The instance has to be leaked on shutdown as it is referred to by a
+  // non-joinable thread but ~MemoryKillsMonitor() can't be explicitly deleted
+  // as it overrides ~SimpleThread(), it should nevertheless never be invoked.
+  NOTREACHED();
+}
+
+// static
+MemoryKillsMonitor::Handle MemoryKillsMonitor::StartMonitoring() {
+#if DCHECK_IS_ON()
+  static volatile bool monitoring_active = false;
+  DCHECK(!monitoring_active);
+  monitoring_active = true;
+#endif
+
+  // Instantiate the MemoryKillsMonitor and its underlying thread. The
+  // MemoryKillsMonitor itself has to be leaked on shutdown per having a
+  // non-joinable thread associated to its state. The MemoryKillsMonitor::Handle
+  // will notify the MemoryKillsMonitor when it is destroyed so that the
+  // underlying thread can at a minimum not do extra work during shutdown.
+  MemoryKillsMonitor* instance = new MemoryKillsMonitor();
+  ANNOTATE_LEAKING_OBJECT_PTR(instance);
+  return Handle(instance);
+}
+
+// static
+void MemoryKillsMonitor::LogLowMemoryKill(
+    const std::string& type, int estimated_freed_kb) {
+  static base::Time last_kill_time;
+  static int low_memory_kills = 0;
+
+  base::Time now = base::Time::Now();
+  LogEvent(now, "LOW_MEMORY_KILL_" + type);
+
+  const TimeDelta time_delta =
+      last_kill_time.is_null() ?
+      kMaxMemoryKillTimeDelta :
+      (now - last_kill_time);
+  UMA_HISTOGRAM_MEMORY_KILL_TIME_INTERVAL(
+            "Arc.LowMemoryKiller.TimeDelta", time_delta);
+  last_kill_time = now;
+
+  ++low_memory_kills;
+  UMA_HISTOGRAM_CUSTOM_COUNTS(
+      "Arc.LowMemoryKiller.Count", low_memory_kills, 1, 1000, 1001);
+
+  UMA_HISTOGRAM_MEMORY_KB("Arc.LowMemoryKiller.FreedSize",
+                          estimated_freed_kb);
+}
+
+// static
+void MemoryKillsMonitor::TryMatchOomKillLine(const std::string& line) {
+  // Sample OOM log line:
+  // 3,1362,97646497541,-;Out of memory: Kill process 29582 (android.vending)
+  // score 961 or sacrifice child.
+  int oom_badness;
+  TimeDelta time_delta;
+  if (RE2::PartialMatch(line,
+                        "Out of memory: Kill process .* score (\\d+)",
+                        &oom_badness)) {
+    int64_t time_stamp = GetTimestamp(line);
+    LogOOMKill(time_stamp, oom_badness);
+  }
+}
+
+void MemoryKillsMonitor::Run() {
+  VLOG(1) << "MemoryKillsMonitor started";
+  base::ScopedFILE kmsg_handle(
+      base::OpenFile(base::FilePath("/dev/kmsg"), "r"));
+  if (!kmsg_handle) {
+    LOG(WARNING) << "Open /dev/kmsg failed: " << base::safe_strerror(errno);
+    return;
+  }
+  // Skip kernel messages prior to the instantiation of this object to avoid
+  // double reporting.
+  fseek(kmsg_handle.get(), 0, SEEK_END);
+
+  static constexpr int kMaxBufSize = 512;
+  char buf[kMaxBufSize];
+
+  while (fgets(buf, kMaxBufSize, kmsg_handle.get())) {
+    if (is_shutting_down_.IsSet()) {
+      // Not guaranteed to execute when the process is shutting down,
+      // because the thread might be blocked in fgets().
+      VLOG(1) << "Chrome is shutting down, MemoryKillsMonitor exits.";
+      break;
+    }
+    TryMatchOomKillLine(buf);
+  }
+}
+
+
+}  // namespace memory
diff --git a/chrome/browser/memory/memory_kills_monitor.h b/chrome/browser/memory/memory_kills_monitor.h
new file mode 100644
index 0000000..8594f53
--- /dev/null
+++ b/chrome/browser/memory/memory_kills_monitor.h
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEMORY_MEMORY_KILLS_MONITOR_H_
+#define CHROME_BROWSER_MEMORY_MEMORY_KILLS_MONITOR_H_
+
+#include <memory>
+#include <string>
+
+#include "base/files/scoped_file.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+
+namespace memory {
+
+// Traces kernel OOM kill events and Low memory kill events (by Chrome
+// TabManager).
+//
+// For OOM kill events, it listens to kernel message (/dev/kmsg) in a blocking
+// manner. It runs in a non-joinable thread in order to avoid blocking shutdown.
+// There should be only one MemoryKillsMonitor instance globally at any given
+// time, otherwise UMA would receive duplicate events.
+//
+// For Low memory kills events, chrome calls the single global instance of
+// MemoryKillsMonitor synchronously. Note that it would be from a browser thread
+// other than the listening thread.
+//
+// For every events, it reports to UMA and optionally a local file specified by
+// --memory-kills-log. The log file is useful if we want to analyze low memory
+// kills. If the flag is not given, it won't write to any file.
+class MemoryKillsMonitor : public base::DelegateSimpleThread::Delegate {
+ public:
+  // A handle representing the MemoryKillsMonitor's lifetime (the monitor itself
+  // can't be destroyed per being a non-joinable Thread).
+  class Handle {
+   public:
+    // Constructs a handle that will flag |outer| as shutting down on
+    // destruction.
+    explicit Handle(MemoryKillsMonitor* outer);
+
+    Handle(Handle&& handle);
+
+    ~Handle();
+
+   private:
+    MemoryKillsMonitor* outer_;
+    DISALLOW_COPY_AND_ASSIGN(Handle);
+  };
+
+  // Instantiates the MemoryKillsMonitor instance and starts it. This must only
+  // be invoked once per process.
+  static Handle StartMonitoring();
+
+  // Logs a low memory kill event.
+  static void LogLowMemoryKill(const std::string& type, int estimated_freed_kb);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(MemoryKillsMonitorTest, TryMatchOomKillLine);
+
+  MemoryKillsMonitor();
+  ~MemoryKillsMonitor() override;
+
+  // Overridden from base::DelegateSimpleThread::Delegate:
+  void Run() override;
+
+  // Try to match a line in kernel message which reports OOM.
+  static void TryMatchOomKillLine(const std::string& line);
+
+  // A flag set when MemoryKillsMonitor is shutdown so that its thread can poll
+  // it and attempt to wind down from that point (to avoid unnecessary work, not
+  // because it blocks shutdown).
+  base::AtomicFlag is_shutting_down_;
+
+  // The underlying worker thread which is non-joinable to avoid blocking
+  // shutdown.
+  std::unique_ptr<base::DelegateSimpleThread> non_joinable_worker_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryKillsMonitor);
+};
+
+}  // namespace memory
+
+#endif  // CHROME_BROWSER_MEMORY_MEMORY_KILLS_MONITOR_H_
diff --git a/chrome/browser/memory/memory_kills_monitor_unittest.cc b/chrome/browser/memory/memory_kills_monitor_unittest.cc
new file mode 100644
index 0000000..339030e
--- /dev/null
+++ b/chrome/browser/memory/memory_kills_monitor_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/memory/memory_kills_monitor.h"
+
+#include "base/macros.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/time/time.h"
+#include "chrome/browser/memory/memory_kills_histogram.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace memory {
+
+using MemoryKillsMonitorTest = testing::Test;
+
+TEST_F(MemoryKillsMonitorTest, LogLowMemoryKill) {
+  MemoryKillsMonitor::LogLowMemoryKill("APP", 123);
+  MemoryKillsMonitor::LogLowMemoryKill("APP", 100);
+  MemoryKillsMonitor::LogLowMemoryKill("TAB", 10000);
+
+  auto histogram_count =
+      base::StatisticsRecorder::FindHistogram("Arc.LowMemoryKiller.Count");
+  ASSERT_TRUE(histogram_count);
+  auto count_samples = histogram_count->SnapshotSamples();
+  EXPECT_EQ(3, count_samples->TotalCount());
+  EXPECT_EQ(1, count_samples->GetCount(1));
+  EXPECT_EQ(1, count_samples->GetCount(2));
+  EXPECT_EQ(1, count_samples->GetCount(3));
+
+  auto histogram_freed_size =
+      base::StatisticsRecorder::FindHistogram("Arc.LowMemoryKiller.FreedSize");
+  ASSERT_TRUE(histogram_freed_size);
+  auto freed_size_samples = histogram_freed_size->SnapshotSamples();
+  EXPECT_EQ(3, freed_size_samples->TotalCount());
+  // 123 and 100 are in the same bucket.
+  EXPECT_EQ(2, freed_size_samples->GetCount(123));
+  EXPECT_EQ(2, freed_size_samples->GetCount(100));
+  EXPECT_EQ(1, freed_size_samples->GetCount(10000));
+
+  auto histogram_time_delta =
+      base::StatisticsRecorder::FindHistogram("Arc.LowMemoryKiller.TimeDelta");
+  ASSERT_TRUE(histogram_time_delta);
+  auto time_delta_samples = histogram_time_delta->SnapshotSamples();
+  EXPECT_EQ(3, time_delta_samples->TotalCount());
+  // First time delta is set to kMaxMemoryKillTimeDelta.
+  EXPECT_EQ(1, time_delta_samples->GetCount(
+      kMaxMemoryKillTimeDelta.InMilliseconds()));
+  // Time delta for the other 2 events depends on Now() so we skip testing it
+  // here.
+}
+
+TEST_F(MemoryKillsMonitorTest, TryMatchOomKillLine) {
+  const char* sample_lines[] = {
+      "3,3429,812967386,-;Out of memory: Kill process 8291 (handle-watcher-) "
+      "score 674 or sacrifice child",
+      "3,3431,812981331,-;Out of memory: Kill process 8271 (.gms.persistent) "
+      "score 652 or sacrifice child",
+      "3,3433,812993014,-;Out of memory: Kill process 9210 (lowpool[11]) "
+      "score 653 or sacrifice child"
+  };
+
+  for (unsigned long i = 0; i < arraysize(sample_lines); ++i) {
+    MemoryKillsMonitor::TryMatchOomKillLine(sample_lines[i]);
+  }
+
+  auto histogram_count =
+      base::StatisticsRecorder::FindHistogram("Arc.OOMKills.Count");
+  ASSERT_TRUE(histogram_count);
+  auto count_samples = histogram_count->SnapshotSamples();
+  EXPECT_EQ(3, count_samples->TotalCount());
+  EXPECT_EQ(1, count_samples->GetCount(1));
+  EXPECT_EQ(1, count_samples->GetCount(2));
+  EXPECT_EQ(1, count_samples->GetCount(3));
+
+  auto histogram_score =
+      base::StatisticsRecorder::FindHistogram("Arc.OOMKills.Score");
+  ASSERT_TRUE(histogram_score);
+  auto score_samples = histogram_score->SnapshotSamples();
+  EXPECT_EQ(3, score_samples->TotalCount());
+  EXPECT_EQ(1, score_samples->GetCount(674));
+  EXPECT_EQ(1, score_samples->GetCount(652));
+  EXPECT_EQ(1, score_samples->GetCount(653));
+
+  auto histogram_time_delta =
+      base::StatisticsRecorder::FindHistogram("Arc.OOMKills.TimeDelta");
+  ASSERT_TRUE(histogram_time_delta);
+  auto time_delta_samples = histogram_time_delta->SnapshotSamples();
+  EXPECT_EQ(3, time_delta_samples->TotalCount());
+  // First time delta is set to kMaxMemoryKillTimeDelta.
+  EXPECT_EQ(1, time_delta_samples->GetCount(
+      kMaxMemoryKillTimeDelta.InMilliseconds()));
+  EXPECT_EQ(1, time_delta_samples->GetCount(11));
+  EXPECT_EQ(1, time_delta_samples->GetCount(13));
+}
+
+}  // namespace memory
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos.cc b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
index 75100d0..b9f1d3e 100644
--- a/chrome/browser/memory/tab_manager_delegate_chromeos.cc
+++ b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
@@ -27,6 +27,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/arc/process/arc_process.h"
 #include "chrome/browser/chromeos/arc/process/arc_process_service.h"
+#include "chrome/browser/memory/memory_kills_monitor.h"
 #include "chrome/browser/memory/tab_stats.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -36,7 +37,6 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/common/process.mojom.h"
-#include "components/arc/metrics/oom_kills_histogram.h"
 #include "components/exo/shell_surface.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
@@ -273,36 +273,6 @@
   return mem_usage.priv;
 }
 
-class TabManagerDelegate::UmaReporter {
- public:
-  UmaReporter() : total_kills_(0) {}
-  ~UmaReporter() {}
-
-  void ReportKill(const int memory_freed);
-
- private:
-  base::Time last_kill_time_;
-  int total_kills_;
-};
-
-void TabManagerDelegate::UmaReporter::ReportKill(const int memory_freed) {
-  base::Time now = base::Time::Now();
-  const TimeDelta time_delta =
-      last_kill_time_.is_null() ?
-      TimeDelta::FromSeconds(arc::kMaxOomMemoryKillTimeDeltaSecs) :
-      (now - last_kill_time_);
-  UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(
-            "Arc.LowMemoryKiller.TimeDelta", time_delta);
-  last_kill_time_ = now;
-
-  ++total_kills_;
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "Arc.LowMemoryKiller.Count", total_kills_, 1, 1000, 1001);
-
-  UMA_HISTOGRAM_MEMORY_KB("Arc.LowMemoryKiller.FreedSize",
-                          memory_freed);
-}
-
 TabManagerDelegate::TabManagerDelegate(
     const base::WeakPtr<TabManager>& tab_manager)
   : TabManagerDelegate(tab_manager, new MemoryStat()) {
@@ -314,7 +284,6 @@
     : tab_manager_(tab_manager),
       focused_process_(new FocusedProcess()),
       mem_stat_(mem_stat),
-      uma_(new UmaReporter()),
       weak_ptr_factory_(this) {
   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
                  content::NotificationService::AllBrowserContextsAndSources());
@@ -631,7 +600,7 @@
           mem_stat_->EstimatedMemoryFreedKB(it->app()->pid());
       if (KillArcProcess(it->app()->nspid())) {
         target_memory_to_free_kb -= estimated_memory_freed_kb;
-        uma_->ReportKill(estimated_memory_freed_kb);
+        MemoryKillsMonitor::LogLowMemoryKill("APP", estimated_memory_freed_kb);
         VLOG(2) << "Killed " << *it;
       }
     } else {
@@ -643,7 +612,7 @@
           mem_stat_->EstimatedMemoryFreedKB(it->tab()->renderer_handle);
       if (KillTab(tab_id)) {
         target_memory_to_free_kb -= estimated_memory_freed_kb;
-        uma_->ReportKill(estimated_memory_freed_kb);
+        MemoryKillsMonitor::LogLowMemoryKill("TAB", estimated_memory_freed_kb);
         VLOG(2) << "Killed " << *it;
       }
     }
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos.h b/chrome/browser/memory/tab_manager_delegate_chromeos.h
index 13c6c8b..af53e01 100644
--- a/chrome/browser/memory/tab_manager_delegate_chromeos.h
+++ b/chrome/browser/memory/tab_manager_delegate_chromeos.h
@@ -97,7 +97,6 @@
 
   class Candidate;
   class FocusedProcess;
-  class UmaReporter;
 
   friend std::ostream& operator<<(std::ostream& out,
                                   const Candidate& candidate);
@@ -169,9 +168,6 @@
   // Util for getting system memory status.
   std::unique_ptr<TabManagerDelegate::MemoryStat> mem_stat_;
 
-  // Reports UMA histograms.
-  std::unique_ptr<UmaReporter> uma_;
-
   // Weak pointer factory used for posting tasks to other threads.
   base::WeakPtrFactory<TabManagerDelegate> weak_ptr_factory_;
 
diff --git a/chrome/browser/resources/chromeos/login/lock.js b/chrome/browser/resources/chromeos/login/lock.js
index 0e2a3d18..ad5919ce 100644
--- a/chrome/browser/resources/chromeos/login/lock.js
+++ b/chrome/browser/resources/chromeos/login/lock.js
@@ -29,7 +29,7 @@
   // Called after polymer has been loaded. Fades the pin element in.
   var onPinLoaded = function(pinKeyboard) {
     var podRow = $('pod-row');
-    podRow.togglePinTransitions(true);
+    podRow.toggleTransitions(true);
     podRow.setFocusedPodPinVisibility(true);
   };
 
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.css b/chrome/browser/resources/chromeos/login/oobe_eula.css
index defff1b..390d952 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.css
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.css
@@ -36,5 +36,9 @@
 }
 
 .bottom-buttons {
-  padding: 0 24px;
+  padding: 0 6px; /* = 8px - 2px back button border */
+}
+
+#eula-accept-button {
+  -webkit-padding-end: 18px;
 }
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.html b/chrome/browser/resources/chromeos/login/oobe_eula.html
index 5fc8eb3..013c863f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.html
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.html
@@ -59,8 +59,12 @@
           </paper-checkbox>
         </div>
       </div>
-      <div class="bottom-buttons flex layout horizontal end-justified">
-        <oobe-text-button inverse on-tap="eulaAccepted_"
+      <div class="bottom-buttons flex layout horizontal">
+        <oobe-back-button on-tap="onEulaBackButtonPressed_">
+        </oobe-back-button>
+        <div class="flex">
+        </div>
+        <oobe-text-button id="eula-accept-button" inverse on-tap="eulaAccepted_"
             disabled="[[acceptButtonDisabled]]">
           <div i18n-content="oobeEulaAcceptAndContinueButtonText"
               id="accept-button-text">
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.js b/chrome/browser/resources/chromeos/login/oobe_eula.js
index 78cf7ed..8ff6cf2 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.js
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.js
@@ -76,7 +76,7 @@
   /**
    * On-change event handler for usageStats.
    *
-   * * @private
+   * @private
    */
   onUsageChanged_: function() {
     this.screen.onUsageStatsClicked_(this.$.usageStats.checked);
@@ -85,7 +85,7 @@
   /**
    * On-tap event handler for installationSettings.
    *
-   * * @private
+   * @private
    */
   onInstallationSettingsClicked_: function() {
     chrome.send('eulaOnInstallationSettingsPopupOpened');
@@ -96,9 +96,18 @@
   /**
    * On-tap event handler for stats-help-link.
    *
-   * * @private
+   * @private
    */
   onUsageStatsHelpLinkClicked_: function() {
     chrome.send('eulaOnLearnMore');
   },
+
+  /**
+   * On-tap event handler for back button.
+   *
+   * @private
+   */
+  onEulaBackButtonPressed_: function() {
+    chrome.send('login.EulaScreen.userActed', ['back-button']);
+  },
 });
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index 9a5b4f5..6d535b1 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -2350,11 +2350,6 @@
 // From an HTTP top frame, navigate to good and bad HTTPS (security state should
 // stay unauthenticated).
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestUnauthenticatedFrameNavigation) {
-  // TODO(crbug.com/668913): Flaky with --site-per-process.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kSitePerProcess)) {
-    return;
-  }
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(https_server_.Start());
   ASSERT_TRUE(https_server_expired_.Start());
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index e2e59da..c64051c 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -859,6 +859,7 @@
   package_dict->SetString(kLastBackupAndroidId, id_str);
   package_dict->SetString(kLastBackupTime, time_str);
   package_dict->SetBoolean(kSystem, package.system);
+  package_dict->SetBoolean(kUninstalled, false);
 }
 
 void ArcAppListPrefs::RemovePackageFromPrefs(PrefService* prefs,
@@ -962,6 +963,7 @@
 
   std::unordered_set<std::string> apps_to_remove =
       GetAppsForPackage(package_name);
+  default_apps_.MaybeMarkPackageUninstalled(package_name, false);
   for (const auto& app : apps) {
     apps_to_remove.erase(GetAppId(app->package_name, app->activity));
     AddApp(*app);
@@ -1064,10 +1066,15 @@
 void ArcAppListPrefs::OnTaskCreated(int32_t task_id,
                                     const std::string& package_name,
                                     const std::string& activity,
-                                    const base::Optional<std::string>& name) {
+                                    const base::Optional<std::string>& name,
+                                    const base::Optional<std::string>& intent) {
   MaybeAddNonLaunchableApp(name, package_name, activity);
-  for (auto& observer : observer_list_)
-    observer.OnTaskCreated(task_id, package_name, activity);
+  for (auto& observer : observer_list_) {
+    observer.OnTaskCreated(task_id,
+                           package_name,
+                           activity,
+                           intent.value_or(std::string()));
+  }
 }
 
 void ArcAppListPrefs::OnTaskDestroyed(int32_t task_id) {
@@ -1193,8 +1200,14 @@
       prefs_->GetDictionary(prefs::kArcPackages);
   for (base::DictionaryValue::Iterator package(*package_prefs);
        !package.IsAtEnd(); package.Advance()) {
+    const base::DictionaryValue* package_info;
+    if (!package.value().GetAsDictionary(&package_info)) {
+      NOTREACHED();
+      continue;
+    }
+
     bool uninstalled = false;
-    package_prefs->GetBoolean(kSystem, &uninstalled);
+    package_info->GetBoolean(kUninstalled, &uninstalled);
     if (installed != !uninstalled)
       continue;
 
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
index d97686a..7c7e613 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
@@ -126,7 +126,8 @@
     // initial activity.
     virtual void OnTaskCreated(int32_t task_id,
                                const std::string& package_name,
-                               const std::string& activity) {}
+                               const std::string& activity,
+                               const std::string& intent) {}
     // Notifies that task has been destroyed.
     virtual void OnTaskDestroyed(int32_t task_id) {}
     // Notifies that task has been activated and moved to the front.
@@ -278,7 +279,8 @@
   void OnTaskCreated(int32_t task_id,
                      const std::string& package_name,
                      const std::string& activity,
-                     const base::Optional<std::string>& name) override;
+                     const base::Optional<std::string>& name,
+                     const base::Optional<std::string>& intent) override;
   void OnTaskDestroyed(int32_t task_id) override;
   void OnTaskSetActive(int32_t task_id) override;
   void OnNotificationsEnabledChanged(const std::string& package_name,
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index c606367..58f3594d 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -372,6 +372,8 @@
   // ArcAppModelBuilderTest:
   void OnBeforeArcTestSetup() override {
     ArcDefaultAppList::UseTestAppsDirectory();
+    arc::ArcPackageSyncableServiceFactory::GetInstance()->SetTestingFactory(
+        profile_.get(), nullptr);
   }
 
  private:
@@ -1111,7 +1113,7 @@
 
   EXPECT_FALSE(prefs->IsRegistered(app_id));
   EXPECT_FALSE(FindArcItem(app_id));
-  app_instance()->SendTaskCreated(0, fake_apps()[0]);
+  app_instance()->SendTaskCreated(0, fake_apps()[0], std::string());
   // App should not appear now in the model but should be registered.
   EXPECT_FALSE(FindArcItem(app_id));
   EXPECT_TRUE(prefs->IsRegistered(app_id));
@@ -1181,11 +1183,13 @@
   }
 
   // And now default apps are ready.
+  std::map<std::string, bool> oem_states;
   for (const auto& default_app : fake_default_apps()) {
-    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(
-        ArcAppTest::GetAppId(default_app));
+    const std::string app_id = ArcAppTest::GetAppId(default_app);
+    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
     ASSERT_TRUE(app_info);
     EXPECT_TRUE(app_info->ready);
+    oem_states[app_id] = prefs->IsOem(app_id);
   }
 
   // Uninstall first default package. Default app should go away.
@@ -1198,6 +1202,33 @@
   all_apps = fake_default_apps();
   all_apps.erase(all_apps.begin());
   ValidateHaveApps(all_apps);
+
+  // Sign-out and sign-in again. Removed default app should not appear.
+  arc_test()->TearDown();
+  ResetBuilder();
+  ArcAppListPrefsFactory::GetInstance()->RecreateServiceInstanceForTesting(
+      profile_.get());
+  arc_test()->SetUp(profile_.get());
+  CreateBuilder();
+
+  // Prefs are changed.
+  prefs = ArcAppListPrefs::Get(profile_.get());
+  ASSERT_NE(nullptr, prefs);
+
+  ValidateHaveApps(all_apps);
+
+  // Install deleted default app again.
+  std::vector<arc::mojom::AppInfo> package_apps;
+  package_apps.push_back(fake_default_apps()[0]);
+  app_instance()->SendPackageAppListRefreshed(
+      fake_default_apps()[0].package_name, package_apps);
+  ValidateHaveApps(fake_default_apps());
+
+  // Validate that OEM state is preserved.
+  for (const auto& default_app : fake_default_apps()) {
+    const std::string app_id = ArcAppTest::GetAppId(default_app);
+    EXPECT_EQ(oem_states[app_id], prefs->IsOem(app_id));
+  }
 }
 
 TEST_F(ArcDefaulAppForManagedUserTest, DefaultAppsForManagedUser) {
diff --git a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
index cde7f22..7aadc019 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
@@ -382,6 +382,7 @@
   EXPECT_FALSE(delegate->IsAppOpen(app_id));
   // Simulate task creation so the app is marked as running/open.
   std::unique_ptr<ArcAppListPrefs::AppInfo> info = app_prefs()->GetApp(app_id);
-  app_host()->OnTaskCreated(0, info->package_name, info->activity, info->name);
+  app_host()->OnTaskCreated(0, info->package_name, info->activity, info->name,
+                            info->intent_uri);
   EXPECT_TRUE(delegate->IsAppOpen(app_id));
 }
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
index 870186d..19a6119 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
@@ -455,7 +455,8 @@
 void ArcAppWindowLauncherController::OnTaskCreated(
     int task_id,
     const std::string& package_name,
-    const std::string& activity_name) {
+    const std::string& activity_name,
+    const std::string& intent) {
   DCHECK(!GetAppWindowForTask(task_id));
   const std::string arc_app_id =
       ArcAppListPrefs::GetAppId(package_name, activity_name);
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h
index a1006dd..8294402 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h
@@ -75,7 +75,8 @@
   void OnAppRemoved(const std::string& app_id) override;
   void OnTaskCreated(int task_id,
                      const std::string& package_name,
-                     const std::string& activity) override;
+                     const std::string& activity,
+                     const std::string& intent) override;
   void OnTaskDestroyed(int task_id) override;
   void OnTaskSetActive(int32_t task_id) override;
   void OnTaskOrientationLockRequested(
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
index 1dd2b49..031e7dc2 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
@@ -867,7 +867,7 @@
                            int32_t task_id) {
     ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
     prefs->OnTaskCreated(task_id, appinfo.package_name, appinfo.activity,
-                         appinfo.name);
+                         appinfo.name, std::string());
   }
 
   void NotifyOnTaskOrientationLockRequested(int32_t task_id,
@@ -1865,13 +1865,15 @@
 
   std::string window_app_id1("org.chromium.arc.1");
   views::Widget* arc_window1 = CreateArcWindow(window_app_id1);
-  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0]);
+  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
+                                            std::string());
   EXPECT_NE(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id1));
 
   std::string window_app_id2("org.chromium.arc.2");
   views::Widget* arc_window2 = CreateArcWindow(window_app_id2);
-  arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1]);
+  arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1],
+                                            std::string());
   EXPECT_NE(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id2));
 
@@ -1885,7 +1887,8 @@
 
   std::string window_app_id3("org.chromium.arc.3");
   views::Widget* arc_window3 = CreateArcWindow(window_app_id3);
-  arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[2]);
+  arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[2],
+                                            std::string());
   EXPECT_EQ(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id3));
 
@@ -1920,11 +1923,13 @@
   std::string window_app_id2("org.chromium.arc.2");
   std::string window_app_id3("org.chromium.arc.3");
   CreateArcWindow(window_app_id1);
-  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0]);
+  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
+                                            std::string());
   EXPECT_NE(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id));
   CreateArcWindow(window_app_id2);
-  arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[0]);
+  arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[0],
+                                            std::string());
   EXPECT_NE(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id));
   arc_test_.app_instance()->SendTaskDestroyed(1);
@@ -1936,7 +1941,8 @@
 
   // Stopping bridge removes apps.
   CreateArcWindow(window_app_id3);
-  arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[0]);
+  arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[0],
+                                            std::string());
   EXPECT_NE(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id));
   arc_test_.StopArcInstance();
@@ -1962,7 +1968,8 @@
   EXPECT_EQ(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id1));
   ASSERT_TRUE(arc_window);
-  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0]);
+  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
+                                            std::string());
   EXPECT_NE(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id1));
   arc_test_.app_instance()->SendTaskDestroyed(1);
@@ -1975,7 +1982,8 @@
 
   // Arc window created after and closed before mojom notification.
   std::string window_app_id2("org.chromium.arc.2");
-  arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1]);
+  arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1],
+                                            std::string());
   EXPECT_NE(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(arc_app_id2));
   arc_window = CreateArcWindow(window_app_id2);
@@ -2001,7 +2009,8 @@
   std::string window_app_id("org.chromium.arc.1");
   views::Widget* arc_window = CreateArcWindow(window_app_id);
   ASSERT_TRUE(arc_window);
-  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0]);
+  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
+                                            std::string());
   const ash::ShelfID shelf_id =
       launcher_controller_->GetShelfIDForAppID(arc_app_id);
   EXPECT_NE(ash::kInvalidShelfID, shelf_id);
@@ -3985,8 +3994,8 @@
 
   std::string window_app_id("org.chromium.arc.1");
   CreateArcWindow(window_app_id);
-  arc_test_.app_instance()->SendTaskCreated(1,
-                                            arc_test_.fake_default_apps()[0]);
+  arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_default_apps()[0],
+                                            std::string());
 
   EXPECT_NE(ash::kInvalidShelfID,
             launcher_controller_->GetShelfIDForAppID(app_id));
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
index 1165229..caeb9e0 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
@@ -211,7 +211,8 @@
   // Arc app is running.
   std::string window_app_id1("org.chromium.arc.1");
   CreateArcWindow(window_app_id1);
-  arc_test().app_instance()->SendTaskCreated(1, arc_test().fake_apps()[0]);
+  arc_test().app_instance()->SendTaskCreated(1, arc_test().fake_apps()[0],
+                                             std::string());
   menu.reset(new ArcLauncherContextMenu(controller(), &item, wm_shelf));
 
   EXPECT_FALSE(
@@ -225,7 +226,8 @@
   const std::string app_id2 = ArcAppTest::GetAppId(arc_test().fake_apps()[1]);
   std::string window_app_id2("org.chromium.arc.2");
   CreateArcWindow(window_app_id2);
-  arc_test().app_instance()->SendTaskCreated(2, arc_test().fake_apps()[1]);
+  arc_test().app_instance()->SendTaskCreated(2, arc_test().fake_apps()[1],
+                                             std::string());
   item.id = controller()->GetShelfIDForAppID(app_id2);
   ASSERT_TRUE(item.id);
   menu.reset(new ArcLauncherContextMenu(controller(), &item, wm_shelf));
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 91b0f3a31..cffc7903 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -431,6 +431,26 @@
   [super dealloc];
 }
 
+// Hack to address crbug.com/667274
+// On TouchBar MacBooks, the touch bar machinery retains a reference
+// to the browser window controller (which is an NSTouchBarProvider by
+// default) but doesn't release it if Chrome quits before it takes the
+// key window (for example, quitting from the Dock icon context menu.)
+//
+// If the window denies being a touch bar provider, it's never added
+// to the set of providers and the reference is never taken. This
+// prevents us from providing a touch bar from the window directly
+// but descendant responders can still provide one.
+//
+// rdar://29467717
+- (BOOL)conformsToProtocol:(Protocol*)protocol {
+  if ([protocol isEqual:NSProtocolFromString(@"NSFunctionBarProvider")] ||
+      [protocol isEqual:NSProtocolFromString(@"NSTouchBarProvider")]) {
+    return NO;
+  }
+  return [super conformsToProtocol:protocol];
+}
+
 - (gfx::Rect)enforceMinWindowSize:(gfx::Rect)bounds {
   gfx::Rect checkedBounds = bounds;
 
diff --git a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm
index dea4c912..f5f0887 100644
--- a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm
+++ b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm
@@ -191,44 +191,46 @@
                          alignment:NSLeftTextAlignment];
   NSSize headingSize = [heading frame].size;
 
+  NSSize extraViewIconSize = NSZeroSize;
+  NSSize extraViewTextSize = NSZeroSize;
+
   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
       extra_view_info = delegate_->GetExtraViewInfo();
 
-  gfx::VectorIconId resource_id = extra_view_info->resource_id;
+  if (extra_view_info) {
+    gfx::VectorIconId resource_id = extra_view_info->resource_id;
+    // The extra view icon is optional.
+    if (resource_id != gfx::VectorIconId::VECTOR_ICON_NONE) {
+      NSImage* image = gfx::Image(gfx::CreateVectorIcon(resource_id, 16,
+                                                        gfx::kChromeIconGrey))
+                           .ToNSImage();
+      NSRect frame = NSMakeRect(0, 0, image.size.width, image.size.height);
+      iconView_ = [[[NSImageView alloc] initWithFrame:frame] autorelease];
+      [iconView_ setImage:image];
+      extraViewIconSize = frame.size;
 
-  NSSize extraViewIconSize = NSZeroSize;
-  // The extra view icon is optional.
-  if (resource_id != gfx::VectorIconId::VECTOR_ICON_NONE) {
-    NSImage* image =
-        gfx::Image(gfx::CreateVectorIcon(resource_id, 16, gfx::kChromeIconGrey))
-            .ToNSImage();
-    NSRect frame = NSMakeRect(0, 0, image.size.width, image.size.height);
-    iconView_ = [[[NSImageView alloc] initWithFrame:frame] autorelease];
-    [iconView_ setImage:image];
-    extraViewIconSize = frame.size;
-
-    [[[self window] contentView] addSubview:iconView_];
-  }
-
-  NSSize extraViewTextSize = NSZeroSize;
-  const base::string16& text = extra_view_info->text;
-  if (!text.empty()) {  // The extra view text is optional.
-    if (extra_view_info->is_text_linked) {
-      NSAttributedString* linkString =
-          [self attributedStringWithString:text
-                                  fontSize:13.0
-                                 alignment:NSLeftTextAlignment];
-      link_ = [HyperlinkButtonCell buttonWithString:linkString.string];
-      [link_ setTarget:self];
-      [link_ setAction:@selector(onButtonClicked:)];
-      [[[self window] contentView] addSubview:link_];
-      [link_ sizeToFit];
-    } else {
-      label_ = [self addTextFieldWithString:text
-                                   fontSize:13.0
-                                  alignment:NSLeftTextAlignment];
+      [[[self window] contentView] addSubview:iconView_];
     }
-    extraViewTextSize = label_ ? [label_ frame].size : [link_ frame].size;
+
+    const base::string16& text = extra_view_info->text;
+    if (!text.empty()) {  // The extra view text is optional.
+      if (extra_view_info->is_text_linked) {
+        NSAttributedString* linkString =
+            [self attributedStringWithString:text
+                                    fontSize:13.0
+                                   alignment:NSLeftTextAlignment];
+        link_ = [HyperlinkButtonCell buttonWithString:linkString.string];
+        [link_ setTarget:self];
+        [link_ setAction:@selector(onButtonClicked:)];
+        [[[self window] contentView] addSubview:link_];
+        [link_ sizeToFit];
+      } else {
+        label_ = [self addTextFieldWithString:text
+                                     fontSize:13.0
+                                    alignment:NSLeftTextAlignment];
+      }
+      extraViewTextSize = label_ ? [label_ frame].size : [link_ frame].size;
+    }
   }
 
   base::string16 cancelStr = delegate_->GetDismissButtonText();
@@ -318,27 +320,27 @@
         dismissButtonSize.height)];
     currentMaxWidth -= (dismissButtonSize.width + kButtonPadding);
   }
-  if (label_ || link_) {
-    CGFloat extraViewTextHeight =
-        currentHeight + (buttonStripHeight - extraViewTextSize.height) / 2.0;
-    NSRect frame = NSMakeRect(currentMaxWidth - extraViewTextSize.width,
-                              extraViewTextHeight, extraViewTextSize.width,
-                              extraViewTextSize.height);
-    if (link_) {
-      [link_ setFrame:frame];
-    } else {
-      [label_ setFrame:frame];
-    }
-    currentMaxWidth -= extraViewTextSize.width + kButtonPadding;
-  }
+  int leftAlignXPos = kHorizontalPadding;
   if (iconView_) {
     CGFloat extraViewIconHeight =
         currentHeight + (buttonStripHeight - extraViewIconSize.height) / 2.0;
 
     [iconView_
-        setFrame:NSMakeRect(kHorizontalPadding, extraViewIconHeight,
+        setFrame:NSMakeRect(leftAlignXPos, extraViewIconHeight,
                             extraViewIconSize.width, extraViewIconSize.height)];
-    currentMaxWidth -= extraViewIconSize.width + kButtonPadding;
+    leftAlignXPos += extraViewIconSize.width + kButtonPadding;
+  }
+  if (label_ || link_) {
+    CGFloat extraViewTextHeight =
+        currentHeight + (buttonStripHeight - extraViewTextSize.height) / 2.0;
+    NSRect frame =
+        NSMakeRect(leftAlignXPos, extraViewTextHeight, extraViewTextSize.width,
+                   extraViewTextSize.height);
+    if (link_) {
+      [link_ setFrame:frame];
+    } else {
+      [label_ setFrame:frame];
+    }
   }
   // Buttons have some inherit padding of their own, so we don't need quite as
   // much space here.
diff --git a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm
index 650ec69..4b416be2 100644
--- a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm
+++ b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm
@@ -6,6 +6,7 @@
 #import "base/mac/foundation_util.h"
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
 #import "chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.h"
@@ -112,10 +113,12 @@
       HeadingString(), BodyString(), ActionString());
   delegate.set_dismiss_button_text(DismissString());
 
-  ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info_linked_text;
-  extra_view_info_linked_text.text = LearnMoreString();
-  extra_view_info_linked_text.is_text_linked = true;
-  delegate.set_extra_view_info(extra_view_info_linked_text);
+  std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+      extra_view_info_linked_text =
+          base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+  extra_view_info_linked_text->text = LearnMoreString();
+  extra_view_info_linked_text->is_text_linked = true;
+  delegate.set_extra_view_info(std::move(extra_view_info_linked_text));
 
   ToolbarActionsBarBubbleMac* bubble = CreateAndShowBubble(&delegate);
   base::scoped_nsobject<WindowObserver> windowObserver(
@@ -201,10 +204,12 @@
     TestToolbarActionsBarBubbleDelegate delegate(
         HeadingString(), BodyString(), ActionString());
 
-    ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info_linked_text;
-    extra_view_info_linked_text.text = LearnMoreString();
-    extra_view_info_linked_text.is_text_linked = true;
-    delegate.set_extra_view_info(extra_view_info_linked_text);
+    std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+        extra_view_info_linked_text =
+            base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+    extra_view_info_linked_text->text = LearnMoreString();
+    extra_view_info_linked_text->is_text_linked = true;
+    delegate.set_extra_view_info(std::move(extra_view_info_linked_text));
 
     delegate.set_dismiss_button_text(DismissString());
     ToolbarActionsBarBubbleMac* bubble = CreateAndShowBubble(&delegate);
@@ -253,17 +258,35 @@
     chrome::testing::NSRunLoopRunAllPending();
   }
 
+  // Test with a null extra view.
+  {
+    TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
+                                                 ActionString());
+    ToolbarActionsBarBubbleMac* bubble = CreateAndShowBubble(&delegate);
+    EXPECT_TRUE([bubble actionButton]);
+    EXPECT_FALSE([bubble iconView]);
+    EXPECT_FALSE([bubble label]);
+    EXPECT_FALSE([bubble link]);
+    EXPECT_FALSE([bubble dismissButton]);
+    EXPECT_FALSE([bubble itemList]);
+
+    [bubble close];
+    chrome::testing::NSRunLoopRunAllPending();
+  }
+
   // Test with an extra view of a (unlinked) text and icon and action button.
   {
     TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
                                                  ActionString());
 
-    ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info;
-    extra_view_info.resource_id = gfx::VectorIconId::BUSINESS;
-    extra_view_info.text =
+    std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+        extra_view_info =
+            base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+    extra_view_info->resource_id = gfx::VectorIconId::BUSINESS;
+    extra_view_info->text =
         l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN);
-    extra_view_info.is_text_linked = false;
-    delegate.set_extra_view_info(extra_view_info);
+    extra_view_info->is_text_linked = false;
+    delegate.set_extra_view_info(std::move(extra_view_info));
 
     ToolbarActionsBarBubbleMac* bubble = CreateAndShowBubble(&delegate);
     EXPECT_TRUE([bubble actionButton]);
@@ -282,10 +305,12 @@
     TestToolbarActionsBarBubbleDelegate delegate(
         HeadingString(), BodyString(), ActionString());
 
-    ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info_linked_text;
-    extra_view_info_linked_text.text = LearnMoreString();
-    extra_view_info_linked_text.is_text_linked = true;
-    delegate.set_extra_view_info(extra_view_info_linked_text);
+    std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+        extra_view_info_linked_text =
+            base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+    extra_view_info_linked_text->text = LearnMoreString();
+    extra_view_info_linked_text->is_text_linked = true;
+    delegate.set_extra_view_info(std::move(extra_view_info_linked_text));
 
     delegate.set_dismiss_button_text(DismissString());
     delegate.set_item_list_text(ItemListString());
diff --git a/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.cc b/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.cc
index 4e2ea3d..69b1a442 100644
--- a/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.cc
+++ b/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.cc
@@ -32,7 +32,10 @@
   }
   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
   GetExtraViewInfo() override {
-    return base::MakeUnique<ExtraViewInfo>(parent_->extra_view_info_);
+    if (parent_->info_)
+      return base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>(
+          *parent_->info_);
+    return nullptr;
   }
   std::string GetAnchorActionId() override { return std::string(); }
   void OnBubbleShown() override {
@@ -57,8 +60,7 @@
       heading_(heading),
       body_(body),
       action_(action),
-      close_on_deactivate_(true) {
-}
+      close_on_deactivate_(true) {}
 
 TestToolbarActionsBarBubbleDelegate::~TestToolbarActionsBarBubbleDelegate() {
   // If the bubble didn't close, it means that it still owns the DelegateImpl,
diff --git a/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.h b/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.h
index 07d5f7b..463ec6f 100644
--- a/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.h
+++ b/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_bubble_delegate.h"
 
 // A test delegate for a bubble to hang off the toolbar actions bar.
@@ -33,10 +34,12 @@
   void set_learn_more_button_text(const base::string16& learn_more) {
     learn_more_ = learn_more;
 
-    ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info_linked_text;
-    extra_view_info_linked_text.text = learn_more;
-    extra_view_info_linked_text.is_text_linked = true;
-    set_extra_view_info(extra_view_info_linked_text);
+    if (!info_) {
+      info_ =
+          base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+    }
+    info_->text = learn_more;
+    info_->is_text_linked = true;
   }
   void set_item_list_text(const base::string16& item_list) {
     item_list_ = item_list;
@@ -45,10 +48,9 @@
     close_on_deactivate_ = close_on_deactivate;
   }
   void set_extra_view_info(
-      const ToolbarActionsBarBubbleDelegate::ExtraViewInfo& extra_view_info) {
-    extra_view_info_ = extra_view_info;
+      std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo> info) {
+    info_ = std::move(info);
   }
-
   const ToolbarActionsBarBubbleDelegate::CloseAction* close_action() const {
     return close_action_.get();
   }
@@ -75,7 +77,7 @@
   bool close_on_deactivate_;
 
   // Information about the extra view to show, if any.
-  ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info_;
+  std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo> info_;
 
   DISALLOW_COPY_AND_ASSIGN(TestToolbarActionsBarBubbleDelegate);
 };
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index dac0eac..82ea9c09 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -142,12 +142,8 @@
     property_util::SetIntProperty(window, ash::kShelfItemTypeKey,
                                   ash::TYPE_APP_PANEL);
   } else {
-    ash::AppType app_type = ash::AppType::CHROME_APP;
-    Profile* profile =
-        Profile::FromBrowserContext(app_window->browser_context());
-    if (profile && chromeos::IsNoteTakingAppWindow(app_window, profile))
-      app_type = ash::AppType::DEFAULT_NOTE_TAKING_APP;
-    window->SetProperty(aura::client::kAppType, static_cast<int>(app_type));
+    window->SetProperty(aura::client::kAppType,
+                        static_cast<int>(ash::AppType::CHROME_APP));
   }
 }
 
diff --git a/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
index 33e1264..869bd7a 100644
--- a/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
+++ b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
@@ -171,11 +171,10 @@
 class DragStartWaiter : public aura::client::DragDropClient {
  public:
   // Starts monitoring |web_contents| for a start of a drag-and-drop.
-  // While alive, prevents a real, OS-level drag-and-drop from starting
-  // for this particular |web_contents|.
   explicit DragStartWaiter(content::WebContents* web_contents)
       : web_contents_(web_contents),
         message_loop_runner_(new content::MessageLoopRunner),
+        suppress_passing_of_start_drag_further_(false),
         drag_started_(false) {
     DCHECK(web_contents_);
 
@@ -193,27 +192,44 @@
     aura::client::SetDragDropClient(root_window, old_client_);
   }
 
-  // Waits until we almost report a drag-and-drop start to the OS
-  // (notifying the OS will be prevented to help the test continue
-  // without entering a nested message loop).
+  // Waits until we almost report a drag-and-drop start to the OS.
+  // At that point
+  // 1) the callback from PostTaskWhenDragStarts will be posted.
+  // 2) the drag-start request will be forwarded to the OS
+  //    (unless SuppressPassingStartDragFurther method was called).
   //
-  // Returns true if drag and drop has indeed started (and in this
-  // case populates |text|, |html| and other parameters with data
-  // that would have been passed to the OS).
-  void WaitUntilDragStartIsIntercepted(
-      std::string* text,
-      std::string* html,
-      int* operation,
-      gfx::Point* location_inside_web_contents) {
+  // Note that if SuppressPassingStartDragFurther was not called then
+  // WaitUntilDragStart can take a long time to return (it returns only after
+  // the OS decides that the drag-and-drop has ended).
+  //
+  // Before returning populates |text|, |html| and other parameters with data
+  // that would have been passed to the OS).  If the caller is not interested in
+  // this data, then the corresponding argument can be null.
+  void WaitUntilDragStart(std::string* text,
+                          std::string* html,
+                          int* operation,
+                          gfx::Point* location_inside_web_contents) {
     message_loop_runner_->Run();
 
     // message_loop_runner_->Quit is only called from StartDragAndDrop.
     DCHECK(drag_started_);
 
-    *text = text_;
-    *html = html_;
-    *operation = operation_;
-    *location_inside_web_contents = location_inside_web_contents_;
+    if (text)
+      *text = text_;
+    if (html)
+      *html = html_;
+    if (operation)
+      *operation = operation_;
+    if (location_inside_web_contents)
+      *location_inside_web_contents = location_inside_web_contents_;
+  }
+
+  void SuppressPassingStartDragFurther() {
+    suppress_passing_of_start_drag_further_ = true;
+  }
+
+  void PostTaskWhenDragStarts(const base::Closure& callback) {
+    callback_to_run_inside_drag_and_drop_message_loop_ = callback;
   }
 
   // aura::client::DragDropClient overrides:
@@ -249,12 +265,19 @@
       operation_ = operation;
     }
 
-    // Forwarding to |old_client_| is undesirable, because test cannot control
-    // next steps after a nested drag-and-drop loop is entered at the OS level
-    // (as is the case in Windows, via DoDragDrop).  Instead, in the test we
-    // kind of ignore renderer's request to start drag and drop and never
-    // forward this request to the OS-specific layers.
-    return 0;
+    if (!callback_to_run_inside_drag_and_drop_message_loop_.is_null()) {
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE,
+          std::move(callback_to_run_inside_drag_and_drop_message_loop_));
+      callback_to_run_inside_drag_and_drop_message_loop_.Reset();
+    }
+
+    if (suppress_passing_of_start_drag_further_)
+      return 0;
+
+    // Start a nested drag-and-drop loop (might not return for a long time).
+    return old_client_->StartDragAndDrop(data, root_window, source_window,
+                                         screen_location, operation, source);
   }
 
   void DragCancel() override {
@@ -267,6 +290,8 @@
   content::WebContents* web_contents_;
   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
   aura::client::DragDropClient* old_client_;
+  base::Closure callback_to_run_inside_drag_and_drop_message_loop_;
+  bool suppress_passing_of_start_drag_further_;
 
   // Data captured during the first intercepted StartDragAndDrop call.
   bool drag_started_;
@@ -396,6 +421,10 @@
  public:
   DragAndDropBrowserTest(){};
 
+  struct DragImageBetweenFrames_TestState;
+  void DragImageBetweenFrames_Step2(DragImageBetweenFrames_TestState*);
+  void DragImageBetweenFrames_Step3(DragImageBetweenFrames_TestState*);
+
  protected:
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
@@ -468,6 +497,16 @@
     return kMiddleOfLeftFrame + gfx::Vector2d(10, 10);
   }
 
+  bool SimulateMouseMoveToLeftFrame() {
+    AssertTestPageIsLoaded();
+    return SimulateMouseMove(kMiddleOfLeftFrame);
+  }
+
+  bool SimulateMouseMoveToRightFrame() {
+    AssertTestPageIsLoaded();
+    return SimulateMouseMove(kMiddleOfRightFrame);
+  }
+
   bool SimulateMouseUp() {
     return ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
                                               ui_controls::UP);
@@ -488,6 +527,13 @@
   }
 
  private:
+  // Constants with coordinates within content/test/data/drag_and_drop/page.html
+  // The precise frame center is at 200,200 and 400,200 coordinates, but slight
+  // differences between left and right frame hopefully make it easier to detect
+  // incorrect dom_drag_and_drop_event.clientX/Y values in test asserts.
+  const gfx::Point kMiddleOfLeftFrame = gfx::Point(155, 150);
+  const gfx::Point kMiddleOfRightFrame = gfx::Point(455, 250);
+
   bool SimulateMouseDown() {
     return ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
                                               ui_controls::DOWN);
@@ -569,13 +615,11 @@
 
   std::unique_ptr<DragAndDropSimulator> drag_simulator_;
 
-  // Constants with coordinates within content/test/data/drag_and_drop/page.html
-  const gfx::Point kMiddleOfLeftFrame = gfx::Point(200, 200);
-  const gfx::Point kMiddleOfRightFrame = gfx::Point(400, 200);
-
   DISALLOW_COPY_AND_ASSIGN(DragAndDropBrowserTest);
 };
 
+// Scenario: drag text from outside the browser and drop to the right frame.
+// Test coverage: dragover, drop DOM events.
 IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, DropTextFromOutside) {
   std::string frame_site = use_cross_site_subframe() ? "b.com" : "a.com";
   ASSERT_TRUE(NavigateToTestPage("a.com"));
@@ -583,11 +627,11 @@
 
   // Setup test expectations.
   DOMDragEventVerifier expected_dom_event_data;
-  expected_dom_event_data.set_expected_client_position("(100, 100)");
+  expected_dom_event_data.set_expected_client_position("(155, 150)");
   expected_dom_event_data.set_expected_drop_effect("none");
   expected_dom_event_data.set_expected_effect_allowed("all");
   expected_dom_event_data.set_expected_mime_types("text/plain");
-  expected_dom_event_data.set_expected_page_position("(100, 100)");
+  expected_dom_event_data.set_expected_page_position("(155, 150)");
 
   // Drag text from outside the browser into/over the right frame.
   {
@@ -610,23 +654,32 @@
   }
 }
 
+// Scenario: starting a drag in left frame
+// Test coverage: dragstart DOM event, dragstart data passed to the OS.
 IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, DragStartInFrame) {
   std::string frame_site = use_cross_site_subframe() ? "b.com" : "a.com";
   ASSERT_TRUE(NavigateToTestPage("a.com"));
   ASSERT_TRUE(NavigateLeftFrame(frame_site, "image_source.html"));
 
   // Setup test expectations.
-  // (dragstart event handler in image_source.html is asking for "copy" only).
   DOMDragEventVerifier expected_dom_event_data;
-  expected_dom_event_data.set_expected_client_position("(100, 100)");
+  expected_dom_event_data.set_expected_client_position("(55, 50)");
   expected_dom_event_data.set_expected_drop_effect("none");
+  // (dragstart event handler in image_source.html is asking for "copy" only).
   expected_dom_event_data.set_expected_effect_allowed("copy");
+  expected_dom_event_data.set_expected_page_position("(55, 50)");
+
+  // TODO(lukasza): Figure out why the dragstart event
+  // - lists "Files" on the mime types list,
+  // - doesn't list "text/plain" on the mime types list.
+  // (i.e. why expectations below differ from expectations for dragenter,
+  // dragover, dragend and/or drop events in DragImageBetweenFrames test).
   expected_dom_event_data.set_expected_mime_types(
       "Files,text/html,text/uri-list");
-  expected_dom_event_data.set_expected_page_position("(100, 100)");
 
   // Start the drag in the left frame.
   DragStartWaiter drag_start_waiter(web_contents());
+  drag_start_waiter.SuppressPassingStartDragFurther();
   DOMDragEventWaiter dragstart_event_waiter("dragstart", left_frame());
   EXPECT_TRUE(SimulateMouseDownAndDragStartInLeftFrame());
 
@@ -644,8 +697,8 @@
     std::string html;
     int operation = 0;
     gfx::Point location_inside_web_contents;
-    drag_start_waiter.WaitUntilDragStartIsIntercepted(
-        &text, &html, &operation, &location_inside_web_contents);
+    drag_start_waiter.WaitUntilDragStart(&text, &html, &operation,
+                                         &location_inside_web_contents);
     EXPECT_EQ(embedded_test_server()->GetURL(frame_site,
                                              "/image_decoding/droids.jpg"),
               text);
@@ -662,6 +715,173 @@
   SimulateMouseUp();
 }
 
+// There is no known way to execute test-controlled tasks during
+// a drag-and-drop loop run by Windows OS.
+#if defined(OS_WIN)
+#define MAYBE_DragImageBetweenFrames DISABLED_DragImageBetweenFrames
+#else
+#define MAYBE_DragImageBetweenFrames DragImageBetweenFrames
+#endif
+
+// Data that needs to be shared across multiple test steps below
+// (i.e. across DragImageBetweenFrames_Step2 and DragImageBetweenFrames_Step3).
+struct DragAndDropBrowserTest::DragImageBetweenFrames_TestState {
+  DOMDragEventVerifier expected_dom_event_data;
+  std::unique_ptr<DOMDragEventWaiter> dragstart_event_waiter;
+  std::unique_ptr<DOMDragEventWaiter> drop_event_waiter;
+  std::unique_ptr<DOMDragEventWaiter> dragend_event_waiter;
+};
+
+// Scenario: drag an image from the left into the right frame.
+// Test coverage: dragleave, dragenter, dragover, dragend, drop DOM events.
+IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, MAYBE_DragImageBetweenFrames) {
+  // Note that drag and drop will not expose data across cross-site frames on
+  // the same page - this is why the same |frame_site| is used below both for
+  // the left and the right frame.  See also https://crbug.com/59081.
+  std::string frame_site = use_cross_site_subframe() ? "b.com" : "a.com";
+  ASSERT_TRUE(NavigateToTestPage("a.com"));
+  ASSERT_TRUE(NavigateLeftFrame(frame_site, "image_source.html"));
+  ASSERT_TRUE(NavigateRightFrame(frame_site, "drop_target.html"));
+
+  // Setup test expectations.
+  DragAndDropBrowserTest::DragImageBetweenFrames_TestState state;
+  state.expected_dom_event_data.set_expected_client_position("(55, 50)");
+  state.expected_dom_event_data.set_expected_drop_effect("none");
+  // (dragstart event handler in image_source.html is asking for "copy" only).
+  state.expected_dom_event_data.set_expected_effect_allowed("copy");
+  state.expected_dom_event_data.set_expected_mime_types(
+      "text/html,text/plain,text/uri-list");
+  state.expected_dom_event_data.set_expected_page_position("(55, 50)");
+
+  // Start the drag in the left frame.
+  DragStartWaiter drag_start_waiter(web_contents());
+  drag_start_waiter.PostTaskWhenDragStarts(
+      base::Bind(&DragAndDropBrowserTest::DragImageBetweenFrames_Step2,
+                 base::Unretained(this), base::Unretained(&state)));
+  state.dragstart_event_waiter.reset(
+      new DOMDragEventWaiter("dragstart", left_frame()));
+  EXPECT_TRUE(SimulateMouseDownAndDragStartInLeftFrame());
+
+  // The next step of the test (DragImageBetweenFrames_Step2) runs inside the
+  // nested drag-and-drop message loop - the call below won't return until the
+  // drag-and-drop has already ended.
+  drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr);
+
+  DragImageBetweenFrames_Step3(&state);
+}
+
+void DragAndDropBrowserTest::DragImageBetweenFrames_Step2(
+    DragAndDropBrowserTest::DragImageBetweenFrames_TestState* state) {
+  // Verify dragstart DOM event.
+  {
+    std::string dragstart_event;
+    EXPECT_TRUE(state->dragstart_event_waiter->WaitForNextMatchingEvent(
+        &dragstart_event));
+    state->dragstart_event_waiter.reset();
+  }
+
+  // While dragging, move mouse within the left frame.
+  // Without this extra mouse move we wouldn't get a dragleave event later on.
+  ASSERT_TRUE(SimulateMouseMoveToLeftFrame());
+
+  // While dragging, move mouse from the left into the right frame.
+  // This should trigger dragleave and dragenter events.
+  {
+    DOMDragEventWaiter dragleave_event_waiter("dragleave", left_frame());
+    DOMDragEventWaiter dragenter_event_waiter("dragenter", right_frame());
+    ASSERT_TRUE(SimulateMouseMoveToRightFrame());
+
+    {  // Verify dragleave DOM event.
+      std::string dragleave_event;
+
+      // TODO(paulmeyer): https://crbug.com/669695: Need to unify coordinates
+      // passed to dragend when OOPIFs are present or not.
+      state->expected_dom_event_data.set_expected_client_position(
+          "<no expectation>");
+      state->expected_dom_event_data.set_expected_page_position(
+          "<no expectation>");
+
+      EXPECT_TRUE(
+          dragleave_event_waiter.WaitForNextMatchingEvent(&dragleave_event));
+      EXPECT_THAT(dragleave_event, state->expected_dom_event_data.Matches());
+    }
+
+    {  // Verify dragenter DOM event.
+      std::string dragenter_event;
+
+      // Update expected event coordinates after SimulateMouseMoveToRightFrame
+      // (these coordinates are relative to the right frame).
+      state->expected_dom_event_data.set_expected_client_position("(155, 150)");
+      state->expected_dom_event_data.set_expected_page_position("(155, 150)");
+
+      EXPECT_TRUE(
+          dragenter_event_waiter.WaitForNextMatchingEvent(&dragenter_event));
+      EXPECT_THAT(dragenter_event, state->expected_dom_event_data.Matches());
+    }
+
+    // Note that ash (unlike aura/x11) will not fire dragover event in response
+    // to the same mouse event that trigerred a dragenter.  Because of that, we
+    // postpone dragover testing until the next test step below.  See
+    // implementation of ash::DragDropController::DragUpdate for details.
+  }
+
+  // Move the mouse twice in the right frame.  The 1st move will ensure that
+  // allowed operations communicated by the renderer will be stored in
+  // WebContentsViewAura::current_drag_op_.  The 2nd move will ensure that this
+  // gets be copied into DesktopDragDropClientAuraX11::negotiated_operation_.
+  for (int i = 0; i < 2; i++) {
+    DOMDragEventWaiter dragover_event_waiter("dragover", right_frame());
+    ASSERT_TRUE(SimulateMouseMoveToRightFrame());
+
+    {  // Verify dragover DOM event.
+      std::string dragover_event;
+      EXPECT_TRUE(
+          dragover_event_waiter.WaitForNextMatchingEvent(&dragover_event));
+      EXPECT_THAT(dragover_event, state->expected_dom_event_data.Matches());
+    }
+  }
+
+  // Release the mouse button to end the drag.
+  state->drop_event_waiter.reset(new DOMDragEventWaiter("drop", right_frame()));
+  state->dragend_event_waiter.reset(
+      new DOMDragEventWaiter("dragend", left_frame()));
+  SimulateMouseUp();
+  // The test will continue in DragImageBetweenFrames_Step3.
+}
+
+void DragAndDropBrowserTest::DragImageBetweenFrames_Step3(
+    DragAndDropBrowserTest::DragImageBetweenFrames_TestState* state) {
+  // Verify drop DOM event.
+  {
+    std::string drop_event;
+    EXPECT_TRUE(
+        state->drop_event_waiter->WaitForNextMatchingEvent(&drop_event));
+    state->drop_event_waiter.reset();
+    EXPECT_THAT(drop_event, state->expected_dom_event_data.Matches());
+  }
+
+  // Verify dragend DOM event.
+  {
+    // TODO(lukasza): Figure out why the drop event sees different values of
+    // DataTransfer.dropEffect and DataTransfer.types properties.
+    state->expected_dom_event_data.set_expected_drop_effect("copy");
+    state->expected_dom_event_data.set_expected_mime_types("");
+
+    // TODO(paulmeyer): https://crbug.com/669695: Need to unify coordinates
+    // passed to dragend when OOPIFs are present or not.
+    state->expected_dom_event_data.set_expected_client_position(
+        "<no expectation>");
+    state->expected_dom_event_data.set_expected_page_position(
+        "<no expectation>");
+
+    std::string dragend_event;
+    EXPECT_TRUE(
+        state->dragend_event_waiter->WaitForNextMatchingEvent(&dragend_event));
+    state->dragend_event_waiter.reset();
+    EXPECT_THAT(dragend_event, state->expected_dom_event_data.Matches());
+  }
+}
+
 INSTANTIATE_TEST_CASE_P(
     SameSiteSubframe, DragAndDropBrowserTest, ::testing::Values(false));
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc
index 64378065..de76808 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc
@@ -122,8 +122,10 @@
 TEST_F(ToolbarActionsBarBubbleViewsTest, TestBubbleLayoutNoButtons) {
   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
                                                ActionString());
-  ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info;
-  delegate.set_extra_view_info(extra_view_info);
+  std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+      extra_view_info =
+          base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+  delegate.set_extra_view_info(std::move(extra_view_info));
   delegate.set_dismiss_button_text(base::string16());
   delegate.set_action_button_text(base::string16());
   ShowBubble(&delegate);
@@ -161,12 +163,12 @@
   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
                                                ActionString());
   delegate.set_dismiss_button_text(DismissString());
-
-  ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info_linked_text;
-
-  extra_view_info_linked_text.text = LearnMoreString();
-  extra_view_info_linked_text.is_text_linked = true;
-  delegate.set_extra_view_info(extra_view_info_linked_text);
+  std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+      extra_view_info_linked_text =
+          base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+  extra_view_info_linked_text->text = LearnMoreString();
+  extra_view_info_linked_text->is_text_linked = true;
+  delegate.set_extra_view_info(std::move(extra_view_info_linked_text));
 
   ShowBubble(&delegate);
 
@@ -303,13 +305,23 @@
   CloseBubble();
 }
 
+TEST_F(ToolbarActionsBarBubbleViewsTest, TestNullExtraView) {
+  TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
+                                               ActionString());
+  ShowBubble(&delegate);
+  std::unique_ptr<views::View> extra_view(TestCreateExtraView());
+  ASSERT_FALSE(extra_view);
+  CloseBubble();
+}
+
 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCreateExtraViewIconOnly) {
   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
                                                ActionString());
-  ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info;
-
-  extra_view_info.resource_id = gfx::VectorIconId::BUSINESS;
-  delegate.set_extra_view_info(extra_view_info);
+  std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+      extra_view_info =
+          base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+  extra_view_info->resource_id = gfx::VectorIconId::BUSINESS;
+  delegate.set_extra_view_info(std::move(extra_view_info));
   ShowBubble(&delegate);
   std::unique_ptr<views::View> extra_view(TestCreateExtraView());
   ASSERT_TRUE(extra_view);
@@ -324,12 +336,13 @@
 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCreateExtraViewLinkedTextOnly) {
   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
                                                ActionString());
-  ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info_linked_text;
-
-  extra_view_info_linked_text.text =
+  std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+      extra_view_info_linked_text =
+          base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+  extra_view_info_linked_text->text =
       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN);
-  extra_view_info_linked_text.is_text_linked = true;
-  delegate.set_extra_view_info(extra_view_info_linked_text);
+  extra_view_info_linked_text->is_text_linked = true;
+  delegate.set_extra_view_info(std::move(extra_view_info_linked_text));
 
   ShowBubble(&delegate);
 
@@ -344,12 +357,13 @@
 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCreateExtraViewLabelTextOnly) {
   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
                                                ActionString());
-  ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info;
-
-  extra_view_info.text =
+  std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+      extra_view_info =
+          base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+  extra_view_info->text =
       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN);
-  extra_view_info.is_text_linked = false;
-  delegate.set_extra_view_info(extra_view_info);
+  extra_view_info->is_text_linked = false;
+  delegate.set_extra_view_info(std::move(extra_view_info));
 
   ShowBubble(&delegate);
 
@@ -364,12 +378,14 @@
 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCreateExtraViewImageAndText) {
   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
                                                ActionString());
-  ToolbarActionsBarBubbleDelegate::ExtraViewInfo extra_view_info;
-  extra_view_info.resource_id = gfx::VectorIconId::BUSINESS;
-  extra_view_info.text =
+  std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
+      extra_view_info =
+          base::MakeUnique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
+  extra_view_info->resource_id = gfx::VectorIconId::BUSINESS;
+  extra_view_info->text =
       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN);
-  extra_view_info.is_text_linked = false;
-  delegate.set_extra_view_info(extra_view_info);
+  extra_view_info->is_text_linked = false;
+  delegate.set_extra_view_info(std::move(extra_view_info));
 
   ShowBubble(&delegate);
 
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index 9134d83..c0087b2 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -157,13 +157,6 @@
       : PeopleHandler(profile) {
     set_web_ui(web_ui);
   }
-  ~TestingPeopleHandler() override {
-    // TODO(tommycli): PeopleHandler needs this call to destruct properly in the
-    // unit testing context. See the destructor to PeopleHandler. This is hacky.
-    set_web_ui(nullptr);
-  }
-
-  void FocusUI() override {}
 
   using PeopleHandler::is_configuring_sync;
 
diff --git a/chrome/installer/linux/common/apt.include b/chrome/installer/linux/common/apt.include
index 76c65a8..14f196f 100644
--- a/chrome/installer/linux/common/apt.include
+++ b/chrome/installer/linux/common/apt.include
@@ -6,14 +6,6 @@
 SOURCES_PREAMBLE="### THIS FILE IS AUTOMATICALLY CONFIGURED ###
 # You may comment out this entry, but any other modifications may be lost.\n"
 
-# Parse apt configuration and return requested variable value.
-apt_config_val() {
-  APTVAR="$1"
-  if [ -x "$APT_CONFIG" ]; then
-    "$APT_CONFIG" dump | sed -e "/^$APTVAR /"'!d' -e "s/^$APTVAR \"\(.*\)\".*/\1/"
-  fi
-}
-
 # Install the repository/package signing keys, if they aren't already.
 # (see also: https://www.google.com/linuxrepositories/)
 install_key() {
@@ -141,14 +133,7 @@
 
 # Set variables for the locations of the apt sources lists.
 find_apt_sources() {
-  # NB: These variables only *sometimes* include a trailing slash. (In
-  # particular, in Ubuntu 16.10 / Debian 9, the default value *stopped*
-  # including the trailing slash.) We have to join them with slashes, even
-  # though that sometimes gives a double slash.
-  APTDIR=$(apt_config_val Dir)
-  APTETC=$(apt_config_val 'Dir::Etc')
-  APT_SOURCES="$APTDIR/$APTETC/$(apt_config_val 'Dir::Etc::sourcelist')"
-  APT_SOURCESDIR="$APTDIR/$APTETC/$(apt_config_val 'Dir::Etc::sourceparts')"
+  eval $("$APT_CONFIG" shell APT_SOURCESDIR 'Dir::Etc::sourceparts/d')
 }
 
 # Update the Google repository if it's not set correctly.
diff --git a/chrome/test/data/drag_and_drop/drop_target.html b/chrome/test/data/drag_and_drop/drop_target.html
index 4f1bbcce..b77ace2c 100644
--- a/chrome/test/data/drag_and_drop/drop_target.html
+++ b/chrome/test/data/drag_and_drop/drop_target.html
@@ -38,7 +38,9 @@
   </script>
 </head>
 <body>
-  <div ondragover="dragover_handler(event);" ondrop="drop_handler(event);">
-    <p>Use this frame as a target of a drag-n-drop</p>
+  <div ondragover="dragover_handler(event);"
+       ondrop="drop_handler(event);"
+       ondragenter="window.reportDragAndDropEvent(event);"
+    ><p>Use this frame as a target of a drag-n-drop</p>
   </div>
 </body>
diff --git a/chrome/test/data/drag_and_drop/event_monitoring.js b/chrome/test/data/drag_and_drop/event_monitoring.js
index badd6ad9..8447cdd 100644
--- a/chrome/test/data/drag_and_drop/event_monitoring.js
+++ b/chrome/test/data/drag_and_drop/event_monitoring.js
@@ -8,11 +8,12 @@
     try {
       return f();
     } catch(err) {
-      return "exception: " + err.message;
+      return "Got exception: " + err.message;
     }
   }
 
-  console.log("got event: " + ev.type);
+  var msg = "Got a " + ev.type + " event from the " + window.name + " frame.";
+  console.log(msg);
 
   if (window.domAutomationController) {
     window.domAutomationController.setAutomationId(0);
diff --git a/chrome/test/data/drag_and_drop/image_source.html b/chrome/test/data/drag_and_drop/image_source.html
index e2a68343..4c4d05c5 100644
--- a/chrome/test/data/drag_and_drop/image_source.html
+++ b/chrome/test/data/drag_and_drop/image_source.html
@@ -22,14 +22,11 @@
       ev.dataTransfer.effectAllowed = "copy";
       window.reportDragAndDropEvent(ev);
     }
-
-    function dragend_handler(ev) {
-      window.reportDragAndDropEvent(ev);
-    }
   </script>
 </head>
 <body>
   <img  ondragstart="dragstart_handler(event);"
-        ondragend="dragend_handler(event);"
+        ondragend="window.reportDragAndDropEvent(event);"
+        ondragleave="window.reportDragAndDropEvent(event);"
         src="/image_decoding/droids.jpg">
 </body>
diff --git a/chrome/test/data/drag_and_drop/page.html b/chrome/test/data/drag_and_drop/page.html
index 2e158b7c..719eb0c 100644
--- a/chrome/test/data/drag_and_drop/page.html
+++ b/chrome/test/data/drag_and_drop/page.html
@@ -19,6 +19,9 @@
     #left { left: 100px; background: green; }
     #right { left: 300px; background: cyan; }
   </style>
+  <script>
+    window.name = "main";
+  </script>
 </head>
 <body>
   <iframe id="left" name="left" srcdoc="<body>blank left frame</body>"></iframe>
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 99aef6c0..c09d970 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -56,9 +56,6 @@
     "kiosk/arc_kiosk_bridge.h",
     "metrics/arc_metrics_service.cc",
     "metrics/arc_metrics_service.h",
-    "metrics/oom_kills_histogram.h",
-    "metrics/oom_kills_monitor.cc",
-    "metrics/oom_kills_monitor.h",
     "net/arc_net_host_impl.cc",
     "net/arc_net_host_impl.h",
     "obb_mounter/arc_obb_mounter_bridge.cc",
diff --git a/components/arc/common/app.mojom b/components/arc/common/app.mojom
index 7087e13..2cad0d4 100644
--- a/components/arc/common/app.mojom
+++ b/components/arc/common/app.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 15
+// Next MinVersion: 16
 
 module arc.mojom;
 
@@ -107,11 +107,14 @@
   // app.
   [MinVersion=1] OnPackageRemoved@3(string package_name);
 
-  // Sends information about newly created task.
+  // Sends information about newly created task |package_name| and |activity|
+  // specifies launch activity and |intent| is initial intent used to start
+  // new task.
   [MinVersion=4] OnTaskCreated@4(int32 task_id@0,
                                  string package_name@1,
                                  string activity@2,
-                                 [MinVersion=13] string? name@3);
+                                 [MinVersion=13] string? name@3,
+                                 [MinVersion=15] string? intent@4);
 
   // Notifies that task has been destroyed.
   [MinVersion=4] OnTaskDestroyed@5(int32 task_id);
diff --git a/components/arc/metrics/arc_metrics_service.cc b/components/arc/metrics/arc_metrics_service.cc
index 9914eae..a7f663a 100644
--- a/components/arc/metrics/arc_metrics_service.cc
+++ b/components/arc/metrics/arc_metrics_service.cc
@@ -29,7 +29,6 @@
     : ArcService(bridge_service),
       binding_(this),
       process_observer_(this),
-      oom_kills_monitor_handle_(OomKillsMonitor::StartMonitoring()),
       weak_ptr_factory_(this) {
   arc_bridge_service()->metrics()->AddObserver(this);
   arc_bridge_service()->process()->AddObserver(&process_observer_);
diff --git a/components/arc/metrics/arc_metrics_service.h b/components/arc/metrics/arc_metrics_service.h
index c3d256b4..7ad1a0be3 100644
--- a/components/arc/metrics/arc_metrics_service.h
+++ b/components/arc/metrics/arc_metrics_service.h
@@ -15,7 +15,6 @@
 #include "components/arc/common/metrics.mojom.h"
 #include "components/arc/common/process.mojom.h"
 #include "components/arc/instance_holder.h"
-#include "components/arc/metrics/oom_kills_monitor.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
 namespace arc {
@@ -73,8 +72,6 @@
   base::ThreadChecker thread_checker_;
   base::RepeatingTimer timer_;
 
-  OomKillsMonitor::Handle oom_kills_monitor_handle_;
-
   base::TimeTicks arc_start_time_;
 
   // Always keep this the last member of this class to make sure it's the
diff --git a/components/arc/metrics/oom_kills_histogram.h b/components/arc/metrics/oom_kills_histogram.h
deleted file mode 100644
index 2a9bf56..0000000
--- a/components/arc/metrics/oom_kills_histogram.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_ARC_METRICS_OOM_KILLS_HISTOGRAM_H_
-#define COMPONENTS_ARC_METRICS_OOM_KILLS_HISTOGRAM_H_
-
-#include "base/metrics/histogram.h"
-
-namespace arc {
-
-const int kMaxOomMemoryKillTimeDeltaSecs = 30;
-
-}  // namespace arc
-
-// Use this macro to report elapsed time since last OOM kill event.
-// Must be a macro as the underlying HISTOGRAM macro creates static variables.
-#define UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(name, sample)  \
-  UMA_HISTOGRAM_CUSTOM_TIMES(                               \
-      name, sample, base::TimeDelta::FromMilliseconds(1),   \
-      base::TimeDelta::FromSeconds(::arc::kMaxOomMemoryKillTimeDeltaSecs), 50)
-
-#endif  // COMPONENTS_ARC_METRICS_OOM_KILLS_HISTOGRAM_H_
diff --git a/components/arc/metrics/oom_kills_monitor.cc b/components/arc/metrics/oom_kills_monitor.cc
deleted file mode 100644
index 719b778..0000000
--- a/components/arc/metrics/oom_kills_monitor.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/arc/metrics/oom_kills_monitor.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/debug/leak_annotations.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/posix/safe_strerror.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/time/time.h"
-#include "components/arc/metrics/oom_kills_histogram.h"
-#include "third_party/re2/src/re2/re2.h"
-#include "third_party/re2/src/re2/stringpiece.h"
-
-namespace arc {
-
-using base::StringPiece;
-
-using base::SequencedWorkerPool;
-using base::TimeDelta;
-
-namespace {
-
-int64_t GetTimestamp(const StringPiece& line) {
-  std::vector<StringPiece> fields = base::SplitStringPiece(
-      line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-  int64_t timestamp = -1;
-  // Timestamp is the third field in a line of /dev/kmsg.
-  if (fields.size() < 3 || !base::StringToInt64(fields[2], &timestamp))
-    return -1;
-  return timestamp;
-}
-
-bool GetTimeDelta(
-    const StringPiece& line, int64_t* last, TimeDelta* time_delta) {
-  int64_t now = GetTimestamp(line);
-  if (now < 0)
-    return false;
-
-  // Sets to |kMaxOomMemoryKillTimeSecs| for the first kill event.
-  if (*last < 0)
-    *time_delta = TimeDelta::FromSeconds(kMaxOomMemoryKillTimeDeltaSecs);
-  else
-    *time_delta = TimeDelta::FromMicroseconds(now - *last);
-
-  *last = now;
-
-  return true;
-}
-
-void LogOOMKill(const StringPiece& line) {
-  static int64_t last_timestamp = -1;
-  static int oom_kills = 0;
-
-  // Sample log line:
-  // 3,1362,97646497541,-;Out of memory: Kill process 29582 (android.vending)
-  // score 961 or sacrifice child.
-  int oom_badness;
-  TimeDelta time_delta;
-  if (RE2::PartialMatch(re2::StringPiece(line.data(), line.size()),
-                        "Out of memory: Kill process .* score (\\d+)",
-                        &oom_badness)) {
-    ++oom_kills;
-    // Report the cumulative count of killed process in one login session.
-    // For example if there are 3 processes killed, it would report 1 for the
-    // first kill, 2 for the second kill, then 3 for the final kill.
-    // It doesn't report a final count at the end of a user session because
-    // the code runs in a dedicated thread and never ends until browser shutdown
-    // (or logout on Chrome OS). And on browser shutdown the thread may be
-    // terminated brutally so there's no chance to execute a "final" block.
-    // More specifically, code outside the main loop of OomKillsMonitor::Run()
-    // are not guaranteed to be executed.
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Arc.OOMKills.Count", oom_kills, 1, 1000, 1001);
-
-    // In practice most process has oom_badness < 1000, but
-    // strictly speaking the number could be [1, 2000]. What it really
-    // means is the baseline, proportion of memory used (normalized to
-    // [0, 1000]), plus an adjustment score oom_score_adj [-1000, 1000],
-    // truncated to 1 if negative (0 means never kill).
-    // Ref: https://lwn.net/Articles/396552/
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Arc.OOMKills.Score", oom_badness, 1, 2000, 2001);
-
-    if (GetTimeDelta(line, &last_timestamp, &time_delta)) {
-      UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(
-          "Arc.OOMKills.TimeDelta", time_delta);
-    }
-  }
-}
-
-void LogLowMemoryKill(const StringPiece& line) {
-  static int64_t last_timestamp = -1;
-  static int low_memory_kills = 0;
-
-  int freed_size;
-  TimeDelta time_delta;
-  // Sample log line:
-  // 6,2302,533604004,-;lowmemorykiller: Killing 'externalstorage' (21742),
-  // adj 1000,\x0a   to free 27320kB on behalf of 'kswapd0' (47) because\x0a
-  // cache 181920kB is below limit 184320kB for oom_score_adj 1000\x0a
-  // Free memory is 1228kB above reserved
-  if (RE2::PartialMatch(re2::StringPiece(line.data(), line.size()),
-                        "lowmemorykiller: .* to free (\\d+)kB",
-                        &freed_size)) {
-    // Report the count for each lowmemorykill event. See comments in
-    // LogOOMKill().
-    ++low_memory_kills;
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Arc.LowMemoryKiller.Count", low_memory_kills, 1, 1000, 1001);
-
-    UMA_HISTOGRAM_MEMORY_KB("Arc.LowMemoryKiller.FreedSize", freed_size);
-
-    if (GetTimeDelta(line, &last_timestamp, &time_delta)) {
-      UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(
-          "Arc.LowMemoryKiller.TimeDelta", time_delta);
-    }
-  }
-}
-
-}  // namespace
-
-OomKillsMonitor::Handle::Handle(OomKillsMonitor* outer) : outer_(outer) {
-  DCHECK(outer_);
-}
-
-OomKillsMonitor::Handle::~Handle() {
-  outer_->is_shutting_down_.Set();
-}
-
-OomKillsMonitor::OomKillsMonitor() {
-  base::SimpleThread::Options non_joinable_options;
-  non_joinable_options.joinable = false;
-  non_joinable_worker_thread_ = base::MakeUnique<base::DelegateSimpleThread>(
-      this, "oom_kills_monitor", non_joinable_options);
-  non_joinable_worker_thread_->Start();
-}
-
-OomKillsMonitor::~OomKillsMonitor() {
-  // The instance has to be leaked on shutdown as it is referred to by a
-  // non-joinable thread but ~OomKillsMonitor() can't be explicitly deleted as
-  // it overrides ~SimpleThread(), it should nevertheless never be invoked.
-  NOTREACHED();
-}
-
-// static
-OomKillsMonitor::Handle OomKillsMonitor::StartMonitoring() {
-#if DCHECK_IS_ON()
-  static volatile bool monitoring_active = false;
-  DCHECK(!monitoring_active);
-  monitoring_active = true;
-#endif
-
-  // Instantiate the OomKillsMonitor and its underlying thread. The
-  // OomKillsMonitor itself has to be leaked on shutdown per having a
-  // non-joinable thread associated to its state. The OomKillsMonitor::Handle
-  // will notify the OomKillsMonitor when it is destroyed so that the underlying
-  // thread can at a minimum not do extra work during shutdown.
-  OomKillsMonitor* instance = new OomKillsMonitor;
-  ANNOTATE_LEAKING_OBJECT_PTR(instance);
-  return Handle(instance);
-}
-
-void OomKillsMonitor::Run() {
-  base::ScopedFILE kmsg_handle(
-      base::OpenFile(base::FilePath("/dev/kmsg"), "r"));
-  if (!kmsg_handle) {
-    LOG(WARNING) << "Open /dev/kmsg failed: " << base::safe_strerror(errno);
-    return;
-  }
-  // Skip kernel messages prior to the instantiation of this object to avoid
-  // double reporting.
-  fseek(kmsg_handle.get(), 0, SEEK_END);
-
-  static const int kMaxBufSize = 512;
-  char buf[kMaxBufSize];
-
-  while (fgets(buf, kMaxBufSize, kmsg_handle.get())) {
-    if (is_shutting_down_.IsSet()) {
-      DVLOG(1) << "Chrome is shutting down, exit now.";
-      break;
-    }
-    const StringPiece buf_string(buf);
-    LogOOMKill(buf_string);
-    LogLowMemoryKill(buf_string);
-  }
-}
-
-}  // namespace arc
diff --git a/components/arc/metrics/oom_kills_monitor.h b/components/arc/metrics/oom_kills_monitor.h
deleted file mode 100644
index 63e249e..0000000
--- a/components/arc/metrics/oom_kills_monitor.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_ARC_METRICS_OOM_KILLS_MONITOR_H_
-#define COMPONENTS_ARC_METRICS_OOM_KILLS_MONITOR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/atomic_flag.h"
-#include "base/threading/simple_thread.h"
-
-namespace arc {
-
-// Traces kernel OOM kill events and lowmemorykiller (if enabled) events.
-//
-// OomKillsMonitor listens to kernel messages for both OOM kills and
-// lowmemorykiller kills, then reports to UMA. It uses a non-joinable thread
-// in order to avoid blocking shutdown.
-//
-// Note: There should be only one OomKillsMonitor instance globally at any given
-// time, otherwise UMA would receive duplicate events.
-class OomKillsMonitor : public base::DelegateSimpleThread::Delegate {
- public:
-  // A handle representing the OomKillsMonitor's lifetime (the monitor itself
-  // can't be destroyed per being a non-joinable Thread).
-  class Handle {
-   public:
-    // Constructs a handle that will flag |outer| as shutting down on
-    // destruction.
-    explicit Handle(OomKillsMonitor* outer);
-
-    ~Handle();
-
-   private:
-    OomKillsMonitor* const outer_;
-  };
-
-  // Instantiates the OomKillsMonitor instance and starts it. This must only
-  // be invoked once per process.
-  static Handle StartMonitoring();
-
- private:
-  OomKillsMonitor();
-  ~OomKillsMonitor() override;
-
-  // Overridden from base::DelegateSimpleThread::Delegate:
-  void Run() override;
-
-  // A flag set when OomKillsMonitor is shutdown so that its thread can poll
-  // it and attempt to wind down from that point (to avoid unnecessary work, not
-  // because it blocks shutdown).
-  base::AtomicFlag is_shutting_down_;
-
-  // The underlying worker thread which is non-joinable to avoid blocking
-  // shutdown.
-  std::unique_ptr<base::DelegateSimpleThread> non_joinable_worker_thread_;
-
-  DISALLOW_COPY_AND_ASSIGN(OomKillsMonitor);
-};
-
-}  // namespace arc
-
-#endif  // COMPONENTS_ARC_METRICS_OOM_KILLS_MONITOR_H_
diff --git a/components/arc/test/fake_app_instance.cc b/components/arc/test/fake_app_instance.cc
index 6e9cdec..7ae00a63 100644
--- a/components/arc/test/fake_app_instance.cc
+++ b/components/arc/test/fake_app_instance.cc
@@ -93,8 +93,13 @@
 }
 
 void FakeAppInstance::SendTaskCreated(int32_t taskId,
-                                      const mojom::AppInfo& app) {
-  app_host_->OnTaskCreated(taskId, app.package_name, app.activity, app.name);
+                                      const mojom::AppInfo& app,
+                                      const std::string& intent) {
+  app_host_->OnTaskCreated(taskId,
+                           app.package_name,
+                           app.activity,
+                           app.name,
+                           intent);
 }
 
 void FakeAppInstance::SendTaskDestroyed(int32_t taskId) {
diff --git a/components/arc/test/fake_app_instance.h b/components/arc/test/fake_app_instance.h
index c487676..eeaac8d 100644
--- a/components/arc/test/fake_app_instance.h
+++ b/components/arc/test/fake_app_instance.h
@@ -118,7 +118,9 @@
   void SendAppAdded(const mojom::AppInfo& app);
   void SendPackageAppListRefreshed(const std::string& package_name,
                                    const std::vector<mojom::AppInfo>& apps);
-  void SendTaskCreated(int32_t taskId, const mojom::AppInfo& app);
+  void SendTaskCreated(int32_t taskId,
+                       const mojom::AppInfo& app,
+                       const std::string& intent);
   void SendTaskDestroyed(int32_t taskId);
   bool GenerateAndSendIcon(const mojom::AppInfo& app,
                            mojom::ScaleFactor scale_factor,
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 3ba8227..725024e 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -270,6 +270,7 @@
   java_files = [
     "api/src/org/chromium/net/BidirectionalStream.java",
     "api/src/org/chromium/net/CronetEngine.java",
+    "api/src/org/chromium/net/CallbackException.java",
     "api/src/org/chromium/net/CronetException.java",
     "api/src/org/chromium/net/ICronetEngineBuilder.java",
     "api/src/org/chromium/net/ImplLoader.java",
@@ -277,6 +278,7 @@
     "api/src/org/chromium/net/ExperimentalCronetEngine.java",
     "api/src/org/chromium/net/ExperimentalUrlRequest.java",
     "api/src/org/chromium/net/InlineExecutionProhibitedException.java",
+    "api/src/org/chromium/net/NetworkException.java",
     "api/src/org/chromium/net/NetworkQualityRttListener.java",
     "api/src/org/chromium/net/NetworkQualityThroughputListener.java",
     "api/src/org/chromium/net/QuicException.java",
@@ -310,9 +312,13 @@
 # by all Cronet engine implementations.
 android_library("cronet_impl_common_java") {
   java_files = [
+    "java/src/org/chromium/net/impl/CallbackExceptionImpl.java",
+    "java/src/org/chromium/net/impl/CronetExceptionImpl.java",
     "java/src/org/chromium/net/impl/CronetEngineBase.java",
     "java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java",
+    "java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
     "java/src/org/chromium/net/impl/Preconditions.java",
+    "java/src/org/chromium/net/impl/QuicExceptionImpl.java",
     "java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java",
     "java/src/org/chromium/net/impl/UrlRequestBase.java",
     "java/src/org/chromium/net/impl/UrlRequestBuilderImpl.java",
diff --git a/components/cronet/android/api/src/org/chromium/net/CallbackException.java b/components/cronet/android/api/src/org/chromium/net/CallbackException.java
new file mode 100644
index 0000000..0e1fa61e
--- /dev/null
+++ b/components/cronet/android/api/src/org/chromium/net/CallbackException.java
@@ -0,0 +1,24 @@
+// 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.
+
+package org.chromium.net;
+
+/**
+ * Exception passed to {@link UrlRequest.Callback#onFailed UrlRequest.Callback.onFailed()} when
+ * {@link UrlRequest.Callback} or {@link UploadDataProvider} method throws an exception. In this
+ * case {@link java.io.IOException#getCause getCause()} can be used to find the thrown
+ * exception.
+ */
+public abstract class CallbackException extends CronetException {
+    /**
+      * Constructs an exception that wraps {@code cause} thrown by a {@link UrlRequest.Callback}.
+      *
+      * @param message explanation of failure.
+      * @param cause exception thrown by {@link UrlRequest.Callback} that's being wrapped. It is
+      *        saved for later retrieval by the {@link java.io.IOException#getCause getCause()}.
+      */
+    protected CallbackException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/components/cronet/android/api/src/org/chromium/net/CronetException.java b/components/cronet/android/api/src/org/chromium/net/CronetException.java
index 8eec778..d21a424 100644
--- a/components/cronet/android/api/src/org/chromium/net/CronetException.java
+++ b/components/cronet/android/api/src/org/chromium/net/CronetException.java
@@ -4,22 +4,21 @@
 
 package org.chromium.net;
 
-/**
- * Exception reported from UrlRequest or BidirectionalStream.
- */
-// TODO(mef): Will replace UrlRequestException soon.
-public class CronetException extends UrlRequestException {
-    /**
-     * @hide only used by internal implementation.
-     */
-    public CronetException(String message, Throwable cause) {
-        super(message, cause);
-    }
+import java.io.IOException;
 
+/**
+ * Base exception passed to {@link UrlRequest.Callback#onFailed UrlRequest.Callback.onFailed()}.
+ */
+public abstract class CronetException extends IOException {
     /**
-     * @hide only used by internal implementation.
+     * Constructs an exception that is caused by {@code cause}.
+     *
+     * @param message explanation of failure.
+     * @param cause the cause (which is saved for later retrieval by the {@link
+     *         java.io.IOException#getCause getCause()} method). A null value is permitted, and
+     *         indicates that the cause is nonexistent or unknown.
      */
-    public CronetException(String message, int errorCode, int cronetInternalErrorCode) {
-        super(message, errorCode, cronetInternalErrorCode);
+    protected CronetException(String message, Throwable cause) {
+        super(message, cause);
     }
 }
diff --git a/components/cronet/android/api/src/org/chromium/net/NetworkException.java b/components/cronet/android/api/src/org/chromium/net/NetworkException.java
new file mode 100644
index 0000000..7458f0fd
--- /dev/null
+++ b/components/cronet/android/api/src/org/chromium/net/NetworkException.java
@@ -0,0 +1,108 @@
+// 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.
+
+package org.chromium.net;
+
+/**
+ * Exception passed to {@link UrlRequest.Callback#onFailed UrlRequest.Callback.onFailed()} when
+ * Cronet fails to process a network request. In this case {@link #getErrorCode} and
+ * {@link #getCronetInternalErrorCode} can be used to get more information about the specific
+ * type of failure. If {@link #getErrorCode} returns {@link #ERROR_QUIC_PROTOCOL_FAILED},
+ * this exception can be cast to a {@link QuicException} which can provide further details.
+ */
+public abstract class NetworkException extends CronetException {
+    /**
+     * Error code indicating the host being sent the request could not be resolved to an IP address.
+     */
+    public static final int ERROR_HOSTNAME_NOT_RESOLVED = 1;
+    /**
+     * Error code indicating the device was not connected to any network.
+     */
+    public static final int ERROR_INTERNET_DISCONNECTED = 2;
+    /**
+     * Error code indicating that as the request was processed the network configuration changed.
+     */
+    public static final int ERROR_NETWORK_CHANGED = 3;
+    /**
+     * Error code indicating a timeout expired. Timeouts expiring while attempting to connect will
+     * be reported as the more specific {@link #ERROR_CONNECTION_TIMED_OUT}.
+     */
+    public static final int ERROR_TIMED_OUT = 4;
+    /**
+     * Error code indicating the connection was closed unexpectedly.
+     */
+    public static final int ERROR_CONNECTION_CLOSED = 5;
+    /**
+     * Error code indicating the connection attempt timed out.
+     */
+    public static final int ERROR_CONNECTION_TIMED_OUT = 6;
+    /**
+     * Error code indicating the connection attempt was refused.
+     */
+    public static final int ERROR_CONNECTION_REFUSED = 7;
+    /**
+     * Error code indicating the connection was unexpectedly reset.
+     */
+    public static final int ERROR_CONNECTION_RESET = 8;
+    /**
+     * Error code indicating the IP address being contacted is unreachable, meaning there is no
+     * route to the specified host or network.
+     */
+    public static final int ERROR_ADDRESS_UNREACHABLE = 9;
+    /**
+     * Error code indicating an error related to the <a href="https://www.chromium.org/quic">
+     * QUIC</a> protocol. When {@link #getErrorCode} returns this code, this exception can be cast
+     * to {@link QuicException} for more information.
+     */
+    public static final int ERROR_QUIC_PROTOCOL_FAILED = 10;
+    /**
+     * Error code indicating another type of error was encountered.
+     * {@link #getCronetInternalErrorCode} can be consulted to get a more specific cause.
+     */
+    public static final int ERROR_OTHER = 11;
+
+    /**
+     * Constructs an exception that is caused by a network error.
+     *
+     * @param message explanation of failure.
+     * @param cause the cause (which is saved for later retrieval by the {@link
+     *         java.io.IOException#getCause getCause()} method). A null value is permitted, and
+     *         indicates that the cause is nonexistent or unknown.
+     */
+    protected NetworkException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Returns error code, one of {@link #ERROR_HOSTNAME_NOT_RESOLVED ERROR_*}.
+     *
+     * @return error code, one of {@link #ERROR_HOSTNAME_NOT_RESOLVED ERROR_*}.
+     */
+    public abstract int getErrorCode();
+
+    /**
+     * Returns a Cronet internal error code. This may provide more specific error
+     * diagnosis than {@link #getErrorCode}, but the constant values are not exposed to Java and
+     * may change over time. See
+     * <a href=https://chromium.googlesource.com/chromium/src/+/master/net/base/net_error_list.h>
+     * here</a> for the lastest list of values.
+     *
+     * @return Cronet internal error code.
+     */
+    public abstract int getCronetInternalErrorCode();
+
+    /**
+     * Returns {@code true} if retrying this request right away might succeed, {@code false}
+     * otherwise. For example returns {@code true} when {@link #getErrorCode} returns
+     * {@link #ERROR_NETWORK_CHANGED} because trying the request might succeed using the new
+     * network configuration, but {@code false} when {@code getErrorCode()} returns
+     * {@link #ERROR_INTERNET_DISCONNECTED} because retrying the request right away will
+     * encounter the same failure (instead retrying should be delayed until device regains
+     * network connectivity).
+     *
+     * @return {@code true} if retrying this request right away might succeed, {@code false}
+     *         otherwise.
+     */
+    public abstract boolean immediatelyRetryable();
+}
diff --git a/components/cronet/android/api/src/org/chromium/net/QuicException.java b/components/cronet/android/api/src/org/chromium/net/QuicException.java
index 54f3626..b302cee 100644
--- a/components/cronet/android/api/src/org/chromium/net/QuicException.java
+++ b/components/cronet/android/api/src/org/chromium/net/QuicException.java
@@ -5,31 +5,24 @@
 package org.chromium.net;
 
 /**
- * Subclass of {@link UrlRequestException} which contains a detailed
+ * Subclass of {@link NetworkException} which contains a detailed
  * <a href="https://www.chromium.org/quic">QUIC</a> error code from <a
  * href=https://cs.chromium.org/chromium/src/net/quic/quic_protocol.h?type=cs&q=%22enum+QuicErrorCode+%7B%22+file:src/net/quic/quic_protocol.h>
  * QuicErrorCode</a>. An instance of {@code QuicException} is passed to {@code onFailed} callbacks
- * when the error code is {@link UrlRequestException#ERROR_QUIC_PROTOCOL_FAILED
- * UrlRequestException.ERROR_QUIC_PROTOCOL_FAILED}.
+ * when the error code is {@link NetworkException#ERROR_QUIC_PROTOCOL_FAILED
+ * NetworkException.ERROR_QUIC_PROTOCOL_FAILED}.
  */
-public class QuicException extends CronetException {
-    private final int mQuicDetailedErrorCode;
-
+public abstract class QuicException extends NetworkException {
     /**
-     * Constructs an exception with a specific error.
+     * Constructs an exception that is caused by a QUIC protocol error.
      *
      * @param message explanation of failure.
-     * @param netErrorCode Error code from
-     * <a href=https://chromium.googlesource.com/chromium/src/+/master/net/base/net_error_list.h>
-     * this list</a>.
-     * @param quicDetailedErrorCode Detailed <a href="https://www.chromium.org/quic">QUIC</a> error
-     * code from <a
-     * href=https://cs.chromium.org/chromium/src/net/quic/quic_protocol.h?type=cs&q=%22enum+QuicErrorCode+%7B%22+file:src/net/quic/quic_protocol.h>
-     * QuicErrorCode</a>.
+     * @param cause the cause (which is saved for later retrieval by the {@link
+     *         java.io.IOException#getCause getCause()} method). A null value is permitted, and
+     *         indicates that the cause is nonexistent or unknown.
      */
-    public QuicException(String message, int netErrorCode, int quicDetailedErrorCode) {
-        super(message, ERROR_QUIC_PROTOCOL_FAILED, netErrorCode);
-        mQuicDetailedErrorCode = quicDetailedErrorCode;
+    protected QuicException(String message, Throwable cause) {
+        super(message, cause);
     }
 
     /**
@@ -38,7 +31,5 @@
      * href=https://cs.chromium.org/chromium/src/net/quic/quic_protocol.h?type=cs&q=%22enum+QuicErrorCode+%7B%22+file:src/net/quic/quic_protocol.h>
      * QuicErrorCode</a>.
      */
-    public int getQuicDetailedErrorCode() {
-        return mQuicDetailedErrorCode;
-    }
+    public abstract int getQuicDetailedErrorCode();
 }
diff --git a/components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java b/components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java
index 595b436..4d9ae5e 100644
--- a/components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java
+++ b/components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java
@@ -294,11 +294,11 @@
     public abstract UrlResponseInfo getResponseInfo();
 
     /**
-     * If the request failed, returns the same {@link UrlRequestException} provided to
+     * If the request failed, returns the same {@link CronetException} provided to
      * {@link UrlRequest.Callback#onFailed}.
      *
-     * @return the request's {@link UrlRequestException}, if the request failed
+     * @return the request's {@link CronetException}, if the request failed
      */
     @Nullable
-    public abstract UrlRequestException getException();
+    public abstract CronetException getException();
 }
diff --git a/components/cronet/android/api/src/org/chromium/net/UploadDataProvider.java b/components/cronet/android/api/src/org/chromium/net/UploadDataProvider.java
index 352a45c..03edd98 100644
--- a/components/cronet/android/api/src/org/chromium/net/UploadDataProvider.java
+++ b/components/cronet/android/api/src/org/chromium/net/UploadDataProvider.java
@@ -46,7 +46,7 @@
      * @throws IOException if any IOException occurred during the process.
      *         {@link UrlRequest.Callback#onFailed} will be called with the
      *         thrown exception set as the cause of the
-     *         {@link UrlRequestException}.
+     *         {@link CallbackException}.
      */
     public abstract void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer)
             throws IOException;
@@ -72,7 +72,7 @@
      * @throws IOException if any IOException occurred during the process.
      *         {@link UrlRequest.Callback#onFailed} will be called with the
      *         thrown exception set as the cause of the
-     *         {@link UrlRequestException}.
+     *         {@link CallbackException}.
      */
     public abstract void rewind(UploadDataSink uploadDataSink) throws IOException;
 
diff --git a/components/cronet/android/api/src/org/chromium/net/UrlRequest.java b/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
index 20ee1e9..726e10ec 100644
--- a/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
+++ b/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
@@ -145,7 +145,7 @@
          * @param newLocationUrl Location where request is redirected.
          * @throws Exception if an error occurs while processing a redirect. {@link #onFailed}
          *         will be called with the thrown exception set as the cause of the
-         *         {@link UrlRequestException}.
+         *         {@link CallbackException}.
          */
         public abstract void onRedirectReceived(
                 UrlRequest request, UrlResponseInfo info, String newLocationUrl) throws Exception;
@@ -165,7 +165,7 @@
          * @param info Response information.
          * @throws Exception if an error occurs while processing response start. {@link #onFailed}
          *         will be called with the thrown exception set as the cause of the
-         *         {@link UrlRequestException}.
+         *         {@link CallbackException}.
          */
         public abstract void onResponseStarted(UrlRequest request, UrlResponseInfo info)
                 throws Exception;
@@ -190,7 +190,7 @@
          *         the received data. The buffer's limit is not changed.
          * @throws Exception if an error occurs while processing a read completion.
          *         {@link #onFailed} will be called with the thrown exception set as the cause of
-         *         the {@link UrlRequestException}.
+         *         the {@link CallbackException}.
          */
         public abstract void onReadCompleted(
                 UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) throws Exception;
@@ -214,8 +214,11 @@
          *         received.
          * @param error information about error.
          */
-        public abstract void onFailed(
-                UrlRequest request, UrlResponseInfo info, UrlRequestException error);
+        public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
+            // TODO(mef): Remove fallback to legacy api and make this method abstract
+            // after complete transition to CronetException.
+            onFailed(request, info, new UrlRequestException(error));
+        }
 
         /**
          * Invoked if request was canceled via {@link UrlRequest#cancel}. Once
@@ -227,6 +230,16 @@
          *         received.
          */
         public void onCanceled(UrlRequest request, UrlResponseInfo info) {}
+
+        /**
+         * @deprecated Use {@code onFailed} instead.
+         * {@hide This method will be removed after complete transition to CronetException}.
+         */
+        @Deprecated
+        // TODO(mef): Remove this after complete transition to CronetException.
+        public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+            assert false;
+        }
     }
 
     /**
diff --git a/components/cronet/android/api/src/org/chromium/net/UrlRequestException.java b/components/cronet/android/api/src/org/chromium/net/UrlRequestException.java
index 03f638b..0bca019 100644
--- a/components/cronet/android/api/src/org/chromium/net/UrlRequestException.java
+++ b/components/cronet/android/api/src/org/chromium/net/UrlRequestException.java
@@ -7,18 +7,10 @@
 import java.io.IOException;
 
 /**
- * Exception passed to {@link UrlRequest.Callback#onFailed UrlRequest.Callback.onFailed()} when:
- * <ul>
- * <li>{@link UrlRequest.Callback} or {@link UploadDataProvider} method throws an exception. In this
- *     case {@link IOException#getCause getCause()} can be used to find the thrown exception.
- *     {@link #getErrorCode} will return {@link #ERROR_LISTENER_EXCEPTION_THROWN}.
- * <li>Cronet fails to process a network request. In this case
- *     {@link #getErrorCode} and {@link #getCronetInternalErrorCode} can be used to get more
- *     information about the specific type of failure. If {@link #getErrorCode}
- *     returns {@link #ERROR_QUIC_PROTOCOL_FAILED}, this exception can be cast to a
- *     {@link QuicException} which can provide further details.
- * </ul>
+ * @deprecated Use {@link CronetException} instead.
+ * {@hide This class will be removed after complete transition to CronetException}.
  */
+@Deprecated
 public class UrlRequestException extends IOException {
     /**
      * Error code indicating this class wraps an exception thrown by {@link UrlRequest.Callback} or
@@ -81,31 +73,15 @@
     // Cronet internal error code.
     private final int mCronetInternalErrorCode;
 
-    /**
-     * Constructs an exception with a specific error.
-     *
-     * @param message explanation of failure.
-     * @param errorCode error code, one of {@link #ERROR_LISTENER_EXCEPTION_THROWN ERROR_*}.
-     * @param cronetInternalErrorCode Cronet internal error code, one of
-     * <a href=https://chromium.googlesource.com/chromium/src/+/master/net/base/net_error_list.h>
-     * these</a>.
-     */
-    public UrlRequestException(String message, int errorCode, int cronetInternalErrorCode) {
-        super(message, null);
-        mErrorCode = errorCode;
-        mCronetInternalErrorCode = cronetInternalErrorCode;
-    }
-
-    /**
-     * Constructs an exception that wraps {@code cause} thrown by a {@link UrlRequest.Callback}.
-     *
-     * @param message explanation of failure.
-     * @param cause exception thrown by {@link UrlRequest.Callback} that's being wrapped.
-     */
-    public UrlRequestException(String message, Throwable cause) {
-        super(message, cause);
-        mErrorCode = ERROR_LISTENER_EXCEPTION_THROWN;
-        mCronetInternalErrorCode = 0;
+    public UrlRequestException(CronetException error) {
+        super(error.getMessage(), error.getCause());
+        if (error instanceof NetworkException) {
+            mErrorCode = ((NetworkException) error).getErrorCode();
+            mCronetInternalErrorCode = ((NetworkException) error).getCronetInternalErrorCode();
+        } else {
+            mErrorCode = 0;
+            mCronetInternalErrorCode = ERROR_LISTENER_EXCEPTION_THROWN;
+        }
     }
 
     /**
@@ -161,15 +137,4 @@
                 return true;
         }
     }
-
-    @Override
-    public String getMessage() {
-        StringBuilder b = new StringBuilder(super.getMessage());
-        b.append(", ErrorCode=").append(mErrorCode);
-        if (mCronetInternalErrorCode != 0) {
-            b.append(", InternalErrorCode=").append(mCronetInternalErrorCode);
-        }
-        b.append(", Retryable=").append(immediatelyRetryable());
-        return b.toString();
-    }
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CallbackExceptionImpl.java b/components/cronet/android/java/src/org/chromium/net/impl/CallbackExceptionImpl.java
new file mode 100644
index 0000000..94009eb
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CallbackExceptionImpl.java
@@ -0,0 +1,16 @@
+// 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.
+
+package org.chromium.net.impl;
+
+import org.chromium.net.CallbackException;
+
+/**
+ * An implementation of {@link CallbackException}.
+ */
+public class CallbackExceptionImpl extends CallbackException {
+    public CallbackExceptionImpl(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
index cee01da2..780fbe2c7 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
@@ -10,12 +10,12 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeClassQualifiedName;
 import org.chromium.net.BidirectionalStream;
+import org.chromium.net.CallbackException;
 import org.chromium.net.CronetException;
 import org.chromium.net.ExperimentalBidirectionalStream;
-import org.chromium.net.QuicException;
+import org.chromium.net.NetworkException;
 import org.chromium.net.RequestFinishedInfo;
 import org.chromium.net.RequestPriority;
-import org.chromium.net.UrlRequestException;
 import org.chromium.net.UrlResponseInfo;
 
 import java.nio.ByteBuffer;
@@ -86,7 +86,7 @@
     private final String mRequestHeaders[];
     private final boolean mDelayRequestHeadersUntilFirstFlush;
     private final Collection<Object> mRequestAnnotations;
-    private UrlRequestException mException;
+    private CronetException mException;
 
     /*
      * Synchronizes access to mNativeStream, mReadState and mWriteState.
@@ -496,7 +496,7 @@
             mResponseInfo = prepareResponseInfoOnNetworkThread(
                     httpStatusCode, negotiatedProtocol, headers, receivedBytesCount);
         } catch (Exception e) {
-            failWithException(new CronetException("Cannot prepare ResponseInfo", null));
+            failWithException(new CronetExceptionImpl("Cannot prepare ResponseInfo", null));
             return;
         }
         postTaskToExecutor(new Runnable() {
@@ -525,11 +525,11 @@
         mResponseInfo.setReceivedBytesCount(receivedBytesCount);
         if (byteBuffer.position() != initialPosition || byteBuffer.limit() != initialLimit) {
             failWithException(
-                    new CronetException("ByteBuffer modified externally during read", null));
+                    new CronetExceptionImpl("ByteBuffer modified externally during read", null));
             return;
         }
         if (bytesRead < 0 || initialPosition + bytesRead > initialLimit) {
-            failWithException(new CronetException("Invalid number of bytes read", null));
+            failWithException(new CronetExceptionImpl("Invalid number of bytes read", null));
             return;
         }
         byteBuffer.position(initialPosition + bytesRead);
@@ -555,8 +555,8 @@
         for (int i = 0; i < byteBuffers.length; i++) {
             ByteBuffer buffer = byteBuffers[i];
             if (buffer.position() != initialPositions[i] || buffer.limit() != initialLimits[i]) {
-                failWithException(
-                        new CronetException("ByteBuffer modified externally during write", null));
+                failWithException(new CronetExceptionImpl(
+                        "ByteBuffer modified externally during write", null));
                 return;
             }
             // Current implementation always writes the complete buffer.
@@ -596,11 +596,12 @@
         if (mResponseInfo != null) {
             mResponseInfo.setReceivedBytesCount(receivedBytesCount);
         }
-        if (errorCode == UrlRequestException.ERROR_QUIC_PROTOCOL_FAILED) {
-            failWithException(new QuicException("Exception in BidirectionalStream: " + errorString,
-                    nativeError, nativeQuicError));
+        if (errorCode == NetworkException.ERROR_QUIC_PROTOCOL_FAILED) {
+            failWithException(
+                    new QuicExceptionImpl("Exception in BidirectionalStream: " + errorString,
+                            nativeError, nativeQuicError));
         } else {
-            failWithException(new CronetException(
+            failWithException(new NetworkExceptionImpl(
                     "Exception in BidirectionalStream: " + errorString, errorCode, nativeError));
         }
     }
@@ -770,8 +771,8 @@
      * Only called on the Executor.
      */
     private void onCallbackException(Exception e) {
-        CronetException streamError =
-                new CronetException("CalledByNative method has thrown an exception", e);
+        CallbackException streamError =
+                new CallbackExceptionImpl("CalledByNative method has thrown an exception", e);
         Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative method", e);
         failWithExceptionOnExecutor(streamError);
     }
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetExceptionImpl.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetExceptionImpl.java
new file mode 100644
index 0000000..de47b14
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetExceptionImpl.java
@@ -0,0 +1,16 @@
+// 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.
+
+package org.chromium.net.impl;
+
+import org.chromium.net.CronetException;
+
+/**
+ * Implements {@link CronetException}.
+ */
+public class CronetExceptionImpl extends CronetException {
+    public CronetExceptionImpl(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
index 6585fd5..56ecf16 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
@@ -10,13 +10,14 @@
 import org.chromium.base.annotations.JNIAdditionalImport;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeClassQualifiedName;
+import org.chromium.net.CallbackException;
+import org.chromium.net.CronetException;
 import org.chromium.net.InlineExecutionProhibitedException;
-import org.chromium.net.QuicException;
+import org.chromium.net.NetworkException;
 import org.chromium.net.RequestFinishedInfo;
 import org.chromium.net.RequestPriority;
 import org.chromium.net.UploadDataProvider;
 import org.chromium.net.UrlRequest;
-import org.chromium.net.UrlRequestException;
 
 import java.nio.ByteBuffer;
 import java.util.AbstractMap;
@@ -82,7 +83,7 @@
     private final Collection<Object> mRequestAnnotations;
     @RequestFinishedInfoImpl.FinishedReason
     private int mFinishedReason;
-    private UrlRequestException mException;
+    private CronetException mException;
     private final boolean mDisableCache;
     private final boolean mDisableConnectionMigration;
 
@@ -368,7 +369,7 @@
             // request's failure, since failWithException does not enforce that onFailed() is not
             // executed inline.
             failWithException(
-                    new UrlRequestException("Exception posting task to executor", failException));
+                    new CronetExceptionImpl("Exception posting task to executor", failException));
         }
     }
 
@@ -429,8 +430,8 @@
      * Only called on the Executor.
      */
     private void onCallbackException(Exception e) {
-        UrlRequestException requestError =
-                new UrlRequestException("Exception received from UrlRequest.Callback", e);
+        CallbackException requestError =
+                new CallbackExceptionImpl("Exception received from UrlRequest.Callback", e);
         Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative method", e);
         failWithException(requestError);
     }
@@ -439,8 +440,8 @@
      * Called when UploadDataProvider encounters an error.
      */
     void onUploadException(Throwable e) {
-        UrlRequestException uploadError =
-                new UrlRequestException("Exception received from UploadDataProvider", e);
+        CallbackException uploadError =
+                new CallbackExceptionImpl("Exception received from UploadDataProvider", e);
         Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in upload method", e);
         failWithException(uploadError);
     }
@@ -448,7 +449,7 @@
     /**
      * Fails the request with an exception. Can be called on any thread.
      */
-    private void failWithException(final UrlRequestException exception) {
+    private void failWithException(final CronetException exception) {
         mException = exception;
         synchronized (mUrlRequestAdapterLock) {
             if (isDoneLocked()) {
@@ -579,7 +580,7 @@
         mResponseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects + receivedBytesCount);
         if (byteBuffer.position() != initialPosition || byteBuffer.limit() != initialLimit) {
             failWithException(
-                    new UrlRequestException("ByteBuffer modified externally during read", null));
+                    new CronetExceptionImpl("ByteBuffer modified externally during read", null));
             return;
         }
         if (mOnReadCompletedTask == null) {
@@ -626,8 +627,8 @@
      * Called when error has occured, no callbacks will be called afterwards.
      *
      * @param errorCode Error code represented by {@code UrlRequestError} that should be mapped
-     *                  to one of {@link UrlRequestException#ERROR_LISTENER_EXCEPTION_THROWN
-     *                  UrlRequestException.ERROR_*}.
+     *                  to one of {@link NetworkException#ERROR_HOSTNAME_NOT_RESOLVED
+     *                  NetworkException.ERROR_*}.
      * @param nativeError native net error code.
      * @param errorString textual representation of the error code.
      * @param receivedBytesCount number of bytes received.
@@ -641,12 +642,12 @@
             mResponseInfo.setReceivedBytesCount(
                     mReceivedBytesCountFromRedirects + receivedBytesCount);
         }
-        if (errorCode == UrlRequestException.ERROR_QUIC_PROTOCOL_FAILED) {
-            failWithException(new QuicException(
+        if (errorCode == NetworkException.ERROR_QUIC_PROTOCOL_FAILED) {
+            failWithException(new QuicExceptionImpl(
                     "Exception in CronetUrlRequest: " + errorString, nativeError, nativeQuicError));
         } else {
             int javaError = mapUrlRequestErrorToApiErrorCode(errorCode);
-            failWithException(new UrlRequestException(
+            failWithException(new NetworkExceptionImpl(
                     "Exception in CronetUrlRequest: " + errorString, javaError, nativeError));
         }
     }
@@ -724,30 +725,28 @@
 
     private int mapUrlRequestErrorToApiErrorCode(int errorCode) {
         switch (errorCode) {
-            case UrlRequestError.LISTENER_EXCEPTION_THROWN:
-                return UrlRequestException.ERROR_LISTENER_EXCEPTION_THROWN;
             case UrlRequestError.HOSTNAME_NOT_RESOLVED:
-                return UrlRequestException.ERROR_HOSTNAME_NOT_RESOLVED;
+                return NetworkException.ERROR_HOSTNAME_NOT_RESOLVED;
             case UrlRequestError.INTERNET_DISCONNECTED:
-                return UrlRequestException.ERROR_INTERNET_DISCONNECTED;
+                return NetworkException.ERROR_INTERNET_DISCONNECTED;
             case UrlRequestError.NETWORK_CHANGED:
-                return UrlRequestException.ERROR_NETWORK_CHANGED;
+                return NetworkException.ERROR_NETWORK_CHANGED;
             case UrlRequestError.TIMED_OUT:
-                return UrlRequestException.ERROR_TIMED_OUT;
+                return NetworkException.ERROR_TIMED_OUT;
             case UrlRequestError.CONNECTION_CLOSED:
-                return UrlRequestException.ERROR_CONNECTION_CLOSED;
+                return NetworkException.ERROR_CONNECTION_CLOSED;
             case UrlRequestError.CONNECTION_TIMED_OUT:
-                return UrlRequestException.ERROR_CONNECTION_TIMED_OUT;
+                return NetworkException.ERROR_CONNECTION_TIMED_OUT;
             case UrlRequestError.CONNECTION_REFUSED:
-                return UrlRequestException.ERROR_CONNECTION_REFUSED;
+                return NetworkException.ERROR_CONNECTION_REFUSED;
             case UrlRequestError.CONNECTION_RESET:
-                return UrlRequestException.ERROR_CONNECTION_RESET;
+                return NetworkException.ERROR_CONNECTION_RESET;
             case UrlRequestError.ADDRESS_UNREACHABLE:
-                return UrlRequestException.ERROR_ADDRESS_UNREACHABLE;
+                return NetworkException.ERROR_ADDRESS_UNREACHABLE;
             case UrlRequestError.QUIC_PROTOCOL_FAILED:
-                return UrlRequestException.ERROR_QUIC_PROTOCOL_FAILED;
+                return NetworkException.ERROR_QUIC_PROTOCOL_FAILED;
             case UrlRequestError.OTHER:
-                return UrlRequestException.ERROR_OTHER;
+                return NetworkException.ERROR_OTHER;
             default:
                 Log.e(CronetUrlRequestContext.LOG_TAG, "Unknown error code: " + errorCode);
                 return errorCode;
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequest.java
index 04897de..2b81e3e 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequest.java
@@ -11,10 +11,10 @@
 
 import android.util.Log;
 
+import org.chromium.net.CronetException;
 import org.chromium.net.InlineExecutionProhibitedException;
 import org.chromium.net.UploadDataProvider;
 import org.chromium.net.UploadDataSink;
-import org.chromium.net.UrlRequestException;
 import org.chromium.net.UrlResponseInfo;
 
 import java.io.Closeable;
@@ -452,7 +452,7 @@
         });
     }
 
-    private void enterErrorState(final UrlRequestException error) {
+    private void enterErrorState(final CronetException error) {
         if (setTerminalState(State.ERROR)) {
             fireDisconnect();
             fireCloseUploadDataProvider();
@@ -482,19 +482,19 @@
     /** Ends the request with an error, caused by an exception thrown from user code. */
     private void enterUserErrorState(final Throwable error) {
         enterErrorState(
-                new UrlRequestException("Exception received from UrlRequest.Callback", error));
+                new CallbackExceptionImpl("Exception received from UrlRequest.Callback", error));
     }
 
     /** Ends the request with an error, caused by an exception thrown from user code. */
     private void enterUploadErrorState(final Throwable error) {
         enterErrorState(
-                new UrlRequestException("Exception received from UploadDataProvider", error));
+                new CallbackExceptionImpl("Exception received from UploadDataProvider", error));
     }
 
     private void enterCronetErrorState(final Throwable error) {
         // TODO(clm) mapping from Java exception (UnknownHostException, for example) to net error
         // code goes here.
-        enterErrorState(new UrlRequestException("System error", error));
+        enterErrorState(new CronetExceptionImpl("System error", error));
     }
 
     /**
@@ -827,7 +827,7 @@
             try {
                 mUserExecutor.execute(userErrorSetting(runnable));
             } catch (RejectedExecutionException e) {
-                enterErrorState(new UrlRequestException("Exception posting task to executor", e));
+                enterErrorState(new CronetExceptionImpl("Exception posting task to executor", e));
             }
         }
 
@@ -889,7 +889,7 @@
             });
         }
 
-        void onFailed(final UrlResponseInfo urlResponseInfo, final UrlRequestException e) {
+        void onFailed(final UrlResponseInfo urlResponseInfo, final CronetException e) {
             closeResponseChannel();
             Runnable runnable = new Runnable() {
                 @Override
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java b/components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java
new file mode 100644
index 0000000..4c328ab
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java
@@ -0,0 +1,72 @@
+// 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.
+
+package org.chromium.net.impl;
+
+import org.chromium.net.NetworkException;
+
+/**
+ * Implements {@link NetworkException}.
+ */
+public class NetworkExceptionImpl extends NetworkException {
+    // Error code, one of ERROR_*
+    private final int mErrorCode;
+    // Cronet internal error code.
+    private final int mCronetInternalErrorCode;
+
+    /**
+     * Constructs an exception with a specific error.
+     *
+     * @param message explanation of failure.
+     * @param errorCode error code, one of {@link #ERROR_HOSTNAME_NOT_RESOLVED ERROR_*}.
+     * @param cronetInternalErrorCode Cronet internal error code, one of
+     * <a href=https://chromium.googlesource.com/chromium/src/+/master/net/base/net_error_list.h>
+     * these</a>.
+     */
+    public NetworkExceptionImpl(String message, int errorCode, int cronetInternalErrorCode) {
+        super(message, null);
+        mErrorCode = errorCode;
+        mCronetInternalErrorCode = cronetInternalErrorCode;
+    }
+
+    @Override
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+
+    @Override
+    public int getCronetInternalErrorCode() {
+        return mCronetInternalErrorCode;
+    }
+
+    @Override
+    public boolean immediatelyRetryable() {
+        switch (mErrorCode) {
+            case ERROR_HOSTNAME_NOT_RESOLVED:
+            case ERROR_INTERNET_DISCONNECTED:
+            case ERROR_CONNECTION_REFUSED:
+            case ERROR_ADDRESS_UNREACHABLE:
+            case ERROR_OTHER:
+            default:
+                return false;
+            case ERROR_NETWORK_CHANGED:
+            case ERROR_TIMED_OUT:
+            case ERROR_CONNECTION_CLOSED:
+            case ERROR_CONNECTION_TIMED_OUT:
+            case ERROR_CONNECTION_RESET:
+                return true;
+        }
+    }
+
+    @Override
+    public String getMessage() {
+        StringBuilder b = new StringBuilder(super.getMessage());
+        b.append(", ErrorCode=").append(mErrorCode);
+        if (mCronetInternalErrorCode != 0) {
+            b.append(", InternalErrorCode=").append(mCronetInternalErrorCode);
+        }
+        b.append(", Retryable=").append(immediatelyRetryable());
+        return b.toString();
+    }
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/QuicExceptionImpl.java b/components/cronet/android/java/src/org/chromium/net/impl/QuicExceptionImpl.java
new file mode 100644
index 0000000..617dd52
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/impl/QuicExceptionImpl.java
@@ -0,0 +1,61 @@
+// 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.
+
+package org.chromium.net.impl;
+
+import org.chromium.net.QuicException;
+
+/**
+ * Implements {@link QuicException}.
+ */
+public class QuicExceptionImpl extends QuicException {
+    private final int mQuicDetailedErrorCode;
+    private final NetworkExceptionImpl mNetworkException;
+
+    /**
+     * Constructs an exception with a specific error.
+     *
+     * @param message explanation of failure.
+     * @param netErrorCode Error code from
+     * <a href=https://chromium.googlesource.com/chromium/src/+/master/net/base/net_error_list.h>
+     * this list</a>.
+     * @param quicDetailedErrorCode Detailed <a href="https://www.chromium.org/quic">QUIC</a> error
+     * code from <a
+     * href=https://cs.chromium.org/chromium/src/net/quic/quic_protocol.h?type=cs&q=%22enum+QuicErrorCode+%7B%22+file:src/net/quic/quic_protocol.h>
+     * QuicErrorCode</a>.
+     */
+    public QuicExceptionImpl(String message, int netErrorCode, int quicDetailedErrorCode) {
+        super(message, null);
+        mNetworkException =
+                new NetworkExceptionImpl(message, ERROR_QUIC_PROTOCOL_FAILED, netErrorCode);
+        mQuicDetailedErrorCode = quicDetailedErrorCode;
+    }
+
+    @Override
+    public String getMessage() {
+        StringBuilder b = new StringBuilder(mNetworkException.getMessage());
+        b.append(", QuicDetailedErrorCode=").append(mQuicDetailedErrorCode);
+        return b.toString();
+    }
+
+    @Override
+    public int getErrorCode() {
+        return mNetworkException.getErrorCode();
+    }
+
+    @Override
+    public int getCronetInternalErrorCode() {
+        return mNetworkException.getCronetInternalErrorCode();
+    }
+
+    @Override
+    public boolean immediatelyRetryable() {
+        return mNetworkException.immediatelyRetryable();
+    }
+
+    @Override
+    public int getQuicDetailedErrorCode() {
+        return mQuicDetailedErrorCode;
+    }
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java b/components/cronet/android/java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java
index 30fbf8c..61aaa39 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java
@@ -7,8 +7,8 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 
+import org.chromium.net.CronetException;
 import org.chromium.net.RequestFinishedInfo;
-import org.chromium.net.UrlRequestException;
 import org.chromium.net.UrlResponseInfo;
 
 import java.lang.annotation.Retention;
@@ -30,7 +30,7 @@
     @Nullable
     private final UrlResponseInfo mResponseInfo;
     @Nullable
-    private final UrlRequestException mException;
+    private final CronetException mException;
 
     @IntDef({SUCCEEDED, FAILED, CANCELED})
     @Retention(RetentionPolicy.SOURCE)
@@ -38,7 +38,7 @@
 
     public RequestFinishedInfoImpl(String url, Collection<Object> annotations,
             RequestFinishedInfo.Metrics metrics, @FinishedReason int finishedReason,
-            @Nullable UrlResponseInfo responseInfo, @Nullable UrlRequestException exception) {
+            @Nullable UrlResponseInfo responseInfo, @Nullable CronetException exception) {
         mUrl = url;
         mAnnotations = annotations;
         mMetrics = metrics;
@@ -79,7 +79,7 @@
 
     @Override
     @Nullable
-    public UrlRequestException getException() {
+    public CronetException getException() {
         return mException;
     }
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java b/components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java
index 7b1633d9..d0c4ace9 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java
@@ -64,7 +64,7 @@
         }
 
         @Override
-        public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+        public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
             mWrappedCallback.onFailed(request, info, error);
         }
 
@@ -72,6 +72,11 @@
         public void onCanceled(UrlRequest request, UrlResponseInfo info) {
             mWrappedCallback.onCanceled(request, info);
         }
+
+        @Override
+        public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+            mWrappedCallback.onFailed(request, info, error);
+        }
     }
 
     /**
@@ -289,4 +294,4 @@
             mWrappedLoader.loadLibrary(libName);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
index 2d565be..e1e6b52 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
@@ -8,8 +8,8 @@
 
 import org.chromium.base.Log;
 import org.chromium.net.CronetEngine;
+import org.chromium.net.CronetException;
 import org.chromium.net.UrlRequest;
-import org.chromium.net.UrlRequestException;
 import org.chromium.net.UrlResponseInfo;
 
 import java.io.FileNotFoundException;
@@ -44,7 +44,7 @@
     private CronetInputStream mInputStream;
     private CronetOutputStream mOutputStream;
     private UrlResponseInfo mResponseInfo;
-    private UrlRequestException mException;
+    private CronetException mException;
     private boolean mOnRedirectCalled = false;
     private boolean mHasResponse = false;
     private List<Map.Entry<String, String>> mResponseHeadersList;
@@ -479,8 +479,7 @@
         }
 
         @Override
-        public void onFailed(
-                UrlRequest request, UrlResponseInfo info, UrlRequestException exception) {
+        public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException exception) {
             if (exception == null) {
                 throw new IllegalStateException(
                         "Exception cannot be null in onFailed.");
diff --git a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
index 3512b9c..39359b4 100644
--- a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
+++ b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
@@ -15,9 +15,9 @@
 
 import org.chromium.base.Log;
 import org.chromium.net.CronetEngine;
+import org.chromium.net.CronetException;
 import org.chromium.net.UploadDataProviders;
 import org.chromium.net.UrlRequest;
-import org.chromium.net.UrlRequestException;
 import org.chromium.net.UrlResponseInfo;
 
 import java.io.ByteArrayOutputStream;
@@ -92,7 +92,7 @@
         }
 
         @Override
-        public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+        public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
             Log.i(TAG, "****** onFailed, error is: %s", error.getMessage());
 
             final String url = mUrl;
diff --git a/components/cronet/android/test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java b/components/cronet/android/test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java
index 430c37a4..340b065 100644
--- a/components/cronet/android/test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java
+++ b/components/cronet/android/test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java
@@ -451,8 +451,7 @@
                 }
 
                 @Override
-                public void onFailed(
-                        UrlRequest request, UrlResponseInfo info, UrlRequestException e) {
+                public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException e) {
                     System.out.println("Async request failed with " + e);
                     mFailed = true;
                 }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamQuicTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamQuicTest.java
index 87fa67f2..16ae9f22 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamQuicTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamQuicTest.java
@@ -330,8 +330,10 @@
         // Server terminated on us, so the stream must fail.
         // QUIC reports this as ERR_QUIC_PROTOCOL_ERROR. Sometimes we get ERR_CONNECTION_REFUSED.
         assertNotNull(callback.mError);
-        assertTrue(NetError.ERR_QUIC_PROTOCOL_ERROR == callback.mError.getCronetInternalErrorCode()
-                || NetError.ERR_CONNECTION_REFUSED == callback.mError.getCronetInternalErrorCode());
+        assertTrue(callback.mError instanceof NetworkException);
+        NetworkException networkError = (NetworkException) callback.mError;
+        assertTrue(NetError.ERR_QUIC_PROTOCOL_ERROR == networkError.getCronetInternalErrorCode()
+                || NetError.ERR_CONNECTION_REFUSED == networkError.getCronetInternalErrorCode());
     }
 
     @SmallTest
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
index 8e7c642..fc2fd96 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
@@ -203,7 +203,7 @@
         assertTrue(stream.isDone());
         assertContains("Exception in BidirectionalStream: net::ERR_DISALLOWED_URL_SCHEME",
                 callback.mError.getMessage());
-        assertEquals(-301, callback.mError.getCronetInternalErrorCode());
+        assertEquals(-301, ((NetworkException) callback.mError).getCronetInternalErrorCode());
     }
 
     @SmallTest
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index 9acfb99..5757a44 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -120,7 +120,7 @@
         }
 
         @Override
-        public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+        public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
             super.onFailed(request, info, error);
             mCronetEngine.shutdown();
             mCallbackCompletionBlock.open();
@@ -785,8 +785,7 @@
             }
 
             @Override
-            public void onFailed(
-                    UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+            public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
                 throw new RuntimeException("Unexpected");
             }
         }
@@ -837,8 +836,7 @@
             }
 
             @Override
-            public void onFailed(
-                    UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+            public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
                 throw new RuntimeException("Unexpected");
             }
         }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
index 3190230..23ce5ab5 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -314,8 +314,7 @@
             }
 
             @Override
-            public void onFailed(
-                    UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+            public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
                 failedExpectation.set(true);
                 fail();
             }
@@ -621,7 +620,8 @@
                         FailurePhase.START, arbitraryNetError));
         assertNull(callback.mResponseInfo);
         assertNotNull(callback.mError);
-        assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCode());
+        assertEquals(arbitraryNetError,
+                ((NetworkException) callback.mError).getCronetInternalErrorCode());
         assertEquals(0, callback.mRedirectCount);
         assertTrue(callback.mOnErrorCalled);
         assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
@@ -638,7 +638,8 @@
         assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
         assertEquals(15, callback.mResponseInfo.getReceivedBytesCount());
         assertNotNull(callback.mError);
-        assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCode());
+        assertEquals(arbitraryNetError,
+                ((NetworkException) callback.mError).getCronetInternalErrorCode());
         assertEquals(0, callback.mRedirectCount);
         assertTrue(callback.mOnErrorCalled);
         assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
@@ -655,7 +656,8 @@
         assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
         assertEquals(15, callback.mResponseInfo.getReceivedBytesCount());
         assertNotNull(callback.mError);
-        assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCode());
+        assertEquals(arbitraryNetError,
+                ((NetworkException) callback.mError).getCronetInternalErrorCode());
         assertEquals(0, callback.mRedirectCount);
         assertTrue(callback.mOnErrorCalled);
         assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
@@ -690,7 +692,7 @@
         assertNull(callback.mResponseInfo);
         assertNotNull(callback.mError);
         assertTrue(callback.mOnErrorCalled);
-        assertEquals(-201, callback.mError.getCronetInternalErrorCode());
+        assertEquals(-201, ((NetworkException) callback.mError).getCronetInternalErrorCode());
         assertContains("Exception in CronetUrlRequest: net::ERR_CERT_DATE_INVALID",
                 callback.mError.getMessage());
         assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
@@ -1932,23 +1934,23 @@
     @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
     public void testErrorCodes() throws Exception {
         checkSpecificErrorCode(
-                -105, UrlRequestException.ERROR_HOSTNAME_NOT_RESOLVED, "NAME_NOT_RESOLVED", false);
-        checkSpecificErrorCode(-106, UrlRequestException.ERROR_INTERNET_DISCONNECTED,
-                "INTERNET_DISCONNECTED", false);
+                -105, NetworkException.ERROR_HOSTNAME_NOT_RESOLVED, "NAME_NOT_RESOLVED", false);
         checkSpecificErrorCode(
-                -21, UrlRequestException.ERROR_NETWORK_CHANGED, "NETWORK_CHANGED", true);
+                -106, NetworkException.ERROR_INTERNET_DISCONNECTED, "INTERNET_DISCONNECTED", false);
         checkSpecificErrorCode(
-                -100, UrlRequestException.ERROR_CONNECTION_CLOSED, "CONNECTION_CLOSED", true);
+                -21, NetworkException.ERROR_NETWORK_CHANGED, "NETWORK_CHANGED", true);
         checkSpecificErrorCode(
-                -102, UrlRequestException.ERROR_CONNECTION_REFUSED, "CONNECTION_REFUSED", false);
+                -100, NetworkException.ERROR_CONNECTION_CLOSED, "CONNECTION_CLOSED", true);
         checkSpecificErrorCode(
-                -101, UrlRequestException.ERROR_CONNECTION_RESET, "CONNECTION_RESET", true);
+                -102, NetworkException.ERROR_CONNECTION_REFUSED, "CONNECTION_REFUSED", false);
         checkSpecificErrorCode(
-                -118, UrlRequestException.ERROR_CONNECTION_TIMED_OUT, "CONNECTION_TIMED_OUT", true);
-        checkSpecificErrorCode(-7, UrlRequestException.ERROR_TIMED_OUT, "TIMED_OUT", true);
+                -101, NetworkException.ERROR_CONNECTION_RESET, "CONNECTION_RESET", true);
         checkSpecificErrorCode(
-                -109, UrlRequestException.ERROR_ADDRESS_UNREACHABLE, "ADDRESS_UNREACHABLE", false);
-        checkSpecificErrorCode(-2, UrlRequestException.ERROR_OTHER, "FAILED", false);
+                -118, NetworkException.ERROR_CONNECTION_TIMED_OUT, "CONNECTION_TIMED_OUT", true);
+        checkSpecificErrorCode(-7, NetworkException.ERROR_TIMED_OUT, "TIMED_OUT", true);
+        checkSpecificErrorCode(
+                -109, NetworkException.ERROR_ADDRESS_UNREACHABLE, "ADDRESS_UNREACHABLE", false);
+        checkSpecificErrorCode(-2, NetworkException.ERROR_OTHER, "FAILED", false);
     }
 
     /*
@@ -1980,22 +1982,85 @@
                         FailurePhase.START, NetError.ERR_QUIC_PROTOCOL_ERROR));
         assertNull(callback.mResponseInfo);
         assertNotNull(callback.mError);
-        assertEquals(
-                UrlRequestException.ERROR_QUIC_PROTOCOL_FAILED, callback.mError.getErrorCode());
+        assertEquals(NetworkException.ERROR_QUIC_PROTOCOL_FAILED,
+                ((NetworkException) callback.mError).getErrorCode());
         assertTrue(callback.mError instanceof QuicException);
         QuicException quicException = (QuicException) callback.mError;
         // 1 is QUIC_INTERNAL_ERROR
         assertEquals(1, quicException.getQuicDetailedErrorCode());
     }
 
+    /**
+     * Tests that legacy onFailed callback is invoked with UrlRequestException if there
+     * is no onFailed callback implementation that takes CronetException.
+     */
+    @SmallTest
+    @Feature({"Cronet"})
+    @OnlyRunNativeCronet
+    public void testLegacyOnFailedCallback() throws Exception {
+        final int netError = -123;
+        final AtomicBoolean failedExpectation = new AtomicBoolean();
+        final ConditionVariable done = new ConditionVariable();
+        UrlRequest.Callback callback = new UrlRequest.Callback() {
+            @Override
+            public void onRedirectReceived(
+                    UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
+                failedExpectation.set(true);
+                fail();
+            }
+
+            @Override
+            public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
+                failedExpectation.set(true);
+                fail();
+            }
+
+            @Override
+            public void onReadCompleted(
+                    UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
+                failedExpectation.set(true);
+                fail();
+            }
+
+            @Override
+            public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
+                failedExpectation.set(true);
+                fail();
+            }
+
+            @Override
+            public void onFailed(
+                    UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+                assertEquals(netError, error.getCronetInternalErrorCode());
+                failedExpectation.set(error.getCronetInternalErrorCode() != netError);
+                done.open();
+            }
+
+            @Override
+            public void onCanceled(UrlRequest request, UrlResponseInfo info) {
+                failedExpectation.set(true);
+                fail();
+            }
+        };
+
+        UrlRequest.Builder builder = mTestFramework.mCronetEngine.newUrlRequestBuilder(
+                MockUrlRequestJobFactory.getMockUrlWithFailure(FailurePhase.START, netError),
+                callback, Executors.newSingleThreadExecutor());
+        final UrlRequest urlRequest = builder.build();
+        urlRequest.start();
+        done.block();
+        // Check that onFailed is called.
+        assertFalse(failedExpectation.get());
+    }
+
     private void checkSpecificErrorCode(int netError, int errorCode, String name,
             boolean immediatelyRetryable) throws Exception {
         TestUrlRequestCallback callback = startAndWaitForComplete(
                 MockUrlRequestJobFactory.getMockUrlWithFailure(FailurePhase.START, netError));
         assertNull(callback.mResponseInfo);
         assertNotNull(callback.mError);
-        assertEquals(netError, callback.mError.getCronetInternalErrorCode());
-        assertEquals(errorCode, callback.mError.getErrorCode());
+        assertEquals(netError, ((NetworkException) callback.mError).getCronetInternalErrorCode());
+        assertEquals(errorCode, ((NetworkException) callback.mError).getErrorCode());
         assertContains(
                 "Exception in CronetUrlRequest: net::ERR_" + name, callback.mError.getMessage());
         assertEquals(0, callback.mRedirectCount);
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
index 9ace3b833..662e6d4 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
@@ -89,7 +89,8 @@
         callback.blockForDone();
         assertNotNull(callback.mError);
         assertTrue(callback.mOnErrorCalled);
-        assertEquals(NetError.ERR_NETWORK_CHANGED, callback.mError.getCronetInternalErrorCode());
+        assertEquals(NetError.ERR_NETWORK_CHANGED,
+                ((NetworkException) callback.mError).getCronetInternalErrorCode());
         assertContains("Exception in CronetUrlRequest: net::ERR_NETWORK_CHANGED",
                 callback.mError.getMessage());
     }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java
index 16342df..fa84f746 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/PkpTest.java
@@ -363,7 +363,7 @@
      */
     private void assertErrorResponse() {
         assertNotNull("Expected an error", mListener.mError);
-        int errorCode = mListener.mError.getCronetInternalErrorCode();
+        int errorCode = ((NetworkException) mListener.mError).getCronetInternalErrorCode();
         Set<Integer> expectedErrors = new HashSet<>();
         expectedErrors.add(NetError.ERR_QUIC_PROTOCOL_ERROR);
         expectedErrors.add(NetError.ERR_CONNECTION_REFUSED);
@@ -379,7 +379,7 @@
     private void assertSuccessfulResponse() {
         if (mListener.mError != null) {
             fail("Did not expect an error but got error code "
-                    + mListener.mError.getCronetInternalErrorCode());
+                    + ((NetworkException) mListener.mError).getCronetInternalErrorCode());
         }
         assertNotNull("Expected non-null response from the server", mListener.mResponseInfo);
         assertEquals(200, mListener.mResponseInfo.getHttpStatusCode());
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java
index 5b0f50e6..9bef689 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java
@@ -206,8 +206,8 @@
         assertTrue(requestInfo.getAnnotations().isEmpty());
         assertEquals(RequestFinishedInfo.FAILED, requestInfo.getFinishedReason());
         assertNotNull(requestInfo.getException());
-        assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
-                requestInfo.getException().getErrorCode());
+        assertEquals(NetworkException.ERROR_CONNECTION_REFUSED,
+                ((NetworkException) requestInfo.getException()).getErrorCode());
         RequestFinishedInfo.Metrics metrics = requestInfo.getMetrics();
         assertNotNull("RequestFinishedInfo.getMetrics() must not be null", metrics);
         // The failure is occasionally fast enough that time reported is 0, so just check for null
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestCallback.java b/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestCallback.java
index b2a0163..b088cfb 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestCallback.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestCallback.java
@@ -15,8 +15,6 @@
 
 import static org.chromium.net.CronetTestBase.assertContains;
 
-import org.chromium.net.impl.UrlRequestError;
-
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.concurrent.ExecutorService;
@@ -33,7 +31,7 @@
     public ArrayList<UrlResponseInfo> mRedirectResponseInfoList = new ArrayList<UrlResponseInfo>();
     public ArrayList<String> mRedirectUrlList = new ArrayList<String>();
     public UrlResponseInfo mResponseInfo;
-    public UrlRequestException mError;
+    public CronetException mError;
 
     public ResponseStep mResponseStep = ResponseStep.NOTHING;
 
@@ -50,7 +48,7 @@
     // that advance it.
     private boolean mAutoAdvance = true;
     // Whether an exception is thrown by maybeThrowCancelOrPause().
-    private boolean mListenerExceptionThrown;
+    private boolean mCallbackExceptionThrown;
 
     // Whether to permit calls on the network thread.
     private boolean mAllowDirectExecutor = false;
@@ -239,7 +237,7 @@
     }
 
     @Override
-    public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
+    public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
         // If the failure is because of prohibited direct execution, the test shouldn't fail
         // since the request already did.
         if (error.getCause() instanceof InlineExecutionProhibitedException) {
@@ -253,14 +251,12 @@
         assertFalse(mOnErrorCalled);
         assertFalse(mOnCanceledCalled);
         assertNull(mError);
-        if (mListenerExceptionThrown) {
-            assertEquals(UrlRequestError.LISTENER_EXCEPTION_THROWN, error.getErrorCode());
-            assertEquals(0, error.getCronetInternalErrorCode());
+        if (mCallbackExceptionThrown) {
+            assertTrue(error instanceof CallbackException);
             assertContains("Exception received from UrlRequest.Callback", error.getMessage());
             assertNotNull(error.getCause());
             assertTrue(error.getCause() instanceof IllegalStateException);
             assertContains("Listener Exception.", error.getCause().getMessage());
-            assertFalse(error.immediatelyRetryable());
         }
 
         mResponseStep = ResponseStep.ON_FAILED;
@@ -326,8 +322,8 @@
         }
 
         if (mFailureType == FailureType.THROW_SYNC) {
-            assertFalse(mListenerExceptionThrown);
-            mListenerExceptionThrown = true;
+            assertFalse(mCallbackExceptionThrown);
+            mCallbackExceptionThrown = true;
             throw new IllegalStateException("Listener Exception.");
         }
         Runnable task = new Runnable() {
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java
index b5a4a095..de4bc266 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java
@@ -200,8 +200,7 @@
         first.block();
         callback.blockForDone();
         assertFalse(callback.mOnCanceledCalled);
-        assertEquals(UrlRequestException.ERROR_LISTENER_EXCEPTION_THROWN,
-                callback.mError.getErrorCode());
+        assertTrue(callback.mError instanceof CallbackException);
         assertContains("Exception received from UploadDataProvider", callback.mError.getMessage());
         assertContains(exceptionMessage, callback.mError.getCause().getMessage());
     }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
index 13603ed..476c174 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
@@ -10,7 +10,7 @@
 import org.chromium.net.CronetTestBase;
 import org.chromium.net.CronetTestFramework;
 import org.chromium.net.NativeTestServer;
-import org.chromium.net.UrlRequestException;
+import org.chromium.net.NetworkException;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -103,9 +103,9 @@
             fail();
         } catch (IOException e) {
             if (!testingSystemHttpURLConnection()) {
-                UrlRequestException requestException = (UrlRequestException) e;
-                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
-                        requestException.getErrorCode());
+                NetworkException requestException = (NetworkException) e;
+                assertEquals(
+                        NetworkException.ERROR_CONNECTION_REFUSED, requestException.getErrorCode());
             }
         }
         // Restarting server to run the test for a second time.
@@ -133,9 +133,9 @@
             fail();
         } catch (IOException e) {
             if (!testingSystemHttpURLConnection()) {
-                UrlRequestException requestException = (UrlRequestException) e;
-                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
-                        requestException.getErrorCode());
+                NetworkException requestException = (NetworkException) e;
+                assertEquals(
+                        NetworkException.ERROR_CONNECTION_REFUSED, requestException.getErrorCode());
             }
         }
         // Make sure IOException is reported again when trying to read response
@@ -146,9 +146,9 @@
         } catch (IOException e) {
             // Expected.
             if (!testingSystemHttpURLConnection()) {
-                UrlRequestException requestException = (UrlRequestException) e;
-                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
-                        requestException.getErrorCode());
+                NetworkException requestException = (NetworkException) e;
+                assertEquals(
+                        NetworkException.ERROR_CONNECTION_REFUSED, requestException.getErrorCode());
             }
         }
         // Restarting server to run the test for a second time.
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
index bb481cd..7ae9ca09 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
@@ -10,7 +10,7 @@
 import org.chromium.net.CronetTestBase;
 import org.chromium.net.CronetTestFramework;
 import org.chromium.net.NativeTestServer;
-import org.chromium.net.UrlRequestException;
+import org.chromium.net.NetworkException;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -85,9 +85,9 @@
         } catch (IOException e) {
             // Expected.
             if (!testingSystemHttpURLConnection()) {
-                UrlRequestException requestException = (UrlRequestException) e;
-                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
-                        requestException.getErrorCode());
+                NetworkException requestException = (NetworkException) e;
+                assertEquals(
+                        NetworkException.ERROR_CONNECTION_REFUSED, requestException.getErrorCode());
             }
         }
         // Restarting server to run the test for a second time.
@@ -114,9 +114,9 @@
             fail();
         } catch (IOException e) {
             if (!testingSystemHttpURLConnection()) {
-                UrlRequestException requestException = (UrlRequestException) e;
-                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
-                        requestException.getErrorCode());
+                NetworkException requestException = (NetworkException) e;
+                assertEquals(
+                        NetworkException.ERROR_CONNECTION_REFUSED, requestException.getErrorCode());
             }
         }
         // Make sure IOException is reported again when trying to read response
@@ -127,9 +127,9 @@
         } catch (IOException e) {
             // Expected.
             if (!testingSystemHttpURLConnection()) {
-                UrlRequestException requestException = (UrlRequestException) e;
-                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
-                        requestException.getErrorCode());
+                NetworkException requestException = (NetworkException) e;
+                assertEquals(
+                        NetworkException.ERROR_CONNECTION_REFUSED, requestException.getErrorCode());
             }
         }
         // Restarting server to run the test for a second time.
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
index f55ef289..989de7d 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
@@ -10,11 +10,11 @@
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetEngine;
+import org.chromium.net.CronetException;
 import org.chromium.net.CronetTestBase;
 import org.chromium.net.CronetTestFramework;
 import org.chromium.net.MockUrlRequestJobFactory;
 import org.chromium.net.NativeTestServer;
-import org.chromium.net.UrlRequestException;
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileNotFoundException;
@@ -271,8 +271,7 @@
             secondConnection.getResponseCode();
             fail();
         } catch (IOException e) {
-            assertTrue(e instanceof java.net.ConnectException
-                    || e instanceof UrlRequestException);
+            assertTrue(e instanceof java.net.ConnectException || e instanceof CronetException);
             assertTrue((e.getMessage().contains("ECONNREFUSED")
                     || (e.getMessage().contains("Connection refused"))
                     || e.getMessage().contains("net::ERR_CONNECTION_REFUSED")));
@@ -297,8 +296,7 @@
             urlConnection.getResponseCode();
             fail();
         } catch (IOException e) {
-            assertTrue(e instanceof java.net.ConnectException
-                    || e instanceof UrlRequestException);
+            assertTrue(e instanceof java.net.ConnectException || e instanceof CronetException);
             assertTrue((e.getMessage().contains("ECONNREFUSED")
                     || (e.getMessage().contains("Connection refused"))
                     || e.getMessage().contains("net::ERR_CONNECTION_REFUSED")));
@@ -322,7 +320,7 @@
             fail();
         } catch (java.net.UnknownHostException e) {
             // Expected.
-        } catch (UrlRequestException e) {
+        } catch (CronetException e) {
             // Expected.
         }
         checkExceptionsAreThrown(urlConnection);
diff --git a/components/new_or_sad_tab_strings.grdp b/components/new_or_sad_tab_strings.grdp
index b04b83d5..a88f6663 100644
--- a/components/new_or_sad_tab_strings.grdp
+++ b/components/new_or_sad_tab_strings.grdp
@@ -31,6 +31,11 @@
       <message name="IDS_SAD_TAB_RELOAD_LABEL" desc="Button label in the sad tab page for reloading a page." formatter_data="android_java">
         Reload
       </message>
+      <if expr="is_android">
+        <message name="IDS_SAD_TAB_SEND_FEEDBACK_LABEL" desc="Button label in the sad tab page for sending feedback. This label replaces the reload button after a crash happens twice in a row." formatter_data="android_java">
+          Send Feedback
+        </message>
+      </if>
 
       <!-- New Tab -->
       <message name="IDS_NEW_TAB_TITLE"
diff --git a/components/omnibox/browser/url_index_private_data.cc b/components/omnibox/browser/url_index_private_data.cc
index a7029482..d297b64 100644
--- a/components/omnibox/browser/url_index_private_data.cc
+++ b/components/omnibox/browser/url_index_private_data.cc
@@ -253,7 +253,7 @@
     for (SearchTermCacheMap::iterator cache_iter = search_term_cache_.begin();
          cache_iter != search_term_cache_.end(); ) {
       if (!cache_iter->second.used_)
-        search_term_cache_.erase(cache_iter++);
+        cache_iter = search_term_cache_.erase(cache_iter);
       else
         ++cache_iter;
     }
@@ -497,6 +497,7 @@
 
 HistoryIDSet URLIndexPrivateData::HistoryIDSetFromWords(
     const String16Vector& unsorted_words) {
+  SCOPED_UMA_HISTOGRAM_TIMER("Omnibox.HistoryQuickHistoryIDSetFromWords");
   // Break the terms down into individual terms (words), get the candidate
   // set for each term, and intersect each to get a final candidate list.
   // Note that a single 'term' from the user's perspective might be
@@ -605,7 +606,7 @@
     for (WordIDSet::iterator word_set_iter = word_id_set.begin();
          word_set_iter != word_id_set.end(); ) {
       if (word_list_[*word_set_iter].find(term) == base::string16::npos)
-        word_id_set.erase(word_set_iter++);
+        word_set_iter = word_id_set.erase(word_set_iter);
       else
         ++word_set_iter;
     }
diff --git a/components/payments/payment_details_validation.cc b/components/payments/payment_details_validation.cc
index 08a6fa3..a01303e 100644
--- a/components/payments/payment_details_validation.cc
+++ b/components/payments/payment_details_validation.cc
@@ -108,19 +108,15 @@
     return false;
   }
 
-  std::set<mojo::String> uniqueMethods;
   for (const auto& modifier : modifiers) {
-    if (modifier->supported_methods.empty()) {
-      *error_message = "Must specify at least one payment method identifier";
+    if (!modifier->method_data) {
+      *error_message = "Method data required";
       return false;
     }
 
-    for (const auto& method : modifier->supported_methods) {
-      if (uniqueMethods.find(method) != uniqueMethods.end()) {
-        *error_message = "Duplicate payment method identifiers are not allowed";
-        return false;
-      }
-      uniqueMethods.insert(method);
+    if (modifier->method_data->supported_methods.empty()) {
+      *error_message = "Must specify at least one payment method identifier";
+      return false;
     }
 
     if (modifier->total) {
diff --git a/components/payments/payment_request.mojom b/components/payments/payment_request.mojom
index 9d95c51..bc42866 100644
--- a/components/payments/payment_request.mojom
+++ b/components/payments/payment_request.mojom
@@ -58,8 +58,8 @@
   // a payment app, for example Android Pay. Browser ensures that the string can
   // be successfully parsed into base::JSONParser. Renderer parses this string
   // via v8::JSON::Parse() and hands off the result to the merchant website.
-  // There's no one format for this object, so richer types cannot be used. A
-  // simple example:
+  // There's no one format for this object, so more specific types cannot be
+  // used. A simple example:
   //
   // {"nameOnCard": "Jon Doe", "pan": "4111 1111 1111 1111"}
   string stringified_details;
@@ -106,10 +106,34 @@
   bool selected;
 };
 
-struct PaymentDetailsModifier {
+struct PaymentMethodData {
   array<string> supported_methods;
+
+  // A JSON string built by the renderer from a JavaScript object that the
+  // merchant website provides. The renderer uses
+  // blink::JSONObject::toJSONString() to generate this string. The browser does
+  // not parse the string and passes it as-is directly to payment apps. There's
+  // no one format for this object, so more specific types cannot be used. A
+  // simple example:
+  //
+  // {"gateway": "stripe"}
+  string stringified_data;
+
+  // Android Pay specific method data is parsed in the renderer.
+  // https://developers.google.com/web/fundamentals/getting-started/primers/payment-request/android-pay
+  // TODO(rouslan): Stop parsing Android Pay data. http://crbug.com/620173
+  AndroidPayEnvironment environment;
+  string? merchant_name;
+  string? merchant_id;
+  array<AndroidPayCardNetwork> allowed_card_networks;
+  AndroidPayTokenization tokenization_type;
+  array<AndroidPayTokenizationParameter> parameters;
+};
+
+struct PaymentDetailsModifier {
   PaymentItem? total;
   array<PaymentItem> additional_display_items;
+  PaymentMethodData method_data;
 };
 
 struct PaymentDetails {
@@ -157,30 +181,6 @@
   string? value;
 };
 
-struct PaymentMethodData {
-  array<string> supported_methods;
-
-  // A JSON string built by the renderer from a JavaScript object that the
-  // merchant website provides. The renderer uses
-  // blink::JSONObject::toJSONString() to generate this string. The browser does
-  // not parse the string and passes it as-is directly to payment apps. There's
-  // no one format for this object, so richer types cannot be used. A simple
-  // example:
-  //
-  // {"gateway": "stripe"}
-  string stringified_data;
-
-  // Android Pay specific method data is parsed in the renderer.
-  // https://developers.google.com/web/fundamentals/getting-started/primers/payment-request/android-pay
-  // TODO(rouslan): Stop parsing Android Pay data. http://crbug.com/620173
-  AndroidPayEnvironment environment;
-  string? merchant_name;
-  string? merchant_id;
-  array<AndroidPayCardNetwork> allowed_card_networks;
-  AndroidPayTokenization tokenization_type;
-  array<AndroidPayTokenizationParameter> parameters;
-};
-
 enum PaymentComplete {
   SUCCESS,
   FAIL,
diff --git a/components/policy/core/common/policy_switches.cc b/components/policy/core/common/policy_switches.cc
index c9682f6..be6a8569 100644
--- a/components/policy/core/common/policy_switches.cc
+++ b/components/policy/core/common/policy_switches.cc
@@ -19,5 +19,9 @@
 // produce verification signatures.
 const char kDisablePolicyKeyVerification[]  = "disable-policy-key-verification";
 
+// Always treat user as affiliated.
+// TODO(antrim): Remove once test servers correctly produce affiliation ids.
+const char kUserAlwaysAffiliated[]  = "user-always-affiliated";
+
 }  // namespace switches
 }  // namespace policy
diff --git a/components/policy/core/common/policy_switches.h b/components/policy/core/common/policy_switches.h
index 1f0426c..34db62f 100644
--- a/components/policy/core/common/policy_switches.h
+++ b/components/policy/core/common/policy_switches.h
@@ -15,6 +15,7 @@
 POLICY_EXPORT extern const char kDeviceManagementUrl[];
 POLICY_EXPORT extern const char kDisableComponentCloudPolicy[];
 POLICY_EXPORT extern const char kDisablePolicyKeyVerification[];
+POLICY_EXPORT extern const char kUserAlwaysAffiliated[];
 
 }  // namespace switches
 }  // namespace policy
diff --git a/components/test_runner/test_interfaces.cc b/components/test_runner/test_interfaces.cc
index e5f0c076..3e3e715 100644
--- a/components/test_runner/test_interfaces.cc
+++ b/components/test_runner/test_interfaces.cc
@@ -96,7 +96,8 @@
   if (spec.find("/inspector/") != std::string::npos ||
       spec.find("/inspector-enabled/") != std::string::npos)
     test_runner_->ClearDevToolsLocalStorage();
-  if (spec.find("/inspector/") != std::string::npos) {
+  if (spec.find("/inspector/") != std::string::npos &&
+      spec.find("unit_test_runner.html") == std::string::npos) {
     // Subfolder name determines default panel to open.
     std::string test_path = spec.substr(spec.find("/inspector/") + 11);
     base::DictionaryValue settings;
diff --git a/content/browser/dom_storage/session_storage_database.cc b/content/browser/dom_storage/session_storage_database.cc
index 8c05c07..0bf1caa 100644
--- a/content/browser/dom_storage/session_storage_database.cc
+++ b/content/browser/dom_storage/session_storage_database.cc
@@ -33,7 +33,12 @@
 enum SessionStorageUMA {
   SESSION_STORAGE_UMA_SUCCESS,
   SESSION_STORAGE_UMA_RECREATED,
-  SESSION_STORAGE_UMA_FAIL,
+  SESSION_STORAGE_UMA_RECREATE_FAIL,  // Deprecated in M56 (issue 183679)
+  SESSION_STORAGE_UMA_RECREATE_NOT_FOUND,
+  SESSION_STORAGE_UMA_RECREATE_NOT_SUPPORTED,
+  SESSION_STORAGE_UMA_RECREATE_CORRUPTION,
+  SESSION_STORAGE_UMA_RECREATE_INVALID_ARGUMENT,
+  SESSION_STORAGE_UMA_RECREATE_IO_ERROR,
   SESSION_STORAGE_UMA_MAX
 };
 
@@ -391,9 +396,30 @@
     if (!s.ok()) {
       LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
                    << ", error: " << s.ToString();
-      UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
-                                SESSION_STORAGE_UMA_FAIL,
-                                SESSION_STORAGE_UMA_MAX);
+      if (s.IsNotFound()) {
+        UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
+                                  SESSION_STORAGE_UMA_RECREATE_NOT_FOUND,
+                                  SESSION_STORAGE_UMA_MAX);
+      } else if (s.IsNotSupportedError()) {
+        UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
+                                  SESSION_STORAGE_UMA_RECREATE_NOT_SUPPORTED,
+                                  SESSION_STORAGE_UMA_MAX);
+      } else if (s.IsCorruption()) {
+        UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
+                                  SESSION_STORAGE_UMA_RECREATE_CORRUPTION,
+                                  SESSION_STORAGE_UMA_MAX);
+      } else if (s.IsInvalidArgument()) {
+        UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
+                                  SESSION_STORAGE_UMA_RECREATE_INVALID_ARGUMENT,
+                                  SESSION_STORAGE_UMA_MAX);
+      } else if (s.IsIOError()) {
+        UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
+                                  SESSION_STORAGE_UMA_RECREATE_IO_ERROR,
+                                  SESSION_STORAGE_UMA_MAX);
+      } else {
+        NOTREACHED();
+      }
+
       DCHECK(db == NULL);
       db_error_ = true;
       return false;
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.cc b/content/browser/frame_host/render_widget_host_view_child_frame.cc
index 1453acb..56e95b8 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.cc
@@ -519,7 +519,7 @@
 
 gfx::Point RenderWidgetHostViewChildFrame::TransformPointToRootCoordSpace(
     const gfx::Point& point) {
-  if (!frame_connector_)
+  if (!frame_connector_ || !local_frame_id_.is_valid())
     return point;
 
   return frame_connector_->TransformPointToRootCoordSpace(
diff --git a/content/browser/indexed_db/cursor_impl.cc b/content/browser/indexed_db/cursor_impl.cc
index 6e4d219..e711d8dd 100644
--- a/content/browser/indexed_db/cursor_impl.cc
+++ b/content/browser/indexed_db/cursor_impl.cc
@@ -12,7 +12,7 @@
 
 class CursorImpl::IDBThreadHelper {
  public:
-  explicit IDBThreadHelper(scoped_refptr<IndexedDBCursor> cursor);
+  explicit IDBThreadHelper(std::unique_ptr<IndexedDBCursor> cursor);
   ~IDBThreadHelper();
 
   void Advance(uint32_t count, scoped_refptr<IndexedDBCallbacks> callbacks);
@@ -24,13 +24,13 @@
 
  private:
   scoped_refptr<IndexedDBDispatcherHost> dispatcher_host_;
-  scoped_refptr<IndexedDBCursor> cursor_;
+  std::unique_ptr<IndexedDBCursor> cursor_;
   const url::Origin origin_;
 
   DISALLOW_COPY_AND_ASSIGN(IDBThreadHelper);
 };
 
-CursorImpl::CursorImpl(scoped_refptr<IndexedDBCursor> cursor,
+CursorImpl::CursorImpl(std::unique_ptr<IndexedDBCursor> cursor,
                        const url::Origin& origin,
                        scoped_refptr<IndexedDBDispatcherHost> dispatcher_host)
     : helper_(new IDBThreadHelper(std::move(cursor))),
@@ -88,10 +88,12 @@
 }
 
 CursorImpl::IDBThreadHelper::IDBThreadHelper(
-    scoped_refptr<IndexedDBCursor> cursor)
+    std::unique_ptr<IndexedDBCursor> cursor)
     : cursor_(std::move(cursor)) {}
 
-CursorImpl::IDBThreadHelper::~IDBThreadHelper() {}
+CursorImpl::IDBThreadHelper::~IDBThreadHelper() {
+  cursor_->RemoveCursorFromTransaction();
+}
 
 void CursorImpl::IDBThreadHelper::Advance(
     uint32_t count,
diff --git a/content/browser/indexed_db/cursor_impl.h b/content/browser/indexed_db/cursor_impl.h
index a0b66c0..69254289c 100644
--- a/content/browser/indexed_db/cursor_impl.h
+++ b/content/browser/indexed_db/cursor_impl.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_INDEXED_DB_CURSOR_IMPL_H_
 #define CONTENT_BROWSER_INDEXED_DB_CURSOR_IMPL_H_
 
+#include <memory>
+
 #include "base/memory/ref_counted.h"
 #include "content/common/indexed_db/indexed_db.mojom.h"
 
@@ -20,7 +22,7 @@
 
 class CursorImpl : public ::indexed_db::mojom::Cursor {
  public:
-  CursorImpl(scoped_refptr<IndexedDBCursor> cursor,
+  CursorImpl(std::unique_ptr<IndexedDBCursor> cursor,
              const url::Origin& origin,
              scoped_refptr<IndexedDBDispatcherHost> dispatcher_host);
   ~CursorImpl() override;
diff --git a/content/browser/indexed_db/database_impl.cc b/content/browser/indexed_db/database_impl.cc
index fd3b190..27b331f 100644
--- a/content/browser/indexed_db/database_impl.cc
+++ b/content/browser/indexed_db/database_impl.cc
@@ -9,6 +9,7 @@
 #include "content/browser/indexed_db/indexed_db_connection.h"
 #include "content/browser/indexed_db/indexed_db_context_impl.h"
 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
 #include "content/browser/indexed_db/indexed_db_value.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
@@ -453,9 +454,13 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->CreateObjectStore(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      name, key_path, auto_increment);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->CreateObjectStore(transaction, object_store_id, name,
+                                             key_path, auto_increment);
 }
 
 void DatabaseImpl::IDBThreadHelper::DeleteObjectStore(int64_t transaction_id,
@@ -463,8 +468,12 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->DeleteObjectStore(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->DeleteObjectStore(transaction, object_store_id);
 }
 
 void DatabaseImpl::IDBThreadHelper::RenameObjectStore(
@@ -474,9 +483,13 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->RenameObjectStore(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      new_name);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->RenameObjectStore(transaction, object_store_id,
+                                             new_name);
 }
 
 void DatabaseImpl::IDBThreadHelper::CreateTransaction(
@@ -486,15 +499,8 @@
   if (!connection_->IsConnected())
     return;
 
-  int64_t host_transaction_id =
-      dispatcher_host_->HostTransactionId(transaction_id);
-  if (!dispatcher_host_->RegisterTransactionId(host_transaction_id, origin_)) {
-    DLOG(ERROR) << "Duplicate host_transaction_id.";
-    return;
-  }
-
-  connection_->database()->CreateTransaction(
-      host_transaction_id, connection_.get(), object_store_ids, mode);
+  connection_->database()->CreateTransaction(transaction_id, connection_.get(),
+                                             object_store_ids, mode);
 }
 
 void DatabaseImpl::IDBThreadHelper::Close() {
@@ -520,11 +526,15 @@
   if (!connection_->IsConnected())
     return;
 
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
   IndexedDBObserver::Options options(include_transaction, no_records, values,
                                      operation_types);
-  connection_->database()->AddPendingObserver(
-      dispatcher_host_->HostTransactionId(transaction_id), observer_id,
-      options);
+  connection_->database()->AddPendingObserver(transaction, observer_id,
+                                              options);
 }
 
 void DatabaseImpl::IDBThreadHelper::RemoveObservers(
@@ -545,10 +555,14 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->Get(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      index_id, base::MakeUnique<IndexedDBKeyRange>(key_range), key_only,
-      callbacks);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->Get(transaction, object_store_id, index_id,
+                               base::MakeUnique<IndexedDBKeyRange>(key_range),
+                               key_only, callbacks);
 }
 
 void DatabaseImpl::IDBThreadHelper::GetAll(
@@ -562,10 +576,15 @@
   if (!connection_->IsConnected())
     return;
 
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
   connection_->database()->GetAll(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      index_id, base::MakeUnique<IndexedDBKeyRange>(key_range), key_only,
-      max_count, std::move(callbacks));
+      transaction, object_store_id, index_id,
+      base::MakeUnique<IndexedDBKeyRange>(key_range), key_only, max_count,
+      std::move(callbacks));
 }
 
 void DatabaseImpl::IDBThreadHelper::Put(
@@ -581,19 +600,22 @@
   if (!connection_->IsConnected())
     return;
 
-  int64_t host_transaction_id =
-      dispatcher_host_->HostTransactionId(transaction_id);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
   uint64_t commit_size = mojo_value->bits.size();
   IndexedDBValue value;
   swap(value.bits, mojo_value->bits);
   swap(value.blob_info, blob_info);
-  connection_->database()->Put(host_transaction_id, object_store_id, &value,
-                               &handles, base::MakeUnique<IndexedDBKey>(key),
-                               mode, std::move(callbacks), index_keys);
+  connection_->database()->Put(transaction, object_store_id, &value, &handles,
+                               base::MakeUnique<IndexedDBKey>(key), mode,
+                               std::move(callbacks), index_keys);
 
   // Size can't be big enough to overflow because it represents the
   // actual bytes passed through IPC.
-  dispatcher_host_->AddToTransaction(host_transaction_id, commit_size);
+  transaction->set_size(transaction->size() + commit_size);
 }
 
 void DatabaseImpl::IDBThreadHelper::SetIndexKeys(
@@ -604,9 +626,14 @@
   if (!connection_->IsConnected())
     return;
 
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
   connection_->database()->SetIndexKeys(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      base::MakeUnique<IndexedDBKey>(primary_key), index_keys);
+      transaction, object_store_id, base::MakeUnique<IndexedDBKey>(primary_key),
+      index_keys);
 }
 
 void DatabaseImpl::IDBThreadHelper::SetIndexesReady(
@@ -616,9 +643,13 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->SetIndexesReady(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      index_ids);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->SetIndexesReady(transaction, object_store_id,
+                                           index_ids);
 }
 
 void DatabaseImpl::IDBThreadHelper::OpenCursor(
@@ -633,10 +664,15 @@
   if (!connection_->IsConnected())
     return;
 
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
   connection_->database()->OpenCursor(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      index_id, base::MakeUnique<IndexedDBKeyRange>(key_range), direction,
-      key_only, task_type, std::move(callbacks));
+      transaction, object_store_id, index_id,
+      base::MakeUnique<IndexedDBKeyRange>(key_range), direction, key_only,
+      task_type, std::move(callbacks));
 }
 
 void DatabaseImpl::IDBThreadHelper::Count(
@@ -648,10 +684,14 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->Count(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      index_id, base::MakeUnique<IndexedDBKeyRange>(key_range),
-      std::move(callbacks));
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->Count(transaction, object_store_id, index_id,
+                                 base::MakeUnique<IndexedDBKeyRange>(key_range),
+                                 std::move(callbacks));
 }
 
 void DatabaseImpl::IDBThreadHelper::DeleteRange(
@@ -662,8 +702,13 @@
   if (!connection_->IsConnected())
     return;
 
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
   connection_->database()->DeleteRange(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
+      transaction, object_store_id,
       base::MakeUnique<IndexedDBKeyRange>(key_range), std::move(callbacks));
 }
 
@@ -674,9 +719,12 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->Clear(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      callbacks);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->Clear(transaction, object_store_id, callbacks);
 }
 
 void DatabaseImpl::IDBThreadHelper::CreateIndex(
@@ -690,9 +738,13 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->CreateIndex(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      index_id, name, key_path, unique, multi_entry);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->CreateIndex(transaction, object_store_id, index_id,
+                                       name, key_path, unique, multi_entry);
 }
 
 void DatabaseImpl::IDBThreadHelper::DeleteIndex(int64_t transaction_id,
@@ -701,9 +753,12 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->DeleteIndex(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      index_id);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->DeleteIndex(transaction, object_store_id, index_id);
 }
 
 void DatabaseImpl::IDBThreadHelper::RenameIndex(
@@ -714,34 +769,39 @@
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->RenameIndex(
-      dispatcher_host_->HostTransactionId(transaction_id), object_store_id,
-      index_id, new_name);
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->database()->RenameIndex(transaction, object_store_id, index_id,
+                                       new_name);
 }
 
 void DatabaseImpl::IDBThreadHelper::Abort(int64_t transaction_id) {
   if (!connection_->IsConnected())
     return;
 
-  connection_->database()->Abort(
-      dispatcher_host_->HostTransactionId(transaction_id));
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
+    return;
+
+  connection_->AbortTransaction(transaction);
 }
 
 void DatabaseImpl::IDBThreadHelper::Commit(int64_t transaction_id) {
   if (!connection_->IsConnected())
     return;
 
-  int64_t host_transaction_id =
-      dispatcher_host_->HostTransactionId(transaction_id);
-  // May have been aborted by back end before front-end could request commit.
-  int64_t transaction_size;
-  if (!dispatcher_host_->GetTransactionSize(host_transaction_id,
-                                            &transaction_size))
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
     return;
 
   // Always allow empty or delete-only transactions.
-  if (transaction_size == 0) {
-    connection_->database()->Commit(host_transaction_id);
+  if (transaction->size() == 0) {
+    connection_->database()->Commit(transaction);
     return;
   }
 
@@ -761,19 +821,17 @@
   if (!connection_->IsConnected())
     return;
 
-  int64_t host_transaction_id =
-      dispatcher_host_->HostTransactionId(transaction_id);
-  // May have aborted while quota check was pending.
-  int64_t transaction_size;
-  if (!dispatcher_host_->GetTransactionSize(host_transaction_id,
-                                            &transaction_size))
+  IndexedDBTransaction* transaction =
+      connection_->GetTransaction(transaction_id);
+  if (!transaction)
     return;
 
-  if (status == storage::kQuotaStatusOk && usage + transaction_size <= quota) {
-    connection_->database()->Commit(host_transaction_id);
+  if (status == storage::kQuotaStatusOk &&
+      usage + transaction->size() <= quota) {
+    connection_->database()->Commit(transaction);
   } else {
-    connection_->database()->Abort(
-        host_transaction_id,
+    connection_->AbortTransaction(
+        transaction,
         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
   }
 }
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 4f80b83..a3a9115 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -2268,7 +2268,7 @@
       ++iter_;
       WriteNextFile();
     } else {
-      callback_->Run(false);
+      callback_->Run(BlobWriteResult::FAILURE_ASYNC);
     }
   }
 
@@ -2290,11 +2290,11 @@
     }
     if (iter_ == blobs_.end()) {
       DCHECK(!self_ref_.get());
-      callback_->Run(true);
+      callback_->Run(BlobWriteResult::SUCCESS_ASYNC);
       return;
     } else {
       if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
-        callback_->Run(false);
+        callback_->Run(BlobWriteResult::FAILURE_ASYNC);
         return;
       }
       waiting_for_callback_ = true;
@@ -2307,6 +2307,9 @@
   WriteDescriptorVec::const_iterator iter_;
   int64_t database_id_;
   IndexedDBBackingStore* backing_store_;
+  // Callback result is useless as call stack is no longer transaction's
+  // operations queue. Errors are instead handled in
+  // IndexedDBTransaction::BlobWriteComplete.
   scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
   std::unique_ptr<FileWriterDelegate> delegate_;
   bool aborted_;
@@ -4045,8 +4048,10 @@
 
 IndexedDBBackingStore::Transaction::Transaction(
     IndexedDBBackingStore* backing_store)
-    : backing_store_(backing_store), database_id_(-1), committing_(false) {
-}
+    : backing_store_(backing_store),
+      database_id_(-1),
+      committing_(false),
+      ptr_factory_(this) {}
 
 IndexedDBBackingStore::Transaction::~Transaction() {
   DCHECK(!committing_);
@@ -4215,7 +4220,7 @@
     // This call will zero out new_blob_entries and new_files_to_write.
     WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback);
   } else {
-    callback->Run(true);
+    return callback->Run(BlobWriteResult::SUCCESS_SYNC);
   }
 
   return leveldb::Status::OK();
@@ -4320,22 +4325,36 @@
 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
     : public IndexedDBBackingStore::BlobWriteCallback {
  public:
-  BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
-                           scoped_refptr<BlobWriteCallback> callback)
-      : transaction_(transaction), callback_(callback) {}
-  void Run(bool succeeded) override {
+  BlobWriteCallbackWrapper(
+      base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,
+      void* tracing_end_ptr,
+      scoped_refptr<BlobWriteCallback> callback)
+      : transaction_(std::move(transaction)),
+        tracing_end_ptr_(tracing_end_ptr),
+        callback_(callback) {}
+  leveldb::Status Run(BlobWriteResult result) override {
+    DCHECK_NE(result, BlobWriteResult::SUCCESS_SYNC);
     IDB_ASYNC_TRACE_END("IndexedDBBackingStore::Transaction::WriteNewBlobs",
-                        transaction_);
-    callback_->Run(succeeded);
-    if (succeeded)  // Else it's already been deleted during rollback.
-      transaction_->chained_blob_writer_ = NULL;
+                        tracing_end_ptr_);
+    leveldb::Status leveldb_result = callback_->Run(result);
+    switch (result) {
+      case BlobWriteResult::FAILURE_ASYNC:
+        break;
+      case BlobWriteResult::SUCCESS_ASYNC:
+      case BlobWriteResult::SUCCESS_SYNC:
+        if (transaction_)
+          transaction_->chained_blob_writer_ = nullptr;
+        break;
+    }
+    return leveldb_result;
   }
 
  private:
   ~BlobWriteCallbackWrapper() override {}
   friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
 
-  IndexedDBBackingStore::Transaction* transaction_;
+  base::WeakPtr<IndexedDBBackingStore::Transaction> transaction_;
+  const void* const tracing_end_ptr_;
   scoped_refptr<BlobWriteCallback> callback_;
 
   DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
@@ -4358,12 +4377,11 @@
       transaction_->Put(blob_entry_iter.first.Encode(),
                         &blob_entry_iter.second);
   }
-  // Creating the writer will start it going asynchronously.
-  chained_blob_writer_ =
-      new ChainedBlobWriterImpl(database_id_,
-                                backing_store_,
-                                new_files_to_write,
-                                new BlobWriteCallbackWrapper(this, callback));
+  // Creating the writer will start it going asynchronously. The transaction
+  // can be destructed before the callback is triggered.
+  chained_blob_writer_ = new ChainedBlobWriterImpl(
+      database_id_, backing_store_, new_files_to_write,
+      new BlobWriteCallbackWrapper(ptr_factory_.GetWeakPtr(), this, callback));
 }
 
 void IndexedDBBackingStore::Transaction::Rollback() {
@@ -4378,7 +4396,7 @@
     chained_blob_writer_->Abort();
     chained_blob_writer_ = NULL;
   }
-  if (transaction_.get() == NULL)
+  if (!transaction_)
     return;
   transaction_->Rollback();
   transaction_ = NULL;
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h
index 00d6678f..46d8591 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -19,6 +19,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -90,9 +91,13 @@
     DISALLOW_COPY_AND_ASSIGN(RecordIdentifier);
   };
 
+  enum class BlobWriteResult { FAILURE_ASYNC, SUCCESS_ASYNC, SUCCESS_SYNC };
+
   class BlobWriteCallback : public base::RefCounted<BlobWriteCallback> {
    public:
-    virtual void Run(bool succeeded) = 0;
+    // TODO(dmurph): Make all calls to this method async after measuring
+    // performance.
+    virtual leveldb::Status Run(BlobWriteResult result) = 0;
 
    protected:
     friend class base::RefCounted<BlobWriteCallback>;
@@ -282,6 +287,8 @@
     // has been bumped, and journal cleaning should be deferred.
     bool committing_;
 
+    base::WeakPtrFactory<Transaction> ptr_factory_;
+
     DISALLOW_COPY_AND_ASSIGN(Transaction);
   };
 
diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
index f9ce90c..ca6ee07 100644
--- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -375,9 +375,18 @@
 class TestCallback : public IndexedDBBackingStore::BlobWriteCallback {
  public:
   TestCallback() : called(false), succeeded(false) {}
-  void Run(bool succeeded_in) override {
+  leveldb::Status Run(IndexedDBBackingStore::BlobWriteResult result) override {
     called = true;
-    succeeded = succeeded_in;
+    switch (result) {
+      case IndexedDBBackingStore::BlobWriteResult::FAILURE_ASYNC:
+        succeeded = false;
+        break;
+      case IndexedDBBackingStore::BlobWriteResult::SUCCESS_ASYNC:
+      case IndexedDBBackingStore::BlobWriteResult::SUCCESS_SYNC:
+        succeeded = true;
+        break;
+    }
+    return leveldb::Status::OK();
   }
   bool called;
   bool succeeded;
diff --git a/content/browser/indexed_db/indexed_db_callbacks.cc b/content/browser/indexed_db/indexed_db_callbacks.cc
index 03dd974..9ecc1ad 100644
--- a/content/browser/indexed_db/indexed_db_callbacks.cc
+++ b/content/browser/indexed_db/indexed_db_callbacks.cc
@@ -23,6 +23,7 @@
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_return_value.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
 #include "content/browser/indexed_db/indexed_db_value.h"
 #include "content/common/indexed_db/indexed_db_constants.h"
 #include "content/common/indexed_db/indexed_db_metadata.h"
@@ -38,7 +39,6 @@
 namespace content {
 
 namespace {
-const int64_t kNoTransaction = -1;
 
 void ConvertBlobInfo(
     const std::vector<IndexedDBBlobInfo>& blob_info,
@@ -144,7 +144,6 @@
     const url::Origin& origin,
     ::indexed_db::mojom::CallbacksAssociatedPtrInfo callbacks_info)
     : dispatcher_host_(std::move(dispatcher_host)),
-      host_transaction_id_(kNoTransaction),
       origin_(origin),
       data_loss_(blink::WebIDBDataLossNone),
       sent_blocked_(false),
@@ -180,7 +179,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(dispatcher_host_);
   DCHECK(io_helper_);
-  DCHECK_EQ(kNoTransaction, host_transaction_id_);
 
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
@@ -221,11 +219,9 @@
   DCHECK(dispatcher_host_);
   DCHECK(io_helper_);
 
-  DCHECK_NE(kNoTransaction, host_transaction_id_);
   DCHECK(!database_sent_);
 
   data_loss_ = data_loss_info.status;
-  dispatcher_host_->RegisterTransactionId(host_transaction_id_, origin_);
   database_sent_ = true;
   auto database = base::MakeUnique<DatabaseImpl>(std::move(connection), origin_,
                                                  dispatcher_host_);
@@ -252,7 +248,6 @@
   DCHECK(dispatcher_host_);
   DCHECK(io_helper_);
 
-  DCHECK_NE(kNoTransaction, host_transaction_id_);
   DCHECK_EQ(database_sent_, !connection);
 
   scoped_refptr<IndexedDBCallbacks> self(this);
@@ -278,7 +273,7 @@
   }
 }
 
-void IndexedDBCallbacks::OnSuccess(scoped_refptr<IndexedDBCursor> cursor,
+void IndexedDBCallbacks::OnSuccess(std::unique_ptr<IndexedDBCursor> cursor,
                                    const IndexedDBKey& key,
                                    const IndexedDBKey& primary_key,
                                    IndexedDBValue* value) {
@@ -286,11 +281,10 @@
   DCHECK(dispatcher_host_);
   DCHECK(io_helper_);
 
-  DCHECK_EQ(kNoTransaction, host_transaction_id_);
   DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
 
-  auto cursor_impl =
-      base::MakeUnique<CursorImpl>(cursor, origin_, dispatcher_host_);
+  auto cursor_impl = base::MakeUnique<CursorImpl>(std::move(cursor), origin_,
+                                                  dispatcher_host_);
 
   ::indexed_db::mojom::ValuePtr mojo_value;
   std::vector<IndexedDBBlobInfo> blob_info;
@@ -315,7 +309,6 @@
   DCHECK(dispatcher_host_);
   DCHECK(io_helper_);
 
-  DCHECK_EQ(kNoTransaction, host_transaction_id_);
   DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
 
   ::indexed_db::mojom::ValuePtr mojo_value;
@@ -343,7 +336,6 @@
   DCHECK_EQ(keys.size(), primary_keys.size());
   DCHECK_EQ(keys.size(), values->size());
 
-  DCHECK_EQ(kNoTransaction, host_transaction_id_);
   DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
 
   std::vector<::indexed_db::mojom::ValuePtr> mojo_values;
@@ -363,7 +355,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(dispatcher_host_);
 
-  DCHECK_EQ(kNoTransaction, host_transaction_id_);
   DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
 
   ::indexed_db::mojom::ReturnValuePtr mojo_value;
@@ -387,7 +378,6 @@
   DCHECK(dispatcher_host_);
   DCHECK(io_helper_);
 
-  DCHECK_EQ(kNoTransaction, host_transaction_id_);
   DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
 
   std::vector<::indexed_db::mojom::ReturnValuePtr> mojo_values;
@@ -407,7 +397,6 @@
   DCHECK(dispatcher_host_);
   DCHECK(io_helper_);
 
-  DCHECK_EQ(kNoTransaction, host_transaction_id_);
   DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
 
   BrowserThread::PostTask(
@@ -433,7 +422,6 @@
   DCHECK(dispatcher_host_);
   DCHECK(io_helper_);
 
-  DCHECK_EQ(kNoTransaction, host_transaction_id_);
   DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
 
   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
diff --git a/content/browser/indexed_db/indexed_db_callbacks.h b/content/browser/indexed_db/indexed_db_callbacks.h
index bf26a1d..c3b564f 100644
--- a/content/browser/indexed_db/indexed_db_callbacks.h
+++ b/content/browser/indexed_db/indexed_db_callbacks.h
@@ -59,7 +59,7 @@
                          const content::IndexedDBDatabaseMetadata& metadata);
 
   // IndexedDBDatabase::OpenCursor
-  virtual void OnSuccess(scoped_refptr<IndexedDBCursor> cursor,
+  virtual void OnSuccess(std::unique_ptr<IndexedDBCursor> cursor,
                          const IndexedDBKey& key,
                          const IndexedDBKey& primary_key,
                          IndexedDBValue* value);
@@ -99,10 +99,6 @@
 
   void SetConnectionOpenStartTime(const base::TimeTicks& start_time);
 
-  void set_host_transaction_id(int64_t host_transaction_id) {
-    host_transaction_id_ = host_transaction_id;
-  }
-
  protected:
   virtual ~IndexedDBCallbacks();
 
@@ -118,7 +114,6 @@
   scoped_refptr<IndexedDBDispatcherHost> dispatcher_host_;
 
   // IndexedDBDatabase callbacks ------------------------
-  int64_t host_transaction_id_;
   url::Origin origin_;
   bool database_sent_ = false;
 
diff --git a/content/browser/indexed_db/indexed_db_class_factory.cc b/content/browser/indexed_db/indexed_db_class_factory.cc
index 2ef35d6..c6e2489e 100644
--- a/content/browser/indexed_db/indexed_db_class_factory.cc
+++ b/content/browser/indexed_db/indexed_db_class_factory.cc
@@ -37,17 +37,15 @@
   return new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
 }
 
-IndexedDBTransaction* IndexedDBClassFactory::CreateIndexedDBTransaction(
+std::unique_ptr<IndexedDBTransaction>
+IndexedDBClassFactory::CreateIndexedDBTransaction(
     int64_t id,
-    base::WeakPtr<IndexedDBConnection> connection,
+    IndexedDBConnection* connection,
     const std::set<int64_t>& scope,
     blink::WebIDBTransactionMode mode,
     IndexedDBBackingStore::Transaction* backing_store_transaction) {
-  // The transaction adds itself to |connection|'s database's transaction
-  // coordinator, which owns the object.
-  IndexedDBTransaction* transaction = new IndexedDBTransaction(
-      id, std::move(connection), scope, mode, backing_store_transaction);
-  return transaction;
+  return std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+      id, connection, scope, mode, backing_store_transaction));
 }
 
 scoped_refptr<LevelDBTransaction>
diff --git a/content/browser/indexed_db/indexed_db_class_factory.h b/content/browser/indexed_db/indexed_db_class_factory.h
index f9fb5fd..6f216e6 100644
--- a/content/browser/indexed_db/indexed_db_class_factory.h
+++ b/content/browser/indexed_db/indexed_db_class_factory.h
@@ -47,9 +47,9 @@
       scoped_refptr<IndexedDBFactory> factory,
       const IndexedDBDatabase::Identifier& unique_identifier);
 
-  virtual IndexedDBTransaction* CreateIndexedDBTransaction(
+  virtual std::unique_ptr<IndexedDBTransaction> CreateIndexedDBTransaction(
       int64_t id,
-      base::WeakPtr<IndexedDBConnection> connection,
+      IndexedDBConnection* connection,
       const std::set<int64_t>& scope,
       blink::WebIDBTransactionMode mode,
       IndexedDBBackingStore::Transaction* backing_store_transaction);
diff --git a/content/browser/indexed_db/indexed_db_connection.cc b/content/browser/indexed_db/indexed_db_connection.cc
index 0c3cb35..216786a 100644
--- a/content/browser/indexed_db/indexed_db_connection.cc
+++ b/content/browser/indexed_db/indexed_db_connection.cc
@@ -5,6 +5,12 @@
 #include "content/browser/indexed_db/indexed_db_connection.h"
 
 #include "base/logging.h"
+#include "base/stl_util.h"
+#include "content/browser/indexed_db/indexed_db_class_factory.h"
+#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
+#include "content/browser/indexed_db/indexed_db_observer.h"
+#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
 
 namespace content {
 
@@ -15,9 +21,11 @@
 }  // namespace
 
 IndexedDBConnection::IndexedDBConnection(
+    int child_process_id,
     scoped_refptr<IndexedDBDatabase> database,
     scoped_refptr<IndexedDBDatabaseCallbacks> callbacks)
     : id_(next_id++),
+      child_process_id_(child_process_id),
       database_(database),
       callbacks_(callbacks),
       weak_factory_(this) {}
@@ -85,8 +93,69 @@
     else
       pending_observer_ids.push_back(id_to_remove);
   }
-  if (!pending_observer_ids.empty())
-    database_->RemovePendingObservers(this, pending_observer_ids);
+  if (pending_observer_ids.empty())
+    return;
+
+  for (const auto& it : transactions_) {
+    it.second->RemovePendingObservers(pending_observer_ids);
+  }
+}
+
+IndexedDBTransaction* IndexedDBConnection::CreateTransaction(
+    int64_t id,
+    const std::set<int64_t>& scope,
+    blink::WebIDBTransactionMode mode,
+    IndexedDBBackingStore::Transaction* backing_store_transaction) {
+  DCHECK_EQ(GetTransaction(id), nullptr) << "Duplicate transaction id." << id;
+  std::unique_ptr<IndexedDBTransaction> transaction =
+      IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
+          id, this, scope, mode, backing_store_transaction);
+  IndexedDBTransaction* transaction_ptr = transaction.get();
+  transactions_[id] = std::move(transaction);
+  return transaction_ptr;
+}
+
+void IndexedDBConnection::AbortTransaction(IndexedDBTransaction* transaction) {
+  IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction->id());
+  transaction->Abort();
+}
+
+void IndexedDBConnection::AbortTransaction(
+    IndexedDBTransaction* transaction,
+    const IndexedDBDatabaseError& error) {
+  IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction->id());
+  transaction->Abort(error);
+}
+
+void IndexedDBConnection::AbortAllTransactions(
+    const IndexedDBDatabaseError& error) {
+  std::unordered_map<int64_t, std::unique_ptr<IndexedDBTransaction>> temp_map;
+  std::swap(temp_map, transactions_);
+  for (const auto& pair : temp_map) {
+    IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", pair.second->id());
+    pair.second->Abort(error);
+  }
+}
+
+IndexedDBTransaction* IndexedDBConnection::GetTransaction(int64_t id) const {
+  auto it = transactions_.find(id);
+  if (it == transactions_.end())
+    return nullptr;
+  return it->second.get();
+}
+
+base::WeakPtr<IndexedDBTransaction>
+IndexedDBConnection::AddTransactionForTesting(
+    std::unique_ptr<IndexedDBTransaction> transaction) {
+  DCHECK(!base::ContainsKey(transactions_, transaction->id()));
+  base::WeakPtr<IndexedDBTransaction> transaction_ptr =
+      transaction->ptr_factory_.GetWeakPtr();
+  transactions_[transaction->id()] = std::move(transaction);
+  return transaction_ptr;
+}
+
+void IndexedDBConnection::RemoveTransaction(int64_t id) {
+  transactions_.erase(id);
 }
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_connection.h b/content/browser/indexed_db/indexed_db_connection.h
index ce60ebd..20acb7b 100644
--- a/content/browser/indexed_db/indexed_db_connection.h
+++ b/content/browser/indexed_db/indexed_db_connection.h
@@ -6,20 +6,25 @@
 #define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CONNECTION_H_
 
 #include <memory>
+#include <set>
+#include <unordered_map>
 #include <vector>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/indexed_db/indexed_db_database.h"
-#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
-#include "content/browser/indexed_db/indexed_db_observer.h"
 
 namespace content {
+class IndexedDBDatabaseCallbacks;
+class IndexedDBDatabaseError;
+class IndexedDBObserver;
+class IndexedDBTransaction;
 
 class CONTENT_EXPORT IndexedDBConnection {
  public:
-  IndexedDBConnection(scoped_refptr<IndexedDBDatabase> db,
+  IndexedDBConnection(int child_process_id,
+                      scoped_refptr<IndexedDBDatabase> db,
                       scoped_refptr<IndexedDBDatabaseCallbacks> callbacks);
   virtual ~IndexedDBConnection();
 
@@ -38,6 +43,7 @@
   virtual void RemoveObservers(const std::vector<int32_t>& remove_observer_ids);
 
   int32_t id() const { return id_; }
+  int child_process_id() const { return child_process_id_; }
 
   IndexedDBDatabase* database() const { return database_.get(); }
   IndexedDBDatabaseCallbacks* callbacks() const { return callbacks_.get(); }
@@ -49,12 +55,42 @@
     return weak_factory_.GetWeakPtr();
   }
 
+  // Creates a transaction for this connection.
+  IndexedDBTransaction* CreateTransaction(
+      int64_t id,
+      const std::set<int64_t>& scope,
+      blink::WebIDBTransactionMode mode,
+      IndexedDBBackingStore::Transaction* backing_store_transaction);
+
+  void AbortTransaction(IndexedDBTransaction* transaction);
+  void AbortTransaction(IndexedDBTransaction* transaction,
+                        const IndexedDBDatabaseError& error);
+
+  void AbortAllTransactions(const IndexedDBDatabaseError& error);
+
+  IndexedDBTransaction* GetTransaction(int64_t id) const;
+
+  base::WeakPtr<IndexedDBTransaction> AddTransactionForTesting(
+      std::unique_ptr<IndexedDBTransaction> transaction);
+
+  // We ignore calls where the id doesn't exist to facilitate the AbortAll call.
+  // TODO(dmurph): Change that so this doesn't need to ignore unknown ids.
+  void RemoveTransaction(int64_t id);
+
  private:
   const int32_t id_;
 
+  // The process id of the child process this connection is associated with.
+  // Tracked for IndexedDBContextImpl::GetAllOriginsDetails and debugging.
+  const int child_process_id_;
+
   // NULL in some unit tests, and after the connection is closed.
   scoped_refptr<IndexedDBDatabase> database_;
 
+  // The connection owns transactions created on this connection.
+  std::unordered_map<int64_t, std::unique_ptr<IndexedDBTransaction>>
+      transactions_;
+
   // The callbacks_ member is cleared when the connection is closed.
   // May be NULL in unit tests.
   scoped_refptr<IndexedDBDatabaseCallbacks> callbacks_;
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 8300e147..449283c1 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -236,13 +236,8 @@
           }
 
           transaction_info->SetDouble(
-              "pid",
-              IndexedDBDispatcherHost::TransactionIdToProcessId(
-                  transaction->id()));
-          transaction_info->SetDouble(
-              "tid",
-              IndexedDBDispatcherHost::TransactionIdToRendererTransactionId(
-                  transaction->id()));
+              "pid", transaction->connection()->child_process_id());
+          transaction_info->SetDouble("tid", transaction->id());
           transaction_info->SetDouble(
               "age",
               (base::Time::Now() - transaction->diagnostics().creation_time)
diff --git a/content/browser/indexed_db/indexed_db_cursor.cc b/content/browser/indexed_db/indexed_db_cursor.cc
index f4b2c5f..005d5411 100644
--- a/content/browser/indexed_db/indexed_db_cursor.cc
+++ b/content/browser/indexed_db/indexed_db_cursor.cc
@@ -18,6 +18,42 @@
 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
 
 namespace content {
+namespace {
+// This should never be script visible: the cursor should either be closed when
+// it hits the end of the range (and script throws an error before the call
+// could be made), if the transaction has finished (ditto), or if there's an
+// incoming request from the front end but the transaction has aborted on the
+// back end; in that case the tx will already have sent an abort to the request
+// so this would be ignored.
+IndexedDBDatabaseError CreateCursorClosedError() {
+  return IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
+                                "The cursor has been closed.");
+}
+
+leveldb::Status InvokeOrSucceed(base::WeakPtr<IndexedDBCursor> weak_cursor,
+                                IndexedDBTransaction::Operation operation,
+                                IndexedDBTransaction* transaction) {
+  if (weak_cursor)
+    return operation.Run(transaction);
+  return leveldb::Status::OK();
+}
+
+// This allows us to bind a function with a return value to a weak ptr, and if
+// the weak pointer is invalidated then we just return a default (success).
+template <typename Functor, typename... Args>
+IndexedDBTransaction::Operation BindWeakOperation(
+    Functor&& functor,
+    base::WeakPtr<IndexedDBCursor> weak_cursor,
+    Args&&... args) {
+  DCHECK(weak_cursor);
+  IndexedDBCursor* cursor_ptr = weak_cursor.get();
+  return base::Bind(
+      &InvokeOrSucceed, std::move(weak_cursor),
+      base::Bind(std::forward<Functor>(functor), base::Unretained(cursor_ptr),
+                 std::forward<Args>(args)...));
+}
+
+}  // namespace
 
 IndexedDBCursor::IndexedDBCursor(
     std::unique_ptr<IndexedDBBackingStore::Cursor> cursor,
@@ -28,13 +64,10 @@
       cursor_type_(cursor_type),
       transaction_(transaction),
       cursor_(std::move(cursor)),
-      closed_(false) {
-  transaction_->RegisterOpenCursor(this);
-}
+      closed_(false),
+      ptr_factory_(this) {}
 
-IndexedDBCursor::~IndexedDBCursor() {
-  transaction_->UnregisterOpenCursor(this);
-}
+IndexedDBCursor::~IndexedDBCursor() {}
 
 void IndexedDBCursor::Continue(std::unique_ptr<IndexedDBKey> key,
                                std::unique_ptr<IndexedDBKey> primary_key,
@@ -42,19 +75,15 @@
   IDB_TRACE("IndexedDBCursor::Continue");
 
   if (closed_) {
-    callbacks->OnError(
-        IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
-                               "The cursor has been closed."));
+    callbacks->OnError(CreateCursorClosedError());
     return;
   }
 
   transaction_->ScheduleTask(
       task_type_,
-      base::Bind(&IndexedDBCursor::CursorIterationOperation,
-                 this,
-                 base::Passed(&key),
-                 base::Passed(&primary_key),
-                 callbacks));
+      BindWeakOperation(&IndexedDBCursor::CursorIterationOperation,
+                        ptr_factory_.GetWeakPtr(), base::Passed(&key),
+                        base::Passed(&primary_key), callbacks));
 }
 
 void IndexedDBCursor::Advance(uint32_t count,
@@ -62,16 +91,19 @@
   IDB_TRACE("IndexedDBCursor::Advance");
 
   if (closed_) {
-    callbacks->OnError(
-        IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
-                               "The cursor has been closed."));
+    callbacks->OnError(CreateCursorClosedError());
     return;
   }
 
   transaction_->ScheduleTask(
       task_type_,
-      base::Bind(
-          &IndexedDBCursor::CursorAdvanceOperation, this, count, callbacks));
+      BindWeakOperation(&IndexedDBCursor::CursorAdvanceOperation,
+                        ptr_factory_.GetWeakPtr(), count, callbacks));
+}
+
+void IndexedDBCursor::RemoveCursorFromTransaction() {
+  if (transaction_)
+    transaction_->UnregisterOpenCursor(this);
 }
 
 leveldb::Status IndexedDBCursor::CursorAdvanceOperation(
@@ -131,18 +163,14 @@
   IDB_TRACE("IndexedDBCursor::PrefetchContinue");
 
   if (closed_) {
-    callbacks->OnError(
-        IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
-                               "The cursor has been closed."));
+    callbacks->OnError(CreateCursorClosedError());
     return;
   }
 
   transaction_->ScheduleTask(
       task_type_,
-      base::Bind(&IndexedDBCursor::CursorPrefetchIterationOperation,
-                 this,
-                 number_to_fetch,
-                 callbacks));
+      BindWeakOperation(&IndexedDBCursor::CursorPrefetchIterationOperation,
+                        ptr_factory_.GetWeakPtr(), number_to_fetch, callbacks));
 }
 
 leveldb::Status IndexedDBCursor::CursorPrefetchIterationOperation(
@@ -244,6 +272,7 @@
   closed_ = true;
   cursor_.reset();
   saved_cursor_.reset();
+  transaction_ = nullptr;
 }
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_cursor.h b/content/browser/indexed_db/indexed_db_cursor.h
index 72852f6..591f9f89 100644
--- a/content/browser/indexed_db/indexed_db_cursor.h
+++ b/content/browser/indexed_db/indexed_db_cursor.h
@@ -12,6 +12,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "content/browser/indexed_db/indexed_db_backing_store.h"
 #include "content/browser/indexed_db/indexed_db_database.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
@@ -20,13 +21,13 @@
 
 namespace content {
 
-class CONTENT_EXPORT IndexedDBCursor
-    : NON_EXPORTED_BASE(public base::RefCounted<IndexedDBCursor>) {
+class CONTENT_EXPORT IndexedDBCursor {
  public:
   IndexedDBCursor(std::unique_ptr<IndexedDBBackingStore::Cursor> cursor,
                   indexed_db::CursorType cursor_type,
                   blink::WebIDBTaskType task_type,
                   IndexedDBTransaction* transaction);
+  ~IndexedDBCursor();
 
   void Advance(uint32_t count, scoped_refptr<IndexedDBCallbacks> callbacks);
   void Continue(std::unique_ptr<IndexedDBKey> key,
@@ -42,6 +43,8 @@
     return (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) ? NULL
                                                          : cursor_->value();
   }
+
+  void RemoveCursorFromTransaction();
   void Close();
 
   leveldb::Status CursorIterationOperation(
@@ -59,13 +62,11 @@
       IndexedDBTransaction* transaction);
 
  private:
-  friend class base::RefCounted<IndexedDBCursor>;
-
-  ~IndexedDBCursor();
-
   blink::WebIDBTaskType task_type_;
   indexed_db::CursorType cursor_type_;
-  const scoped_refptr<IndexedDBTransaction> transaction_;
+
+  // We rely on the transaction calling Close() to clear this.
+  IndexedDBTransaction* transaction_;
 
   // Must be destroyed before transaction_.
   std::unique_ptr<IndexedDBBackingStore::Cursor> cursor_;
@@ -74,6 +75,8 @@
 
   bool closed_;
 
+  base::WeakPtrFactory<IndexedDBCursor> ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(IndexedDBCursor);
 };
 
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index f15d312..b910f2b 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -70,6 +70,19 @@
   return KEY_PATH_TYPE_NONE;
 }
 
+// The database will be closed (IndexedDBFactory::ForceClose) during this call.
+// This should NOT be used in an method scheduled as a transaction operation.
+void ReportError(leveldb::Status status,
+                 const url::Origin& origin,
+                 IndexedDBFactory* factory,
+                 const IndexedDBDatabaseError& error) {
+  DCHECK(!status.ok());
+  if (status.IsCorruption())
+    factory->HandleBackingStoreCorruption(origin, error);
+  else
+    factory->HandleBackingStoreFailure(origin);
+}
+
 }  // namespace
 
 // This represents what script calls an 'IDBOpenDBRequest' - either a database
@@ -464,7 +477,6 @@
 }
 
 IndexedDBDatabase::~IndexedDBDatabase() {
-  DCHECK(transactions_.empty());
   DCHECK(!active_request_);
   DCHECK(pending_requests_.empty());
 }
@@ -477,20 +489,13 @@
     scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
     int child_process_id) {
   std::unique_ptr<IndexedDBConnection> connection(
-      base::MakeUnique<IndexedDBConnection>(this, database_callbacks));
+      base::MakeUnique<IndexedDBConnection>(child_process_id, this,
+                                            database_callbacks));
   connections_.insert(connection.get());
   backing_store_->GrantChildProcessPermissions(child_process_id);
   return connection;
 }
 
-IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
-    int64_t transaction_id) const {
-  const auto& trans_iterator = transactions_.find(transaction_id);
-  if (trans_iterator == transactions_.end())
-    return NULL;
-  return trans_iterator->second;
-}
-
 bool IndexedDBDatabase::ValidateObjectStoreId(int64_t object_store_id) const {
   if (!base::ContainsKey(metadata_.object_stores, object_store_id)) {
     DLOG(ERROR) << "Invalid object_store_id";
@@ -542,15 +547,14 @@
   return true;
 }
 
-void IndexedDBDatabase::CreateObjectStore(int64_t transaction_id,
+void IndexedDBDatabase::CreateObjectStore(IndexedDBTransaction* transaction,
                                           int64_t object_store_id,
                                           const base::string16& name,
                                           const IndexedDBKeyPath& key_path,
                                           bool auto_increment) {
-  IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id",
+             transaction->id());
   DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
 
   if (base::ContainsKey(metadata_.object_stores, object_store_id)) {
@@ -585,9 +589,7 @@
         blink::WebIDBDatabaseExceptionUnknownError,
         ASCIIToUTF16("Internal error creating object store '") +
             object_store_metadata.name + ASCIIToUTF16("'."));
-    transaction->Abort(error);
-    if (s.IsCorruption())
-      factory_->HandleBackingStoreCorruption(backing_store_->origin(), error);
+    ReportError(s, origin(), factory_.get(), error);
     return;
   }
 
@@ -598,12 +600,11 @@
                  object_store_id));
 }
 
-void IndexedDBDatabase::DeleteObjectStore(int64_t transaction_id,
+void IndexedDBDatabase::DeleteObjectStore(IndexedDBTransaction* transaction,
                                           int64_t object_store_id) {
-  IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id",
+             transaction->id());
   DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
 
   if (!ValidateObjectStoreId(object_store_id))
@@ -615,13 +616,12 @@
                  object_store_id));
 }
 
-void IndexedDBDatabase::RenameObjectStore(int64_t transaction_id,
+void IndexedDBDatabase::RenameObjectStore(IndexedDBTransaction* transaction,
                                           int64_t object_store_id,
                                           const base::string16& new_name) {
-  IDB_TRACE1("IndexedDBDatabase::RenameObjectStore", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::RenameObjectStore", "txn.id",
+             transaction->id());
   DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
 
   if (!ValidateObjectStoreId(object_store_id))
@@ -643,9 +643,7 @@
         ASCIIToUTF16("Internal error renaming object store '") +
             object_store_metadata.name + ASCIIToUTF16("' to '") + new_name +
             ASCIIToUTF16("'."));
-    transaction->Abort(error);
-    if (s.IsCorruption())
-      factory_->HandleBackingStoreCorruption(backing_store_->origin(), error);
+    ReportError(s, origin(), factory_.get(), error);
     return;
   }
 
@@ -657,17 +655,15 @@
   SetObjectStoreName(object_store_id, new_name);
 }
 
-void IndexedDBDatabase::CreateIndex(int64_t transaction_id,
+void IndexedDBDatabase::CreateIndex(IndexedDBTransaction* transaction,
                                     int64_t object_store_id,
                                     int64_t index_id,
                                     const base::string16& name,
                                     const IndexedDBKeyPath& key_path,
                                     bool unique,
                                     bool multi_entry) {
-  IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction->id());
   DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
 
   if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
@@ -714,13 +710,11 @@
   RemoveIndex(object_store_id, index_id);
 }
 
-void IndexedDBDatabase::DeleteIndex(int64_t transaction_id,
+void IndexedDBDatabase::DeleteIndex(IndexedDBTransaction* transaction,
                                     int64_t object_store_id,
                                     int64_t index_id) {
-  IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction->id());
   DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
 
   if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
@@ -748,15 +742,8 @@
                                   transaction->database()->id(),
                                   object_store_id,
                                   index_id);
-  if (!s.ok()) {
-    base::string16 error_string =
-        ASCIIToUTF16("Internal error deleting index '") +
-        index_metadata.name + ASCIIToUTF16("'.");
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 error_string);
-    transaction->Abort(error);
+  if (!s.ok())
     return s;
-  }
 
   RemoveIndex(object_store_id, index_id);
   transaction->ScheduleAbortTask(
@@ -774,14 +761,12 @@
   AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
 }
 
-void IndexedDBDatabase::RenameIndex(int64_t transaction_id,
+void IndexedDBDatabase::RenameIndex(IndexedDBTransaction* transaction,
                                     int64_t object_store_id,
                                     int64_t index_id,
                                     const base::string16& new_name) {
-  IDB_TRACE1("IndexedDBDatabase::RenameIndex", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::RenameIndex", "txn.id", transaction->id());
   DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
 
   if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
@@ -806,11 +791,7 @@
         ASCIIToUTF16("'.");
     IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
                                  error_string);
-    transaction->Abort(error);
-    if (s.IsCorruption())
-      factory_->HandleBackingStoreCorruption(origin(), error);
-    else
-      factory_->HandleBackingStoreFailure(origin());
+    ReportError(s, origin(), factory_.get(), error);
     return;
   }
 
@@ -831,67 +812,30 @@
   SetIndexName(object_store_id, index_id, old_name);
 }
 
-void IndexedDBDatabase::Commit(int64_t transaction_id) {
+void IndexedDBDatabase::Commit(IndexedDBTransaction* transaction) {
   // The frontend suggests that we commit, but we may have previously initiated
   // an abort, and so have disposed of the transaction. on_abort has already
   // been dispatched to the frontend, so it will find out about that
   // asynchronously.
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
   if (transaction) {
     scoped_refptr<IndexedDBFactory> factory = factory_;
     leveldb::Status result = transaction->Commit();
     if (!result.ok()) {
-      if (result.IsCorruption()) {
-        IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                     base::ASCIIToUTF16(result.ToString()));
-        factory->HandleBackingStoreCorruption(origin(), error);
-      } else {
-        factory->HandleBackingStoreFailure(origin());
-      }
+      IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+                                   base::ASCIIToUTF16(result.ToString()));
+      ReportError(result, origin(), factory_.get(), error);
     }
   }
 }
 
-void IndexedDBDatabase::Abort(int64_t transaction_id) {
-  // If the transaction is unknown, then it has already been aborted by the
-  // backend before this call so it is safe to ignore it.
-  IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (transaction)
-    transaction->Abort();
-}
-
-void IndexedDBDatabase::Abort(int64_t transaction_id,
-                              const IndexedDBDatabaseError& error) {
-  IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id);
-  // If the transaction is unknown, then it has already been aborted by the
-  // backend before this call so it is safe to ignore it.
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (transaction)
-    transaction->Abort(error);
-}
-
 void IndexedDBDatabase::AddPendingObserver(
-    int64_t transaction_id,
+    IndexedDBTransaction* transaction,
     int32_t observer_id,
     const IndexedDBObserver::Options& options) {
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
   transaction->AddPendingObserver(observer_id, options);
 }
 
-void IndexedDBDatabase::RemovePendingObservers(
-    IndexedDBConnection* connection,
-    const std::vector<int32_t>& pending_observer_ids) {
-  for (const auto& it : transactions_) {
-    // Avoid call to RemovePendingObservers for transactions on other
-    // connections.
-    if (it.second->connection() == connection)
-      it.second->RemovePendingObservers(pending_observer_ids);
-  }
-}
-
 // TODO(palakj): Augment the function with IDBValue later. Issue
 // crbug.com/609934.
 void IndexedDBDatabase::FilterObservation(IndexedDBTransaction* transaction,
@@ -928,17 +872,15 @@
   }
 }
 
-void IndexedDBDatabase::GetAll(int64_t transaction_id,
+void IndexedDBDatabase::GetAll(IndexedDBTransaction* transaction,
                                int64_t object_store_id,
                                int64_t index_id,
                                std::unique_ptr<IndexedDBKeyRange> key_range,
                                bool key_only,
                                int64_t max_count,
                                scoped_refptr<IndexedDBCallbacks> callbacks) {
-  IDB_TRACE1("IndexedDBDatabase::GetAll", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::GetAll", "txn.id", transaction->id());
 
   if (!ValidateObjectStoreId(object_store_id))
     return;
@@ -950,16 +892,14 @@
       max_count, callbacks));
 }
 
-void IndexedDBDatabase::Get(int64_t transaction_id,
+void IndexedDBDatabase::Get(IndexedDBTransaction* transaction,
                             int64_t object_store_id,
                             int64_t index_id,
                             std::unique_ptr<IndexedDBKeyRange> key_range,
                             bool key_only,
                             scoped_refptr<IndexedDBCallbacks> callbacks) {
-  IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction->id());
 
   if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
     return;
@@ -1025,13 +965,8 @@
           &s);
     }
 
-    if (!s.ok()) {
-      DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
-      IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                   "Internal error deleting data in range");
-      callbacks->OnError(error);
+    if (!s.ok())
       return s;
-    }
 
     if (!backing_store_cursor) {
       // This means we've run out of data.
@@ -1051,12 +986,8 @@
                                   object_store_id,
                                   *key,
                                   &value);
-    if (!s.ok()) {
-      IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                   "Internal error in GetRecord.");
-      callbacks->OnError(error);
+    if (!s.ok())
       return s;
-    }
 
     if (value.empty()) {
       callbacks->OnSuccess();
@@ -1086,12 +1017,9 @@
       index_id,
       *key,
       &primary_key);
-  if (!s.ok()) {
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 "Internal error in GetPrimaryKeyViaIndex.");
-    callbacks->OnError(error);
+  if (!s.ok())
     return s;
-  }
+
   if (!primary_key) {
     callbacks->OnSuccess();
     return s;
@@ -1109,12 +1037,8 @@
                                 object_store_id,
                                 *primary_key,
                                 &value);
-  if (!s.ok()) {
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 "Internal error in GetRecord.");
-    callbacks->OnError(error);
+  if (!s.ok())
     return s;
-  }
 
   if (value.empty()) {
     callbacks->OnSuccess();
@@ -1180,9 +1104,6 @@
 
   if (!s.ok()) {
     DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 "Internal error in GetAllOperation");
-    callbacks->OnError(error);
     return s;
   }
 
@@ -1209,12 +1130,8 @@
       cursor_valid = cursor->FirstSeek(&s);
       did_first_seek = true;
     }
-    if (!s.ok()) {
-      IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                   "Internal error in GetAllOperation.");
-      callbacks->OnError(error);
+    if (!s.ok())
       return s;
-    }
 
     if (!cursor_valid)
       break;
@@ -1310,7 +1227,7 @@
 };
 
 void IndexedDBDatabase::Put(
-    int64_t transaction_id,
+    IndexedDBTransaction* transaction,
     int64_t object_store_id,
     IndexedDBValue* value,
     std::vector<std::unique_ptr<storage::BlobDataHandle>>* handles,
@@ -1318,10 +1235,8 @@
     blink::WebIDBPutMode put_mode,
     scoped_refptr<IndexedDBCallbacks> callbacks,
     const std::vector<IndexedDBIndexKeys>& index_keys) {
-  IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction->id());
   DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
 
   if (!ValidateObjectStoreId(object_store_id))
@@ -1385,12 +1300,8 @@
         *key,
         &record_identifier,
         &found);
-    if (!s.ok()) {
-      IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                   "Internal error checking key existence.");
-      params->callbacks->OnError(error);
+    if (!s.ok())
       return s;
-    }
     if (found) {
       params->callbacks->OnError(
           IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
@@ -1429,13 +1340,9 @@
   s = backing_store_->PutRecord(transaction->BackingStoreTransaction(), id(),
                                 params->object_store_id, *key, &params->value,
                                 &params->handles, &record_identifier);
-  if (!s.ok()) {
-    IndexedDBDatabaseError error(
-        blink::WebIDBDatabaseExceptionUnknownError,
-        "Internal error: backing store error performing put/add.");
-    params->callbacks->OnError(error);
+  if (!s.ok())
     return s;
-  }
+
   {
     IDB_TRACE1("IndexedDBDatabase::PutOperation.UpdateIndexes", "txn.id",
                transaction->id());
@@ -1453,12 +1360,8 @@
                transaction->id());
     s = UpdateKeyGenerator(backing_store_.get(), transaction, id(),
                            params->object_store_id, *key, !key_was_generated);
-    if (!s.ok()) {
-      IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                   "Internal error updating key generator.");
-      params->callbacks->OnError(error);
+    if (!s.ok())
       return s;
-    }
   }
   {
     IDB_TRACE1("IndexedDBDatabase::PutOperation.Callbacks", "txn.id",
@@ -1474,15 +1377,12 @@
 }
 
 void IndexedDBDatabase::SetIndexKeys(
-    int64_t transaction_id,
+    IndexedDBTransaction* transaction,
     int64_t object_store_id,
     std::unique_ptr<IndexedDBKey> primary_key,
     const std::vector<IndexedDBIndexKeys>& index_keys) {
-  IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id);
-
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction->id());
   DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
 
   // TODO(alecflett): This method could be asynchronous, but we need to
@@ -1499,9 +1399,7 @@
   if (!s.ok()) {
     IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
                                  "Internal error setting index keys.");
-    transaction->Abort(error);
-    if (s.IsCorruption())
-      factory_->HandleBackingStoreCorruption(backing_store_->origin(), error);
+    ReportError(s, origin(), factory_.get(), error);
     return;
   }
   if (!found) {
@@ -1547,12 +1445,10 @@
   }
 }
 
-void IndexedDBDatabase::SetIndexesReady(int64_t transaction_id,
+void IndexedDBDatabase::SetIndexesReady(IndexedDBTransaction* transaction,
                                         int64_t,
                                         const std::vector<int64_t>& index_ids) {
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
   DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange);
 
   transaction->ScheduleTask(
@@ -1585,7 +1481,7 @@
 };
 
 void IndexedDBDatabase::OpenCursor(
-    int64_t transaction_id,
+    IndexedDBTransaction* transaction,
     int64_t object_store_id,
     int64_t index_id,
     std::unique_ptr<IndexedDBKeyRange> key_range,
@@ -1593,10 +1489,8 @@
     bool key_only,
     blink::WebIDBTaskType task_type,
     scoped_refptr<IndexedDBCallbacks> callbacks) {
-  IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction->id());
 
   if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
     return;
@@ -1674,8 +1568,6 @@
 
   if (!s.ok()) {
     DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 "Internal error opening cursor operation");
     return s;
   }
 
@@ -1685,23 +1577,23 @@
     return s;
   }
 
-  scoped_refptr<IndexedDBCursor> cursor =
-      new IndexedDBCursor(std::move(backing_store_cursor), params->cursor_type,
-                          params->task_type, transaction);
-  params->callbacks->OnSuccess(
-      cursor, cursor->key(), cursor->primary_key(), cursor->Value());
+  std::unique_ptr<IndexedDBCursor> cursor = base::MakeUnique<IndexedDBCursor>(
+      std::move(backing_store_cursor), params->cursor_type, params->task_type,
+      transaction);
+  IndexedDBCursor* cursor_ptr = cursor.get();
+  transaction->RegisterOpenCursor(cursor_ptr);
+  params->callbacks->OnSuccess(std::move(cursor), cursor_ptr->key(),
+                               cursor_ptr->primary_key(), cursor_ptr->Value());
   return s;
 }
 
-void IndexedDBDatabase::Count(int64_t transaction_id,
+void IndexedDBDatabase::Count(IndexedDBTransaction* transaction,
                               int64_t object_store_id,
                               int64_t index_id,
                               std::unique_ptr<IndexedDBKeyRange> key_range,
                               scoped_refptr<IndexedDBCallbacks> callbacks) {
-  IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction->id());
 
   if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
     return;
@@ -1745,8 +1637,6 @@
   }
   if (!s.ok()) {
     DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 "Internal error performing count operation");
     return s;
   }
   if (!backing_store_cursor) {
@@ -1765,14 +1655,12 @@
 }
 
 void IndexedDBDatabase::DeleteRange(
-    int64_t transaction_id,
+    IndexedDBTransaction* transaction,
     int64_t object_store_id,
     std::unique_ptr<IndexedDBKeyRange> key_range,
     scoped_refptr<IndexedDBCallbacks> callbacks) {
-  IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction->id());
   DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
 
   if (!ValidateObjectStoreId(object_store_id))
@@ -1796,27 +1684,19 @@
   leveldb::Status s =
       backing_store_->DeleteRange(transaction->BackingStoreTransaction(), id(),
                                   object_store_id, *key_range, &delete_count);
-  if (!s.ok()) {
-    base::string16 error_string =
-        ASCIIToUTF16("Internal error deleting data in range");
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 error_string);
-    transaction->Abort(error);
+  if (!s.ok())
     return s;
-  }
   callbacks->OnSuccess();
   FilterObservation(transaction, object_store_id, blink::WebIDBDelete,
                     *key_range);
   return s;
 }
 
-void IndexedDBDatabase::Clear(int64_t transaction_id,
+void IndexedDBDatabase::Clear(IndexedDBTransaction* transaction,
                               int64_t object_store_id,
                               scoped_refptr<IndexedDBCallbacks> callbacks) {
-  IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id);
-  IndexedDBTransaction* transaction = GetTransaction(transaction_id);
-  if (!transaction)
-    return;
+  DCHECK(transaction);
+  IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction->id());
   DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly);
 
   if (!ValidateObjectStoreId(object_store_id))
@@ -1833,12 +1713,8 @@
   IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction->id());
   leveldb::Status s = backing_store_->ClearObjectStore(
       transaction->BackingStoreTransaction(), id(), object_store_id);
-  if (!s.ok()) {
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 "Internal error clearing object store");
-    callbacks->OnError(error);
+  if (!s.ok())
     return s;
-  }
   callbacks->OnSuccess();
 
   FilterObservation(transaction, object_store_id, blink::WebIDBClear,
@@ -1859,15 +1735,8 @@
       backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
                                         transaction->database()->id(),
                                         object_store_id);
-  if (!s.ok()) {
-    base::string16 error_string =
-        ASCIIToUTF16("Internal error deleting object store '") +
-        object_store_metadata.name + ASCIIToUTF16("'.");
-    IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
-                                 error_string);
-    transaction->Abort(error);
+  if (!s.ok())
     return s;
-  }
 
   RemoveObjectStore(object_store_id);
   transaction->ScheduleAbortTask(
@@ -1900,10 +1769,10 @@
 
 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
                                             bool committed) {
-  IDB_TRACE1("IndexedDBTransaction::TransactionFinished", "txn.id", id());
-  DCHECK(transactions_.find(transaction->id()) != transactions_.end());
-  DCHECK_EQ(transactions_[transaction->id()], transaction);
-  transactions_.erase(transaction->id());
+  IDB_TRACE1("IndexedDBTransaction::TransactionFinished", "txn.id",
+             transaction->id());
+  --transaction_count_;
+  DCHECK_GE(transaction_count_, 0);
 
   // This may be an unrelated transaction finishing while waiting for
   // connections to close, or the actual upgrade transaction from an active
@@ -1956,27 +1825,22 @@
     blink::WebIDBTransactionMode mode) {
   IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
   DCHECK(connections_.count(connection));
-  DCHECK(transactions_.find(transaction_id) == transactions_.end());
-  if (transactions_.find(transaction_id) != transactions_.end())
-    return nullptr;
 
   UMA_HISTOGRAM_COUNTS_1000(
       "WebCore.IndexedDB.Database.OutstandingTransactionCount",
-      transactions_.size());
+      transaction_count_);
 
-  // The transaction will add itself to this database's coordinator, which
-  // manages the lifetime of the object.
-  IndexedDBTransaction* transaction =
-      IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
-          transaction_id, connection->GetWeakPtr(),
-          std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()),
-          mode, new IndexedDBBackingStore::Transaction(backing_store_.get()));
+  IndexedDBTransaction* transaction = connection->CreateTransaction(
+      transaction_id,
+      std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()), mode,
+      new IndexedDBBackingStore::Transaction(backing_store_.get()));
   TransactionCreated(transaction);
   return transaction;
 }
 
 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
-  transactions_[transaction->id()] = transaction;
+  transaction_count_++;
+  transaction_coordinator_.DidCreateTransaction(transaction);
 }
 
 void IndexedDBDatabase::OpenConnection(
@@ -2016,15 +1880,8 @@
   // happen if the close is requested by the connection itself as the
   // front-end defers the close until all transactions are complete, but can
   // occur on process termination or forced close.
-  {
-    auto transactions(transactions_);
-    for (const auto& it : transactions) {
-      if (it.second->callbacks() == connection->callbacks())
-        it.second->Abort(
-            IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
-                                   "Connection is closing."));
-    }
-  }
+  connection->AbortAllTransactions(IndexedDBDatabaseError(
+      blink::WebIDBDatabaseExceptionUnknownError, "Connection is closing."));
 
   // Abort transactions before removing the connection; aborting may complete
   // an upgrade, and thus allow the next open/delete requests to proceed. The
@@ -2041,7 +1898,6 @@
   // If there are no more connections (current, active, or pending), tell the
   // factory to clean us up.
   if (connections_.empty() && !active_request_ && pending_requests_.empty()) {
-    DCHECK(transactions_.empty());
     backing_store_ = nullptr;
     factory_->ReleaseDatabase(identifier_, forced);
   }
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h
index 1dc57be..1a82f30 100644
--- a/content/browser/indexed_db/indexed_db_database.h
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -82,13 +82,14 @@
   void DeleteDatabase(scoped_refptr<IndexedDBCallbacks> callbacks);
   const IndexedDBDatabaseMetadata& metadata() const { return metadata_; }
 
-  void CreateObjectStore(int64_t transaction_id,
+  void CreateObjectStore(IndexedDBTransaction* transaction,
                          int64_t object_store_id,
                          const base::string16& name,
                          const IndexedDBKeyPath& key_path,
                          bool auto_increment);
-  void DeleteObjectStore(int64_t transaction_id, int64_t object_store_id);
-  void RenameObjectStore(int64_t transaction_id,
+  void DeleteObjectStore(IndexedDBTransaction* transaction,
+                         int64_t object_store_id);
+  void RenameObjectStore(IndexedDBTransaction* transaction,
                          int64_t object_store_id,
                          const base::string16& new_name);
 
@@ -107,21 +108,19 @@
   // pending connection.
   void VersionChangeIgnored();
 
-  void Commit(int64_t transaction_id);
-  void Abort(int64_t transaction_id);
-  void Abort(int64_t transaction_id, const IndexedDBDatabaseError& error);
+  void Commit(IndexedDBTransaction* transaction);
 
-  void CreateIndex(int64_t transaction_id,
+  void CreateIndex(IndexedDBTransaction* transaction,
                    int64_t object_store_id,
                    int64_t index_id,
                    const base::string16& name,
                    const IndexedDBKeyPath& key_path,
                    bool unique,
                    bool multi_entry);
-  void DeleteIndex(int64_t transaction_id,
+  void DeleteIndex(IndexedDBTransaction* transaction,
                    int64_t object_store_id,
                    int64_t index_id);
-  void RenameIndex(int64_t transaction_id,
+  void RenameIndex(IndexedDBTransaction* transaction,
                    int64_t object_store_id,
                    int64_t index_id,
                    const base::string16& new_name);
@@ -136,11 +135,9 @@
   void TransactionCreated(IndexedDBTransaction* transaction);
   void TransactionFinished(IndexedDBTransaction* transaction, bool committed);
 
-  void AddPendingObserver(int64_t transaction_id,
+  void AddPendingObserver(IndexedDBTransaction* transaction,
                           int32_t observer_id,
                           const IndexedDBObserver::Options& options);
-  void RemovePendingObservers(IndexedDBConnection* connection,
-                              const std::vector<int32_t>& pending_observer_ids);
 
   void FilterObservation(IndexedDBTransaction*,
                          int64_t object_store_id,
@@ -149,20 +146,20 @@
   void SendObservations(
       std::map<int32_t, ::indexed_db::mojom::ObserverChangesPtr> change_map);
 
-  void Get(int64_t transaction_id,
+  void Get(IndexedDBTransaction* transaction,
            int64_t object_store_id,
            int64_t index_id,
            std::unique_ptr<IndexedDBKeyRange> key_range,
            bool key_only,
            scoped_refptr<IndexedDBCallbacks> callbacks);
-  void GetAll(int64_t transaction_id,
+  void GetAll(IndexedDBTransaction* transaction,
               int64_t object_store_id,
               int64_t index_id,
               std::unique_ptr<IndexedDBKeyRange> key_range,
               bool key_only,
               int64_t max_count,
               scoped_refptr<IndexedDBCallbacks> callbacks);
-  void Put(int64_t transaction_id,
+  void Put(IndexedDBTransaction* transaction,
            int64_t object_store_id,
            IndexedDBValue* value,
            std::vector<std::unique_ptr<storage::BlobDataHandle>>* handles,
@@ -170,14 +167,14 @@
            blink::WebIDBPutMode mode,
            scoped_refptr<IndexedDBCallbacks> callbacks,
            const std::vector<IndexedDBIndexKeys>& index_keys);
-  void SetIndexKeys(int64_t transaction_id,
+  void SetIndexKeys(IndexedDBTransaction* transaction,
                     int64_t object_store_id,
                     std::unique_ptr<IndexedDBKey> primary_key,
                     const std::vector<IndexedDBIndexKeys>& index_keys);
-  void SetIndexesReady(int64_t transaction_id,
+  void SetIndexesReady(IndexedDBTransaction* transaction,
                        int64_t object_store_id,
                        const std::vector<int64_t>& index_ids);
-  void OpenCursor(int64_t transaction_id,
+  void OpenCursor(IndexedDBTransaction* transaction,
                   int64_t object_store_id,
                   int64_t index_id,
                   std::unique_ptr<IndexedDBKeyRange> key_range,
@@ -185,16 +182,16 @@
                   bool key_only,
                   blink::WebIDBTaskType task_type,
                   scoped_refptr<IndexedDBCallbacks> callbacks);
-  void Count(int64_t transaction_id,
+  void Count(IndexedDBTransaction* transaction,
              int64_t object_store_id,
              int64_t index_id,
              std::unique_ptr<IndexedDBKeyRange> key_range,
              scoped_refptr<IndexedDBCallbacks> callbacks);
-  void DeleteRange(int64_t transaction_id,
+  void DeleteRange(IndexedDBTransaction* transaction,
                    int64_t object_store_id,
                    std::unique_ptr<IndexedDBKeyRange> key_range,
                    scoped_refptr<IndexedDBCallbacks> callbacks);
-  void Clear(int64_t transaction_id,
+  void Clear(IndexedDBTransaction* transaction,
              int64_t object_store_id,
              scoped_refptr<IndexedDBCallbacks> callbacks);
 
@@ -306,8 +303,6 @@
       scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
       int child_process_id);
 
-  IndexedDBTransaction* GetTransaction(int64_t transaction_id) const;
-
   bool ValidateObjectStoreId(int64_t object_store_id) const;
   bool ValidateObjectStoreIdAndIndexId(int64_t object_store_id,
                                        int64_t index_id) const;
@@ -323,8 +318,7 @@
   scoped_refptr<IndexedDBFactory> factory_;
 
   IndexedDBTransactionCoordinator transaction_coordinator_;
-
-  std::map<int64_t, IndexedDBTransaction*> transactions_;
+  int64_t transaction_count_ = 0;
 
   list_set<IndexedDBConnection*> connections_;
 
diff --git a/content/browser/indexed_db/indexed_db_database_callbacks.cc b/content/browser/indexed_db/indexed_db_database_callbacks.cc
index 90daa5ff..bc7e6b9 100644
--- a/content/browser/indexed_db/indexed_db_database_callbacks.cc
+++ b/content/browser/indexed_db/indexed_db_database_callbacks.cc
@@ -7,6 +7,7 @@
 #include "content/browser/indexed_db/indexed_db_context_impl.h"
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
 
 using ::indexed_db::mojom::DatabaseCallbacksAssociatedPtrInfo;
 
@@ -67,33 +68,33 @@
                  base::Unretained(io_helper_.get()), old_version, new_version));
 }
 
-void IndexedDBDatabaseCallbacks::OnAbort(int64_t host_transaction_id,
-                                         const IndexedDBDatabaseError& error) {
+void IndexedDBDatabaseCallbacks::OnAbort(
+    const IndexedDBTransaction& transaction,
+    const IndexedDBDatabaseError& error) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (!dispatcher_host_)
     return;
 
-  dispatcher_host_->FinishTransaction(host_transaction_id, false);
   DCHECK(io_helper_);
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(&IOThreadHelper::SendAbort, base::Unretained(io_helper_.get()),
-                 dispatcher_host_->RendererTransactionId(host_transaction_id),
-                 error));
+                 transaction.id(), error));
 }
 
-void IndexedDBDatabaseCallbacks::OnComplete(int64_t host_transaction_id) {
+void IndexedDBDatabaseCallbacks::OnComplete(
+    const IndexedDBTransaction& transaction) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (!dispatcher_host_)
     return;
 
-  dispatcher_host_->FinishTransaction(host_transaction_id, true);
+  dispatcher_host_->context()->TransactionComplete(
+      transaction.database()->origin());
   DCHECK(io_helper_);
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(&IOThreadHelper::SendComplete,
-                 base::Unretained(io_helper_.get()),
-                 dispatcher_host_->RendererTransactionId(host_transaction_id)));
+                 base::Unretained(io_helper_.get()), transaction.id()));
 }
 
 void IndexedDBDatabaseCallbacks::OnDatabaseChange(
diff --git a/content/browser/indexed_db/indexed_db_database_callbacks.h b/content/browser/indexed_db/indexed_db_database_callbacks.h
index 23298cde..1978d72 100644
--- a/content/browser/indexed_db/indexed_db_database_callbacks.h
+++ b/content/browser/indexed_db/indexed_db_database_callbacks.h
@@ -17,6 +17,7 @@
 namespace content {
 class IndexedDBDatabaseError;
 class IndexedDBDispatcherHost;
+class IndexedDBTransaction;
 
 class CONTENT_EXPORT IndexedDBDatabaseCallbacks
     : public base::RefCounted<IndexedDBDatabaseCallbacks> {
@@ -28,9 +29,9 @@
   virtual void OnForcedClose();
   virtual void OnVersionChange(int64_t old_version, int64_t new_version);
 
-  virtual void OnAbort(int64_t host_transaction_id,
+  virtual void OnAbort(const IndexedDBTransaction& transaction,
                        const IndexedDBDatabaseError& error);
-  virtual void OnComplete(int64_t host_transaction_id);
+  virtual void OnComplete(const IndexedDBTransaction& transaction);
   virtual void OnDatabaseChange(
       ::indexed_db::mojom::ObserverChangesPtr changes);
 
diff --git a/content/browser/indexed_db/indexed_db_database_unittest.cc b/content/browser/indexed_db/indexed_db_database_unittest.cc
index 7b322ef..5d85503 100644
--- a/content/browser/indexed_db/indexed_db_database_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_database_unittest.cc
@@ -361,13 +361,13 @@
     db_->OpenConnection(std::move(connection));
     EXPECT_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata().version);
 
-    connection_ = base::MakeUnique<IndexedDBConnection>(db_, callbacks_);
-    transaction_ = IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
-        transaction_id, connection_->GetWeakPtr(),
-        std::set<int64_t>() /*scope*/,
+    connection_ = base::MakeUnique<IndexedDBConnection>(kFakeChildProcessId,
+                                                        db_, callbacks_);
+    transaction_ = connection_->CreateTransaction(
+        transaction_id, std::set<int64_t>() /*scope*/,
         blink::WebIDBTransactionModeVersionChange,
         new IndexedDBFakeBackingStore::FakeTransaction(commit_success_));
-    db_->TransactionCreated(transaction_.get());
+    db_->TransactionCreated(transaction_);
 
     // Add a dummy task which takes the place of the VersionChangeOperation
     // which kicks off the upgrade. This ensures that the transaction has
@@ -382,7 +382,7 @@
   scoped_refptr<IndexedDBDatabase> db_;
   scoped_refptr<MockIndexedDBCallbacks> request_;
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_;
-  scoped_refptr<IndexedDBTransaction> transaction_;
+  IndexedDBTransaction* transaction_;
   std::unique_ptr<IndexedDBConnection> connection_;
 
   leveldb::Status commit_success_;
@@ -397,11 +397,8 @@
 TEST_F(IndexedDBDatabaseOperationTest, CreateObjectStore) {
   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
   const int64_t store_id = 1001;
-  db_->CreateObjectStore(transaction_->id(),
-                         store_id,
-                         ASCIIToUTF16("store"),
-                         IndexedDBKeyPath(),
-                         false /*auto_increment*/);
+  db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"),
+                         IndexedDBKeyPath(), false /*auto_increment*/);
   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
   RunPostedTasks();
   transaction_->Commit();
@@ -411,20 +408,12 @@
 TEST_F(IndexedDBDatabaseOperationTest, CreateIndex) {
   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
   const int64_t store_id = 1001;
-  db_->CreateObjectStore(transaction_->id(),
-                         store_id,
-                         ASCIIToUTF16("store"),
-                         IndexedDBKeyPath(),
-                         false /*auto_increment*/);
+  db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"),
+                         IndexedDBKeyPath(), false /*auto_increment*/);
   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
   const int64_t index_id = 2002;
-  db_->CreateIndex(transaction_->id(),
-                   store_id,
-                   index_id,
-                   ASCIIToUTF16("index"),
-                   IndexedDBKeyPath(),
-                   false /*unique*/,
-                   false /*multi_entry*/);
+  db_->CreateIndex(transaction_, store_id, index_id, ASCIIToUTF16("index"),
+                   IndexedDBKeyPath(), false /*unique*/, false /*multi_entry*/);
   EXPECT_EQ(
       1ULL,
       db_->metadata().object_stores.find(store_id)->second.indexes.size());
@@ -450,11 +439,8 @@
 TEST_F(IndexedDBDatabaseOperationAbortTest, CreateObjectStore) {
   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
   const int64_t store_id = 1001;
-  db_->CreateObjectStore(transaction_->id(),
-                         store_id,
-                         ASCIIToUTF16("store"),
-                         IndexedDBKeyPath(),
-                         false /*auto_increment*/);
+  db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"),
+                         IndexedDBKeyPath(), false /*auto_increment*/);
   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
   RunPostedTasks();
   transaction_->Commit();
@@ -464,20 +450,12 @@
 TEST_F(IndexedDBDatabaseOperationAbortTest, CreateIndex) {
   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
   const int64_t store_id = 1001;
-  db_->CreateObjectStore(transaction_->id(),
-                         store_id,
-                         ASCIIToUTF16("store"),
-                         IndexedDBKeyPath(),
-                         false /*auto_increment*/);
+  db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"),
+                         IndexedDBKeyPath(), false /*auto_increment*/);
   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
   const int64_t index_id = 2002;
-  db_->CreateIndex(transaction_->id(),
-                   store_id,
-                   index_id,
-                   ASCIIToUTF16("index"),
-                   IndexedDBKeyPath(),
-                   false /*unique*/,
-                   false /*multi_entry*/);
+  db_->CreateIndex(transaction_, store_id, index_id, ASCIIToUTF16("index"),
+                   IndexedDBKeyPath(), false /*unique*/, false /*multi_entry*/);
   EXPECT_EQ(
       1ULL,
       db_->metadata().object_stores.find(store_id)->second.indexes.size());
@@ -491,11 +469,8 @@
   const int64_t store_id = 1001;
 
   // Creation is synchronous.
-  db_->CreateObjectStore(transaction_->id(),
-                         store_id,
-                         ASCIIToUTF16("store"),
-                         IndexedDBKeyPath(),
-                         false /*auto_increment*/);
+  db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"),
+                         IndexedDBKeyPath(), false /*auto_increment*/);
   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
 
 
@@ -506,12 +481,11 @@
   std::vector<IndexedDBIndexKeys> index_keys;
   scoped_refptr<MockIndexedDBCallbacks> request(
       new MockIndexedDBCallbacks(false));
-  db_->Put(transaction_->id(), store_id, &value, &handles, std::move(key),
+  db_->Put(transaction_, store_id, &value, &handles, std::move(key),
            blink::WebIDBPutModeAddOnly, request, index_keys);
 
   // Deletion is asynchronous.
-  db_->DeleteObjectStore(transaction_->id(),
-                         store_id);
+  db_->DeleteObjectStore(transaction_, store_id);
   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
 
   // This will execute the Put then Delete.
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.cc b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
index 7461b76e..8e5b198 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
@@ -100,61 +100,6 @@
   return false;
 }
 
-bool IndexedDBDispatcherHost::RegisterTransactionId(int64_t host_transaction_id,
-                                                    const url::Origin& origin) {
-  if (base::ContainsKey(transaction_size_map_, host_transaction_id))
-    return false;
-  transaction_size_map_[host_transaction_id] = 0;
-  transaction_origin_map_[host_transaction_id] = origin;
-  return true;
-}
-
-bool IndexedDBDispatcherHost::GetTransactionSize(int64_t host_transaction_id,
-                                                 int64_t* transaction_size) {
-  const auto it = transaction_size_map_.find(host_transaction_id);
-  if (it == transaction_size_map_.end())
-    return false;
-  *transaction_size = it->second;
-  return true;
-}
-
-void IndexedDBDispatcherHost::AddToTransaction(int64_t host_transaction_id,
-                                               int64_t value_length) {
-  transaction_size_map_[host_transaction_id] += value_length;
-}
-
-int64_t IndexedDBDispatcherHost::HostTransactionId(int64_t transaction_id) {
-  // Inject the renderer process id into the transaction id, to
-  // uniquely identify this transaction, and effectively bind it to
-  // the renderer that initiated it. The lower 32 bits of
-  // transaction_id are guaranteed to be unique within that renderer.
-  base::ProcessId pid = peer_pid();
-  DCHECK(!(transaction_id >> 32)) << "Transaction ids can only be 32 bits";
-  static_assert(sizeof(base::ProcessId) <= sizeof(int32_t),
-                "Process ID must fit in 32 bits");
-
-  return transaction_id | (static_cast<uint64_t>(pid) << 32);
-}
-
-int64_t IndexedDBDispatcherHost::RendererTransactionId(
-    int64_t host_transaction_id) {
-  DCHECK(host_transaction_id >> 32 == peer_pid())
-      << "Invalid renderer target for transaction id";
-  return host_transaction_id & 0xffffffff;
-}
-
-// static
-uint32_t IndexedDBDispatcherHost::TransactionIdToRendererTransactionId(
-    int64_t host_transaction_id) {
-  return host_transaction_id & 0xffffffff;
-}
-
-// static
-uint32_t IndexedDBDispatcherHost::TransactionIdToProcessId(
-    int64_t host_transaction_id) {
-  return (host_transaction_id >> 32) & 0xffffffff;
-}
-
 std::string IndexedDBDispatcherHost::HoldBlobData(
     const IndexedDBBlobInfo& blob_info) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -286,15 +231,12 @@
   base::TimeTicks begin_time = base::TimeTicks::Now();
   base::FilePath indexed_db_path = indexed_db_context_->data_path();
 
-  int64_t host_transaction_id = HostTransactionId(transaction_id);
-
   // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
   // created) if this origin is already over quota.
   callbacks->SetConnectionOpenStartTime(begin_time);
-  callbacks->set_host_transaction_id(host_transaction_id);
   std::unique_ptr<IndexedDBPendingConnection> connection =
       base::MakeUnique<IndexedDBPendingConnection>(
-          callbacks, database_callbacks, ipc_process_id_, host_transaction_id,
+          callbacks, database_callbacks, ipc_process_id_, transaction_id,
           version);
   DCHECK(request_context_getter_);
   context()->GetIDBFactory()->Open(name, std::move(connection),
@@ -314,15 +256,4 @@
       name, request_context_getter_, callbacks, origin, indexed_db_path);
 }
 
-void IndexedDBDispatcherHost::FinishTransaction(int64_t host_transaction_id,
-                                                bool committed) {
-  DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
-  if (committed) {
-    context()->TransactionComplete(
-        transaction_origin_map_[host_transaction_id]);
-  }
-  transaction_origin_map_.erase(host_transaction_id);
-  transaction_size_map_.erase(host_transaction_id);
-}
-
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.h b/content/browser/indexed_db/indexed_db_dispatcher_host.h
index 28f1966..33a55d74 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.h
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.h
@@ -21,7 +21,6 @@
 #include "content/public/browser/browser_message_filter.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "storage/browser/blob/blob_data_handle.h"
-#include "url/gurl.h"
 
 namespace url {
 class Origin;
@@ -50,8 +49,6 @@
   void OnDestruct() const override;
   bool OnMessageReceived(const IPC::Message& message) override;
 
-  void FinishTransaction(int64_t host_transaction_id, bool committed);
-
   // A shortcut for accessing our context.
   IndexedDBContextImpl* context() const { return indexed_db_context_.get(); }
   storage::BlobStorageContext* blob_storage_context() const {
@@ -59,23 +56,6 @@
   }
   int ipc_process_id() const { return ipc_process_id_; }
 
-  bool RegisterTransactionId(int64_t host_transaction_id,
-                             const url::Origin& origin);
-  bool GetTransactionSize(int64_t host_transaction_id,
-                          int64_t* transaction_size);
-  void AddToTransaction(int64_t host_transaction_id, int64_t value_length);
-
-  // These are called to map a 32-bit front-end (renderer-specific) transaction
-  // id to and from a back-end ("host") transaction id that encodes the process
-  // id in the high 32 bits. The mapping is host-specific and ids are validated.
-  int64_t HostTransactionId(int64_t transaction_id);
-  int64_t RendererTransactionId(int64_t host_transaction_id);
-
-  // These are called to decode a host transaction ID, for diagnostic purposes.
-  static uint32_t TransactionIdToRendererTransactionId(
-      int64_t host_transaction_id);
-  static uint32_t TransactionIdToProcessId(int64_t host_transaction_id);
-
   std::string HoldBlobData(const IndexedDBBlobInfo& blob_info);
   void DropBlobData(const std::string& uuid);
 
@@ -88,10 +68,6 @@
   friend class BrowserThread;
   friend class base::DeleteHelper<IndexedDBDispatcherHost>;
 
-  // Used in nested classes.
-  typedef std::map<int64_t, int64_t> TransactionIDToSizeMap;
-  typedef std::map<int64_t, url::Origin> TransactionIDToOriginMap;
-
   ~IndexedDBDispatcherHost() override;
 
   // indexed_db::mojom::Factory implementation:
@@ -138,8 +114,6 @@
 
   // Only access on IndexedDB thread.
   bool is_open_ = true;
-  TransactionIDToSizeMap transaction_size_map_;
-  TransactionIDToOriginMap transaction_origin_map_;
 
   // Used to set file permissions for blob storage.
   int ipc_process_id_;
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 24c25bf..6494017 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -469,7 +469,7 @@
 
   // Open at version 2, then close.
   {
-    scoped_refptr<MockIndexedDBCallbacks> callbacks(
+    scoped_refptr<UpgradeNeededCallbacks> callbacks(
         new UpgradeNeededCallbacks());
     std::unique_ptr<IndexedDBPendingConnection> connection(
         base::MakeUnique<IndexedDBPendingConnection>(
@@ -483,7 +483,8 @@
     // Pump the message loop so the upgrade transaction can run.
     base::RunLoop().RunUntilIdle();
     EXPECT_TRUE(callbacks->connection());
-    callbacks->connection()->database()->Commit(transaction_id);
+    callbacks->connection()->database()->Commit(
+        callbacks->connection()->GetTransaction(transaction_id));
 
     callbacks->connection()->Close();
     EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
diff --git a/content/browser/indexed_db/indexed_db_fake_backing_store.cc b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
index cd61ebb..d89073d 100644
--- a/content/browser/indexed_db/indexed_db_fake_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
@@ -218,7 +218,7 @@
 void IndexedDBFakeBackingStore::FakeTransaction::Begin() {}
 leveldb::Status IndexedDBFakeBackingStore::FakeTransaction::CommitPhaseOne(
     scoped_refptr<BlobWriteCallback> callback) {
-  callback->Run(true);
+  callback->Run(IndexedDBBackingStore::BlobWriteResult::SUCCESS_SYNC);
   return leveldb::Status::OK();
 }
 leveldb::Status IndexedDBFakeBackingStore::FakeTransaction::CommitPhaseTwo() {
diff --git a/content/browser/indexed_db/indexed_db_pending_connection.cc b/content/browser/indexed_db/indexed_db_pending_connection.cc
index 34f49242..0f6dcea 100644
--- a/content/browser/indexed_db/indexed_db_pending_connection.cc
+++ b/content/browser/indexed_db/indexed_db_pending_connection.cc
@@ -7,16 +7,16 @@
 namespace content {
 
 IndexedDBPendingConnection::IndexedDBPendingConnection(
-    scoped_refptr<IndexedDBCallbacks> callbacks_in,
-    scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_in,
-    int child_process_id_in,
-    int64_t transaction_id_in,
-    int64_t version_in)
-    : callbacks(callbacks_in),
-      database_callbacks(database_callbacks_in),
-      child_process_id(child_process_id_in),
-      transaction_id(transaction_id_in),
-      version(version_in) {}
+    scoped_refptr<IndexedDBCallbacks> callbacks,
+    scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
+    int child_process_id,
+    int64_t transaction_id,
+    int64_t version)
+    : callbacks(callbacks),
+      database_callbacks(database_callbacks),
+      child_process_id(child_process_id),
+      transaction_id(transaction_id),
+      version(version) {}
 
 IndexedDBPendingConnection::IndexedDBPendingConnection(
     const IndexedDBPendingConnection& other) = default;
diff --git a/content/browser/indexed_db/indexed_db_pending_connection.h b/content/browser/indexed_db/indexed_db_pending_connection.h
index 6458a7e..b434ad4 100644
--- a/content/browser/indexed_db/indexed_db_pending_connection.h
+++ b/content/browser/indexed_db/indexed_db_pending_connection.h
@@ -12,6 +12,7 @@
 #include "content/browser/indexed_db/indexed_db_data_loss_info.h"
 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
 #include "content/common/content_export.h"
+#include "url/origin.h"
 
 namespace content {
 
@@ -20,11 +21,11 @@
 
 struct CONTENT_EXPORT IndexedDBPendingConnection {
   IndexedDBPendingConnection(
-      scoped_refptr<IndexedDBCallbacks> callbacks_in,
-      scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_in,
-      int child_process_id_in,
-      int64_t transaction_id_in,
-      int64_t version_in);
+      scoped_refptr<IndexedDBCallbacks> callbacks,
+      scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
+      int child_process_id,
+      int64_t transaction_id,
+      int64_t version);
   IndexedDBPendingConnection(const IndexedDBPendingConnection& other);
   ~IndexedDBPendingConnection();
   scoped_refptr<IndexedDBCallbacks> callbacks;
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
index 40de131..064d5a0 100644
--- a/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -30,7 +30,9 @@
 
 // Helper for posting a task to call IndexedDBTransaction::Commit when we know
 // the transaction had no requests and therefore the commit must succeed.
-void CommitUnused(scoped_refptr<IndexedDBTransaction> transaction) {
+void CommitUnused(base::WeakPtr<IndexedDBTransaction> transaction) {
+  if (!transaction)
+    return;
   leveldb::Status status = transaction->Commit();
   DCHECK(status.ok());
 }
@@ -85,7 +87,7 @@
 
 IndexedDBTransaction::IndexedDBTransaction(
     int64_t id,
-    base::WeakPtr<IndexedDBConnection> connection,
+    IndexedDBConnection* connection,
     const std::set<int64_t>& object_store_ids,
     blink::WebIDBTransactionMode mode,
     IndexedDBBackingStore::Transaction* backing_store_transaction)
@@ -93,12 +95,11 @@
       object_store_ids_(object_store_ids),
       mode_(mode),
       connection_(std::move(connection)),
-      transaction_(backing_store_transaction) {
+      transaction_(backing_store_transaction),
+      ptr_factory_(this) {
   callbacks_ = connection_->callbacks();
   database_ = connection_->database();
 
-  database_->transaction_coordinator().DidCreateTransaction(this);
-
   diagnostics_.tasks_scheduled = 0;
   diagnostics_.tasks_completed = 0;
   diagnostics_.creation_time = base::Time::Now();
@@ -112,7 +113,6 @@
   DCHECK_EQ(pending_preemptive_events_, 0);
   DCHECK(task_queue_.empty());
   DCHECK(abort_task_stack_.empty());
-  DCHECK(pending_observers_.empty());
   DCHECK(!processing_event_queue_);
 }
 
@@ -152,7 +152,8 @@
 
   should_process_queue_ = true;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&IndexedDBTransaction::ProcessTaskQueue, this));
+      FROM_HERE, base::Bind(&IndexedDBTransaction::ProcessTaskQueue,
+                            ptr_factory_.GetWeakPtr()));
 }
 
 void IndexedDBTransaction::Abort() {
@@ -162,14 +163,10 @@
 
 void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) {
   IDB_TRACE1("IndexedDBTransaction::Abort", "txn.id", id());
+  DCHECK(!processing_event_queue_);
   if (state_ == FINISHED)
     return;
 
-  // The last reference to this object may be released while performing the
-  // abort steps below. We therefore take a self reference to keep ourselves
-  // alive while executing this method.
-  scoped_refptr<IndexedDBTransaction> protect(this);
-
   timeout_timer_.Stop();
 
   state_ = FINISHED;
@@ -202,13 +199,12 @@
 #endif
 
   if (callbacks_.get())
-    callbacks_->OnAbort(id_, error);
+    callbacks_->OnAbort(*this, error);
 
   database_->TransactionFinished(this, false);
 
-  database_ = NULL;
-  connection_ = nullptr;
-  pending_observers_.clear();
+  // RemoveTransaction will delete |this|.
+  connection_->RemoveTransaction(id_);
 }
 
 bool IndexedDBTransaction::IsTaskQueueEmpty() const {
@@ -239,7 +235,7 @@
       // front-end previously requested a commit; do the commit now, but not
       // re-entrantly as that may renter the coordinator.
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&CommitUnused, make_scoped_refptr(this)));
+          FROM_HERE, base::Bind(&CommitUnused, ptr_factory_.GetWeakPtr()));
     }
     return;
   }
@@ -250,35 +246,50 @@
 class BlobWriteCallbackImpl : public IndexedDBBackingStore::BlobWriteCallback {
  public:
   explicit BlobWriteCallbackImpl(
-      scoped_refptr<IndexedDBTransaction> transaction)
-      : transaction_(transaction) {}
-  void Run(bool succeeded) override {
-    transaction_->BlobWriteComplete(succeeded);
+      base::WeakPtr<IndexedDBTransaction> transaction)
+      : transaction_(std::move(transaction)) {}
+
+  leveldb::Status Run(IndexedDBBackingStore::BlobWriteResult result) override {
+    if (!transaction_)
+      return leveldb::Status::OK();
+    return transaction_->BlobWriteComplete(result);
   }
 
  protected:
   ~BlobWriteCallbackImpl() override {}
 
  private:
-  scoped_refptr<IndexedDBTransaction> transaction_;
+  base::WeakPtr<IndexedDBTransaction> transaction_;
 };
 
-void IndexedDBTransaction::BlobWriteComplete(bool success) {
+leveldb::Status IndexedDBTransaction::BlobWriteComplete(
+    IndexedDBBackingStore::BlobWriteResult result) {
   IDB_TRACE("IndexedDBTransaction::BlobWriteComplete");
   if (state_ == FINISHED)  // aborted
-    return;
+    return leveldb::Status::OK();
   DCHECK_EQ(state_, COMMITTING);
 
-  if (!success) {
-    Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError,
-                                 "Failed to write blobs."));
-    return;
+  leveldb::Status s = leveldb::Status::OK();
+  // Switch statement to protect against adding new enum values.
+  switch (result) {
+    case IndexedDBBackingStore::BlobWriteResult::FAILURE_ASYNC:
+      Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError,
+                                   "Failed to write blobs."));
+      return leveldb::Status::OK();
+    case IndexedDBBackingStore::BlobWriteResult::SUCCESS_ASYNC:
+    case IndexedDBBackingStore::BlobWriteResult::SUCCESS_SYNC: {
+      // Save the database as |this| can be destroyed in the next line. We also
+      // make
+      // sure to handle the error if we're not being called synchronously.
+      scoped_refptr<IndexedDBDatabase> database = database_;
+      s = CommitPhaseTwo();
+      if (!s.ok() &&
+          result == IndexedDBBackingStore::BlobWriteResult::SUCCESS_ASYNC)
+        ReportError(s, database, database->factory());
+      break;
+    }
   }
-  // Save the database as |this| can be destroyed in the next line.
-  scoped_refptr<IndexedDBDatabase> database = database_;
-  leveldb::Status s = CommitPhaseTwo();
-  if (!s.ok())
-    ReportError(s, database, database->factory());
+  return s;
 }
 
 leveldb::Status IndexedDBTransaction::Commit() {
@@ -315,13 +326,10 @@
     s = CommitPhaseTwo();
   } else {
     scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback(
-        new BlobWriteCallbackImpl(this));
+        new BlobWriteCallbackImpl(ptr_factory_.GetWeakPtr()));
     // CommitPhaseOne will call the callback synchronously if there are no blobs
     // to write.
     s = transaction_->CommitPhaseOne(callback);
-    if (!s.ok())
-      Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError,
-                                   "Error processing blob journal."));
   }
 
   return s;
@@ -334,11 +342,6 @@
 
   DCHECK_EQ(state_, COMMITTING);
 
-  // The last reference to this object may be released while performing the
-  // commit steps below. We therefore take a self reference to keep ourselves
-  // alive while executing this method.
-  scoped_refptr<IndexedDBTransaction> protect(this);
-
   state_ = FINISHED;
 
   leveldb::Status s;
@@ -375,7 +378,7 @@
       IDB_TRACE1(
           "IndexedDBTransaction::CommitPhaseTwo.TransactionCompleteCallbacks",
           "txn.id", id());
-      callbacks_->OnComplete(id_);
+      callbacks_->OnComplete(*this);
     }
     if (!pending_observers_.empty() && connection_) {
       connection_->ActivatePendingObservers(std::move(pending_observers_));
@@ -383,6 +386,9 @@
     }
 
     database_->TransactionFinished(this, true);
+    // RemoveTransaction will delete |this|.
+    connection_->RemoveTransaction(id_);
+    return s;
   } else {
     while (!abort_task_stack_.empty())
       abort_task_stack_.pop().Run();
@@ -396,12 +402,9 @@
       error = IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
                                      "Internal error committing transaction.");
     }
-    callbacks_->OnAbort(id_, error);
-
+    callbacks_->OnAbort(*this, error);
     database_->TransactionFinished(this, false);
   }
-
-  database_ = NULL;
   return s;
 }
 
@@ -424,11 +427,6 @@
     backing_store_transaction_begun_ = true;
   }
 
-  // The last reference to this object may be released while performing the
-  // tasks. Take take a self reference to keep this object alive so that
-  // the loop termination conditions can be checked.
-  scoped_refptr<IndexedDBTransaction> protect(this);
-
   TaskQueue* task_queue =
       pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
   while (!task_queue->empty() && state_ != FINISHED) {
@@ -441,8 +439,7 @@
     }
     if (!result.ok()) {
       processing_event_queue_ = false;
-      if (!result.ok())
-        ReportError(result, database_, database_->factory());
+      ReportError(result, database_, database_->factory());
       return;
     }
 
@@ -454,8 +451,9 @@
   // If there are no pending tasks, we haven't already committed/aborted,
   // and the front-end requested a commit, it is now safe to do so.
   if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) {
-    leveldb::Status result = Commit();
     processing_event_queue_ = false;
+    // This can delete |this|.
+    leveldb::Status result = Commit();
     if (!result.ok())
       ReportError(result, database_, database_->factory());
     return;
@@ -473,8 +471,9 @@
   // never requests further activity. Read-only transactions don't
   // block other transactions, so don't time those out.
   if (mode_ != blink::WebIDBTransactionModeReadOnly) {
-    timeout_timer_.Start(FROM_HERE, GetInactivityTimeout(),
-                         base::Bind(&IndexedDBTransaction::Timeout, this));
+    timeout_timer_.Start(
+        FROM_HERE, GetInactivityTimeout(),
+        base::Bind(&IndexedDBTransaction::Timeout, ptr_factory_.GetWeakPtr()));
   }
   processing_event_queue_ = false;
 }
diff --git a/content/browser/indexed_db/indexed_db_transaction.h b/content/browser/indexed_db/indexed_db_transaction.h
index 1d7c382..8315eb2 100644
--- a/content/browser/indexed_db/indexed_db_transaction.h
+++ b/content/browser/indexed_db/indexed_db_transaction.h
@@ -15,6 +15,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "content/browser/indexed_db/indexed_db_backing_store.h"
@@ -30,8 +31,7 @@
 class IndexedDBCursor;
 class IndexedDBDatabaseCallbacks;
 
-class CONTENT_EXPORT IndexedDBTransaction
-    : public NON_EXPORTED_BASE(base::RefCounted<IndexedDBTransaction>) {
+class CONTENT_EXPORT IndexedDBTransaction {
  public:
   using Operation = base::Callback<leveldb::Status(IndexedDBTransaction*)>;
   using AbortOperation = base::Closure;
@@ -44,8 +44,12 @@
     FINISHED,    // Either aborted or committed.
   };
 
-  virtual void Abort();
+  virtual ~IndexedDBTransaction();
+
   leveldb::Status Commit();
+
+  // This object is destroyed by these method calls.
+  virtual void Abort();
   void Abort(const IndexedDBDatabaseError& error);
 
   // Called by the transaction coordinator when this transaction is unblocked.
@@ -54,6 +58,7 @@
   blink::WebIDBTransactionMode mode() const { return mode_; }
   const std::set<int64_t>& scope() const { return object_store_ids_; }
 
+  // Tasks cannot call Commit.
   void ScheduleTask(Operation task) {
     ScheduleTask(blink::WebIDBTaskTypeNormal, task);
   }
@@ -86,7 +91,7 @@
 
   IndexedDBDatabase* database() const { return database_.get(); }
   IndexedDBDatabaseCallbacks* callbacks() const { return callbacks_.get(); }
-  IndexedDBConnection* connection() const { return connection_.get(); }
+  IndexedDBConnection* connection() const { return connection_; }
 
   State state() const { return state_; }
   bool IsTimeoutTimerRunning() const { return timeout_timer_.IsRunning(); }
@@ -100,16 +105,18 @@
 
   const Diagnostics& diagnostics() const { return diagnostics_; }
 
+  void set_size(int64_t size) { size_ = size; }
+  int64_t size() const { return size_; }
+
  protected:
   // Test classes may derive, but most creation should be done via
   // IndexedDBClassFactory.
   IndexedDBTransaction(
       int64_t id,
-      base::WeakPtr<IndexedDBConnection> connection,
+      IndexedDBConnection* connection,
       const std::set<int64_t>& object_store_ids,
       blink::WebIDBTransactionMode mode,
       IndexedDBBackingStore::Transaction* backing_store_transaction);
-  virtual ~IndexedDBTransaction();
 
   // May be overridden in tests.
   virtual base::TimeDelta GetInactivityTimeout() const;
@@ -117,6 +124,7 @@
  private:
   friend class BlobWriteCallbackImpl;
   friend class IndexedDBClassFactory;
+  friend class IndexedDBConnection;
   friend class base::RefCounted<IndexedDBTransaction>;
 
   FRIEND_TEST_ALL_PREFIXES(IndexedDBTransactionTestMode, AbortPreemptive);
@@ -135,7 +143,8 @@
   bool IsTaskQueueEmpty() const;
   bool HasPendingTasks() const;
 
-  void BlobWriteComplete(bool success);
+  leveldb::Status BlobWriteComplete(
+      IndexedDBBackingStore::BlobWriteResult result);
   void ProcessTaskQueue();
   void CloseOpenCursors();
   leveldb::Status CommitPhaseTwo();
@@ -148,7 +157,8 @@
   bool used_ = false;
   State state_ = CREATED;
   bool commit_pending_ = false;
-  base::WeakPtr<IndexedDBConnection> connection_;
+  // We are owned by the connection object.
+  IndexedDBConnection* connection_;
   scoped_refptr<IndexedDBDatabaseCallbacks> callbacks_;
   scoped_refptr<IndexedDBDatabase> database_;
 
@@ -157,6 +167,9 @@
   std::map<int32_t, ::indexed_db::mojom::ObserverChangesPtr>
       connection_changes_map_;
 
+  // Metrics for quota.
+  int64_t size_ = 0;
+
   class TaskQueue {
    public:
     TaskQueue();
@@ -206,6 +219,8 @@
   base::OneShotTimer timeout_timer_;
 
   Diagnostics diagnostics_;
+
+  base::WeakPtrFactory<IndexedDBTransaction> ptr_factory_;
 };
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_transaction_coordinator.cc b/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
index c7d171bc..68e0172 100644
--- a/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
+++ b/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
@@ -18,7 +18,7 @@
 }
 
 void IndexedDBTransactionCoordinator::DidCreateTransaction(
-    scoped_refptr<IndexedDBTransaction> transaction) {
+    IndexedDBTransaction* transaction) {
   DCHECK(!queued_transactions_.count(transaction));
   DCHECK(!started_transactions_.count(transaction));
   DCHECK_EQ(IndexedDBTransaction::CREATED, transaction->state());
@@ -65,12 +65,11 @@
 std::vector<const IndexedDBTransaction*>
 IndexedDBTransactionCoordinator::GetTransactions() const {
   std::vector<const IndexedDBTransaction*> result;
-
-  for (const auto& transaction : started_transactions_)
-    result.push_back(transaction.get());
-  for (const auto& transaction : queued_transactions_)
-    result.push_back(transaction.get());
-
+  result.reserve(started_transactions_.size() + queued_transactions_.size());
+  result.insert(result.end(), started_transactions_.begin(),
+                started_transactions_.end());
+  result.insert(result.end(), queued_transactions_.begin(),
+                queued_transactions_.end());
   return result;
 }
 
@@ -98,9 +97,9 @@
 
   auto it = queued_transactions_.begin();
   while (it != queued_transactions_.end()) {
-    scoped_refptr<IndexedDBTransaction> transaction = *it;
+    IndexedDBTransaction* transaction = *it;
     ++it;
-    if (CanStartTransaction(transaction.get(), locked_scope)) {
+    if (CanStartTransaction(transaction, locked_scope)) {
       DCHECK_EQ(IndexedDBTransaction::CREATED, transaction->state());
       queued_transactions_.erase(transaction);
       started_transactions_.insert(transaction);
diff --git a/content/browser/indexed_db/indexed_db_transaction_coordinator.h b/content/browser/indexed_db/indexed_db_transaction_coordinator.h
index c75d141..865ddc7c 100644
--- a/content/browser/indexed_db/indexed_db_transaction_coordinator.h
+++ b/content/browser/indexed_db/indexed_db_transaction_coordinator.h
@@ -13,7 +13,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "content/browser/indexed_db/list_set.h"
 
 namespace content {
@@ -27,7 +26,7 @@
   ~IndexedDBTransactionCoordinator();
 
   // Called by transactions as they start and finish.
-  void DidCreateTransaction(scoped_refptr<IndexedDBTransaction> transaction);
+  void DidCreateTransaction(IndexedDBTransaction* transaction);
   void DidFinishTransaction(IndexedDBTransaction* transaction);
 
   bool IsRunningVersionChangeTransaction() const;
@@ -47,8 +46,8 @@
   // Transactions in different states are grouped below.
   // list_set is used to provide stable ordering; required by spec
   // for the queue, convenience for diagnostics for the rest.
-  list_set<scoped_refptr<IndexedDBTransaction>> queued_transactions_;
-  list_set<scoped_refptr<IndexedDBTransaction>> started_transactions_;
+  list_set<IndexedDBTransaction*> queued_transactions_;
+  list_set<IndexedDBTransaction*> started_transactions_;
 
   DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionCoordinator);
 };
diff --git a/content/browser/indexed_db/indexed_db_transaction_unittest.cc b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
index f10d5af..1233b29d 100644
--- a/content/browser/indexed_db/indexed_db_transaction_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
@@ -5,6 +5,7 @@
 #include "content/browser/indexed_db/indexed_db_transaction.h"
 
 #include <stdint.h>
+#include <memory>
 
 #include "base/bind.h"
 #include "base/logging.h"
@@ -20,6 +21,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
+const int kFakeProcessId = 10;
 
 class AbortObserver {
  public:
@@ -90,11 +92,11 @@
   const leveldb::Status commit_success = leveldb::Status::OK();
   std::unique_ptr<IndexedDBConnection> connection(
       base::MakeUnique<IndexedDBConnection>(
-          db_, new MockIndexedDBDatabaseCallbacks()));
-  scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
-      id, connection->GetWeakPtr(), scope,
-      blink::WebIDBTransactionModeReadWrite,
-      new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+          kFakeProcessId, db_, new MockIndexedDBDatabaseCallbacks()));
+  std::unique_ptr<IndexedDBTransaction> transaction =
+      std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+          id, connection.get(), scope, blink::WebIDBTransactionModeReadWrite,
+          new IndexedDBFakeBackingStore::FakeTransaction(commit_success)));
   db_->TransactionCreated(transaction.get());
 
   // No conflicting transactions, so coordinator will start it immediately:
@@ -136,10 +138,11 @@
   const leveldb::Status commit_success = leveldb::Status::OK();
   std::unique_ptr<IndexedDBConnection> connection(
       base::MakeUnique<IndexedDBConnection>(
-          db_, new MockIndexedDBDatabaseCallbacks()));
-  scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
-      id, connection->GetWeakPtr(), scope, blink::WebIDBTransactionModeReadOnly,
-      new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+          kFakeProcessId, db_, new MockIndexedDBDatabaseCallbacks()));
+  std::unique_ptr<IndexedDBTransaction> transaction =
+      std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+          id, connection.get(), scope, blink::WebIDBTransactionModeReadOnly,
+          new IndexedDBFakeBackingStore::FakeTransaction(commit_success)));
   db_->TransactionCreated(transaction.get());
 
   // No conflicting transactions, so coordinator will start it immediately:
@@ -168,10 +171,11 @@
   const leveldb::Status commit_success = leveldb::Status::OK();
   std::unique_ptr<IndexedDBConnection> connection(
       base::MakeUnique<IndexedDBConnection>(
-          db_, new MockIndexedDBDatabaseCallbacks()));
-  scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
-      id, connection->GetWeakPtr(), scope, GetParam(),
-      new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+          kFakeProcessId, db_, new MockIndexedDBDatabaseCallbacks()));
+  std::unique_ptr<IndexedDBTransaction> transaction =
+      std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+          id, connection.get(), scope, GetParam(),
+          new IndexedDBFakeBackingStore::FakeTransaction(commit_success)));
 
   EXPECT_FALSE(transaction->HasPendingTasks());
   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
@@ -229,10 +233,11 @@
   const leveldb::Status commit_success = leveldb::Status::OK();
   std::unique_ptr<IndexedDBConnection> connection(
       base::MakeUnique<IndexedDBConnection>(
-          db_, new MockIndexedDBDatabaseCallbacks()));
-  scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
-      id, connection->GetWeakPtr(), scope, GetParam(),
-      new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+          kFakeProcessId, db_, new MockIndexedDBDatabaseCallbacks()));
+  std::unique_ptr<IndexedDBTransaction> transaction =
+      std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+          id, connection.get(), scope, GetParam(),
+          new IndexedDBFakeBackingStore::FakeTransaction(commit_success)));
 
   EXPECT_FALSE(transaction->HasPendingTasks());
   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
@@ -292,11 +297,12 @@
   const leveldb::Status commit_failure = leveldb::Status::Corruption("Ouch.");
   std::unique_ptr<IndexedDBConnection> connection(
       base::MakeUnique<IndexedDBConnection>(
-          db_, new MockIndexedDBDatabaseCallbacks()));
-  scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
-      id, connection->GetWeakPtr(), scope,
-      blink::WebIDBTransactionModeVersionChange,
-      new IndexedDBFakeBackingStore::FakeTransaction(commit_failure));
+          kFakeProcessId, db_, new MockIndexedDBDatabaseCallbacks()));
+  std::unique_ptr<IndexedDBTransaction> transaction =
+      std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+          id, connection.get(), scope,
+          blink::WebIDBTransactionModeVersionChange,
+          new IndexedDBFakeBackingStore::FakeTransaction(commit_failure)));
 
   EXPECT_FALSE(transaction->HasPendingTasks());
   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
@@ -353,10 +359,11 @@
   const leveldb::Status commit_failure = leveldb::Status::Corruption("Ouch.");
   std::unique_ptr<IndexedDBConnection> connection(
       base::MakeUnique<IndexedDBConnection>(
-          db_, new MockIndexedDBDatabaseCallbacks()));
-  scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
-      id, connection->GetWeakPtr(), scope, GetParam(),
-      new IndexedDBFakeBackingStore::FakeTransaction(commit_failure));
+          kFakeProcessId, db_, new MockIndexedDBDatabaseCallbacks()));
+  std::unique_ptr<IndexedDBTransaction> transaction =
+      std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+          id, connection.get(), scope, GetParam(),
+          new IndexedDBFakeBackingStore::FakeTransaction(commit_failure)));
   db_->TransactionCreated(transaction.get());
 
   AbortObserver observer;
@@ -382,10 +389,11 @@
   const leveldb::Status commit_success = leveldb::Status::OK();
   std::unique_ptr<IndexedDBConnection> connection(
       base::MakeUnique<IndexedDBConnection>(
-          db_, new MockIndexedDBDatabaseCallbacks()));
-  scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
-      id, connection->GetWeakPtr(), scope, GetParam(),
-      new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+          kFakeProcessId, db_, new MockIndexedDBDatabaseCallbacks()));
+  std::unique_ptr<IndexedDBTransaction> transaction =
+      std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+          id, connection.get(), scope, GetParam(),
+          new IndexedDBFakeBackingStore::FakeTransaction(commit_success)));
   db_->TransactionCreated(transaction.get());
 
   // No conflicting transactions, so coordinator will start it immediately:
@@ -433,11 +441,15 @@
   const leveldb::Status commit_success = leveldb::Status::OK();
   std::unique_ptr<IndexedDBConnection> connection(
       base::MakeUnique<IndexedDBConnection>(
-          db_, new MockIndexedDBDatabaseCallbacks()));
-  scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
-      id, connection->GetWeakPtr(), scope,
-      blink::WebIDBTransactionModeReadWrite,
-      new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+          kFakeProcessId, db_, new MockIndexedDBDatabaseCallbacks()));
+
+  base::WeakPtr<IndexedDBTransaction> transaction =
+      connection->AddTransactionForTesting(
+          std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
+              id, connection.get(), scope,
+              blink::WebIDBTransactionModeReadWrite,
+              new IndexedDBFakeBackingStore::FakeTransaction(commit_success))));
+  ASSERT_TRUE(transaction);
   db_->TransactionCreated(transaction.get());
 
   EXPECT_EQ(0UL, transaction->pending_observers_.size());
@@ -459,7 +471,7 @@
 
   // After commit, observer moved to connection's active observer.
   transaction->Commit();
-  EXPECT_EQ(0UL, transaction->pending_observers_.size());
+  EXPECT_FALSE(transaction);
   EXPECT_EQ(1UL, connection->active_observers().size());
 
   // Observer does not exist, so no change to active_observers.
diff --git a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
index 2ba375f..c408494 100644
--- a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
+++ b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
@@ -66,12 +66,12 @@
  public:
   IndexedDBTestTransaction(
       int64_t id,
-      base::WeakPtr<IndexedDBConnection> connection,
+      IndexedDBConnection* connection,
       const std::set<int64_t>& scope,
       blink::WebIDBTransactionMode mode,
       IndexedDBBackingStore::Transaction* backing_store_transaction)
       : IndexedDBTransaction(id,
-                             std::move(connection),
+                             connection,
                              scope,
                              mode,
                              backing_store_transaction) {}
@@ -264,15 +264,15 @@
                                    unique_identifier);
 }
 
-IndexedDBTransaction*
+std::unique_ptr<IndexedDBTransaction>
 MockBrowserTestIndexedDBClassFactory::CreateIndexedDBTransaction(
     int64_t id,
-    base::WeakPtr<IndexedDBConnection> connection,
+    IndexedDBConnection* connection,
     const std::set<int64_t>& scope,
     blink::WebIDBTransactionMode mode,
     IndexedDBBackingStore::Transaction* backing_store_transaction) {
-  return new IndexedDBTestTransaction(id, std::move(connection), scope, mode,
-                                      backing_store_transaction);
+  return std::unique_ptr<IndexedDBTransaction>(new IndexedDBTestTransaction(
+      id, connection, scope, mode, backing_store_transaction));
 }
 
 scoped_refptr<LevelDBTransaction>
diff --git a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
index 955b0435..d8db0c6 100644
--- a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
+++ b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
@@ -46,9 +46,9 @@
       scoped_refptr<IndexedDBBackingStore> backing_store,
       scoped_refptr<IndexedDBFactory> factory,
       const IndexedDBDatabase::Identifier& unique_identifier) override;
-  IndexedDBTransaction* CreateIndexedDBTransaction(
+  std::unique_ptr<IndexedDBTransaction> CreateIndexedDBTransaction(
       int64_t id,
-      base::WeakPtr<IndexedDBConnection> connection,
+      IndexedDBConnection* connection,
       const std::set<int64_t>& scope,
       blink::WebIDBTransactionMode mode,
       IndexedDBBackingStore::Transaction* backing_store_transaction) override;
diff --git a/content/browser/indexed_db/mock_indexed_db_database_callbacks.cc b/content/browser/indexed_db/mock_indexed_db_database_callbacks.cc
index 51fb078a..ceabc8b5b 100644
--- a/content/browser/indexed_db/mock_indexed_db_database_callbacks.cc
+++ b/content/browser/indexed_db/mock_indexed_db_database_callbacks.cc
@@ -18,7 +18,7 @@
 }
 
 void MockIndexedDBDatabaseCallbacks::OnAbort(
-    int64_t transaction_id,
+    const IndexedDBTransaction& transaction,
     const IndexedDBDatabaseError& error) {
   abort_called_ = true;
 }
diff --git a/content/browser/indexed_db/mock_indexed_db_database_callbacks.h b/content/browser/indexed_db/mock_indexed_db_database_callbacks.h
index 20ea2b5..a8e5290 100644
--- a/content/browser/indexed_db/mock_indexed_db_database_callbacks.h
+++ b/content/browser/indexed_db/mock_indexed_db_database_callbacks.h
@@ -19,9 +19,9 @@
 
   void OnVersionChange(int64_t old_version, int64_t new_version) override {}
   void OnForcedClose() override;
-  void OnAbort(int64_t transaction_id,
+  void OnAbort(const IndexedDBTransaction& transaction,
                const IndexedDBDatabaseError& error) override;
-  void OnComplete(int64_t transaction_id) override {}
+  void OnComplete(const IndexedDBTransaction& transaction) override {}
 
   bool abort_called() const { return abort_called_; }
   bool forced_close_called() const { return forced_close_called_; }
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 091cb0e..709ea5d 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -550,7 +550,8 @@
     rwhv->SetSize(size);
 }
 
-void WebContentsViewAura::EndDrag(blink::WebDragOperationsMask ops) {
+void WebContentsViewAura::EndDrag(RenderWidgetHost* source_rwh,
+                                  blink::WebDragOperationsMask ops) {
   if (!web_contents_)
     return;
 
@@ -566,7 +567,9 @@
   // the coordinates local to |drag_start_rwh_|? See crbug.com/647249.
   web_contents_->DragSourceEndedAt(client_loc.x(), client_loc.y(),
                                    screen_loc.x(), screen_loc.y(), ops,
-                                   drag_start_rwh_.get());
+                                   source_rwh);
+
+  web_contents_->SystemDragEnded(source_rwh);
 }
 
 void WebContentsViewAura::InstallOverscrollControllerDelegate(
@@ -896,7 +899,13 @@
     return;
   }
 
-  drag_start_rwh_ = source_rwh->GetWeakPtr();
+  // Grab a weak pointer to the RenderWidgetHost, since it can be destroyed
+  // during the drag and drop nested message loop in StartDragAndDrop.
+  // For example, the RenderWidgetHost can be deleted if a cross-process
+  // transfer happens while dragging, since the RenderWidgetHost is deleted in
+  // that case.
+  base::WeakPtr<RenderWidgetHostImpl> source_rwh_weak_ptr =
+      source_rwh->GetWeakPtr();
 
   ui::TouchSelectionController* selection_controller = GetSelectionController();
   if (selection_controller)
@@ -940,8 +949,7 @@
     return;
   }
 
-  EndDrag(ConvertToWeb(result_op));
-  web_contents_->SystemDragEnded(source_rwh);
+  EndDrag(source_rwh_weak_ptr.get(), ConvertToWeb(result_op));
 }
 
 void WebContentsViewAura::UpdateDragCursor(blink::WebDragOperation operation) {
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index d032f18..6869095 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -66,7 +66,7 @@
 
   void SizeChangedCommon(const gfx::Size& size);
 
-  void EndDrag(blink::WebDragOperationsMask ops);
+  void EndDrag(RenderWidgetHost* source_rwh, blink::WebDragOperationsMask ops);
 
   void InstallOverscrollControllerDelegate(RenderWidgetHostViewAura* view);
 
@@ -203,10 +203,6 @@
   // view. |current_rvh_for_drag_| should not be dereferenced.
   void* current_rvh_for_drag_;
 
-  // We keep track of the RenderWidgetHost from which the current drag started,
-  // in order to properly route the drag end message to it.
-  base::WeakPtr<RenderWidgetHostImpl> drag_start_rwh_;
-
   // The overscroll gesture currently in progress.
   OverscrollMode current_overscroll_gesture_;
 
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 139301f..988f637f 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1303,7 +1303,8 @@
     case base::TERMINATION_STATUS_STILL_RUNNING:
       break;
     default:
-      message_loop_runner_->Quit();
+      if (message_loop_runner_.get())
+        message_loop_runner_->Quit();
       break;
   }
 }
diff --git a/content/shell/browser/layout_test/blink_test_controller.cc b/content/shell/browser/layout_test/blink_test_controller.cc
index 5b585a7..bf4e5458 100644
--- a/content/shell/browser/layout_test/blink_test_controller.cc
+++ b/content/shell/browser/layout_test/blink_test_controller.cc
@@ -268,7 +268,10 @@
   current_working_directory_ = current_working_directory;
   enable_pixel_dumping_ = enable_pixel_dumping;
   expected_pixel_hash_ = expected_pixel_hash;
-  test_url_ = test_url;
+  if (test_url.spec().find("/inspector-unit/") == std::string::npos)
+    test_url_ = test_url;
+  else
+    test_url_ = LayoutTestDevToolsFrontend::MapJSTestURL(test_url);
   did_send_initial_test_configuration_ = false;
   printer_->reset();
   frame_to_layout_dump_map_.clear();
@@ -279,10 +282,10 @@
   ShellBrowserContext* browser_context =
       ShellContentBrowserClient::Get()->browser_context();
   is_compositing_test_ =
-      test_url.spec().find("compositing/") != std::string::npos;
+      test_url_.spec().find("compositing/") != std::string::npos;
   initial_size_ = Shell::GetShellDefaultSize();
   // The W3C SVG layout tests use a different size than the other layout tests.
-  if (test_url.spec().find("W3C-SVG-1.1") != std::string::npos)
+  if (test_url_.spec().find("W3C-SVG-1.1") != std::string::npos)
     initial_size_ = gfx::Size(kTestSVGWindowWidthDip, kTestSVGWindowHeightDip);
   if (!main_window_) {
     main_window_ = content::Shell::CreateNewWindow(
@@ -294,7 +297,7 @@
     current_pid_ = base::kNullProcessId;
     default_prefs_ =
       main_window_->web_contents()->GetRenderViewHost()->GetWebkitPreferences();
-    main_window_->LoadURL(test_url);
+    main_window_->LoadURL(test_url_);
   } else {
 #if defined(OS_MACOSX)
     // Shell::SizeTo is not implemented on all platforms.
@@ -320,7 +323,7 @@
     render_view_host->UpdateWebkitPreferences(default_prefs_);
     HandleNewRenderFrameHost(render_view_host->GetMainFrame());
 
-    NavigationController::LoadURLParams params(test_url);
+    NavigationController::LoadURLParams params(test_url_);
     params.transition_type = ui::PageTransitionFromInt(
         ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
     params.should_clear_history_list = true;
diff --git a/content/shell/browser/layout_test/layout_test_devtools_frontend.cc b/content/shell/browser/layout_test/layout_test_devtools_frontend.cc
index d12ddd0e..917a8764 100644
--- a/content/shell/browser/layout_test/layout_test_devtools_frontend.cc
+++ b/content/shell/browser/layout_test/layout_test_devtools_frontend.cc
@@ -73,6 +73,17 @@
   return GURL(url_string);
 }
 
+// static.
+GURL LayoutTestDevToolsFrontend::MapJSTestURL(const GURL& test_url) {
+  std::string url_string = GetDevToolsPathAsURL(std::string()).spec();
+  std::string inspector_file_name = "inspector.html";
+  size_t start_position = url_string.find(inspector_file_name);
+  url_string.replace(start_position, inspector_file_name.length(),
+                     "unit_test_runner.html");
+  url_string += "&test=" + test_url.spec();
+  return GURL(url_string);
+}
+
 void LayoutTestDevToolsFrontend::ReuseFrontend(const std::string& settings,
                                                const std::string frontend_url) {
   DisconnectFromTarget();
diff --git a/content/shell/browser/layout_test/layout_test_devtools_frontend.h b/content/shell/browser/layout_test/layout_test_devtools_frontend.h
index a0720d21..a58c6f4 100644
--- a/content/shell/browser/layout_test/layout_test_devtools_frontend.h
+++ b/content/shell/browser/layout_test/layout_test_devtools_frontend.h
@@ -22,6 +22,8 @@
 
   static GURL GetDevToolsPathAsURL(const std::string& frontend_url);
 
+  static GURL MapJSTestURL(const GURL& test_url);
+
   void ReuseFrontend(const std::string& settings,
                      const std::string frontend_url);
   void EvaluateInFrontend(int call_id, const std::string& expression);
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index e583efca..a8e3bdb 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -309,6 +309,10 @@
 
   v8::Local<v8::Function> function =
       GetModuleFunction(module_name, method_name);
+  if (function.IsEmpty()) {
+    NOTREACHED() << "GetModuleFunction() returns empty function handle";
+    return handle_scope.Escape(v8::Undefined(GetIsolate()));
+  }
 
   v8::Local<v8::Value> result;
   {
@@ -350,6 +354,10 @@
 
   v8::Local<v8::Function> function =
       GetModuleFunction(module_name, method_name);
+  if (function.IsEmpty()) {
+    NOTREACHED() << "GetModuleFunction() returns empty function handle";
+    return;
+  }
 
   {
     v8::TryCatch try_catch(GetIsolate());
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index 39c7bbe..b78c429 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -76,8 +76,6 @@
   {'name': 'cull_face'},
   {'name': 'depth_test', 'state_flag': 'framebuffer_state_.clear_state_dirty'},
   {'name': 'dither', 'default': True},
-  {'name': 'framebuffer_srgb_ext', 'default': True,
-   'extension_flag': 'ext_srgb_write_control'},
   {'name': 'polygon_offset_fill'},
   {'name': 'sample_alpha_to_coverage'},
   {'name': 'sample_coverage'},
@@ -1539,14 +1537,6 @@
       'GL_COMPARE_REF_TO_TEXTURE',
     ],
   },
-  'TextureSrgbDecodeExt': {
-    'type': 'GLenum',
-    'is_complete': True,
-    'valid': [
-      'GL_DECODE_EXT',
-      'GL_SKIP_DECODE_EXT',
-    ],
-  },
   'TextureSwizzle': {
     'type': 'GLenum',
     'is_complete': True,
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc
index 817efc1..74ccfec 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.cc
+++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -428,6 +428,14 @@
       return 1;
     case GL_TEXTURE_MAX_ANISOTROPY_EXT:
       return 1;
+    case GL_TEXTURE_SWIZZLE_R:
+      return 1;
+    case GL_TEXTURE_SWIZZLE_G:
+      return 1;
+    case GL_TEXTURE_SWIZZLE_B:
+      return 1;
+    case GL_TEXTURE_SWIZZLE_A:
+      return 1;
 
     // -- glGetVertexAttrib
     case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
@@ -1951,4 +1959,3 @@
 
 }  // namespace gles2
 }  // namespace gpu
-
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
index 1775e7d..64a72fc 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
@@ -96,7 +96,6 @@
     uint32_t value);
 static std::string GetStringTextureSizedTextureFilterableInternalFormat(
     uint32_t value);
-static std::string GetStringTextureSrgbDecodeExt(uint32_t value);
 static std::string GetStringTextureStencilRenderableInternalFormat(
     uint32_t value);
 static std::string GetStringTextureSwizzle(uint32_t value);
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index 5a6325c..9d6b4a5 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -4828,15 +4828,6 @@
                                            arraysize(string_table), value);
 }
 
-std::string GLES2Util::GetStringTextureSrgbDecodeExt(uint32_t value) {
-  static const EnumToString string_table[] = {
-      {GL_DECODE_EXT, "GL_DECODE_EXT"},
-      {GL_SKIP_DECODE_EXT, "GL_SKIP_DECODE_EXT"},
-  };
-  return GLES2Util::GetQualifiedEnumString(string_table,
-                                           arraysize(string_table), value);
-}
-
 std::string GLES2Util::GetStringTextureStencilRenderableInternalFormat(
     uint32_t value) {
   static const EnumToString string_table[] = {
diff --git a/gpu/command_buffer/service/context_state.cc b/gpu/command_buffer/service/context_state.cc
index 5aa24fb..94192a6 100644
--- a/gpu/command_buffer/service/context_state.cc
+++ b/gpu/command_buffer/service/context_state.cc
@@ -221,6 +221,7 @@
       pack_reverse_row_order(false),
       ignore_cached_state(false),
       fbo_binding_for_scissor_workaround_dirty(false),
+      framebuffer_srgb_(false),
       feature_info_(feature_info),
       error_state_(ErrorState::Create(error_state_client, logger)) {
   Initialize();
@@ -504,8 +505,15 @@
   RestoreIndexedUniformBufferBindings(prev_state);
   RestoreGlobalState(prev_state);
 
-  // FRAMEBUFFER_SRGB will be restored lazily at render time.
-  framebuffer_srgb_valid_ = false;
+  if (!prev_state) {
+    if (feature_info_->feature_flags().desktop_srgb_support) {
+      framebuffer_srgb_ = false;
+      glDisable(GL_FRAMEBUFFER_SRGB);
+    }
+  } else if (framebuffer_srgb_ != prev_state->framebuffer_srgb_) {
+    // FRAMEBUFFER_SRGB will be restored lazily at render time.
+    framebuffer_srgb_ = prev_state->framebuffer_srgb_;
+  }
 }
 
 ErrorState* ContextState::GetErrorState() {
@@ -698,11 +706,10 @@
 }
 
 void ContextState::EnableDisableFramebufferSRGB(bool enable) {
-  if (framebuffer_srgb_valid_ && framebuffer_srgb_ == enable)
+  if (framebuffer_srgb_ == enable)
     return;
   EnableDisable(GL_FRAMEBUFFER_SRGB, enable);
   framebuffer_srgb_ = enable;
-  framebuffer_srgb_valid_ = true;
 }
 
 void ContextState::InitStateManual(const ContextState*) const {
diff --git a/gpu/command_buffer/service/context_state.h b/gpu/command_buffer/service/context_state.h
index 6566974..72e4c6c2 100644
--- a/gpu/command_buffer/service/context_state.h
+++ b/gpu/command_buffer/service/context_state.h
@@ -354,11 +354,7 @@
 
   void InitStateManual(const ContextState* prev_state) const;
 
-  // EnableDisableFramebufferSRGB is called at very high frequency. Cache the
-  // true value of FRAMEBUFFER_SRGB, if we know it, to elide some of these
-  // calls.
-  bool framebuffer_srgb_valid_ = false;
-  bool framebuffer_srgb_ = false;
+  bool framebuffer_srgb_;
 
   // Generic vertex attrib base types: FLOAT, INT, or UINT.
   // Each base type is encoded into 2 bits, the lowest 2 bits for location 0,
diff --git a/gpu/command_buffer/service/context_state_autogen.h b/gpu/command_buffer/service/context_state_autogen.h
index 38fae6e..c0da2cd 100644
--- a/gpu/command_buffer/service/context_state_autogen.h
+++ b/gpu/command_buffer/service/context_state_autogen.h
@@ -22,8 +22,6 @@
   bool cached_depth_test;
   bool dither;
   bool cached_dither;
-  bool framebuffer_srgb_ext;
-  bool cached_framebuffer_srgb_ext;
   bool polygon_offset_fill;
   bool cached_polygon_offset_fill;
   bool sample_alpha_to_coverage;
@@ -145,12 +143,6 @@
         return;
       enable_flags.cached_dither = enable;
       break;
-    case GL_FRAMEBUFFER_SRGB_EXT:
-      if (enable_flags.cached_framebuffer_srgb_ext == enable &&
-          !ignore_cached_state)
-        return;
-      enable_flags.cached_framebuffer_srgb_ext = enable;
-      break;
     case GL_POLYGON_OFFSET_FILL:
       if (enable_flags.cached_polygon_offset_fill == enable &&
           !ignore_cached_state)
diff --git a/gpu/command_buffer/service/context_state_impl_autogen.h b/gpu/command_buffer/service/context_state_impl_autogen.h
index 3569bbf..6bc4a82 100644
--- a/gpu/command_buffer/service/context_state_impl_autogen.h
+++ b/gpu/command_buffer/service/context_state_impl_autogen.h
@@ -21,8 +21,6 @@
       cached_depth_test(false),
       dither(true),
       cached_dither(true),
-      framebuffer_srgb_ext(true),
-      cached_framebuffer_srgb_ext(true),
       polygon_offset_fill(false),
       cached_polygon_offset_fill(false),
       sample_alpha_to_coverage(false),
@@ -169,13 +167,6 @@
     if (prev_state->enable_flags.cached_dither != enable_flags.cached_dither) {
       EnableDisable(GL_DITHER, enable_flags.cached_dither);
     }
-    if (feature_info_->feature_flags().ext_srgb_write_control) {
-      if (prev_state->enable_flags.cached_framebuffer_srgb_ext !=
-          enable_flags.cached_framebuffer_srgb_ext) {
-        EnableDisable(GL_FRAMEBUFFER_SRGB_EXT,
-                      enable_flags.cached_framebuffer_srgb_ext);
-      }
-    }
     if (prev_state->enable_flags.cached_polygon_offset_fill !=
         enable_flags.cached_polygon_offset_fill) {
       EnableDisable(GL_POLYGON_OFFSET_FILL,
@@ -228,10 +219,6 @@
     EnableDisable(GL_CULL_FACE, enable_flags.cached_cull_face);
     EnableDisable(GL_DEPTH_TEST, enable_flags.cached_depth_test);
     EnableDisable(GL_DITHER, enable_flags.cached_dither);
-    if (feature_info_->feature_flags().ext_srgb_write_control) {
-      EnableDisable(GL_FRAMEBUFFER_SRGB_EXT,
-                    enable_flags.cached_framebuffer_srgb_ext);
-    }
     EnableDisable(GL_POLYGON_OFFSET_FILL,
                   enable_flags.cached_polygon_offset_fill);
     EnableDisable(GL_SAMPLE_ALPHA_TO_COVERAGE,
@@ -446,8 +433,6 @@
       return enable_flags.depth_test;
     case GL_DITHER:
       return enable_flags.dither;
-    case GL_FRAMEBUFFER_SRGB_EXT:
-      return enable_flags.framebuffer_srgb_ext;
     case GL_POLYGON_OFFSET_FILL:
       return enable_flags.polygon_offset_fill;
     case GL_SAMPLE_ALPHA_TO_COVERAGE:
@@ -850,12 +835,6 @@
         params[0] = static_cast<GLint>(enable_flags.dither);
       }
       return true;
-    case GL_FRAMEBUFFER_SRGB_EXT:
-      *num_written = 1;
-      if (params) {
-        params[0] = static_cast<GLint>(enable_flags.framebuffer_srgb_ext);
-      }
-      return true;
     case GL_POLYGON_OFFSET_FILL:
       *num_written = 1;
       if (params) {
@@ -1290,12 +1269,6 @@
         params[0] = static_cast<GLfloat>(enable_flags.dither);
       }
       return true;
-    case GL_FRAMEBUFFER_SRGB_EXT:
-      *num_written = 1;
-      if (params) {
-        params[0] = static_cast<GLfloat>(enable_flags.framebuffer_srgb_ext);
-      }
-      return true;
     case GL_POLYGON_OFFSET_FILL:
       *num_written = 1;
       if (params) {
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index ba0f616d..3a34afa 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -572,13 +572,11 @@
     validators_.index_type.AddValue(GL_UNSIGNED_INT);
   }
 
-  bool has_srgb_framebuffer_support = false;
   if (gl_version_info_->IsAtLeastGL(3, 2) ||
       (gl_version_info_->IsAtLeastGL(2, 0) &&
        (extensions.Contains("GL_EXT_framebuffer_sRGB") ||
         extensions.Contains("GL_ARB_framebuffer_sRGB")))) {
     feature_flags_.desktop_srgb_support = true;
-    has_srgb_framebuffer_support = true;
   }
   // With EXT_sRGB, unsized SRGB_EXT and SRGB_ALPHA_EXT are accepted by the
   // <format> and <internalformat> parameter of TexImage2D. GLES3 adds support
@@ -601,28 +599,6 @@
         GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT);
     validators_.texture_unsized_internal_format.AddValue(GL_SRGB_EXT);
     validators_.texture_unsized_internal_format.AddValue(GL_SRGB_ALPHA_EXT);
-    has_srgb_framebuffer_support = true;
-  }
-  if (gl_version_info_->is_es3)
-    has_srgb_framebuffer_support = true;
-
-  if (has_srgb_framebuffer_support && !IsWebGLContext()) {
-    // GL_FRAMEBUFFER_SRGB_EXT is exposed by the GLES extension
-    // GL_EXT_sRGB_write_control (which is not part of the core, even in GLES3),
-    // and the desktop extension GL_ARB_framebuffer_sRGB (part of the core in
-    // 3.0).
-    if (feature_flags_.desktop_srgb_support ||
-        extensions.Contains("GL_EXT_sRGB_write_control")) {
-      feature_flags_.ext_srgb_write_control = true;
-      AddExtensionString("GL_EXT_sRGB_write_control");
-      validators_.capability.AddValue(GL_FRAMEBUFFER_SRGB_EXT);
-    }
-  }
-
-  // The extension GL_EXT_texture_sRGB_decode is the same on desktop and GLES.
-  if (extensions.Contains("GL_EXT_texture_sRGB_decode") && !IsWebGLContext()) {
-    AddExtensionString("GL_EXT_texture_sRGB_decode");
-    validators_.texture_parameter.AddValue(GL_TEXTURE_SRGB_DECODE_EXT);
   }
 
   // On desktop, GL_EXT_texture_sRGB is required regardless of GL version,
@@ -1515,6 +1491,13 @@
     validators_.texture_sized_texture_filterable_internal_format.AddValue(
         GL_BGRA8_EXT);
   }
+
+  if (!IsWebGLContext()) {
+    validators_.texture_parameter.AddValue(GL_TEXTURE_SWIZZLE_R);
+    validators_.texture_parameter.AddValue(GL_TEXTURE_SWIZZLE_G);
+    validators_.texture_parameter.AddValue(GL_TEXTURE_SWIZZLE_B);
+    validators_.texture_parameter.AddValue(GL_TEXTURE_SWIZZLE_A);
+  }
 }
 
 bool FeatureInfo::IsWebGLContext() const {
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index 8128c72..d72dfc1 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -98,7 +98,6 @@
     bool khr_debug = false;
     bool chromium_bind_generates_resource = false;
     bool angle_webgl_compatibility = false;
-    bool ext_srgb_write_control = false;
   };
 
   FeatureInfo();
diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc
index 902a9531..49b8d19 100644
--- a/gpu/command_buffer/service/feature_info_unittest.cc
+++ b/gpu/command_buffer/service/feature_info_unittest.cc
@@ -246,11 +246,8 @@
       NOTREACHED();
       break;
   }
-  // Note that because GL_EXT_sRGB is a substring of GL_EXT_sRGB_write_control,
-  // which is not part of the ES3 core, we have to be careful to search for
-  // "GL_EXT_sRGB ", and append a space to the end of the extension string.
   if (expect_ext_srgb) {
-    EXPECT_THAT(info_->extensions() + " ", HasSubstr("GL_EXT_sRGB "));
+    EXPECT_THAT(info_->extensions(), HasSubstr("GL_EXT_sRGB"));
     EXPECT_TRUE(info_->validators()->texture_format.IsValid(GL_SRGB_EXT));
     EXPECT_TRUE(info_->validators()->texture_format.IsValid(GL_SRGB_ALPHA_EXT));
     EXPECT_TRUE(info_->validators()->texture_internal_format.IsValid(
@@ -262,7 +259,7 @@
     EXPECT_TRUE(info_->validators()->framebuffer_parameter.IsValid(
         GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT));
   } else {
-    EXPECT_THAT(info_->extensions() + " ", Not(HasSubstr("GL_EXT_sRGB ")));
+    EXPECT_THAT(info_->extensions(), Not(HasSubstr("GL_EXT_sRGB")));
     EXPECT_FALSE(info_->validators()->texture_format.IsValid(GL_SRGB_EXT));
     EXPECT_FALSE(info_->validators()->texture_format.IsValid(
         GL_SRGB_ALPHA_EXT));
@@ -596,7 +593,7 @@
   SetupInitExpectations("GL_EXT_sRGB GL_OES_rgb8_rgba8");
 
   if (GetContextType() == CONTEXT_TYPE_OPENGLES3) {
-    EXPECT_THAT(info_->extensions() + " ", Not(HasSubstr("GL_EXT_sRGB ")));
+    EXPECT_THAT(info_->extensions(), Not(HasSubstr("GL_EXT_sRGB")));
     EXPECT_FALSE(info_->validators()->texture_format.IsValid(GL_SRGB_EXT));
     EXPECT_FALSE(
         info_->validators()->texture_format.IsValid(GL_SRGB_ALPHA_EXT));
@@ -609,7 +606,7 @@
     EXPECT_FALSE(info_->validators()->framebuffer_parameter.IsValid(
         GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT));
   } else {
-    EXPECT_THAT(info_->extensions() + " ", HasSubstr("GL_EXT_sRGB "));
+    EXPECT_THAT(info_->extensions(), HasSubstr("GL_EXT_sRGB"));
     EXPECT_TRUE(info_->validators()->texture_format.IsValid(GL_SRGB_EXT));
     EXPECT_TRUE(info_->validators()->texture_format.IsValid(GL_SRGB_ALPHA_EXT));
     EXPECT_TRUE(
@@ -925,7 +922,7 @@
 
 TEST_P(FeatureInfoTest, Initialize_sRGBGLES3) {
   SetupInitExpectationsWithGLVersion("", "", "OpenGL ES 3.0");
-  EXPECT_THAT(info_->extensions() + " ", Not(HasSubstr("GL_EXT_sRGB ")));
+  EXPECT_THAT(info_->extensions(), Not(HasSubstr("GL_EXT_sRGB")));
   EXPECT_FALSE(info_->validators()->texture_format.IsValid(
       GL_SRGB_EXT));
   EXPECT_FALSE(info_->validators()->texture_format.IsValid(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index f5c727b..bb5ccae 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -707,7 +707,6 @@
   // Workarounds
   void OnFboChanged() const;
   void OnUseFramebuffer() const;
-  void UpdateFramebufferSRGB(Framebuffer* framebuffer);
 
   error::ContextLostReason GetContextLostReasonFromResetStatus(
       GLenum reset_status) const;
@@ -3459,7 +3458,6 @@
   DoBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
   DoBindFramebuffer(GL_FRAMEBUFFER, 0);
   DoBindRenderbuffer(GL_RENDERBUFFER, 0);
-  UpdateFramebufferSRGB(nullptr);
 
   bool call_gl_clear = !surfaceless_ && !offscreen;
 #if defined(OS_ANDROID)
@@ -4297,37 +4295,21 @@
   Framebuffer* framebuffer = GetFramebufferInfoForTarget(target);
   bool valid = CheckFramebufferValid(
       framebuffer, target, GL_INVALID_FRAMEBUFFER_OPERATION, func_name);
-  if (!valid)
-    return false;
-
-  if (!features().chromium_framebuffer_multisample)
+  if (valid && !features().chromium_framebuffer_multisample)
     OnUseFramebuffer();
-
-  UpdateFramebufferSRGB(framebuffer);
-  return true;
-}
-
-void GLES2DecoderImpl::UpdateFramebufferSRGB(Framebuffer* framebuffer) {
-  // Manually set the value of FRAMEBUFFER_SRGB based on the state that was set
-  // by the client.
-  bool needs_enable_disable_framebuffer_srgb = false;
-  bool enable_framebuffer_srgb = true;
-  if (feature_info_->feature_flags().ext_srgb_write_control) {
-    needs_enable_disable_framebuffer_srgb = true;
-    enable_framebuffer_srgb &= state_.GetEnabled(GL_FRAMEBUFFER_SRGB);
-  }
-  // On desktop, enable FRAMEBUFFER_SRGB only if the framebuffer contains sRGB
-  // attachments. In theory, we can just leave FRAMEBUFFER_SRGB enabled,
-  // however,
-  // many drivers behave incorrectly when no attachments are sRGB. When at
-  // least one attachment is sRGB, then they behave correctly.
-  if (feature_info_->feature_flags().desktop_srgb_support) {
-    needs_enable_disable_framebuffer_srgb = true;
-    // Assume that the default fbo does not have an sRGB image.
-    enable_framebuffer_srgb &= framebuffer && framebuffer->HasSRGBAttachments();
-  }
-  if (needs_enable_disable_framebuffer_srgb)
+  if (valid && feature_info_->feature_flags().desktop_srgb_support) {
+    // If framebuffer contains sRGB images, then enable FRAMEBUFFER_SRGB.
+    // Otherwise, disable FRAMEBUFFER_SRGB. Assume default fbo does not have
+    // sRGB image.
+    // In theory, we can just leave FRAMEBUFFER_SRGB on. However, many drivers
+    // behave incorrectly when all images are linear encoding, they still apply
+    // the sRGB conversion, but when at least one image is sRGB, then they
+    // behave correctly.
+    bool enable_framebuffer_srgb =
+        framebuffer && framebuffer->HasSRGBAttachments();
     state_.EnableDisableFramebufferSRGB(enable_framebuffer_srgb);
+  }
+  return valid;
 }
 
 bool GLES2DecoderImpl::CheckBoundReadFramebufferValid(
@@ -7442,11 +7424,6 @@
       // DrawElements* for old desktop GL.
       return;
     }
-    if (cap == GL_FRAMEBUFFER_SRGB) {
-      // Enable and Disable GL_FRAMEBUFFER_SRGB is done manually in
-      // CheckBoundDrawFramebufferValid.
-      return;
-    }
     glDisable(cap);
   }
 }
@@ -7459,11 +7436,6 @@
       // DrawElements* for old desktop GL.
       return;
     }
-    if (cap == GL_FRAMEBUFFER_SRGB) {
-      // Enable and Disable GL_FRAMEBUFFER_SRGB is done manually in
-      // CheckBoundDrawFramebufferValid.
-      return;
-    }
     glEnable(cap);
   }
 }
@@ -10561,6 +10533,7 @@
         }
         return;
       }
+      break;
     case GL_TEXTURE_MAX_LEVEL:
       if (workarounds().use_shadowed_tex_level_params) {
         if (fparams) {
@@ -10570,6 +10543,35 @@
         }
         return;
       }
+      break;
+    case GL_TEXTURE_SWIZZLE_R:
+      if (fparams) {
+        fparams[0] = static_cast<GLfloat>(texture->swizzle_r());
+      } else {
+        iparams[0] = texture->swizzle_r();
+      }
+      return;
+    case GL_TEXTURE_SWIZZLE_G:
+      if (fparams) {
+        fparams[0] = static_cast<GLfloat>(texture->swizzle_g());
+      } else {
+        iparams[0] = texture->swizzle_g();
+      }
+      return;
+    case GL_TEXTURE_SWIZZLE_B:
+      if (fparams) {
+        fparams[0] = static_cast<GLfloat>(texture->swizzle_b());
+      } else {
+        iparams[0] = texture->swizzle_b();
+      }
+      return;
+    case GL_TEXTURE_SWIZZLE_A:
+      if (fparams) {
+        fparams[0] = static_cast<GLfloat>(texture->swizzle_a());
+      } else {
+        iparams[0] = texture->swizzle_a();
+      }
+      return;
     default:
       break;
   }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index 1f961d1..0895cdd 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -5140,14 +5140,6 @@
         return true;
       }
       return false;
-    case GL_FRAMEBUFFER_SRGB_EXT:
-      state_.enable_flags.framebuffer_srgb_ext = enabled;
-      if (state_.enable_flags.cached_framebuffer_srgb_ext != enabled ||
-          state_.ignore_cached_state) {
-        state_.enable_flags.cached_framebuffer_srgb_ext = enabled;
-        return true;
-      }
-      return false;
     case GL_POLYGON_OFFSET_FILL:
       state_.enable_flags.polygon_offset_fill = enabled;
       if (state_.enable_flags.cached_polygon_offset_fill != enabled ||
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 87c5e46..211792c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -90,6 +90,15 @@
 TEST_P(GLES3DecoderTest, Basic) {
   // Make sure the setup is correct for ES3.
   EXPECT_TRUE(feature_info()->IsWebGL2OrES3Context());
+  EXPECT_FALSE(feature_info()->IsWebGLContext());
+  EXPECT_TRUE(feature_info()->validators()->texture_bind_target.IsValid(
+      GL_TEXTURE_3D));
+}
+
+TEST_P(WebGL2DecoderTest, Basic) {
+  // Make sure the setup is correct for WebGL2.
+  EXPECT_TRUE(feature_info()->IsWebGL2OrES3Context());
+  EXPECT_TRUE(feature_info()->IsWebGLContext());
   EXPECT_TRUE(feature_info()->validators()->texture_bind_target.IsValid(
       GL_TEXTURE_3D));
 }
@@ -1695,6 +1704,14 @@
   InitDecoder(init);
 }
 
+void WebGL2DecoderTest::SetUp() {
+  InitState init;
+  init.gl_version = "OpenGL ES 3.0";
+  init.bind_generates_resource = true;
+  init.context_type = CONTEXT_TYPE_WEBGL2;
+  InitDecoder(init);
+}
+
 void GLES3DecoderWithShaderTest::SetUp() {
   InitState init;
   init.gl_version = "OpenGL ES 3.0";
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.h
index 9eab7fc..72577a3 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.h
@@ -78,6 +78,15 @@
   void SetUp() override;
 };
 
+class WebGL2DecoderTest : public GLES2DecoderTest {
+ public:
+  WebGL2DecoderTest() { shader_language_version_ = 300; }
+
+  // Override default setup so ES3 capabilities are enabled by default
+  // and WebGL2 specific rules are enforced.
+  void SetUp() override;
+};
+
 class GLES3DecoderWithShaderTest : public GLES2DecoderWithShaderTest {
  public:
   GLES3DecoderWithShaderTest() { shader_language_version_ = 300; }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
index d155c35..af2e41c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
@@ -17,9 +17,6 @@
   ExpectEnableDisable(GL_CULL_FACE, false);
   ExpectEnableDisable(GL_DEPTH_TEST, false);
   ExpectEnableDisable(GL_DITHER, true);
-  if (group_->feature_info()->feature_flags().ext_srgb_write_control) {
-    ExpectEnableDisable(GL_FRAMEBUFFER_SRGB_EXT, true);
-  }
   ExpectEnableDisable(GL_POLYGON_OFFSET_FILL, false);
   ExpectEnableDisable(GL_SAMPLE_ALPHA_TO_COVERAGE, false);
   ExpectEnableDisable(GL_SAMPLE_COVERAGE, false);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 9352747..bbc96b4 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -435,13 +435,6 @@
       .Times(1)
       .RetiresOnSaturation();
 
-  if (group_->feature_info()->feature_flags().ext_srgb_write_control ||
-      group_->feature_info()->feature_flags().desktop_srgb_support) {
-    EXPECT_CALL(*gl_, Disable(GL_FRAMEBUFFER_SRGB))
-        .Times(1)
-        .RetiresOnSaturation();
-  }
-
   // TODO(boliu): Remove OS_ANDROID once crbug.com/259023 is fixed and the
   // workaround has been reverted.
 #if !defined(OS_ANDROID)
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
index 67e71bd4..266869dfd 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
@@ -4720,6 +4720,68 @@
   EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
 }
 
+TEST_P(GLES3DecoderTest, TexSwizzleAllowed) {
+  const GLenum kTarget = GL_TEXTURE_2D;
+  const GLenum kSwizzleParam = GL_TEXTURE_SWIZZLE_R;
+  const GLenum kSwizzleValue = GL_BLUE;
+  const GLenum kInvalidSwizzleValue = GL_RG;
+
+  {
+    EXPECT_CALL(*gl_, TexParameteri(kTarget, kSwizzleParam, kSwizzleValue));
+    TexParameteri cmd;
+    cmd.Init(kTarget, kSwizzleParam, kSwizzleValue);
+    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+    EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  }
+
+  {
+    TexParameteri cmd;
+    cmd.Init(kTarget, kSwizzleParam, kInvalidSwizzleValue);
+    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+    EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+  }
+
+  {
+    EXPECT_CALL(*gl_, GetError())
+        .WillOnce(Return(GL_NO_ERROR))
+        .WillOnce(Return(GL_NO_ERROR))
+        .RetiresOnSaturation();
+    typedef GetTexParameteriv::Result Result;
+    Result* result = static_cast<Result*>(shared_memory_address_);
+    result->size = 0;
+    GetTexParameteriv cmd;
+    cmd.Init(kTarget, kSwizzleParam, shared_memory_id_, shared_memory_offset_);
+    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+    EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(kSwizzleParam),
+              result->GetNumResults());
+    EXPECT_EQ(GL_NO_ERROR, GetGLError());
+    EXPECT_EQ(kSwizzleValue, static_cast<GLenum>(result->GetData()[0]));
+  }
+}
+
+TEST_P(WebGL2DecoderTest, TexSwizzleDisabled) {
+  const GLenum kTarget = GL_TEXTURE_2D;
+  const GLenum kSwizzleParam = GL_TEXTURE_SWIZZLE_R;
+  const GLenum kSwizzleValue = GL_BLUE;
+
+  {
+    TexParameteri cmd;
+    cmd.Init(kTarget, kSwizzleParam, kSwizzleValue);
+    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+    EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+  }
+
+  {
+    typedef GetTexParameteriv::Result Result;
+    Result* result = static_cast<Result*>(shared_memory_address_);
+    result->size = 0;
+    GetTexParameteriv cmd;
+    cmd.Init(kTarget, kSwizzleParam, shared_memory_id_, shared_memory_offset_);
+    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+    EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+  }
+}
+
 // TODO(gman): Complete this test.
 // TEST_P(GLES2DecoderTest, CompressedTexImage2DGLError) {
 // }
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
index 2be3bab..1223a22 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
@@ -344,12 +344,6 @@
 ValueValidator<GLenum> texture_parameter;
 ValueValidator<GLenum> texture_sized_color_renderable_internal_format;
 ValueValidator<GLenum> texture_sized_texture_filterable_internal_format;
-class TextureSrgbDecodeExtValidator {
- public:
-  bool IsValid(const GLenum value) const;
-};
-TextureSrgbDecodeExtValidator texture_srgb_decode_ext;
-
 ValueValidator<GLenum> texture_stencil_renderable_internal_format;
 class TextureSwizzleValidator {
  public:
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
index 280f736..632eaeed 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -1216,16 +1216,6 @@
         GL_RGB_YCBCR_420V_CHROMIUM,
 };
 
-bool Validators::TextureSrgbDecodeExtValidator::IsValid(
-    const GLenum value) const {
-  switch (value) {
-    case GL_DECODE_EXT:
-    case GL_SKIP_DECODE_EXT:
-      return true;
-  }
-  return false;
-};
-
 static const GLenum
     valid_texture_stencil_renderable_internal_format_table_es3[] = {
         GL_STENCIL_INDEX8, GL_DEPTH24_STENCIL8, GL_DEPTH32F_STENCIL8,
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 8522da8..b02272b 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -1353,11 +1353,6 @@
       }
       swizzle_a_ = param;
       break;
-    case GL_TEXTURE_SRGB_DECODE_EXT:
-      if (!feature_info->validators()->texture_srgb_decode_ext.IsValid(param)) {
-        return GL_INVALID_ENUM;
-      }
-      break;
     case GL_TEXTURE_IMMUTABLE_FORMAT:
     case GL_TEXTURE_IMMUTABLE_LEVELS:
       return GL_INVALID_ENUM;
diff --git a/gpu/command_buffer/tests/gl_test_utils.cc b/gpu/command_buffer/tests/gl_test_utils.cc
index f10a919..bf860a6 100644
--- a/gpu/command_buffer/tests/gl_test_utils.cc
+++ b/gpu/command_buffer/tests/gl_test_utils.cc
@@ -71,13 +71,9 @@
 }
 
 bool GLTestHelper::HasExtension(const char* extension) {
-  // Pad with an extra space to ensure that |extension| is not a substring of
-  // another extension.
-  std::string extensions =
-      std::string(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))) +
-      " ";
-  std::string extension_padded = std::string(extension) + " ";
-  return extensions.find(extension_padded) != std::string::npos;
+  std::string extensions(
+      reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)));
+  return extensions.find(extension) != std::string::npos;
 }
 
 bool GLTestHelper::CheckGLError(const char* msg, int line) {
diff --git a/ios/chrome/browser/experimental_flags.mm b/ios/chrome/browser/experimental_flags.mm
index 4a1009d..5a4ef07 100644
--- a/ios/chrome/browser/experimental_flags.mm
+++ b/ios/chrome/browser/experimental_flags.mm
@@ -29,8 +29,8 @@
     @"HeuristicsForPasswordGeneration";
 NSString* const kEnableNewClearBrowsingDataUI = @"EnableNewClearBrowsingDataUI";
 NSString* const kMDMIntegrationDisabled = @"MDMIntegrationDisabled";
-NSString* const kPendingIndexNavigationEnabled =
-    @"PendingIndexNavigationEnabled";
+NSString* const kPendingIndexNavigationDisabled =
+    @"PendingIndexNavigationDisabled";
 const base::Feature kIOSDownloadImageRenaming{
     "IOSDownloadImageRenaming", base::FEATURE_DISABLED_BY_DEFAULT};
 }  // namespace
@@ -185,8 +185,8 @@
 }
 
 bool IsPendingIndexNavigationEnabled() {
-  return [[NSUserDefaults standardUserDefaults]
-      boolForKey:kPendingIndexNavigationEnabled];
+  return ![[NSUserDefaults standardUserDefaults]
+      boolForKey:kPendingIndexNavigationDisabled];
 }
 
 bool IsDownloadRenamingEnabled() {
diff --git a/ios/web/public/test/earl_grey/js_test_util.mm b/ios/web/public/test/earl_grey/js_test_util.mm
index f6d96f9e..b2bdcdf4 100644
--- a/ios/web/public/test/earl_grey/js_test_util.mm
+++ b/ios/web/public/test/earl_grey/js_test_util.mm
@@ -84,14 +84,14 @@
   __block id script_result = nil;
   __block bool did_finish = false;
   web::ExecuteScriptForTesting(interstitial, script, ^(id result, NSError*) {
-    script_result = [[result copy] autorelease];
+    script_result = [result copy];
     did_finish = true;
   });
   BOOL suceeded = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
     return did_finish;
   });
   GREYAssert(suceeded, @"Script execution timed out");
-  return script_result;
+  return [script_result autorelease];
 }
 
 }  // namespace web
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index e884334b..75bee627 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -2280,19 +2280,32 @@
   CRWSessionEntry* fromEntry = self.sessionController.currentEntry;
 
   NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
-  if ([userDefaults boolForKey:@"PendingIndexNavigationEnabled"]) {
-    [_delegate webWillFinishHistoryNavigationFromEntry:fromEntry];
-
+  if (![userDefaults boolForKey:@"PendingIndexNavigationDisabled"]) {
     BOOL sameDocumentNavigation =
         [sessionController isSameDocumentNavigationBetweenEntry:fromEntry
                                                        andEntry:entries[index]];
     if (sameDocumentNavigation) {
       [self.sessionController goToEntryAtIndex:index];
+
+      // Implementation of |webWillFinishHistoryNavigationFromEntry:| expects
+      // that NavigationManager has either a pending item or already made the
+      // navigation. Hence this delegate must be called after changing current
+      // navigation item. TODO(crbug.com/670149): Remove this delegate method as
+      // CRWWebController does not need to delegate setting Desktop User Agent.
+      [_delegate webWillFinishHistoryNavigationFromEntry:fromEntry];
       [self updateHTML5HistoryState];
     } else {
       [sessionController discardNonCommittedEntries];
       [sessionController setPendingEntryIndex:index];
 
+      // Implementation of |webWillFinishHistoryNavigationFromEntry:| expects
+      // that NavigationManager has either a pending item or already made the
+      // navigation. Hence this delegate must be called after changing pending
+      // navigation index. TODO(crbug.com/670149): Remove this delegate method
+      // as CRWWebController does not need to delegate setting Desktop User
+      // Agent.
+      [_delegate webWillFinishHistoryNavigationFromEntry:fromEntry];
+
       web::NavigationItemImpl* pendingItem =
           sessionController.pendingEntry.navigationItemImpl;
       pendingItem->SetTransitionType(ui::PageTransitionFromInt(
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 987b45d..db764547 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -19,6 +19,7 @@
 
   flags = [
     "ENABLE_AC3_EAC3_AUDIO_DEMUXING=$enable_ac3_eac3_audio_demuxing",
+    "ENABLE_CBCS_ENCRYPTION_SCHEME=$enable_cbcs_encryption_scheme",
     "ENABLE_HEVC_DEMUXING=$enable_hevc_demuxing",
     "ENABLE_MSE_MPEG2TS_STREAM_PARSER=$enable_mse_mpeg2ts_stream_parser",
     "ENABLE_MEDIA_REMOTING=$enable_media_remoting",
diff --git a/media/formats/mp4/box_definitions.cc b/media/formats/mp4/box_definitions.cc
index 773b4a2..6743605 100644
--- a/media/formats/mp4/box_definitions.cc
+++ b/media/formats/mp4/box_definitions.cc
@@ -26,6 +26,12 @@
 namespace media {
 namespace mp4 {
 
+namespace {
+
+const size_t kKeyIdSize = 16;
+
+}  // namespace
+
 FileType::FileType() {}
 FileType::FileType(const FileType& other) = default;
 FileType::~FileType() {}
@@ -160,8 +166,10 @@
                                   uint8_t iv_size,
                                   bool has_subsamples) {
   // According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either
-  // 64-bit (8-byte) or 128-bit (16-byte).
-  RCHECK(iv_size == 8 || iv_size == 16);
+  // 64-bit (8-byte) or 128-bit (16-byte). The 3rd Edition allows |iv_size|
+  // to be 0, for the case of a "constant IV". In this case, the existence of
+  // the constant IV must be ensured by the caller.
+  RCHECK(iv_size == 0 || iv_size == 8 || iv_size == 16);
 
   memset(initialization_vector, 0, sizeof(initialization_vector));
   for (uint8_t i = 0; i < iv_size; i++)
@@ -235,7 +243,15 @@
 }
 
 TrackEncryption::TrackEncryption()
-  : is_encrypted(false), default_iv_size(0) {
+    : is_encrypted(false),
+      default_iv_size(0)
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+      ,
+      default_crypt_byte_block(0),
+      default_skip_byte_block(0),
+      default_constant_iv_size(0)
+#endif
+{
 }
 TrackEncryption::TrackEncryption(const TrackEncryption& other) = default;
 TrackEncryption::~TrackEncryption() {}
@@ -243,14 +259,31 @@
 
 bool TrackEncryption::Parse(BoxReader* reader) {
   uint8_t flag;
+  uint8_t possible_pattern_info;
   RCHECK(reader->ReadFullBoxHeader() &&
-         reader->SkipBytes(2) &&
-         reader->Read1(&flag) &&
+         reader->SkipBytes(1) &&  // skip reserved byte
+         reader->Read1(&possible_pattern_info) && reader->Read1(&flag) &&
          reader->Read1(&default_iv_size) &&
-         reader->ReadVec(&default_kid, 16));
+         reader->ReadVec(&default_kid, kKeyIdSize));
   is_encrypted = (flag != 0);
   if (is_encrypted) {
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+    if (reader->version() > 0) {
+      default_crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
+      default_skip_byte_block = possible_pattern_info & 0x0f;
+    }
+    if (default_iv_size == 0) {
+      RCHECK(reader->Read1(&default_constant_iv_size));
+      RCHECK(default_constant_iv_size == 8 || default_constant_iv_size == 16);
+      memset(default_constant_iv, 0, sizeof(default_constant_iv));
+      for (uint8_t i = 0; i < default_constant_iv_size; i++)
+        RCHECK(reader->Read1(default_constant_iv + i));
+    } else {
+      RCHECK(default_iv_size == 8 || default_iv_size == 16);
+    }
+#else
     RCHECK(default_iv_size == 8 || default_iv_size == 16);
+#endif
   } else {
     RCHECK(default_iv_size == 0);
   }
@@ -276,15 +309,26 @@
   RCHECK(reader->ScanChildren() &&
          reader->ReadChild(&format) &&
          reader->ReadChild(&type));
-  if (type.type == FOURCC_CENC)
+  if (HasSupportedScheme())
     RCHECK(reader->ReadChild(&info));
   // Other protection schemes are silently ignored. Since the protection scheme
   // type can't be determined until this box is opened, we return 'true' for
-  // non-CENC protection scheme types. It is the parent box's responsibility to
+  // unsupported protection schemes. It is the parent box's responsibility to
   // ensure that this scheme type is a supported one.
   return true;
 }
 
+bool ProtectionSchemeInfo::HasSupportedScheme() const {
+  FourCC fourCC = type.type;
+  if (fourCC == FOURCC_CENC)
+    return true;
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+  if (fourCC == FOURCC_CBCS)
+    return true;
+#endif
+  return false;
+}
+
 MovieHeader::MovieHeader()
     : version(0),
       creation_time(0),
@@ -652,7 +696,7 @@
   if (format == FOURCC_ENCV) {
     // Continue scanning until a recognized protection scheme is found, or until
     // we run out of protection schemes.
-    while (sinf.type.type != FOURCC_CENC) {
+    while (!sinf.HasSupportedScheme()) {
       if (!reader->ReadChild(&sinf))
         return false;
     }
@@ -802,7 +846,7 @@
   if (format == FOURCC_ENCA) {
     // Continue scanning until a recognized protection scheme is found, or until
     // we run out of protection schemes.
-    while (sinf.type.type != FOURCC_CENC) {
+    while (!sinf.HasSupportedScheme()) {
       if (!reader->ReadChild(&sinf))
         return false;
     }
@@ -1142,11 +1186,50 @@
 }
 
 CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry()
-    : is_encrypted(false), iv_size(0) {}
+    : is_encrypted(false),
+      iv_size(0)
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+      ,
+      crypt_byte_block(0),
+      skip_byte_block(0),
+      constant_iv_size(0)
+#endif
+{
+}
 CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry(
     const CencSampleEncryptionInfoEntry& other) = default;
 CencSampleEncryptionInfoEntry::~CencSampleEncryptionInfoEntry() {}
 
+bool CencSampleEncryptionInfoEntry::Parse(BoxReader* reader) {
+  uint8_t flag;
+  uint8_t possible_pattern_info;
+  RCHECK(reader->SkipBytes(1) &&  // reserved.
+         reader->Read1(&possible_pattern_info) && reader->Read1(&flag) &&
+         reader->Read1(&iv_size) && reader->ReadVec(&key_id, kKeyIdSize));
+
+  is_encrypted = (flag != 0);
+  if (is_encrypted) {
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+    crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
+    skip_byte_block = possible_pattern_info & 0x0f;
+    if (iv_size == 0) {
+      RCHECK(reader->Read1(&constant_iv_size));
+      RCHECK(constant_iv_size == 8 || constant_iv_size == 16);
+      memset(constant_iv, 0, sizeof(constant_iv));
+      for (uint8_t i = 0; i < constant_iv_size; i++)
+        RCHECK(reader->Read1(constant_iv + i));
+    } else {
+      RCHECK(iv_size == 8 || iv_size == 16);
+    }
+#else
+    RCHECK(iv_size == 8 || iv_size == 16);
+#endif
+  } else {
+    RCHECK(iv_size == 0);
+  }
+  return true;
+}
+
 SampleGroupDescription::SampleGroupDescription() : grouping_type(0) {}
 SampleGroupDescription::SampleGroupDescription(
     const SampleGroupDescription& other) = default;
@@ -1165,7 +1248,6 @@
 
   const uint8_t version = reader->version();
 
-  const size_t kKeyIdSize = 16;
   const size_t kEntrySize = sizeof(uint32_t) + kKeyIdSize;
   uint32_t default_length = 0;
   if (version == 1) {
@@ -1184,19 +1266,7 @@
         RCHECK(description_length >= kEntrySize);
       }
     }
-
-    uint8_t flag;
-    RCHECK(reader->SkipBytes(2) &&  // reserved.
-           reader->Read1(&flag) &&
-           reader->Read1(&entries[i].iv_size) &&
-           reader->ReadVec(&entries[i].key_id, kKeyIdSize));
-
-    entries[i].is_encrypted = (flag != 0);
-    if (entries[i].is_encrypted) {
-      RCHECK(entries[i].iv_size == 8 || entries[i].iv_size == 16);
-    } else {
-      RCHECK(entries[i].iv_size == 0);
-    }
+    RCHECK(entries[i].Parse(reader));
   }
   return true;
 }
diff --git a/media/formats/mp4/box_definitions.h b/media/formats/mp4/box_definitions.h
index 1493edc..311e782 100644
--- a/media/formats/mp4/box_definitions.h
+++ b/media/formats/mp4/box_definitions.h
@@ -20,10 +20,14 @@
 #include "media/formats/mp4/avc.h"
 #include "media/formats/mp4/box_reader.h"
 #include "media/formats/mp4/fourccs.h"
+#include "media/media_features.h"
 
 namespace media {
 namespace mp4 {
 
+// Size in bytes needed to store largest IV.
+const int kInitializationVectorSize = 16;
+
 enum TrackType { kInvalid = 0, kVideo, kAudio, kText, kHint };
 
 enum SampleFlags {
@@ -91,7 +95,7 @@
   // anywhere.
   bool GetTotalSizeOfSubsamples(size_t* total_size) const;
 
-  uint8_t initialization_vector[16];
+  uint8_t initialization_vector[kInitializationVectorSize];
   std::vector<SubsampleEntry> subsamples;
 };
 
@@ -129,6 +133,12 @@
   bool is_encrypted;
   uint8_t default_iv_size;
   std::vector<uint8_t> default_kid;
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+  uint8_t default_crypt_byte_block;
+  uint8_t default_skip_byte_block;
+  uint8_t default_constant_iv_size;
+  uint8_t default_constant_iv[kInitializationVectorSize];
+#endif
 };
 
 struct MEDIA_EXPORT SchemeInfo : Box {
@@ -143,6 +153,8 @@
   OriginalFormat format;
   SchemeType type;
   SchemeInfo info;
+
+  bool HasSupportedScheme() const;
 };
 
 struct MEDIA_EXPORT MovieHeader : Box {
@@ -289,10 +301,17 @@
   CencSampleEncryptionInfoEntry();
   CencSampleEncryptionInfoEntry(const CencSampleEncryptionInfoEntry& other);
   ~CencSampleEncryptionInfoEntry();
+  bool Parse(BoxReader* reader);
 
   bool is_encrypted;
   uint8_t iv_size;
   std::vector<uint8_t> key_id;
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+  uint8_t crypt_byte_block;
+  uint8_t skip_byte_block;
+  uint8_t constant_iv_size;
+  uint8_t constant_iv[kInitializationVectorSize];
+#endif
 };
 
 struct MEDIA_EXPORT SampleGroupDescription : Box {  // 'sgpd'.
diff --git a/media/formats/mp4/fourccs.h b/media/formats/mp4/fourccs.h
index 0d913e5d..e0a2922 100644
--- a/media/formats/mp4/fourccs.h
+++ b/media/formats/mp4/fourccs.h
@@ -22,6 +22,7 @@
   FOURCC_AVC3 = 0x61766333,
   FOURCC_AVCC = 0x61766343,
   FOURCC_BLOC = 0x626C6F63,
+  FOURCC_CBCS = 0x63626373,
   FOURCC_CENC = 0x63656e63,
   FOURCC_CO64 = 0x636f3634,
   FOURCC_CTTS = 0x63747473,
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index 8e8ec08..97d47457 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -17,6 +17,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "media/base/audio_decoder_config.h"
+#include "media/base/encryption_scheme.h"
 #include "media/base/media_tracks.h"
 #include "media/base/media_util.h"
 #include "media/base/stream_parser_buffer.h"
@@ -35,6 +36,41 @@
 
 namespace {
 const int kMaxEmptySampleLogs = 20;
+
+// Caller should be prepared to handle return of Unencrypted() in case of
+// unsupported scheme.
+EncryptionScheme GetEncryptionScheme(const ProtectionSchemeInfo& sinf) {
+  if (!sinf.HasSupportedScheme())
+    return Unencrypted();
+  FourCC fourcc = sinf.type.type;
+  EncryptionScheme::CipherMode mode = EncryptionScheme::CIPHER_MODE_UNENCRYPTED;
+  EncryptionScheme::Pattern pattern;
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+  bool uses_pattern_encryption = false;
+#endif
+  switch (fourcc) {
+    case FOURCC_CENC:
+      mode = EncryptionScheme::CIPHER_MODE_AES_CTR;
+      break;
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+    case FOURCC_CBCS:
+      mode = EncryptionScheme::CIPHER_MODE_AES_CBC;
+      uses_pattern_encryption = true;
+      break;
+#endif
+    default:
+      NOTREACHED();
+      break;
+  }
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+  if (uses_pattern_encryption) {
+    uint8_t crypt = sinf.info.track_encryption.default_crypt_byte_block;
+    uint8_t skip = sinf.info.track_encryption.default_skip_byte_block;
+    pattern = EncryptionScheme::Pattern(crypt, skip);
+  }
+#endif
+  return EncryptionScheme(mode, pattern);
+}
 }  // namespace
 
 MP4StreamParser::MP4StreamParser(const std::set<int>& audio_object_types,
@@ -317,10 +353,15 @@
       }
       bool is_track_encrypted = entry.sinf.info.track_encryption.is_encrypted;
       is_track_encrypted_[audio_track_id] = is_track_encrypted;
-      audio_config.Initialize(
-          codec, sample_format, channel_layout, sample_per_second, extra_data,
-          is_track_encrypted ? AesCtrEncryptionScheme() : Unencrypted(),
-          base::TimeDelta(), 0);
+      EncryptionScheme scheme = Unencrypted();
+      if (is_track_encrypted) {
+        scheme = GetEncryptionScheme(entry.sinf);
+        if (!scheme.is_encrypted())
+          return false;
+      }
+      audio_config.Initialize(codec, sample_format, channel_layout,
+                              sample_per_second, extra_data, scheme,
+                              base::TimeDelta(), 0);
       DVLOG(1) << "audio_track_id=" << audio_track_id
                << " config=" << audio_config.AsHumanReadableString();
       if (!audio_config.IsValidConfig()) {
@@ -378,13 +419,18 @@
       }
       bool is_track_encrypted = entry.sinf.info.track_encryption.is_encrypted;
       is_track_encrypted_[video_track_id] = is_track_encrypted;
-      video_config.Initialize(
-          entry.video_codec, entry.video_codec_profile, PIXEL_FORMAT_YV12,
-          COLOR_SPACE_HD_REC709, coded_size, visible_rect, natural_size,
-          // No decoder-specific buffer needed for AVC;
-          // SPS/PPS are embedded in the video stream
-          EmptyExtraData(),
-          is_track_encrypted ? AesCtrEncryptionScheme() : Unencrypted());
+      EncryptionScheme scheme = Unencrypted();
+      if (is_track_encrypted) {
+        scheme = GetEncryptionScheme(entry.sinf);
+        if (!scheme.is_encrypted())
+          return false;
+      }
+      video_config.Initialize(entry.video_codec, entry.video_codec_profile,
+                              PIXEL_FORMAT_YV12, COLOR_SPACE_HD_REC709,
+                              coded_size, visible_rect, natural_size,
+                              // No decoder-specific buffer needed for AVC;
+                              // SPS/PPS are embedded in the video stream
+                              EmptyExtraData(), scheme);
       DVLOG(1) << "video_track_id=" << video_track_id
                << " config=" << video_config.AsHumanReadableString();
       if (!video_config.IsValidConfig()) {
@@ -622,11 +668,9 @@
 
   if (decrypt_config) {
     if (!subsamples.empty()) {
-    // Create a new config with the updated subsamples.
-    decrypt_config.reset(new DecryptConfig(
-        decrypt_config->key_id(),
-        decrypt_config->iv(),
-        subsamples));
+      // Create a new config with the updated subsamples.
+      decrypt_config.reset(new DecryptConfig(decrypt_config->key_id(),
+                                             decrypt_config->iv(), subsamples));
     }
     // else, use the existing config.
   } else if (is_track_encrypted_[runs_->track_id()]) {
diff --git a/media/formats/mp4/track_run_iterator.cc b/media/formats/mp4/track_run_iterator.cc
index bdd4e07..b2f6e739 100644
--- a/media/formats/mp4/track_run_iterator.cc
+++ b/media/formats/mp4/track_run_iterator.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "media/formats/mp4/rcheck.h"
 #include "media/formats/mp4/sample_to_group_iterator.h"
+#include "media/media_features.h"
 
 namespace media {
 namespace mp4 {
@@ -323,24 +324,21 @@
       tri.fragment_sample_encryption_info =
           traf.sample_group_description.entries;
 
-      uint8_t default_iv_size = 0;
+      const TrackEncryption* track_encryption;
       tri.is_audio = (stsd.type == kAudio);
       if (tri.is_audio) {
         RCHECK(!stsd.audio_entries.empty());
         if (desc_idx > stsd.audio_entries.size())
           desc_idx = 0;
         tri.audio_description = &stsd.audio_entries[desc_idx];
-        default_iv_size =
-            tri.audio_description->sinf.info.track_encryption.default_iv_size;
+        track_encryption = &tri.audio_description->sinf.info.track_encryption;
       } else {
         RCHECK(!stsd.video_entries.empty());
         if (desc_idx > stsd.video_entries.size())
           desc_idx = 0;
         tri.video_description = &stsd.video_entries[desc_idx];
-        default_iv_size =
-            tri.video_description->sinf.info.track_encryption.default_iv_size;
+        track_encryption = &tri.video_description->sinf.info.track_encryption;
       }
-
       // Initialize aux_info variables only if no sample encryption entries.
       if (sample_encryption_entries_count == 0 &&
           traf.auxiliary_offset.offsets.size() > j) {
@@ -408,12 +406,46 @@
         tri.sample_encryption_entries.resize(trun.sample_count);
         for (size_t k = 0; k < trun.sample_count; k++) {
           uint32_t index = tri.samples[k].cenc_group_description_index;
-          const uint8_t iv_size =
-              index == 0 ? default_iv_size
-                         : GetSampleEncryptionInfoEntry(tri, index)->iv_size;
-          RCHECK(tri.sample_encryption_entries[k].Parse(
-              sample_encryption_reader.get(), iv_size,
-              traf.sample_encryption.use_subsample_encryption));
+          const CencSampleEncryptionInfoEntry* info_entry =
+              index == 0 ? nullptr : GetSampleEncryptionInfoEntry(tri, index);
+          const uint8_t iv_size = index == 0 ? track_encryption->default_iv_size
+                                             : info_entry->iv_size;
+          SampleEncryptionEntry& entry = tri.sample_encryption_entries[k];
+          RCHECK(entry.Parse(sample_encryption_reader.get(), iv_size,
+                             traf.sample_encryption.use_subsample_encryption));
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+          // If we don't have a per-sample IV, get the constant IV.
+          bool is_encrypted = index == 0 ? track_encryption->is_encrypted
+                                         : info_entry->is_encrypted;
+          // We only support setting the pattern values in the 'tenc' box for
+          // the track (not varying on per sample group basis).
+          // Thus we need to verify that the settings in the sample group match
+          // those in the 'tenc'.
+          if (is_encrypted && index != 0) {
+            RCHECK_MEDIA_LOGGED(info_entry->crypt_byte_block ==
+                                    track_encryption->default_crypt_byte_block,
+                                media_log_,
+                                "Pattern value (crypt byte block) for the "
+                                "sample group does not match that in the tenc "
+                                "box . This is not currently supported.");
+            RCHECK_MEDIA_LOGGED(info_entry->skip_byte_block ==
+                                    track_encryption->default_skip_byte_block,
+                                media_log_,
+                                "Pattern value (skip byte block) for the "
+                                "sample group does not match that in the tenc "
+                                "box . This is not currently supported.");
+          }
+          if (is_encrypted && !iv_size) {
+            const uint8_t constant_iv_size =
+                index == 0 ? track_encryption->default_constant_iv_size
+                           : info_entry->constant_iv_size;
+            RCHECK(constant_iv_size != 0);
+            const uint8_t* constant_iv =
+                index == 0 ? track_encryption->default_constant_iv
+                           : info_entry->constant_iv;
+            memcpy(entry.initialization_vector, constant_iv, constant_iv_size);
+          }
+#endif
         }
       }
       runs_.push_back(tri);
@@ -474,8 +506,14 @@
       BufferReader reader(buf + pos, info_size);
       const uint8_t iv_size = GetIvSize(i);
       const bool has_subsamples = info_size > iv_size;
-      RCHECK(
-          sample_encryption_entries[i].Parse(&reader, iv_size, has_subsamples));
+      SampleEncryptionEntry& entry = sample_encryption_entries[i];
+      RCHECK(entry.Parse(&reader, iv_size, has_subsamples));
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+      // if we don't have a per-sample IV, get the constant IV.
+      if (!iv_size) {
+        RCHECK(ApplyConstantIv(i, &entry));
+      }
+#endif
     }
     pos += info_size;
   }
@@ -592,14 +630,28 @@
 
 std::unique_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
   DCHECK(is_encrypted());
+  size_t sample_idx = sample_itr_ - run_itr_->samples.begin();
+  const std::vector<uint8_t>& kid = GetKeyId(sample_idx);
 
   if (run_itr_->sample_encryption_entries.empty()) {
     DCHECK_EQ(0, aux_info_size());
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+    // The 'cbcs' scheme allows empty aux info when a constant IV is in use
+    // with full sample encryption. That case will fall through to here.
+    SampleEncryptionEntry sample_encryption_entry;
+    if (ApplyConstantIv(sample_idx, &sample_encryption_entry)) {
+      return std::unique_ptr<DecryptConfig>(new DecryptConfig(
+          std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()),
+          std::string(reinterpret_cast<const char*>(
+                          sample_encryption_entry.initialization_vector),
+                      arraysize(sample_encryption_entry.initialization_vector)),
+          sample_encryption_entry.subsamples));
+    }
+#endif
     MEDIA_LOG(ERROR, media_log_) << "Sample encryption info is not available.";
     return std::unique_ptr<DecryptConfig>();
   }
 
-  size_t sample_idx = sample_itr_ - run_itr_->samples.begin();
   DCHECK_LT(sample_idx, run_itr_->sample_encryption_entries.size());
   const SampleEncryptionEntry& sample_encryption_entry =
       run_itr_->sample_encryption_entries[sample_idx];
@@ -612,7 +664,6 @@
     return std::unique_ptr<DecryptConfig>();
   }
 
-  const std::vector<uint8_t>& kid = GetKeyId(sample_idx);
   return std::unique_ptr<DecryptConfig>(new DecryptConfig(
       std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()),
       std::string(reinterpret_cast<const char*>(
@@ -648,5 +699,24 @@
                       : GetSampleEncryptionInfoEntry(*run_itr_, index)->iv_size;
 }
 
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+bool TrackRunIterator::ApplyConstantIv(size_t sample_index,
+                                       SampleEncryptionEntry* entry) const {
+  DCHECK(IsSampleEncrypted(sample_index));
+  uint32_t index = GetGroupDescriptionIndex(sample_index);
+  const uint8_t constant_iv_size =
+      index == 0
+          ? track_encryption().default_constant_iv_size
+          : GetSampleEncryptionInfoEntry(*run_itr_, index)->constant_iv_size;
+  RCHECK(constant_iv_size != 0);
+  const uint8_t* constant_iv =
+      index == 0 ? track_encryption().default_constant_iv
+                 : GetSampleEncryptionInfoEntry(*run_itr_, index)->constant_iv;
+  RCHECK(constant_iv != nullptr);
+  memcpy(entry->initialization_vector, constant_iv, kInitializationVectorSize);
+  return true;
+}
+#endif
+
 }  // namespace mp4
 }  // namespace media
diff --git a/media/formats/mp4/track_run_iterator.h b/media/formats/mp4/track_run_iterator.h
index a04f2be..127cafa 100644
--- a/media/formats/mp4/track_run_iterator.h
+++ b/media/formats/mp4/track_run_iterator.h
@@ -17,6 +17,7 @@
 #include "media/base/media_log.h"
 #include "media/base/stream_parser_buffer.h"
 #include "media/formats/mp4/box_definitions.h"
+#include "media/media_features.h"
 
 namespace media {
 
@@ -99,6 +100,9 @@
   bool IsSampleEncrypted(size_t sample_index) const;
   uint8_t GetIvSize(size_t sample_index) const;
   const std::vector<uint8_t>& GetKeyId(size_t sample_index) const;
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+  bool ApplyConstantIv(size_t sample_index, SampleEncryptionEntry* entry) const;
+#endif
 
   const Movie* moov_;
   scoped_refptr<MediaLog> media_log_;
diff --git a/media/formats/mp4/track_run_iterator_unittest.cc b/media/formats/mp4/track_run_iterator_unittest.cc
index d1b3129..7c2043b 100644
--- a/media/formats/mp4/track_run_iterator_unittest.cc
+++ b/media/formats/mp4/track_run_iterator_unittest.cc
@@ -97,6 +97,48 @@
     0x74, 0x43, 0x65, 0x6e, 0x63, 0x53, 0x61, 0x6d,
 };
 
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+// Sample encryption data for two samples, using constant IV (defined by 'tenc'
+// or sample group entry).
+const uint8_t kSampleEncryptionDataWithSubsamplesAndConstantIv[] = {
+    // Sample count.
+    0x00, 0x00, 0x00, 0x05,
+    // Sample 1: Subsample count.
+    0x00, 0x01,
+    // Sample 1: Subsample 1.
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
+    // Sample 2: Subsample count.
+    0x00, 0x02,
+    // Sample 2: Subsample 1.
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
+    // Sample 2: Subsample 2.
+    0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
+    // Sample 3: Subsample count.
+    0x00, 0x01,
+    // Sample 3: Subsample 1.
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
+    // Sample 4: Subsample count.
+    0x00, 0x01,
+    // Sample 4: Subsample 1.
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
+    // Sample 5: Subsample count.
+    0x00, 0x01,
+    // Sample 5: Subsample 1.
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
+};
+
+// Size of these IVs are 16 bytes.
+const char kIv4[] = {
+    0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x34,
+    0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+};
+
+const char kIv5[] = {
+    0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x35,
+    0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+};
+#endif
+
 }  // namespace
 
 namespace media {
@@ -258,8 +300,7 @@
     return moof;
   }
 
-  // Update the first sample description of a Track to indicate encryption
-  void AddEncryption(Track* track) {
+  ProtectionSchemeInfo* GetProtectionSchemeInfoForTrack(Track* track) {
     SampleDescription* stsd =
         &track->media.information.sample_table.description;
     ProtectionSchemeInfo* sinf;
@@ -268,7 +309,12 @@
     } else {
        sinf = &stsd->audio_entries[0].sinf;
     }
+    return sinf;
+  }
 
+  // Update the first sample description of a Track to indicate CENC encryption
+  void AddEncryption(Track* track) {
+    ProtectionSchemeInfo* sinf = GetProtectionSchemeInfoForTrack(track);
     sinf->type.type = FOURCC_CENC;
     sinf->info.track_encryption.is_encrypted = true;
     sinf->info.track_encryption.default_iv_size = 8;
@@ -354,6 +400,70 @@
     }
   }
 
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+  // Update the first sample description of a Track to indicate CBCS encryption
+  // with a constant IV and pattern.
+  void AddEncryptionCbcs(Track* track) {
+    ProtectionSchemeInfo* sinf = GetProtectionSchemeInfoForTrack(track);
+    sinf->type.type = FOURCC_CBCS;
+    sinf->info.track_encryption.is_encrypted = true;
+    sinf->info.track_encryption.default_iv_size = 0;
+    sinf->info.track_encryption.default_crypt_byte_block = 1;
+    sinf->info.track_encryption.default_skip_byte_block = 9;
+    sinf->info.track_encryption.default_constant_iv_size = 16;
+    memcpy(sinf->info.track_encryption.default_constant_iv, kIv3, 16);
+    sinf->info.track_encryption.default_kid.assign(kKeyId,
+                                                   kKeyId + arraysize(kKeyId));
+  }
+
+  void AddConstantIvsToCencSampleGroup(Track* track, TrackFragment* frag) {
+    auto& track_cenc_group =
+        track->media.information.sample_table.sample_group_description;
+    track_cenc_group.entries[0].iv_size = 0;
+    track_cenc_group.entries[0].crypt_byte_block = 1;
+    track_cenc_group.entries[0].skip_byte_block = 9;
+    track_cenc_group.entries[0].constant_iv_size = 16;
+    memcpy(track_cenc_group.entries[0].constant_iv, kIv4, 16);
+
+    frag->sample_group_description.entries[1].iv_size = 0;
+    frag->sample_group_description.entries[1].crypt_byte_block = 1;
+    frag->sample_group_description.entries[1].skip_byte_block = 9;
+    frag->sample_group_description.entries[1].constant_iv_size = 16;
+    memcpy(frag->sample_group_description.entries[1].constant_iv, kIv5, 16);
+    frag->sample_group_description.entries[2].iv_size = 0;
+    frag->sample_group_description.entries[2].crypt_byte_block = 1;
+    frag->sample_group_description.entries[2].skip_byte_block = 9;
+    frag->sample_group_description.entries[2].constant_iv_size = 16;
+    memcpy(frag->sample_group_description.entries[2].constant_iv, kIv5, 16);
+  }
+
+  void AddSampleEncryptionCbcs(TrackFragment* frag) {
+    frag->sample_encryption.use_subsample_encryption = true;
+    frag->sample_encryption.sample_encryption_data.assign(
+        kSampleEncryptionDataWithSubsamplesAndConstantIv,
+        kSampleEncryptionDataWithSubsamplesAndConstantIv +
+            arraysize(kSampleEncryptionDataWithSubsamplesAndConstantIv));
+
+    // Update sample sizes and aux info header.
+    frag->runs.resize(1);
+    frag->runs[0].sample_count = 5;
+    frag->auxiliary_offset.offsets.push_back(0);
+    frag->auxiliary_size.sample_count = 5;
+    // Update sample sizes to match with subsample entries above.
+    frag->runs[0].sample_sizes[0] = 3;
+    frag->runs[0].sample_sizes[1] = 10;
+    frag->runs[0].sample_sizes[2] = 3;
+    frag->runs[0].sample_sizes[3] = 3;
+    frag->runs[0].sample_sizes[4] = 3;
+    // Set aux info header.
+    frag->auxiliary_size.sample_info_sizes.push_back(16);
+    frag->auxiliary_size.sample_info_sizes.push_back(30);
+    frag->auxiliary_size.sample_info_sizes.push_back(16);
+    frag->auxiliary_size.sample_info_sizes.push_back(16);
+    frag->auxiliary_size.sample_info_sizes.push_back(16);
+  }
+#endif
+
   bool InitMoofWithArbitraryAuxInfo(MovieFragment* moof) {
     // Add aux info header (equal sized aux info for every sample).
     for (uint32_t i = 0; i < moof->tracks.size(); ++i) {
@@ -879,5 +989,97 @@
   EXPECT_EQ("2 K P P P K P", KeyframeAndRAPInfo(iter_.get()));
 }
 
+#if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME)
+TEST_F(TrackRunIteratorTest, DecryptConfigTestWithConstantIvNoAuxInfo) {
+  AddEncryptionCbcs(&moov_.tracks[1]);
+  iter_.reset(new TrackRunIterator(&moov_, media_log_));
+
+  MovieFragment moof = CreateFragment();
+
+  ASSERT_TRUE(iter_->Init(moof));
+
+  // The run for track 2 will be the second.
+  iter_->AdvanceRun();
+  EXPECT_EQ(iter_->track_id(), 2u);
+  EXPECT_TRUE(iter_->is_encrypted());
+  ASSERT_FALSE(iter_->AuxInfoNeedsToBeCached());
+  EXPECT_EQ(iter_->sample_offset(), 200);
+  std::unique_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
+  EXPECT_EQ(
+      std::string(reinterpret_cast<const char*>(kKeyId), arraysize(kKeyId)),
+      config->key_id());
+  EXPECT_EQ(std::string(reinterpret_cast<const char*>(kIv3), arraysize(kIv3)),
+            config->iv());
+  EXPECT_TRUE(config->subsamples().empty());
+  iter_->AdvanceSample();
+  config = iter_->GetDecryptConfig();
+  EXPECT_EQ(
+      std::string(reinterpret_cast<const char*>(kKeyId), arraysize(kKeyId)),
+      config->key_id());
+  EXPECT_EQ(std::string(reinterpret_cast<const char*>(kIv3), arraysize(kIv3)),
+            config->iv());
+  EXPECT_TRUE(config->subsamples().empty());
+}
+
+TEST_F(TrackRunIteratorTest, DecryptConfigTestWithSampleGroupsAndConstantIv) {
+  // Add TrackEncryption Box.
+  AddEncryptionCbcs(&moov_.tracks[1]);
+
+  MovieFragment moof = CreateFragment();
+  AddSampleEncryptionCbcs(&moof.tracks[1]);
+
+  const SampleToGroupEntry kSampleToGroupTable[] = {
+      // Associated with the 2nd entry in fragment SampleGroupDescription Box.
+      {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 2},
+      // Associated with the default values specified in TrackEncryption Box.
+      {1, 0},
+      // Associated with the 1st entry in fragment SampleGroupDescription Box.
+      {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 1},
+      // Associated with the 1st entry in track SampleGroupDescription Box.
+      {1, 1}};
+  AddCencSampleGroup(&moov_.tracks[1], &moof.tracks[1], kSampleToGroupTable,
+                     arraysize(kSampleToGroupTable));
+  AddConstantIvsToCencSampleGroup(&moov_.tracks[1], &moof.tracks[1]);
+  iter_.reset(new TrackRunIterator(&moov_, media_log_));
+  ASSERT_TRUE(iter_->Init(moof));
+
+  // The run for track 2 will be the second.
+  iter_->AdvanceRun();
+
+  std::string track_encryption_iv(kIv3, kIv3 + arraysize(kIv3));
+  std::string track_cenc_sample_group_iv(kIv4, kIv4 + arraysize(kIv4));
+  std::string fragment_cenc_sample_group_iv(kIv5, kIv5 + arraysize(kIv5));
+
+  for (size_t i = 0; i < kSampleToGroupTable[0].sample_count; ++i) {
+    EXPECT_TRUE(iter_->is_encrypted());
+    EXPECT_EQ(fragment_cenc_sample_group_iv, iter_->GetDecryptConfig()->iv());
+    iter_->AdvanceSample();
+  }
+
+  for (size_t i = 0; i < kSampleToGroupTable[1].sample_count; ++i) {
+    EXPECT_TRUE(iter_->is_encrypted());
+    EXPECT_EQ(track_encryption_iv, iter_->GetDecryptConfig()->iv());
+    iter_->AdvanceSample();
+  }
+
+  for (size_t i = 0; i < kSampleToGroupTable[2].sample_count; ++i) {
+    EXPECT_FALSE(iter_->is_encrypted());
+    iter_->AdvanceSample();
+  }
+
+  for (size_t i = 0; i < kSampleToGroupTable[3].sample_count; ++i) {
+    EXPECT_TRUE(iter_->is_encrypted());
+    EXPECT_EQ(track_cenc_sample_group_iv, iter_->GetDecryptConfig()->iv());
+    iter_->AdvanceSample();
+  }
+
+  // The remaining samples should be associated with the default values
+  // specified in TrackEncryption Box.
+  EXPECT_TRUE(iter_->is_encrypted());
+  EXPECT_EQ(track_encryption_iv, iter_->GetDecryptConfig()->iv());
+}
+
+#endif
+
 }  // namespace mp4
 }  // namespace media
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index 1fe3474a..55e49f8 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -884,9 +884,9 @@
     picture_buffer_manager_.CodecChanged(nullptr);
   }
 
-  codec_config_->task_type_ =
+  base::Optional<TaskType> task_type =
       AVDACodecAllocator::Instance()->TaskTypeForAllocation();
-  if (codec_config_->task_type_ == TaskType::FAILED_CODEC) {
+  if (!task_type) {
     // If there is no free thread, then just fail.
     OnCodecConfigured(nullptr);
     return;
@@ -894,12 +894,13 @@
 
   // If autodetection is disallowed, fall back to Chrome's software decoders
   // instead of using the software decoders provided by MediaCodec.
-  if (codec_config_->task_type_ == TaskType::SW_CODEC &&
+  if (task_type == TaskType::SW_CODEC &&
       IsMediaCodecSoftwareDecodingForbidden()) {
     OnCodecConfigured(nullptr);
     return;
   }
 
+  codec_config_->task_type_ = task_type.value();
   AVDACodecAllocator::Instance()->CreateMediaCodecAsync(
       weak_this_factory_.GetWeakPtr(), codec_config_);
 }
@@ -910,13 +911,15 @@
   DCHECK_NE(state_, WAITING_FOR_CODEC);
   state_ = WAITING_FOR_CODEC;
 
-  codec_config_->task_type_ =
+  base::Optional<TaskType> task_type =
       AVDACodecAllocator::Instance()->TaskTypeForAllocation();
-  if (codec_config_->task_type_ == TaskType::FAILED_CODEC) {
+  if (!task_type) {
+    // If there is no free thread, then just fail.
     OnCodecConfigured(nullptr);
     return false;
   }
 
+  codec_config_->task_type_ = task_type.value();
   std::unique_ptr<VideoCodecBridge> media_codec =
       AVDACodecAllocator::Instance()->CreateMediaCodecSync(codec_config_);
   OnCodecConfigured(std::move(media_codec));
diff --git a/media/gpu/avda_codec_allocator.cc b/media/gpu/avda_codec_allocator.cc
index 0c1239d..1e4515a 100644
--- a/media/gpu/avda_codec_allocator.cc
+++ b/media/gpu/avda_codec_allocator.cc
@@ -78,37 +78,27 @@
   return g_avda_codec_allocator.Pointer();
 }
 
-// Make sure the construction threads are started for |client|.
 bool AVDACodecAllocator::StartThread(AVDACodecAllocatorClient* client) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Cancel any pending StopThreadTask()s because we need the threads now.
   weak_this_factory_.InvalidateWeakPtrs();
 
-  // Try to start all threads if they haven't been started.  Remember that
-  // threads fail to start fairly often.
-  for (size_t i = 0; i < threads_.size(); i++) {
-    if (threads_[i]->thread.IsRunning())
+  // Try to start the threads if they haven't been started.
+  for (auto* thread : threads_) {
+    if (thread->thread.IsRunning())
       continue;
 
-    if (!threads_[i]->thread.Start())
-      continue;
+    if (!thread->thread.Start())
+      return false;
 
     // Register the hang detector to observe the thread's MessageLoop.
-    threads_[i]->thread.task_runner()->PostTask(
-        FROM_HERE,
-        base::Bind(&base::MessageLoop::AddTaskObserver,
-                   base::Unretained(threads_[i]->thread.message_loop()),
-                   &threads_[i]->hang_detector));
+    thread->thread.task_runner()->PostTask(
+        FROM_HERE, base::Bind(&base::MessageLoop::AddTaskObserver,
+                              base::Unretained(thread->thread.message_loop()),
+                              &thread->hang_detector));
   }
 
-  // Make sure that the construction thread started, else refuse to run.
-  // If other threads fail to start, then we'll post to the GPU main thread for
-  // those cases.  SW allocation failures are much less rare, so this usually
-  // just costs us the latency of doing the codec allocation on the main thread.
-  if (!threads_[TaskType::AUTO_CODEC]->thread.IsRunning())
-    return false;
-
   clients_.insert(client);
   return true;
 }
@@ -124,42 +114,29 @@
     return;
   }
 
-  // Post a task to stop the thread through the thread's task runner and back
-  // to this thread. This ensures that all pending tasks are run first. If the
-  // thread is hung we don't post a task to avoid leaking an unbounded number
-  // of tasks on its queue. If the thread is not hung, but appears to be, it
-  // will stay alive until next time an AVDA tries to stop it. We're
-  // guaranteed to not run StopThreadTask() when the thread is hung because if
-  // an AVDA queues tasks after DoNothing(), the StopThreadTask() reply will
-  // be canceled by invalidating its weak pointer.
+  // Post a task to stop each thread through its task runner and back to this
+  // thread. This ensures that all pending tasks are run first. If a new AVDA
+  // calls StartThread() before StopThreadTask() runs, it's canceled by
+  // invalidating its weak pointer. As a result we're guaranteed to only call
+  // Thread::Stop() while there are no tasks on its queue. We don't try to stop
+  // hung threads. But if it recovers it will be stopped the next time a client
+  // calls this.
   for (size_t i = 0; i < threads_.size(); i++) {
     if (threads_[i]->thread.IsRunning() &&
         !threads_[i]->hang_detector.IsThreadLikelyHung()) {
       threads_[i]->thread.task_runner()->PostTaskAndReply(
           FROM_HERE, base::Bind(&base::DoNothing),
-          base::Bind(
-              &AVDACodecAllocator::StopThreadTask,
-              weak_this_factory_.GetWeakPtr(), i,
-              (i == TaskType::AUTO_CODEC ? stop_event_for_testing_ : nullptr)));
+          base::Bind(&AVDACodecAllocator::StopThreadTask,
+                     weak_this_factory_.GetWeakPtr(), i));
     }
   }
 }
 
-// Return the task runner for tasks of type |type|.  If that thread failed
-// to start, then fall back to the GPU main thread.
+// Return the task runner for tasks of type |type|.
 scoped_refptr<base::SingleThreadTaskRunner> AVDACodecAllocator::TaskRunnerFor(
     TaskType task_type) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  base::Thread& thread = threads_[task_type]->thread;
-
-  // Fail over to the main thread if this thread failed to start.  Note that
-  // if the AUTO_CODEC thread fails to start, then AVDA init will fail.
-  // We won't fall back autodetection to the main thread, even without a
-  // special case here.
-  if (!thread.IsRunning())
-    return base::ThreadTaskRunnerHandle::Get();
-
-  return thread.task_runner();
+  return threads_[task_type]->thread.task_runner();
 }
 
 bool AVDACodecAllocator::AllocateSurface(AVDACodecAllocatorClient* client,
@@ -270,8 +247,7 @@
   // |needs_protected_surface_| implies encrypted stream.
   DCHECK(!codec_config->needs_protected_surface_ || media_crypto);
 
-  const bool require_software_codec =
-      codec_config->task_type_ == TaskType::SW_CODEC;
+  const bool require_software_codec = codec_config->task_type_ == SW_CODEC;
 
   std::unique_ptr<VideoCodecBridge> codec(VideoCodecBridge::CreateDecoder(
       codec_config->codec_, codec_config->needs_protected_surface_,
@@ -314,8 +290,6 @@
   base::WaitableEvent* released =
       &pending_codec_releases_.find(surface_id)->second;
 
-  // TODO(watk): Even if this is the current thread, things will work, but we
-  // should refactor this to not tolerate threads failing to start.
   TaskRunnerFor(task_type)->PostTaskAndReply(
       FROM_HERE, base::Bind(&DeleteMediaCodecAndSignal,
                             base::Passed(std::move(media_codec)), released),
@@ -351,15 +325,14 @@
   return !clients_.empty();
 }
 
-TaskType AVDACodecAllocator::TaskTypeForAllocation() {
-  if (!IsThreadLikelyHung(TaskType::AUTO_CODEC))
-    return TaskType::AUTO_CODEC;
+base::Optional<TaskType> AVDACodecAllocator::TaskTypeForAllocation() {
+  if (!IsThreadLikelyHung(AUTO_CODEC))
+    return AUTO_CODEC;
 
-  if (!IsThreadLikelyHung(TaskType::SW_CODEC))
-    return TaskType::SW_CODEC;
+  if (!IsThreadLikelyHung(SW_CODEC))
+    return SW_CODEC;
 
-  // If nothing is working, then we can't allocate anyway.
-  return TaskType::FAILED_CODEC;
+  return base::nullopt;
 }
 
 base::Thread& AVDACodecAllocator::GetThreadForTesting(TaskType task_type) {
@@ -372,28 +345,27 @@
   // We leak the clock we create, but that's okay because we're a singleton.
   auto clock = tick_clock ? tick_clock : new base::DefaultTickClock();
 
-  // Create threads with names / indices that match up with TaskType.
-  DCHECK_EQ(threads_.size(), TaskType::AUTO_CODEC);
+  // Create threads with names and indices that match up with TaskType.
   threads_.push_back(new ThreadAndHangDetector("AVDAAutoThread", clock));
-  DCHECK_EQ(threads_.size(), TaskType::SW_CODEC);
   threads_.push_back(new ThreadAndHangDetector("AVDASWThread", clock));
+  static_assert(AUTO_CODEC == 0 && SW_CODEC == 1,
+                "TaskType values are not ordered correctly.");
 }
 
 AVDACodecAllocator::~AVDACodecAllocator() {
   // Only tests should reach here.  Shut down threads so that we guarantee that
   // nothing will use the threads.
-  for (size_t i = 0; i < threads_.size(); i++) {
-    if (!threads_[i]->thread.IsRunning())
-      continue;
-    threads_[i]->thread.Stop();
-  }
+  for (auto* thread : threads_)
+    thread->thread.Stop();
 }
 
-void AVDACodecAllocator::StopThreadTask(size_t index,
-                                        base::WaitableEvent* event) {
+void AVDACodecAllocator::StopThreadTask(size_t index) {
   threads_[index]->thread.Stop();
-  if (event)
-    event->Signal();
+  // Signal the stop event after both threads are stopped.
+  if (stop_event_for_testing_ && !threads_[AUTO_CODEC]->thread.IsRunning() &&
+      !threads_[SW_CODEC]->thread.IsRunning()) {
+    stop_event_for_testing_->Signal();
+  }
 }
 
 }  // namespace media
diff --git a/media/gpu/avda_codec_allocator.h b/media/gpu/avda_codec_allocator.h
index 4553ebf..ce05ca7 100644
--- a/media/gpu/avda_codec_allocator.h
+++ b/media/gpu/avda_codec_allocator.h
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/optional.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/sys_info.h"
 #include "base/threading/thread.h"
@@ -32,17 +33,12 @@
 
 // For TaskRunnerFor. These are used as vector indices, so please update
 // AVDACodecAllocator's constructor if you add / change them.
-// TODO(watk): Hide this from AVDA now that we manage codec creation.
 enum TaskType {
   // Task for an autodetected MediaCodec instance.
   AUTO_CODEC = 0,
 
   // Task for a software-codec-required MediaCodec.
   SW_CODEC = 1,
-
-  // Special value to indicate "none".  This is not used as an array index. It
-  // must, however, be positive to keep the enum unsigned.
-  FAILED_CODEC = 99,
 };
 
 // Configuration info for MediaCodec.
@@ -121,15 +117,12 @@
   // browser UI thread.
   void OnSurfaceDestroyed(int surface_id);
 
-  // Make sure the construction thread is started for |client|.
+  // Make sure the construction threads are started for |client|. Returns true
+  // on success.
   bool StartThread(AVDACodecAllocatorClient* client);
 
   void StopThread(AVDACodecAllocatorClient* client);
 
-  // Return the task runner for tasks of type |type|.  If that thread failed
-  // to start, then fall back to the GPU main thread.
-  scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerFor(TaskType task_type);
-
   // Returns true if the caller now owns the surface, or false if someone else
   // owns the surface. |client| will be notified when the surface is available
   // via OnSurfaceAvailable().
@@ -166,9 +159,9 @@
   // Return true if and only if there is any AVDA registered.
   bool IsAnyRegisteredAVDA();
 
-  // Return the task type to use for a new codec allocation, or FAILED_CODEC if
-  // there is no thread that can support it.
-  TaskType TaskTypeForAllocation();
+  // Return the task type to use for a new codec allocation, or nullopt if
+  // both threads are hung.
+  base::Optional<TaskType> TaskTypeForAllocation();
 
   // Return a reference to the thread for unit tests.
   base::Thread& GetThreadForTesting(TaskType task_type);
@@ -213,10 +206,14 @@
                      base::WaitableEvent* stop_event = nullptr);
   ~AVDACodecAllocator();
 
+  // Return the task runner for tasks of type |type|.
+  scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerFor(TaskType task_type);
+
   void OnMediaCodecAndSurfaceReleased(int surface_id);
 
-  // Stop the thread indicated by |index|, then signal |event| if provided.
-  void StopThreadTask(size_t index, base::WaitableEvent* event = nullptr);
+  // Stop the thread indicated by |index|. This signals stop_event_for_testing_
+  // after both threads are stopped.
+  void StopThreadTask(size_t index);
 
   // All registered AVDAs.
   std::set<AVDACodecAllocatorClient*> clients_;
diff --git a/media/gpu/avda_codec_allocator_unittest.cc b/media/gpu/avda_codec_allocator_unittest.cc
index 5d4d1be1..8c066f2 100644
--- a/media/gpu/avda_codec_allocator_unittest.cc
+++ b/media/gpu/avda_codec_allocator_unittest.cc
@@ -136,7 +136,7 @@
             allocator_, task_type));
   }
 
-  TaskType TaskTypeForAllocation() {
+  base::Optional<TaskType> TaskTypeForAllocation() {
     return PostAndWait(FROM_HERE,
                        base::Bind(&AVDACodecAllocator::TaskTypeForAllocation,
                                   base::Unretained(allocator_)));
@@ -185,26 +185,26 @@
 };
 
 TEST_F(AVDACodecAllocatorTest, ThreadsStartWhenClientsStart) {
-  ASSERT_FALSE(IsThreadRunning(TaskType::AUTO_CODEC));
-  ASSERT_FALSE(IsThreadRunning(TaskType::SW_CODEC));
-  ASSERT_TRUE(StartThread(avda1_));
-  // Assert that the AUTO_CODEC thread is started. The other might not be.
-  ASSERT_TRUE(IsThreadRunning(TaskType::AUTO_CODEC));
+  ASSERT_FALSE(IsThreadRunning(AUTO_CODEC));
+  ASSERT_FALSE(IsThreadRunning(SW_CODEC));
+  StartThread(avda1_);
+  ASSERT_TRUE(IsThreadRunning(AUTO_CODEC));
+  ASSERT_TRUE(IsThreadRunning(SW_CODEC));
 }
 
 TEST_F(AVDACodecAllocatorTest, ThreadsStopAfterAllClientsStop) {
   StartThread(avda1_);
   StartThread(avda2_);
   StopThread(avda1_);
-  ASSERT_TRUE(IsThreadRunning(TaskType::AUTO_CODEC));
+  ASSERT_TRUE(IsThreadRunning(AUTO_CODEC));
   StopThread(avda2_);
-  ASSERT_FALSE(IsThreadRunning(TaskType::AUTO_CODEC));
-  // Note the SW_CODEC thread might still be running.
+  ASSERT_FALSE(IsThreadRunning(AUTO_CODEC));
+  ASSERT_FALSE(IsThreadRunning(SW_CODEC));
 }
 
 TEST_F(AVDACodecAllocatorTest, TestHangThread) {
   StartThread(avda1_);
-  ASSERT_EQ(TaskType::AUTO_CODEC, TaskTypeForAllocation());
+  ASSERT_EQ(AUTO_CODEC, TaskTypeForAllocation().value());
 
   // Hang the AUTO_CODEC thread.
   base::WaitableEvent about_to_wait_event(
@@ -213,7 +213,7 @@
   base::WaitableEvent wait_event(
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
-  TaskRunnerFor(TaskType::AUTO_CODEC)
+  TaskRunnerFor(AUTO_CODEC)
       ->PostTask(FROM_HERE, base::Bind(&WaitUntilRestarted,
                                        &about_to_wait_event, &wait_event));
   // Wait until the task starts, so that |allocator_| starts the hang timer.
@@ -221,24 +221,21 @@
 
   // Verify that we've failed over after a long time has passed.
   tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
-  // Note that this should return the SW codec task type even if that thread
-  // failed to start.  TaskRunnerFor() will return the current thread in that
-  // case too.
-  ASSERT_EQ(TaskType::SW_CODEC, TaskTypeForAllocation());
+  ASSERT_EQ(SW_CODEC, TaskTypeForAllocation().value());
 
   // Un-hang the thread and wait for it to let another task run.  This will
   // notify |allocator_| that the thread is no longer hung.
   base::WaitableEvent done_waiting_event(
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
-  TaskRunnerFor(TaskType::AUTO_CODEC)
+  TaskRunnerFor(AUTO_CODEC)
       ->PostTask(FROM_HERE,
                  base::Bind(&SignalImmediately, &done_waiting_event));
   wait_event.Signal();
   done_waiting_event.Wait();
 
   // Verify that we've un-failed over.
-  ASSERT_EQ(TaskType::AUTO_CODEC, TaskTypeForAllocation());
+  ASSERT_EQ(AUTO_CODEC, TaskTypeForAllocation().value());
 }
 
 TEST_F(AVDACodecAllocatorTest, AllocatingASurfaceTextureAlwaysSucceeds) {
diff --git a/media/gpu/vt_video_decode_accelerator_mac.cc b/media/gpu/vt_video_decode_accelerator_mac.cc
index e179548..f79b2da9 100644
--- a/media/gpu/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/vt_video_decode_accelerator_mac.cc
@@ -815,8 +815,13 @@
 
   FinishDelayedFrames();
 
-  // Always queue a task, even if FinishDelayedFrames() fails, so that
-  // destruction always completes.
+  if (type == TASK_DESTROY && session_) {
+    // Destroy the decoding session before returning from the decoder thread.
+    VTDecompressionSessionInvalidate(session_);
+    session_.reset();
+  }
+
+  // Queue a task even if flushing fails, so that destruction always completes.
   gpu_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&VTVideoDecodeAccelerator::FlushDone, weak_this_, type));
diff --git a/media/media_options.gni b/media/media_options.gni
index 61464d4..7307730 100644
--- a/media/media_options.gni
+++ b/media/media_options.gni
@@ -41,6 +41,10 @@
   enable_mse_mpeg2ts_stream_parser =
       (proprietary_codecs && is_chromecast) || use_libfuzzer || use_afl
 
+  # Enable support for the 'cbcs' encryption scheme added by MPEG Common
+  # Encryption 3rd Edition (ISO/IEC 23001-7), published 02/15/2016.
+  enable_cbcs_encryption_scheme = is_chromecast
+
   # Enable HEVC/H265 demuxing. Actual decoding must be provided by the
   # platform. Enable by default for Chromecast.
   enable_hevc_demuxing = proprietary_codecs && is_chromecast
diff --git a/media/mojo/BUILD.gn b/media/mojo/BUILD.gn
index bece3de..27af0fe3 100644
--- a/media/mojo/BUILD.gn
+++ b/media/mojo/BUILD.gn
@@ -25,7 +25,7 @@
     "//media/mojo/clients",
     "//media/mojo/common",
     "//media/mojo/interfaces",
-    "//media/mojo/services",
+    "//media/mojo/services:lib",
     "//services/service_manager/tests:interfaces",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index c2f93d7e..dd57bde 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -49,6 +49,15 @@
 component("services") {
   output_name = "media_mojo_services"
 
+  public_deps = [
+    ":lib",
+  ]
+}
+
+# TODO(xhwang): Add this intermediate target because ServiceTest doesn't support
+# services that depend on shared libraries in component build.
+# See http://crbug.com/670094
+source_set("lib") {
   sources = [
     "demuxer_stream_provider_shim.cc",
     "demuxer_stream_provider_shim.h",
@@ -126,15 +135,19 @@
   ]
 
   public_deps = [
-    ":services",
     "//base",
     "//media",
   ]
 
   deps = [
+    ":lib",
     "//mojo/public/c/system",
     "//services/service_manager/public/cpp",
   ]
+
+  data_deps = [
+    ":media_manifest",
+  ]
 }
 
 test("media_mojo_shell_unittests") {
@@ -157,8 +170,8 @@
   ]
 
   data_deps = [
-    "//media/mojo/services:test_manifest",
-    "//media/mojo/services:media",
+    ":media",
+    ":test_manifest",
   ]
 }
 
diff --git a/media/mojo/services/apptest_manifest.json b/media/mojo/services/apptest_manifest.json
deleted file mode 100644
index 797c48b..0000000
--- a/media/mojo/services/apptest_manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "media_apptests",
-  "display_name": "Media Apptests",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "requires": {
-        "*": [ "app" ],
-        "media": [ "media:interface_factory" ]
-      }
-    }
-  }
-}
diff --git a/media/mojo/services/media_manifest.json b/media/mojo/services/media_manifest.json
index f1c7205..64c6194 100644
--- a/media/mojo/services/media_manifest.json
+++ b/media/mojo/services/media_manifest.json
@@ -4,8 +4,7 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "provides": {
-        "media:media": [ "media::mojom::MediaService" ],
-        "media:interface_factory": [ "media::mojom::InterfaceFactory" ]
+        "media:media": [ "media::mojom::MediaService" ]
       },
       "requires": {
         "*": [ "app" ]
diff --git a/media/mojo/services/media_mojo_unittest.cc b/media/mojo/services/media_mojo_unittest.cc
index 9939e957..ed5e770 100644
--- a/media/mojo/services/media_mojo_unittest.cc
+++ b/media/mojo/services/media_mojo_unittest.cc
@@ -19,8 +19,11 @@
 #include "media/mojo/interfaces/content_decryption_module.mojom.h"
 #include "media/mojo/interfaces/decryptor.mojom.h"
 #include "media/mojo/interfaces/interface_factory.mojom.h"
+#include "media/mojo/interfaces/media_service.mojom.h"
 #include "media/mojo/interfaces/renderer.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/public/cpp/service_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -65,7 +68,7 @@
 class MediaServiceTest : public service_manager::test::ServiceTest {
  public:
   MediaServiceTest()
-      : ServiceTest("media_mojo_unittests"),
+      : ServiceTest("media_mojo_shell_unittests"),
         renderer_client_binding_(&renderer_client_),
         video_stream_(DemuxerStream::VIDEO) {}
   ~MediaServiceTest() override {}
@@ -74,10 +77,19 @@
     ServiceTest::SetUp();
 
     connection_ = connector()->Connect("media");
-    connection_->SetConnectionLostClosure(base::Bind(
-        &MediaServiceTest::ConnectionClosed, base::Unretained(this)));
+    media::mojom::MediaServicePtr media_service;
+    connection_->GetInterface(&media_service);
 
-    connection_->GetInterface(&interface_factory_);
+    auto registry =
+        base::MakeUnique<service_manager::InterfaceRegistry>(std::string());
+    service_manager::mojom::InterfaceProviderPtr interfaces;
+    registry->Bind(GetProxy(&interfaces), service_manager::Identity(),
+                   service_manager::InterfaceProviderSpec(),
+                   service_manager::Identity(),
+                   service_manager::InterfaceProviderSpec());
+
+    media_service->CreateInterfaceFactory(mojo::GetProxy(&interface_factory_),
+                                          std::move(interfaces));
 
     run_loop_.reset(new base::RunLoop());
   }
@@ -135,6 +147,7 @@
   MOCK_METHOD0(ConnectionClosed, void());
 
  protected:
+  std::unique_ptr<service_manager::Connection> connection_;
   std::unique_ptr<base::RunLoop> run_loop_;
 
   mojom::InterfaceFactoryPtr interface_factory_;
@@ -148,8 +161,6 @@
   std::unique_ptr<MojoDemuxerStreamImpl> mojo_video_stream_;
 
  private:
-  std::unique_ptr<service_manager::Connection> connection_;
-
   DISALLOW_COPY_AND_ASSIGN(MediaServiceTest);
 };
 
@@ -190,6 +201,9 @@
 #endif  // defined(ENABLE_MOJO_RENDERER)
 
 TEST_F(MediaServiceTest, Lifetime) {
+  connection_->SetConnectionLostClosure(
+      base::Bind(&MediaServiceTest::ConnectionClosed, base::Unretained(this)));
+
   // Disconnecting CDM and Renderer services doesn't terminate the app.
   cdm_.reset();
   renderer_.reset();
diff --git a/media/mojo/services/test_manifest.json b/media/mojo/services/test_manifest.json
index 4a60bf0d..27063eb 100644
--- a/media/mojo/services/test_manifest.json
+++ b/media/mojo/services/test_manifest.json
@@ -5,7 +5,7 @@
     "service_manager:connector": {
       "requires": {
         "*": [ "app" ],
-        "media": [ "media:interface_factory" ]
+        "media": [ "media:media" ]
       }
     }
   }
diff --git a/net/BUILD.gn b/net/BUILD.gn
index abd9c740..5754c47 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1149,6 +1149,8 @@
     "tools/quic/quic_simple_server_stream.h",
     "tools/quic/quic_spdy_client_stream.cc",
     "tools/quic/quic_spdy_client_stream.h",
+    "tools/quic/quic_spdy_server_stream_base.cc",
+    "tools/quic/quic_spdy_server_stream_base.h",
     "tools/quic/quic_time_wait_list_manager.cc",
     "tools/quic/quic_time_wait_list_manager.h",
     "tools/quic/stateless_rejector.cc",
diff --git a/net/cert/nss_cert_database_unittest.cc b/net/cert/nss_cert_database_unittest.cc
index 9e20f886..9832289 100644
--- a/net/cert/nss_cert_database_unittest.cc
+++ b/net/cert/nss_cert_database_unittest.cc
@@ -286,6 +286,26 @@
   EXPECT_EQ(0U, ListCerts().size());
 }
 
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12EmptyPassword) {
+  std::string pkcs12_data = ReadTestFile("client-empty-password.p12");
+
+  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(GetPublicModule(), pkcs12_data,
+                                           base::string16(),
+                                           true,  // is_extractable
+                                           NULL));
+  EXPECT_EQ(1U, ListCerts().size());
+}
+
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12NullPassword) {
+  std::string pkcs12_data = ReadTestFile("client-null-password.p12");
+
+  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(GetPublicModule(), pkcs12_data,
+                                           base::string16(),
+                                           true,  // is_extractable
+                                           NULL));
+  EXPECT_EQ(1U, ListCerts().size());
+}
+
 TEST_F(CertDatabaseNSSTest, ImportCACert_SSLTrust) {
   CertificateList certs = CreateCertificateListFromFile(
       GetTestCertsDirectory(), "root_ca_cert.pem",
diff --git a/net/data/quic_http_response_cache_data/quic-datatesturl.com/index.html b/net/data/quic_http_response_cache_data/quic-datatesturl.com/index.html
deleted file mode 100644
index 71aaeb0..0000000
--- a/net/data/quic_http_response_cache_data/quic-datatesturl.com/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-HTTP/1.1 200 OK
-Date: Sat, 22 Jun 2013 01:21:01 GMT
-Server: Apache/2.2.22 (Ubuntu)
-Last-Modified: Fri, 21 Jun 2013 22:20:24 GMT
-ETag: "2a9d2-98-4dfb17827450a"
-Accept-Ranges: bytes
-Vary: Accept-Encoding
-Content-Encoding: None 
-Keep-Alive: timeout=5, max=100
-Connection: close
-Content-Type: text/html
-X-Original-Url: http://quic.test.url/index.html
-
-This is a test page.
diff --git a/net/data/quic_http_response_cache_data/www.example.com/index.html b/net/data/quic_http_response_cache_data/www.example.com/index.html
new file mode 100644
index 0000000..5edaf9af
--- /dev/null
+++ b/net/data/quic_http_response_cache_data/www.example.com/index.html
@@ -0,0 +1,63 @@
+HTTP/1.1 200 OK
+Date: Tue, 28 Aug 2012 15:08:56 GMT
+Server: Apache/2.2.3 (CentOS)
+X-Powered-By: PHP/5.1.6
+Set-Cookie: bblastvisit=1346166536; expires=Wed, 28-Aug-2013 15:08:56 GMT; path=/; domain=.nasioc.com
+Set-Cookie: bblastactivity=0; expires=Wed, 28-Aug-2013 15:08:56 GMT; path=/; domain=.nasioc.com
+Expires: 0
+Cache-Control: private, post-check=0, pre-check=0, max-age=0
+Pragma: no-cache
+X-UA-Compatible: IE=7
+Connection: close
+Content-Type: text/html; charset=ISO-8859-1
+
+<!doctype html>
+<html>
+<head>
+    <title>Example Domain</title>
+
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type="text/css">
+    body {
+        background-color: #f0f0f2;
+        margin: 0;
+        padding: 0;
+        font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+        
+    }
+    div {
+        width: 600px;
+        margin: 5em auto;
+        padding: 50px;
+        background-color: #fff;
+        border-radius: 1em;
+    }
+    a:link, a:visited {
+        color: #38488f;
+        text-decoration: none;
+    }
+    @media (max-width: 700px) {
+        body {
+            background-color: #fff;
+        }
+        div {
+            width: auto;
+            margin: 0 auto;
+            border-radius: 0;
+            padding: 1em;
+        }
+    }
+    </style>    
+</head>
+
+<body>
+<div>
+    <h1>Example Domain</h1>
+    <p>This domain is established to be used for illustrative examples in documents. You may use this
+    domain in examples without prior coordination or asking for permission.</p>
+    <p><a href="http://www.iana.org/domains/example">More information...</a></p>
+</div>
+</body>
+</html>
diff --git a/net/data/quic_http_response_cache_data/www.example.com/map.html b/net/data/quic_http_response_cache_data/www.example.com/map.html
new file mode 100644
index 0000000..470faa1
--- /dev/null
+++ b/net/data/quic_http_response_cache_data/www.example.com/map.html
@@ -0,0 +1,65 @@
+HTTP/1.1 200 OK
+Date: Tue, 28 Aug 2012 15:08:56 GMT
+Server: Apache/2.2.3 (CentOS)
+X-Powered-By: PHP/5.1.6
+Set-Cookie: bblastvisit=1346166536; expires=Wed, 28-Aug-2013 15:08:56 GMT; path=/; domain=.nasioc.com
+Set-Cookie: bblastactivity=0; expires=Wed, 28-Aug-2013 15:08:56 GMT; path=/; domain=.nasioc.com
+Expires: 0
+Cache-Control: private, post-check=0, pre-check=0, max-age=0
+Pragma: no-cache
+X-UA-Compatible: IE=7
+Connection: close
+Content-Type: text/html; charset=ISO-8859-1
+X-Original-Url: http://www.example.com/site_map.html
+
+
+<!doctype html>
+<html>
+<head>
+    <title>Example Domain</title>
+
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type="text/css">
+    body {
+        background-color: #f0f0f2;
+        margin: 0;
+        padding: 0;
+        font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+        
+    }
+    div {
+        width: 600px;
+        margin: 5em auto;
+        padding: 50px;
+        background-color: #fff;
+        border-radius: 1em;
+    }
+    a:link, a:visited {
+        color: #38488f;
+        text-decoration: none;
+    }
+    @media (max-width: 700px) {
+        body {
+            background-color: #fff;
+        }
+        div {
+            width: auto;
+            margin: 0 auto;
+            border-radius: 0;
+            padding: 1em;
+        }
+    }
+    </style>    
+</head>
+
+<body>
+<div>
+    <h1>Example Domain</h1>
+    <p>This domain is established to be used for illustrative examples in documents. You may use this
+    domain in examples without prior coordination or asking for permission.</p>
+    <p><a href="http://www.iana.org/domains/example">More information...</a></p>
+</div>
+</body>
+</html>
diff --git a/net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/favicon.ico b/net/data/quic_http_response_cache_data_with_push/www.example.com/favicon.ico
similarity index 94%
rename from net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/favicon.ico
rename to net/data/quic_http_response_cache_data_with_push/www.example.com/favicon.ico
index a559c58..b7b01b5 100644
--- a/net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/favicon.ico
+++ b/net/data/quic_http_response_cache_data_with_push/www.example.com/favicon.ico
Binary files differ
diff --git a/net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/index.html b/net/data/quic_http_response_cache_data_with_push/www.example.com/index.html
similarity index 78%
rename from net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/index.html
rename to net/data/quic_http_response_cache_data_with_push/www.example.com/index.html
index d13a4d02..bfcb614 100644
--- a/net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/index.html
+++ b/net/data/quic_http_response_cache_data_with_push/www.example.com/index.html
@@ -9,7 +9,7 @@
 Keep-Alive: timeout=5, max=100
 Connection: close
 Content-Type: text/html
-X-Original-Url: https://quic.test.url/
-X-Push-Url: https://quic.test.url/kitten-1.jpg
+X-Original-Url: https://www.example.com/
+X-Push-Url: https://www.example.com/kitten-1.jpg
 
 This is a test page.
diff --git a/net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/index2.html b/net/data/quic_http_response_cache_data_with_push/www.example.com/index2.html
similarity index 68%
rename from net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/index2.html
rename to net/data/quic_http_response_cache_data_with_push/www.example.com/index2.html
index 74afa66..58c5db9 100644
--- a/net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/index2.html
+++ b/net/data/quic_http_response_cache_data_with_push/www.example.com/index2.html
@@ -9,8 +9,8 @@
 Keep-Alive: timeout=5, max=100
 Connection: close
 Content-Type: text/html
-X-Original-Url: https://quic.test.url/index2.html
-X-Push-Url: https://quic.test.url/kitten-1.jpg
-X-Push-Url: https://quic.test.url/favicon.ico
+X-Original-Url: https://www.example.com/index2.html
+X-Push-Url: https://www.example.com/kitten-1.jpg
+X-Push-Url: https://www.example.com/favicon.ico
 
 This is a test page.
diff --git a/net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/kitten-1.jpg b/net/data/quic_http_response_cache_data_with_push/www.example.com/kitten-1.jpg
similarity index 99%
rename from net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/kitten-1.jpg
rename to net/data/quic_http_response_cache_data_with_push/www.example.com/kitten-1.jpg
index 25ba08a..25d543b8 100644
--- a/net/data/quic_http_response_cache_data_with_push/quic-datatesturl.com/kitten-1.jpg
+++ b/net/data/quic_http_response_cache_data_with_push/www.example.com/kitten-1.jpg
Binary files differ
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README
index 2d32454..962978b 100644
--- a/net/data/ssl/certificates/README
+++ b/net/data/ssl/certificates/README
@@ -79,6 +79,15 @@
 - client-nokey.p12 : A PKCS #12 file containing a client certificate (the same
      as the one in client.p12) but no private key. The password is "12345".
 
+- client-empty-password.p12 : A PKCS #12 file containing an unencrypted client
+     certificate and a encrypted private key.  The password is the empty string,
+     encoded as two zero bytes.  (PKCS#12 passwords are encoded as
+     NUL-terminated UTF-16.)
+
+- client-null-password.p12 : A PKCS #12 file containing an unencrypted client
+     certificate and a encrypted private key.  The password is the empty string,
+     encoded as the empty byte string.
+
 - unittest.selfsigned.der : A self-signed certificate generated using private
      key in unittest.key.bin. The common name is "unittest".
 
diff --git a/net/data/ssl/certificates/client-empty-password.p12 b/net/data/ssl/certificates/client-empty-password.p12
new file mode 100644
index 0000000..e0b514c4
--- /dev/null
+++ b/net/data/ssl/certificates/client-empty-password.p12
Binary files differ
diff --git a/net/data/ssl/certificates/client-null-password.p12 b/net/data/ssl/certificates/client-null-password.p12
new file mode 100644
index 0000000..a8c45a7
--- /dev/null
+++ b/net/data/ssl/certificates/client-null-password.p12
Binary files differ
diff --git a/net/data/ssl/certificates/quic_test.example.com.key.sct b/net/data/ssl/certificates/quic_test.example.com.key.sct
index 55b0b42..0d19282 100644
--- a/net/data/ssl/certificates/quic_test.example.com.key.sct
+++ b/net/data/ssl/certificates/quic_test.example.com.key.sct
Binary files differ
diff --git a/net/data/ssl/certificates/quic_test_ecc.example.com.sct b/net/data/ssl/certificates/quic_test_ecc.example.com.sct
new file mode 100644
index 0000000..3758265
--- /dev/null
+++ b/net/data/ssl/certificates/quic_test_ecc.example.com.sct
Binary files differ
diff --git a/net/net.gypi b/net/net.gypi
index c6eeabb..598a8d3 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -1902,6 +1902,8 @@
       'quic/test_tools/simulator/alarm_factory.h',
       'quic/test_tools/simulator/link.cc',
       'quic/test_tools/simulator/link.h',
+      'quic/test_tools/simulator/packet_filter.cc',
+      'quic/test_tools/simulator/packet_filter.h',
       'quic/test_tools/simulator/port.cc',
       'quic/test_tools/simulator/port.h',
       'quic/test_tools/simulator/queue.cc',
@@ -2063,6 +2065,7 @@
       'tools/quic/quic_simple_server_stream_test.cc',
       'tools/quic/quic_simple_server_test.cc',
       'tools/quic/quic_spdy_client_stream_test.cc',
+      'tools/quic/quic_spdy_server_stream_base_test.cc',
       'tools/quic/quic_time_wait_list_manager_test.cc',
       'tools/quic/stateless_rejector_test.cc',
       'tools/quic/test_tools/limited_mtu_test_writer.cc',
diff --git a/net/quic/core/congestion_control/bbr_sender.cc b/net/quic/core/congestion_control/bbr_sender.cc
index 04908c2..af248b3 100644
--- a/net/quic/core/congestion_control/bbr_sender.cc
+++ b/net/quic/core/congestion_control/bbr_sender.cc
@@ -99,7 +99,8 @@
       last_sample_is_app_limited_(false),
       recovery_state_(NOT_IN_RECOVERY),
       end_recovery_at_(0),
-      recovery_window_(max_congestion_window_) {
+      recovery_window_(max_congestion_window_),
+      enforce_startup_pacing_rate_increase_(FLAGS_quic_bbr_faster_startup) {
   EnterStartupMode();
 }
 
@@ -454,7 +455,24 @@
     return;
   }
 
-  pacing_rate_ = pacing_gain_ * BandwidthEstimate();
+  QuicBandwidth target_rate = pacing_gain_ * BandwidthEstimate();
+
+  // Ensure that the pacing rate does not drop too low during the startup.
+  if (!is_at_full_bandwidth_ && enforce_startup_pacing_rate_increase_) {
+    // Pace at the rate of initial_window / RTT as soon as RTT measurements are
+    // available.
+    if (pacing_rate_.IsZero() && !rtt_stats_->min_rtt().IsZero()) {
+      pacing_rate_ = QuicBandwidth::FromBytesAndTimeDelta(
+          initial_congestion_window_, rtt_stats_->min_rtt());
+      return;
+    }
+
+    // Do not decrease the pacing rate during the startup.
+    pacing_rate_ = std::max(pacing_rate_, target_rate);
+    return;
+  }
+
+  pacing_rate_ = target_rate;
 }
 
 void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked) {
diff --git a/net/quic/core/congestion_control/bbr_sender.h b/net/quic/core/congestion_control/bbr_sender.h
index 952568d..0838c54 100644
--- a/net/quic/core/congestion_control/bbr_sender.h
+++ b/net/quic/core/congestion_control/bbr_sender.h
@@ -270,6 +270,10 @@
   // A window used to limit the number of bytes in flight during loss recovery.
   QuicByteCount recovery_window_;
 
+  // Indicates whether to always only increase the pacing rate during startup.
+  // Latches |FLAGS_quic_bbr_faster_startup|.
+  bool enforce_startup_pacing_rate_increase_;
+
   DISALLOW_COPY_AND_ASSIGN(BbrSender);
 };
 
diff --git a/net/quic/core/congestion_control/bbr_sender_test.cc b/net/quic/core/congestion_control/bbr_sender_test.cc
index e85c86f4..b4ffa58 100644
--- a/net/quic/core/congestion_control/bbr_sender_test.cc
+++ b/net/quic/core/congestion_control/bbr_sender_test.cc
@@ -78,6 +78,8 @@
                   "BBR sender",
                   Perspective::IS_SERVER,
                   42) {
+    FLAGS_quic_bbr_faster_startup = true;
+
     rtt_stats_ = bbr_sender_.connection()->sent_packet_manager().GetRttStats();
     sender_ = new BbrSender(simulator_.GetClock(), rtt_stats_,
                             QuicSentPacketManagerPeer::GetUnackedPacketMap(
@@ -109,6 +111,7 @@
   const QuicClock* clock_;
   const RttStats* rtt_stats_;
   BbrSender* sender_;
+  QuicFlagSaver flags_;
 
   // Creates a default setup, which is a network with a bottleneck between the
   // receiver and the switch.  The switch has the buffers four times larger than
@@ -233,7 +236,7 @@
   float loss_rate =
       static_cast<float>(bbr_sender_.connection()->GetStats().packets_lost) /
       bbr_sender_.connection()->GetStats().packets_sent;
-  EXPECT_LE(loss_rate, 0.20);
+  EXPECT_LE(loss_rate, 0.27);
 }
 
 // Ensures the code transitions loss recovery states correctly (NOT_IN_RECOVERY
@@ -430,5 +433,36 @@
   EXPECT_EQ(2, sender_->ExportDebugState().gain_cycle_index);
 }
 
+// Ensure that the pacing rate does not drop at startup.
+TEST_F(BbrSenderTest, NoBandwidthDropOnStartup) {
+  CreateDefaultSetup();
+
+  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
+  bool simulator_result;
+
+  QuicBandwidth initial_rate = QuicBandwidth::FromBytesAndTimeDelta(
+      kInitialCongestionWindowPackets * kDefaultTCPMSS,
+      QuicTime::Delta::FromMicroseconds(rtt_stats_->initial_rtt_us()));
+  EXPECT_GE(sender_->PacingRate(0), initial_rate);
+
+  // Send a packet.
+  bbr_sender_.AddBytesToTransfer(1000);
+  simulator_result = simulator_.RunUntilOrTimeout(
+      [this]() { return receiver_.bytes_received() == 1000; }, timeout);
+  ASSERT_TRUE(simulator_result);
+  EXPECT_GE(sender_->PacingRate(0), initial_rate);
+
+  // Wait for a while.
+  simulator_.RunFor(QuicTime::Delta::FromSeconds(2));
+  EXPECT_GE(sender_->PacingRate(0), initial_rate);
+
+  // Send another packet.
+  bbr_sender_.AddBytesToTransfer(1000);
+  simulator_result = simulator_.RunUntilOrTimeout(
+      [this]() { return receiver_.bytes_received() == 2000; }, timeout);
+  ASSERT_TRUE(simulator_result);
+  EXPECT_GE(sender_->PacingRate(0), initial_rate);
+}
+
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/core/congestion_control/cubic.cc b/net/quic/core/congestion_control/cubic.cc
index 7b7d9f8..8a5083b 100644
--- a/net/quic/core/congestion_control/cubic.cc
+++ b/net/quic/core/congestion_control/cubic.cc
@@ -44,7 +44,8 @@
       num_connections_(kDefaultNumConnections),
       epoch_(QuicTime::Zero()),
       app_limited_start_time_(QuicTime::Zero()),
-      last_update_time_(QuicTime::Zero()) {
+      last_update_time_(QuicTime::Zero()),
+      fix_convex_mode_(false) {
   Reset();
 }
 
@@ -80,6 +81,7 @@
   origin_point_congestion_window_ = 0;
   time_to_origin_point_ = 0;
   last_target_congestion_window_ = 0;
+  fix_convex_mode_ = false;
 }
 
 void Cubic::OnApplicationLimited() {
@@ -88,6 +90,10 @@
   epoch_ = QuicTime::Zero();
 }
 
+void Cubic::SetFixConvexMode(bool fix_convex_mode) {
+  fix_convex_mode_ = fix_convex_mode;
+}
+
 QuicPacketCount Cubic::CongestionWindowAfterPacketLoss(
     QuicPacketCount current_congestion_window) {
   if (current_congestion_window < last_max_congestion_window_) {
@@ -139,16 +145,29 @@
   // Change the time unit from microseconds to 2^10 fractions per second. Take
   // the round trip time in account. This is done to allow us to use shift as a
   // divide operator.
-  int64_t elapsed_time =
+  const int64_t elapsed_time =
       ((current_time + delay_min - epoch_).ToMicroseconds() << 10) /
       kNumMicrosPerSecond;
+  DCHECK_GE(elapsed_time, 0);
 
   int64_t offset = time_to_origin_point_ - elapsed_time;
+  if (fix_convex_mode_) {
+    // Right-shifts of negative, signed numbers have
+    // implementation-dependent behavior.  Force the offset to be
+    // positive, similar to the kernel implementation.
+    offset = std::abs(time_to_origin_point_ - elapsed_time);
+  }
+
   QuicPacketCount delta_congestion_window =
       (kCubeCongestionWindowScale * offset * offset * offset) >> kCubeScale;
 
+  const bool add_delta = elapsed_time > time_to_origin_point_;
+  DCHECK(add_delta ||
+         (origin_point_congestion_window_ > delta_congestion_window));
   QuicPacketCount target_congestion_window =
-      origin_point_congestion_window_ - delta_congestion_window;
+      (fix_convex_mode_ && add_delta)
+          ? origin_point_congestion_window_ + delta_congestion_window
+          : origin_point_congestion_window_ - delta_congestion_window;
 
   // Limit the CWND increase to half the acked packets rounded up to the
   // nearest packet.
diff --git a/net/quic/core/congestion_control/cubic.h b/net/quic/core/congestion_control/cubic.h
index 30657e5..dba6493c 100644
--- a/net/quic/core/congestion_control/cubic.h
+++ b/net/quic/core/congestion_control/cubic.h
@@ -34,9 +34,9 @@
   QuicPacketCount CongestionWindowAfterPacketLoss(QuicPacketCount current);
 
   // Compute a new congestion window to use after a received ACK.
-  // Returns the new congestion window in packets. The new congestion window
-  // follows a cubic function that depends on the time passed since last
-  // packet loss.
+  // Returns the new congestion window in packets. The new congestion
+  // window follows a cubic function that depends on the time passed
+  // since last packet loss.
   QuicPacketCount CongestionWindowAfterAck(QuicPacketCount current,
                                            QuicTime::Delta delay_min);
 
@@ -44,6 +44,11 @@
   // window. Resets Cubic state during quiescence.
   void OnApplicationLimited();
 
+  // If true, enable the fix for the convex-mode signing bug.  See
+  // b/32170105 for more information about the bug.
+  // TODO(jokulik):  Remove once the fix is enabled by default.
+  void SetFixConvexMode(bool fix_convex_mode);
+
  private:
   static const QuicTime::Delta MaxCubicTimeInterval() {
     return QuicTime::Delta::FromMilliseconds(30);
@@ -97,6 +102,10 @@
   // Last congestion window in packets computed by cubic function.
   QuicPacketCount last_target_congestion_window_;
 
+  // Fix convex mode for cubic.
+  // TODO(jokulik):  Remove once the cubic convex experiment is done.
+  bool fix_convex_mode_;
+
   DISALLOW_COPY_AND_ASSIGN(Cubic);
 };
 
diff --git a/net/quic/core/congestion_control/cubic_bytes.cc b/net/quic/core/congestion_control/cubic_bytes.cc
index 6f62aa4..fd7a945 100644
--- a/net/quic/core/congestion_control/cubic_bytes.cc
+++ b/net/quic/core/congestion_control/cubic_bytes.cc
@@ -43,7 +43,8 @@
     : clock_(clock),
       num_connections_(kDefaultNumConnections),
       epoch_(QuicTime::Zero()),
-      last_update_time_(QuicTime::Zero()) {
+      last_update_time_(QuicTime::Zero()),
+      fix_convex_mode_(false) {
   Reset();
 }
 
@@ -77,6 +78,11 @@
   origin_point_congestion_window_ = 0;
   time_to_origin_point_ = 0;
   last_target_congestion_window_ = 0;
+  fix_convex_mode_ = false;
+}
+
+void CubicBytes::SetFixConvexMode(bool fix_convex_mode) {
+  fix_convex_mode_ = fix_convex_mode;
 }
 
 void CubicBytes::OnApplicationLimited() {
@@ -146,20 +152,36 @@
       kNumMicrosPerSecond;
 
   int64_t offset = time_to_origin_point_ - elapsed_time;
+  if (fix_convex_mode_) {
+    // Right-shifts of negative, signed numbers have
+    // implementation-dependent behavior.  In the fix, force the
+    // offset to be positive, as is done in the kernel.
+    const int64_t positive_offset =
+        std::abs(time_to_origin_point_ - elapsed_time);
+    offset = positive_offset;
+  }
   QuicByteCount delta_congestion_window =
       ((kCubeCongestionWindowScale * offset * offset * offset) >> kCubeScale) *
       kDefaultTCPMSS;
 
+  const bool add_delta = elapsed_time > time_to_origin_point_;
+  DCHECK(add_delta ||
+         (origin_point_congestion_window_ > delta_congestion_window));
   QuicByteCount target_congestion_window =
-      origin_point_congestion_window_ - delta_congestion_window;
+      (fix_convex_mode_ && add_delta)
+          ? origin_point_congestion_window_ + delta_congestion_window
+          : origin_point_congestion_window_ - delta_congestion_window;
   // Limit the CWND increase to half the acked bytes.
   target_congestion_window =
       min(target_congestion_window,
           current_congestion_window + acked_bytes_count_ / 2);
 
   DCHECK_LT(0u, estimated_tcp_congestion_window_);
-  // Increase the window by Alpha * 1 MSS of bytes every time we ack an
-  // estimated tcp window of bytes.
+  // Increase the window by approximately Alpha * 1 MSS of bytes every
+  // time we ack an estimated tcp window of bytes.  For small
+  // congestion windows (less than 25), the formula below will
+  // increase slightly slower than linearly per estimated tcp window
+  // of bytes.
   estimated_tcp_congestion_window_ += acked_bytes_count_ *
                                       (Alpha() * kDefaultTCPMSS) /
                                       estimated_tcp_congestion_window_;
diff --git a/net/quic/core/congestion_control/cubic_bytes.h b/net/quic/core/congestion_control/cubic_bytes.h
index 73998ad..784ce32 100644
--- a/net/quic/core/congestion_control/cubic_bytes.h
+++ b/net/quic/core/congestion_control/cubic_bytes.h
@@ -45,6 +45,8 @@
   // window. Resets Cubic state during quiescence.
   void OnApplicationLimited();
 
+  void SetFixConvexMode(bool fix_convex_mode);
+
  private:
   static const QuicTime::Delta MaxCubicTimeInterval() {
     return QuicTime::Delta::FromMilliseconds(30);
@@ -89,6 +91,10 @@
   // Last congestion window in packets computed by cubic function.
   QuicByteCount last_target_congestion_window_;
 
+  // Fix convex mode for cubic.
+  // TODO(jokulik):  Remove once the cubic convex experiment is done.
+  bool fix_convex_mode_;
+
   DISALLOW_COPY_AND_ASSIGN(CubicBytes);
 };
 
diff --git a/net/quic/core/congestion_control/cubic_bytes_test.cc b/net/quic/core/congestion_control/cubic_bytes_test.cc
index 85ed458..57c5ef1 100644
--- a/net/quic/core/congestion_control/cubic_bytes_test.cc
+++ b/net/quic/core/congestion_control/cubic_bytes_test.cc
@@ -18,48 +18,182 @@
 const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
                                 (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
 
-class CubicBytesTest : public ::testing::Test {
+class CubicBytesTest : public ::testing::TestWithParam<bool> {
  protected:
   CubicBytesTest()
       : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
         hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
-        cubic_(&clock_) {}
+        cubic_(&clock_),
+        fix_convex_mode_(GetParam()) {
+    cubic_.SetFixConvexMode(fix_convex_mode_);
+  }
+
+  QuicByteCount RenoCwndInBytes(QuicByteCount current_cwnd) {
+    QuicByteCount reno_estimated_cwnd =
+        current_cwnd +
+        kDefaultTCPMSS * (kNConnectionAlpha * kDefaultTCPMSS) / current_cwnd;
+    return reno_estimated_cwnd;
+  }
+
+  QuicByteCount ConservativeCwndInBytes(QuicByteCount current_cwnd) {
+    QuicByteCount conservative_cwnd = current_cwnd + kDefaultTCPMSS / 2;
+    return conservative_cwnd;
+  }
+
+  QuicByteCount CubicConvexCwndInBytes(QuicByteCount initial_cwnd,
+                                       int64_t rtt_ms,
+                                       int64_t elapsed_time_ms) {
+    const int64_t offset = ((elapsed_time_ms + rtt_ms) << 10) / 1000;
+    const QuicByteCount delta_congestion_window =
+        ((410 * offset * offset * offset) >> 40) * kDefaultTCPMSS;
+    const QuicByteCount cubic_cwnd = initial_cwnd + delta_congestion_window;
+    return cubic_cwnd;
+  }
+
   const QuicTime::Delta one_ms_;
   const QuicTime::Delta hundred_ms_;
   MockClock clock_;
   CubicBytes cubic_;
+  bool fix_convex_mode_;
 };
 
-TEST_F(CubicBytesTest, AboveOrigin) {
+INSTANTIATE_TEST_CASE_P(CubicBytesTests, CubicBytesTest, testing::Bool());
+
+// TODO(jokulik): The original "AboveOrigin" test, below, is very
+// loose.  It's nearly impossible to make the test tighter without
+// deploying the fix for convex mode.  Once cubic convex is deployed,
+// replace "AboveOrigin" with this test.
+TEST_P(CubicBytesTest, AboveOriginWithTighterBounds) {
+  if (!fix_convex_mode_) {
+    // Without convex mode fixed, the behavior of the algorithm is so
+    // far from expected, there's no point in doing a tighter test.
+    return;
+  }
+  // Convex growth.
+  const QuicTime::Delta rtt_min = hundred_ms_;
+  int64_t rtt_min_ms = rtt_min.ToMilliseconds();
+  float rtt_min_s = rtt_min_ms / 1000.0;
+  QuicByteCount current_cwnd = 10 * kDefaultTCPMSS;
+  const QuicByteCount initial_cwnd = current_cwnd;
+
+  clock_.AdvanceTime(one_ms_);
+  const QuicTime initial_time = clock_.ApproximateNow();
+  const QuicByteCount expected_first_cwnd = RenoCwndInBytes(current_cwnd);
+  current_cwnd =
+      cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
+  ASSERT_EQ(expected_first_cwnd, current_cwnd);
+
+  // Normal TCP phase.
+  // The maximum number of expected Reno RTTs is calculated by
+  // finding the point where the cubic curve and the reno curve meet.
+  const int max_reno_rtts =
+      std::sqrt(kNConnectionAlpha / (.4 * rtt_min_s * rtt_min_s * rtt_min_s)) -
+      1;
+  for (int i = 0; i < max_reno_rtts; ++i) {
+    // Alternatively, we expect it to increase by one, every time we
+    // receive current_cwnd/Alpha acks back.  (This is another way of
+    // saying we expect cwnd to increase by approximately Alpha once
+    // we receive current_cwnd number ofacks back).
+    const uint64_t num_acks_this_epoch =
+        current_cwnd / kDefaultTCPMSS / kNConnectionAlpha;
+    const QuicByteCount initial_cwnd_this_epoch = current_cwnd;
+    for (QuicPacketCount n = 0; n < num_acks_this_epoch; ++n) {
+      // Call once per ACK.
+      const QuicByteCount expected_next_cwnd = RenoCwndInBytes(current_cwnd);
+      current_cwnd = cubic_.CongestionWindowAfterAck(kDefaultTCPMSS,
+                                                     current_cwnd, rtt_min);
+      ASSERT_EQ(expected_next_cwnd, current_cwnd);
+    }
+    // Our byte-wise Reno implementation is an estimate.  We expect
+    // the cwnd to increase by approximately one MSS every
+    // cwnd/kDefaultTCPMSS/Alpha acks, but it may be off by as much as
+    // half a packet for smaller values of current_cwnd.
+    const QuicByteCount cwnd_change_this_epoch =
+        current_cwnd - initial_cwnd_this_epoch;
+    ASSERT_NEAR(kDefaultTCPMSS, cwnd_change_this_epoch, kDefaultTCPMSS / 2);
+    clock_.AdvanceTime(hundred_ms_);
+  }
+
+  // Because our byte-wise Reno under-estimates the cwnd, we switch to
+  // conservative increases for a few acks before switching to true
+  // cubic increases.
+  for (int i = 0; i < 3; ++i) {
+    const QuicByteCount next_expected_cwnd =
+        ConservativeCwndInBytes(current_cwnd);
+    current_cwnd =
+        cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
+    ASSERT_EQ(next_expected_cwnd, current_cwnd);
+  }
+
+  for (int i = 0; i < 54; ++i) {
+    const uint64_t max_acks_this_epoch = current_cwnd / kDefaultTCPMSS;
+    const int elapsed_time_ms =
+        (clock_.ApproximateNow() - initial_time).ToMilliseconds();
+    const QuicByteCount expected_cwnd = CubicConvexCwndInBytes(
+        initial_cwnd, rtt_min.ToMilliseconds(), elapsed_time_ms);
+    current_cwnd =
+        cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
+    ASSERT_EQ(expected_cwnd, current_cwnd);
+
+    for (QuicPacketCount n = 1; n < max_acks_this_epoch; ++n) {
+      // Call once per ACK.
+      ASSERT_EQ(current_cwnd, cubic_.CongestionWindowAfterAck(
+                                  kDefaultTCPMSS, current_cwnd, rtt_min));
+    }
+    clock_.AdvanceTime(hundred_ms_);
+  }
+  const int elapsed_time_ms =
+      (clock_.ApproximateNow() - initial_time).ToMilliseconds();
+  const QuicByteCount expected_cwnd = CubicConvexCwndInBytes(
+      initial_cwnd, rtt_min.ToMilliseconds(), elapsed_time_ms);
+  current_cwnd =
+      cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
+  ASSERT_EQ(expected_cwnd, current_cwnd);
+}
+
+TEST_P(CubicBytesTest, AboveOrigin) {
   // Convex growth.
   const QuicTime::Delta rtt_min = hundred_ms_;
   QuicByteCount current_cwnd = 10 * kDefaultTCPMSS;
-  QuicByteCount expected_cwnd = current_cwnd + (kDefaultTCPMSS / 2);
+  // Without the signed-integer, cubic-convex fix, we start out in the
+  // wrong mode.
+  QuicPacketCount expected_cwnd = fix_convex_mode_
+                                      ? RenoCwndInBytes(current_cwnd)
+                                      : ConservativeCwndInBytes(current_cwnd);
   // Initialize the state.
   clock_.AdvanceTime(one_ms_);
-  EXPECT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck(
+  ASSERT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck(
                                kDefaultTCPMSS, current_cwnd, rtt_min));
   current_cwnd = expected_cwnd;
+  const QuicPacketCount initial_cwnd = expected_cwnd;
   // Normal TCP phase.
   for (int i = 0; i < 48; ++i) {
     for (QuicPacketCount n = 1;
          n < current_cwnd / kDefaultTCPMSS / kNConnectionAlpha; ++n) {
       // Call once per ACK.
-      EXPECT_NEAR(current_cwnd, cubic_.CongestionWindowAfterAck(
+      ASSERT_NEAR(current_cwnd, cubic_.CongestionWindowAfterAck(
                                     kDefaultTCPMSS, current_cwnd, rtt_min),
                   kDefaultTCPMSS);
     }
     clock_.AdvanceTime(hundred_ms_);
     current_cwnd =
         cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
-    EXPECT_NEAR(expected_cwnd, current_cwnd, kDefaultTCPMSS);
-    expected_cwnd += kDefaultTCPMSS;
+    if (fix_convex_mode_) {
+      // When we fix convex mode and the uint64 arithmetic, we
+      // increase the expected_cwnd only after after the first 100ms,
+      // rather than after the initial 1ms.
+      expected_cwnd += kDefaultTCPMSS;
+      ASSERT_NEAR(expected_cwnd, current_cwnd, kDefaultTCPMSS);
+    } else {
+      ASSERT_NEAR(expected_cwnd, current_cwnd, kDefaultTCPMSS);
+      expected_cwnd += kDefaultTCPMSS;
+    }
   }
   // Cubic phase.
   for (int i = 0; i < 52; ++i) {
     for (QuicPacketCount n = 1; n < current_cwnd / kDefaultTCPMSS; ++n) {
       // Call once per ACK.
-      EXPECT_NEAR(current_cwnd, cubic_.CongestionWindowAfterAck(
+      ASSERT_NEAR(current_cwnd, cubic_.CongestionWindowAfterAck(
                                     kDefaultTCPMSS, current_cwnd, rtt_min),
                   kDefaultTCPMSS);
     }
@@ -71,14 +205,23 @@
   float elapsed_time_s = 10.0f + 0.1f;
   // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
   expected_cwnd =
-      11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024;
+      initial_cwnd / kDefaultTCPMSS +
+      (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024;
+  // Without the convex mode fix, the result is off by one.
+  if (!fix_convex_mode_) {
+    ++expected_cwnd;
+  }
   EXPECT_EQ(expected_cwnd, current_cwnd / kDefaultTCPMSS);
 }
 
-TEST_F(CubicBytesTest, LossEvents) {
+TEST_P(CubicBytesTest, LossEvents) {
   const QuicTime::Delta rtt_min = hundred_ms_;
   QuicByteCount current_cwnd = 422 * kDefaultTCPMSS;
-  QuicPacketCount expected_cwnd = current_cwnd + kDefaultTCPMSS / 2;
+  // Without the signed-integer, cubic-convex fix, we mistakenly
+  // increment cwnd after only one_ms_ and a single ack.
+  QuicPacketCount expected_cwnd = fix_convex_mode_
+                                      ? RenoCwndInBytes(current_cwnd)
+                                      : current_cwnd + kDefaultTCPMSS / 2;
   // Initialize the state.
   clock_.AdvanceTime(one_ms_);
   EXPECT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck(
@@ -91,11 +234,15 @@
             cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
 }
 
-TEST_F(CubicBytesTest, BelowOrigin) {
+TEST_P(CubicBytesTest, BelowOrigin) {
   // Concave growth.
   const QuicTime::Delta rtt_min = hundred_ms_;
   QuicByteCount current_cwnd = 422 * kDefaultTCPMSS;
-  QuicPacketCount expected_cwnd = current_cwnd + kDefaultTCPMSS / 2;
+  // Without the signed-integer, cubic-convex fix, we mistakenly
+  // increment cwnd after only one_ms_ and a single ack.
+  QuicPacketCount expected_cwnd = fix_convex_mode_
+                                      ? RenoCwndInBytes(current_cwnd)
+                                      : current_cwnd + kDefaultTCPMSS / 2;
   // Initialize the state.
   clock_.AdvanceTime(one_ms_);
   EXPECT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck(
diff --git a/net/quic/core/congestion_control/cubic_test.cc b/net/quic/core/congestion_control/cubic_test.cc
index a048f85c..578b94e 100644
--- a/net/quic/core/congestion_control/cubic_test.cc
+++ b/net/quic/core/congestion_control/cubic_test.cc
@@ -18,62 +18,97 @@
 const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
                                 (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
 
-class CubicTest : public ::testing::Test {
+// TODO(jokulik): Once we've rolled out the cubic convex fix, we will
+// no longer need a parameterized test.
+class CubicTest : public ::testing::TestWithParam<bool> {
  protected:
   CubicTest()
       : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
         hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
-        cubic_(&clock_) {}
+        cubic_(&clock_) {
+    fix_convex_mode_ = GetParam();
+    cubic_.SetFixConvexMode(fix_convex_mode_);
+  }
   const QuicTime::Delta one_ms_;
   const QuicTime::Delta hundred_ms_;
   MockClock clock_;
   Cubic cubic_;
+  bool fix_convex_mode_;
 };
 
-TEST_F(CubicTest, AboveOrigin) {
+INSTANTIATE_TEST_CASE_P(CubicTests, CubicTest, testing::Bool());
+
+TEST_P(CubicTest, AboveOrigin) {
   // Convex growth.
   const QuicTime::Delta rtt_min = hundred_ms_;
+  const float rtt_min_s = rtt_min.ToMilliseconds() / 1000.0;
   QuicPacketCount current_cwnd = 10;
-  QuicPacketCount expected_cwnd = current_cwnd + 1;
+  // Without the signed-integer, cubic-convex fix, we mistakenly
+  // increment cwnd after only one_ms_ and a single ack.
+  QuicPacketCount expected_cwnd =
+      fix_convex_mode_ ? current_cwnd : current_cwnd + 1;
   // Initialize the state.
   clock_.AdvanceTime(one_ms_);
-  EXPECT_EQ(expected_cwnd,
-            cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
-  current_cwnd = expected_cwnd;
+  const QuicTime initial_time = clock_.ApproximateNow();
+  current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+  ASSERT_EQ(expected_cwnd, current_cwnd);
+  const QuicPacketCount initial_cwnd = current_cwnd;
   // Normal TCP phase.
-  for (int i = 0; i < 48; ++i) {
-    for (QuicPacketCount n = 1; n < current_cwnd / kNConnectionAlpha; ++n) {
+  // The maximum number of expected reno RTTs can be calculated by
+  // finding the point where the cubic curve and the reno curve meet.
+  const int max_reno_rtts =
+      std::sqrt(kNConnectionAlpha / (.4 * rtt_min_s * rtt_min_s * rtt_min_s)) -
+      1;
+  for (int i = 0; i < max_reno_rtts; ++i) {
+    const QuicByteCount max_per_ack_cwnd = current_cwnd;
+    for (QuicPacketCount n = 1; n < max_per_ack_cwnd / kNConnectionAlpha; ++n) {
       // Call once per ACK.
-      EXPECT_NEAR(current_cwnd,
-                  cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min), 1);
+      const QuicByteCount next_cwnd =
+          cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+      ASSERT_EQ(current_cwnd, next_cwnd);
     }
     clock_.AdvanceTime(hundred_ms_);
     current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
-    EXPECT_NEAR(expected_cwnd, current_cwnd, 1);
-    expected_cwnd++;
+    if (fix_convex_mode_) {
+      // When we fix convex mode and the uint64 arithmetic, we
+      // increase the expected_cwnd only after after the first 100ms,
+      // rather than after the initial 1ms.
+      expected_cwnd++;
+      ASSERT_EQ(expected_cwnd, current_cwnd);
+    } else {
+      ASSERT_EQ(expected_cwnd, current_cwnd);
+      expected_cwnd++;
+    }
   }
   // Cubic phase.
   for (int i = 0; i < 52; ++i) {
     for (QuicPacketCount n = 1; n < current_cwnd; ++n) {
       // Call once per ACK.
-      EXPECT_EQ(current_cwnd,
+      ASSERT_EQ(current_cwnd,
                 cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
     }
     clock_.AdvanceTime(hundred_ms_);
     current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
   }
   // Total time elapsed so far; add min_rtt (0.1s) here as well.
-  float elapsed_time_s = 10.0f + 0.1f;
+  const float elapsed_time_ms =
+      (clock_.ApproximateNow() - initial_time).ToMilliseconds() +
+      rtt_min.ToMilliseconds();
+  const float elapsed_time_s = elapsed_time_ms / 1000.0;
   // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
   expected_cwnd =
-      11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024;
+      initial_cwnd +
+      (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024;
   EXPECT_EQ(expected_cwnd, current_cwnd);
 }
 
-TEST_F(CubicTest, LossEvents) {
+TEST_P(CubicTest, LossEvents) {
   const QuicTime::Delta rtt_min = hundred_ms_;
   QuicPacketCount current_cwnd = 422;
-  QuicPacketCount expected_cwnd = current_cwnd + 1;
+  // Without the signed-integer, cubic-convex fix, we mistakenly
+  // increment cwnd after only one_ms_ and a single ack.
+  QuicPacketCount expected_cwnd =
+      fix_convex_mode_ ? current_cwnd : current_cwnd + 1;
   // Initialize the state.
   clock_.AdvanceTime(one_ms_);
   EXPECT_EQ(expected_cwnd,
@@ -86,11 +121,14 @@
             cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
 }
 
-TEST_F(CubicTest, BelowOrigin) {
+TEST_P(CubicTest, BelowOrigin) {
   // Concave growth.
   const QuicTime::Delta rtt_min = hundred_ms_;
   QuicPacketCount current_cwnd = 422;
-  QuicPacketCount expected_cwnd = current_cwnd + 1;
+  // Without the signed-integer, cubic-convex fix, we mistakenly
+  // increment cwnd after only one_ms_ and a single ack.
+  QuicPacketCount expected_cwnd =
+      fix_convex_mode_ ? current_cwnd : current_cwnd + 1;
   // Initialize the state.
   clock_.AdvanceTime(one_ms_);
   EXPECT_EQ(expected_cwnd,
diff --git a/net/quic/core/congestion_control/tcp_cubic_sender_bytes.cc b/net/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
index f793211..0f914b2 100644
--- a/net/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
+++ b/net/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
@@ -47,6 +47,16 @@
 
 TcpCubicSenderBytes::~TcpCubicSenderBytes() {}
 
+void TcpCubicSenderBytes::SetFromConfig(const QuicConfig& config,
+                                        Perspective perspective) {
+  TcpCubicSenderBase::SetFromConfig(config, perspective);
+  if (FLAGS_quic_fix_cubic_convex_mode &&
+      config.HasReceivedConnectionOptions() &&
+      ContainsQuicTag(config.ReceivedConnectionOptions(), kCCVX)) {
+    cubic_.SetFixConvexMode(true);
+  }
+}
+
 void TcpCubicSenderBytes::SetCongestionWindowFromBandwidthAndRtt(
     QuicBandwidth bandwidth,
     QuicTime::Delta rtt) {
diff --git a/net/quic/core/congestion_control/tcp_cubic_sender_bytes.h b/net/quic/core/congestion_control/tcp_cubic_sender_bytes.h
index 1359b64..5711ccd 100644
--- a/net/quic/core/congestion_control/tcp_cubic_sender_bytes.h
+++ b/net/quic/core/congestion_control/tcp_cubic_sender_bytes.h
@@ -39,6 +39,8 @@
   ~TcpCubicSenderBytes() override;
 
   // Start implementation of SendAlgorithmInterface.
+  void SetFromConfig(const QuicConfig& config,
+                     Perspective perspective) override;
   void SetNumEmulatedConnections(int num_connections) override;
   void OnConnectionMigration() override;
   QuicByteCount GetCongestionWindow() const override;
diff --git a/net/quic/core/congestion_control/tcp_cubic_sender_packets.cc b/net/quic/core/congestion_control/tcp_cubic_sender_packets.cc
index 0a5d265..2a54db6b 100644
--- a/net/quic/core/congestion_control/tcp_cubic_sender_packets.cc
+++ b/net/quic/core/congestion_control/tcp_cubic_sender_packets.cc
@@ -46,6 +46,16 @@
 
 TcpCubicSenderPackets::~TcpCubicSenderPackets() {}
 
+void TcpCubicSenderPackets::SetFromConfig(const QuicConfig& config,
+                                          Perspective perspective) {
+  TcpCubicSenderBase::SetFromConfig(config, perspective);
+  if (FLAGS_quic_fix_cubic_convex_mode &&
+      config.HasReceivedConnectionOptions() &&
+      ContainsQuicTag(config.ReceivedConnectionOptions(), kCCVX)) {
+    cubic_.SetFixConvexMode(true);
+  }
+}
+
 void TcpCubicSenderPackets::SetCongestionWindowFromBandwidthAndRtt(
     QuicBandwidth bandwidth,
     QuicTime::Delta rtt) {
diff --git a/net/quic/core/congestion_control/tcp_cubic_sender_packets.h b/net/quic/core/congestion_control/tcp_cubic_sender_packets.h
index 8f2286de..5e3c7c2 100644
--- a/net/quic/core/congestion_control/tcp_cubic_sender_packets.h
+++ b/net/quic/core/congestion_control/tcp_cubic_sender_packets.h
@@ -41,6 +41,8 @@
   ~TcpCubicSenderPackets() override;
 
   // Start implementation of SendAlgorithmInterface.
+  void SetFromConfig(const QuicConfig& config,
+                     Perspective perspective) override;
   void SetNumEmulatedConnections(int num_connections) override;
   void OnConnectionMigration() override;
   QuicByteCount GetCongestionWindow() const override;
diff --git a/net/quic/core/crypto/crypto_protocol.h b/net/quic/core/crypto/crypto_protocol.h
index 432b9f5..9772f171 100644
--- a/net/quic/core/crypto/crypto_protocol.h
+++ b/net/quic/core/crypto/crypto_protocol.h
@@ -107,6 +107,7 @@
                                                  // Retransmissions.
 const QuicTag kLFAK = TAG('L', 'F', 'A', 'K');   // Don't invoke FACK on the
                                                  // first ack.
+const QuicTag kCCVX = TAG('C', 'C', 'V', 'X');   // Fix Cubic convex bug.
 
 // Optional support of truncated Connection IDs.  If sent by a peer, the value
 // is the minimum number of bytes allowed for the connection ID sent to the
diff --git a/net/quic/core/crypto/crypto_server_test.cc b/net/quic/core/crypto/crypto_server_test.cc
index 535dfa0..11838fe4 100644
--- a/net/quic/core/crypto/crypto_server_test.cc
+++ b/net/quic/core/crypto/crypto_server_test.cc
@@ -121,7 +121,6 @@
     client_version_string_ =
         QuicTagToString(QuicVersionToQuicTag(client_version_));
 
-    FLAGS_quic_require_handshake_confirmation_pre33 = false;
     FLAGS_enable_quic_stateless_reject_support =
         GetParam().enable_stateless_rejects;
     use_stateless_rejects_ = GetParam().use_stateless_rejects;
diff --git a/net/quic/core/crypto/quic_crypto_server_config.cc b/net/quic/core/crypto/quic_crypto_server_config.cc
index a3f5e2b..d7db1fa 100644
--- a/net/quic/core/crypto/quic_crypto_server_config.cc
+++ b/net/quic/core/crypto/quic_crypto_server_config.cc
@@ -461,8 +461,8 @@
   if (result->error_code == QUIC_NO_ERROR) {
     // QUIC requires a new proof for each CHLO so clear any existing proof.
     signed_config->chain = nullptr;
-    signed_config->signature = "";
-    signed_config->cert_sct = "";
+    signed_config->proof.signature = "";
+    signed_config->proof.leaf_cert_scts = "";
     EvaluateClientHello(server_ip, version, requested_config, primary_config,
                         signed_config, result, std::move(done_cb));
   } else {
@@ -554,9 +554,7 @@
            std::unique_ptr<ProofSource::Details> details) override {
     if (ok) {
       signed_config_->chain = chain;
-      signed_config_->signature = proof.signature;
-      signed_config_->cert_sct = proof.leaf_cert_scts;
-      signed_config_->send_expect_ct_header = proof.send_expect_ct_header;
+      signed_config_->proof = proof;
     }
     config_->ProcessClientHelloAfterGetProof(
         !ok, std::move(details), *validate_chlo_result_, reject_only_,
@@ -702,8 +700,7 @@
       helper.Fail(QUIC_HANDSHAKE_FAILED, "Missing or invalid crypto proof.");
       return;
     }
-    signed_config->signature = proof.signature;
-    signed_config->cert_sct = proof.leaf_cert_scts;
+    signed_config->proof = proof;
   }
 
   helper.DetachCallback();
@@ -1148,9 +1145,7 @@
            std::unique_ptr<ProofSource::Details> details) override {
     if (ok) {
       signed_config_->chain = chain;
-      signed_config_->signature = proof.signature;
-      signed_config_->cert_sct = proof.leaf_cert_scts;
-      signed_config_->send_expect_ct_header = proof.send_expect_ct_header;
+      signed_config_->proof = proof;
     }
     config_.EvaluateClientHelloAfterGetProof(
         found_error_, server_ip_, version_, requested_config_, primary_config_,
@@ -1278,8 +1273,7 @@
     if (proof_source_->GetProof(
             server_ip, info->sni.as_string(), serialized_config, version,
             chlo_hash, connection_options, &signed_config->chain, &proof)) {
-      signed_config->signature = proof.signature;
-      signed_config->cert_sct = proof.leaf_cert_scts;
+      signed_config->proof = proof;
     } else {
       get_proof_failed = true;
     }
@@ -1589,22 +1583,23 @@
                 "overhead calculation may underflow");
   bool should_return_sct =
       params->sct_supported_by_client && enable_serving_sct_;
-  const size_t sct_size = should_return_sct ? signed_config.cert_sct.size() : 0;
+  const string& cert_sct = signed_config.proof.leaf_cert_scts;
+  const size_t sct_size = should_return_sct ? cert_sct.size() : 0;
   const size_t total_size =
-      signed_config.signature.size() + compressed.size() + sct_size;
+      signed_config.proof.signature.size() + compressed.size() + sct_size;
   if (info.valid_source_address_token || total_size < max_unverified_size) {
     out->SetStringPiece(kCertificateTag, compressed);
-    out->SetStringPiece(kPROF, signed_config.signature);
+    out->SetStringPiece(kPROF, signed_config.proof.signature);
     if (should_return_sct) {
-      if (signed_config.cert_sct.empty()) {
+      if (cert_sct.empty()) {
         DLOG(WARNING) << "SCT is expected but it is empty.";
       } else {
-        out->SetStringPiece(kCertificateSCTTag, signed_config.cert_sct);
+        out->SetStringPiece(kCertificateSCTTag, cert_sct);
       }
     }
   } else {
     DLOG(WARNING) << "Sending inchoate REJ for hostname: " << info.sni
-                  << " signature: " << signed_config.signature.size()
+                  << " signature: " << signed_config.proof.signature.size()
                   << " cert: " << compressed.size() << " sct:" << sct_size
                   << " total: " << total_size
                   << " max: " << max_unverified_size;
@@ -2013,8 +2008,7 @@
 QuicCryptoServerConfig::Config::~Config() {
 }
 
-QuicSignedServerConfig::QuicSignedServerConfig()
-    : send_expect_ct_header(false) {}
+QuicSignedServerConfig::QuicSignedServerConfig() {}
 QuicSignedServerConfig::~QuicSignedServerConfig() {}
 
 }  // namespace net
diff --git a/net/quic/core/crypto/quic_crypto_server_config.h b/net/quic/core/crypto/quic_crypto_server_config.h
index 24b476c..444102e7 100644
--- a/net/quic/core/crypto/quic_crypto_server_config.h
+++ b/net/quic/core/crypto/quic_crypto_server_config.h
@@ -26,6 +26,7 @@
 #include "net/quic/core/crypto/crypto_secret_boxer.h"
 #include "net/quic/core/crypto/proof_source.h"
 #include "net/quic/core/crypto/quic_compressed_certs_cache.h"
+#include "net/quic/core/crypto/quic_crypto_proof.h"
 #include "net/quic/core/proto/cached_network_parameters.pb.h"
 #include "net/quic/core/proto/source_address_token.pb.h"
 #include "net/quic/core/quic_time.h"
@@ -788,22 +789,12 @@
     : public base::RefCounted<QuicSignedServerConfig> {
   QuicSignedServerConfig();
 
-  // TODO(eranm): Have a QuicCryptoProof field instead of signature, cert_sct.
-  std::string signature;
+  QuicCryptoProof proof;
   scoped_refptr<ProofSource::Chain> chain;
-  std::string cert_sct;
   // The server config that is used for this proof (and the rest of the
   // request).
   scoped_refptr<QuicCryptoServerConfig::Config> config;
   std::string primary_scid;
-  // Indication whether the Expect-CT header should be sent on the session
-  // this proof relates to (for background, see
-  // https://www.ietf.org/id/draft-stark-expect-ct-00.txt).
-  // NOTE: This field is intentionally independent from the |cert_sct| one
-  // and can be true even if |cert_sct| is empty.
-  // The goal of the Expect-CT header is uncover cases where valid SCTs are
-  // expected to be served, but aren't.
-  bool send_expect_ct_header;
 
  private:
   friend class base::RefCounted<QuicSignedServerConfig>;
diff --git a/net/quic/core/quic_client_promised_info.cc b/net/quic/core/quic_client_promised_info.cc
index 1648a2bc..186b192c 100644
--- a/net/quic/core/quic_client_promised_info.cc
+++ b/net/quic/core/quic_client_promised_info.cc
@@ -26,11 +26,7 @@
 void QuicClientPromisedInfo::CleanupAlarm::OnAlarm() {
   DVLOG(1) << "self GC alarm for stream " << promised_->id_;
   promised_->session()->OnPushStreamTimedOut(promised_->id_);
-  if (FLAGS_quic_send_push_stream_timed_out_error) {
-    promised_->Reset(QUIC_PUSH_STREAM_TIMED_OUT);
-  } else {
-    promised_->Reset(QUIC_STREAM_CANCELLED);
-  }
+  promised_->Reset(QUIC_PUSH_STREAM_TIMED_OUT);
 }
 
 void QuicClientPromisedInfo::Init() {
diff --git a/net/quic/core/quic_client_promised_info_test.cc b/net/quic/core/quic_client_promised_info_test.cc
index 2b90272..c26823a 100644
--- a/net/quic/core/quic_client_promised_info_test.cc
+++ b/net/quic/core/quic_client_promised_info_test.cc
@@ -124,13 +124,8 @@
   ASSERT_NE(promised, nullptr);
 
   // Fire the alarm that will cancel the promised stream.
-  if (FLAGS_quic_send_push_stream_timed_out_error) {
-    EXPECT_CALL(*connection_,
-                SendRstStream(promise_id_, QUIC_PUSH_STREAM_TIMED_OUT, 0));
-  } else {
-    EXPECT_CALL(*connection_,
-                SendRstStream(promise_id_, QUIC_STREAM_CANCELLED, 0));
-  }
+  EXPECT_CALL(*connection_,
+              SendRstStream(promise_id_, QUIC_PUSH_STREAM_TIMED_OUT, 0));
   alarm_factory_.FireAlarm(QuicClientPromisedInfoPeer::GetAlarm(promised));
 
   // Verify that the promise is gone after the alarm fires.
diff --git a/net/quic/core/quic_client_session_base.cc b/net/quic/core/quic_client_session_base.cc
index ff61a9d..2abe509 100644
--- a/net/quic/core/quic_client_session_base.cc
+++ b/net/quic/core/quic_client_session_base.cc
@@ -166,9 +166,6 @@
   SendRstStream(id, error_code, 0);
   if (!IsOpenStream(id)) {
     MaybeIncreaseLargestPeerStreamId(id);
-    if (!FLAGS_quic_bugfix_reset_promised) {
-      InsertLocallyClosedStreamsHighestOffset(id, 0);
-    }
   }
 }
 
diff --git a/net/quic/core/quic_connection.cc b/net/quic/core/quic_connection.cc
index 61b14b8f..d2a468b 100644
--- a/net/quic/core/quic_connection.cc
+++ b/net/quic/core/quic_connection.cc
@@ -2007,6 +2007,8 @@
 }
 
 void QuicConnection::CancelAllAlarms() {
+  DVLOG(1) << "Cancelling all QuicConnection alarms.";
+
   ack_alarm_->Cancel();
   ping_alarm_->Cancel();
   resume_writes_alarm_->Cancel();
diff --git a/net/quic/core/quic_connection_test.cc b/net/quic/core/quic_connection_test.cc
index 0c58039..da606e6e 100644
--- a/net/quic/core/quic_connection_test.cc
+++ b/net/quic/core/quic_connection_test.cc
@@ -44,7 +44,6 @@
 using std::vector;
 using testing::AnyNumber;
 using testing::AtLeast;
-using testing::Contains;
 using testing::DoAll;
 using testing::InSequence;
 using testing::InvokeWithoutArgs;
diff --git a/net/quic/core/quic_crypto_server_stream.h b/net/quic/core/quic_crypto_server_stream.h
index 1a08745e..27735755 100644
--- a/net/quic/core/quic_crypto_server_stream.h
+++ b/net/quic/core/quic_crypto_server_stream.h
@@ -129,7 +129,7 @@
   // However, it is exposed here because that is the only place where the
   // configuration for the certificate used in the connection is accessible.
   bool ShouldSendExpectCTHeader() const {
-    return signed_config_->send_expect_ct_header;
+    return signed_config_->proof.send_expect_ct_header;
   }
 
  protected:
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h
index 785f0b32..baaf6f7 100644
--- a/net/quic/core/quic_flags_list.h
+++ b/net/quic/core/quic_flags_list.h
@@ -61,10 +61,6 @@
 // If true, use async codepaths to invoke ProofSource::GetProof.
 QUIC_FLAG(bool, FLAGS_enable_async_get_proof, false)
 
-// If true, requires handshake confirmations for all QUIC handshakes with
-// versions less than 33.
-QUIC_FLAG(bool, FLAGS_quic_require_handshake_confirmation_pre33, false)
-
 // If true, only open limited number of quic sessions per epoll event. Leave the
 // rest to next event.
 QUIC_FLAG(bool, FLAGS_quic_limit_num_new_sessions_per_epoll_loop, true)
@@ -115,14 +111,6 @@
 // If true, rejected packet number is removed from public reset packet.
 QUIC_FLAG(bool, FLAGS_quic_remove_packet_number_from_public_reset, false)
 
-// If true, will send QUIC_PUSH_STREAM_TIMED_OUT when push stream is unclaimed
-// and times out.
-QUIC_FLAG(bool, FLAGS_quic_send_push_stream_timed_out_error, true)
-
-// If true, enable bugfix for FHOL experiment (fin-only
-// WritevStreamData).
-QUIC_FLAG(bool, FLAGS_quic_bugfix_fhol_writev_fin_only_v2, true)
-
 // If true, v33 QUIC client uses 1 bit to specify 8-byte connection id in
 // public flag.
 QUIC_FLAG(bool, FLAGS_quic_remove_v33_hacks2, false)
@@ -138,10 +126,6 @@
 // Cubic in packets.
 QUIC_FLAG(bool, FLAGS_quic_default_enable_cubic_bytes, true)
 
-// If enabled, fix double call of
-// InsertLocallyClosedStreamsHighestOffset in ResetPromised.
-QUIC_FLAG(bool, FLAGS_quic_bugfix_reset_promised, true)
-
 // Set the retransmission alarm only when there are unacked
 // retransmittable packets.
 QUIC_FLAG(bool, FLAGS_quic_more_conservative_retransmission_alarm, true)
@@ -160,3 +144,10 @@
 // Add a new client connection options field to QuicOptions which is only used
 // to configure client side features, such as congestion control.
 QUIC_FLAG(bool, FLAGS_quic_client_connection_options, true)
+
+// If true, fix some casts that were causing off-by-one errors in QUIC's cubic
+// "convex" increases.
+QUIC_FLAG(bool, FLAGS_quic_fix_cubic_convex_mode, false)
+
+// Ensure that BBR startup pacing rate does not drop below the initial one.
+QUIC_FLAG(bool, FLAGS_quic_bbr_faster_startup, false)
diff --git a/net/quic/core/quic_headers_stream.cc b/net/quic/core/quic_headers_stream.cc
index 53ca278..383d44f 100644
--- a/net/quic/core/quic_headers_stream.cc
+++ b/net/quic/core/quic_headers_stream.cc
@@ -400,90 +400,46 @@
   QuicConsumedData result(0, false);
   size_t total_length = iov.total_length;
 
-  if (!FLAGS_quic_bugfix_fhol_writev_fin_only_v2) {
-    // Encapsulate the data into HTTP/2 DATA frames.  The outer loop
-    // handles each element of the source iov, the inner loop handles
-    // the possibility of fragmenting eacho of those into multiple DATA
-    // frames, as the DATA frames have a max size of 16KB.
-    for (int i = 0; i < iov.iov_count; i++) {
-      size_t offset = 0;
-      const struct iovec* src_iov = &iov.iov[i];
-      do {
-        size_t len = std::min(std::min(src_iov->iov_len - offset, max_len),
-                              total_length);
-        char* data = static_cast<char*>(src_iov->iov_base) + offset;
-        SpdyDataIR spdy_data(id, StringPiece(data, len));
-        offset += len;
-        // fin handling, only set it for the final HTTP/2 DATA frame.
-        bool last_iov = i == iov.iov_count - 1;
-        bool last_fragment_within_iov = offset >= src_iov->iov_len;
-        bool frame_fin = (last_iov && last_fragment_within_iov) ? fin : false;
-        spdy_data.set_fin(frame_fin);
-        if (frame_fin) {
-          result.fin_consumed = true;
-        }
-        SpdySerializedFrame frame(spdy_framer_.SerializeFrame(spdy_data));
-        DVLOG(1) << "Encapsulating in DATA frame for stream " << id << " len "
-                 << len << " fin " << spdy_data.fin() << " remaining "
-                 << src_iov->iov_len - offset;
+  if (total_length == 0 && fin) {
+    WriteDataFrame(id, StringPiece(), true, ack_notifier_delegate);
+    result.fin_consumed = true;
+    return result;
+  }
 
-        scoped_refptr<ForceHolAckListener> ack_listener;
-        if (ack_notifier_delegate != nullptr) {
-          ack_listener = new ForceHolAckListener(ack_notifier_delegate,
-                                                 frame.size() - len);
-        }
-
-        WriteOrBufferData(StringPiece(frame.data(), frame.size()), false,
-                          ack_listener.get());
-        result.bytes_consumed += len;
-        total_length -= len;
-        if (total_length <= 0) {
-          return result;
-        }
-      } while (offset < src_iov->iov_len);
-    }
-  } else {
-    if (total_length == 0 && fin) {
-      WriteDataFrame(id, StringPiece(), true, ack_notifier_delegate);
-      result.fin_consumed = true;
-      return result;
-    }
-
-    // Encapsulate the data into HTTP/2 DATA frames.  The outer loop
-    // handles each element of the source iov, the inner loop handles
-    // the possibility of fragmenting each of those into multiple DATA
-    // frames, as the DATA frames have a max size of 16KB.
-    for (int i = 0; i < iov.iov_count; i++) {
-      size_t src_iov_offset = 0;
-      const struct iovec* src_iov = &iov.iov[i];
-      do {
-        if (queued_data_bytes() > 0) {
-          // Limit the amount of buffering to the minimum needed to
-          // preserve framing.
-          return result;
-        }
-        size_t len = std::min(
-            std::min(src_iov->iov_len - src_iov_offset, max_len), total_length);
-        char* data = static_cast<char*>(src_iov->iov_base) + src_iov_offset;
-        src_iov_offset += len;
-        offset += len;
-        // fin handling, only set it for the final HTTP/2 DATA frame.
-        bool last_iov = i == iov.iov_count - 1;
-        bool last_fragment_within_iov = src_iov_offset >= src_iov->iov_len;
-        bool frame_fin = (last_iov && last_fragment_within_iov) ? fin : false;
-        WriteDataFrame(id, StringPiece(data, len), frame_fin,
-                       ack_notifier_delegate);
-        result.bytes_consumed += len;
-        if (frame_fin) {
-          result.fin_consumed = true;
-        }
-        DCHECK_GE(total_length, len);
-        total_length -= len;
-        if (total_length <= 0) {
-          return result;
-        }
-      } while (src_iov_offset < src_iov->iov_len);
-    }
+  // Encapsulate the data into HTTP/2 DATA frames.  The outer loop
+  // handles each element of the source iov, the inner loop handles
+  // the possibility of fragmenting each of those into multiple DATA
+  // frames, as the DATA frames have a max size of 16KB.
+  for (int i = 0; i < iov.iov_count; i++) {
+    size_t src_iov_offset = 0;
+    const struct iovec* src_iov = &iov.iov[i];
+    do {
+      if (queued_data_bytes() > 0) {
+        // Limit the amount of buffering to the minimum needed to
+        // preserve framing.
+        return result;
+      }
+      size_t len = std::min(
+          std::min(src_iov->iov_len - src_iov_offset, max_len), total_length);
+      char* data = static_cast<char*>(src_iov->iov_base) + src_iov_offset;
+      src_iov_offset += len;
+      offset += len;
+      // fin handling, only set it for the final HTTP/2 DATA frame.
+      bool last_iov = i == iov.iov_count - 1;
+      bool last_fragment_within_iov = src_iov_offset >= src_iov->iov_len;
+      bool frame_fin = (last_iov && last_fragment_within_iov) ? fin : false;
+      WriteDataFrame(id, StringPiece(data, len), frame_fin,
+                     ack_notifier_delegate);
+      result.bytes_consumed += len;
+      if (frame_fin) {
+        result.fin_consumed = true;
+      }
+      DCHECK_GE(total_length, len);
+      total_length -= len;
+      if (total_length <= 0) {
+        return result;
+      }
+    } while (src_iov_offset < src_iov->iov_len);
   }
 
   return result;
diff --git a/net/quic/core/quic_headers_stream_test.cc b/net/quic/core/quic_headers_stream_test.cc
index 667715a..4b27ee1 100644
--- a/net/quic/core/quic_headers_stream_test.cc
+++ b/net/quic/core/quic_headers_stream_test.cc
@@ -995,7 +995,6 @@
 }
 
 TEST_P(QuicHeadersStreamTest, WritevStreamDataFinOnly) {
-  FLAGS_quic_bugfix_fhol_writev_fin_only_v2 = true;
   struct iovec iov;
   string data;
 
@@ -1012,7 +1011,6 @@
 }
 
 TEST_P(QuicHeadersStreamTest, WritevStreamDataSendBlocked) {
-  FLAGS_quic_bugfix_fhol_writev_fin_only_v2 = true;
   QuicStreamId id = kClientDataStreamId1;
   QuicStreamOffset offset = 0;
   struct iovec iov;
diff --git a/net/quic/core/quic_spdy_session.cc b/net/quic/core/quic_spdy_session.cc
index b2bb42f..99af124 100644
--- a/net/quic/core/quic_spdy_session.cc
+++ b/net/quic/core/quic_spdy_session.cc
@@ -121,24 +121,15 @@
   if (FLAGS_quic_enable_force_hol_blocking && version > QUIC_VERSION_35 &&
       config()->ForceHolBlocking(perspective())) {
     force_hol_blocking_ = true;
-    if (!FLAGS_quic_bugfix_fhol_writev_fin_only_v2) {
-      // Autotuning makes sure that the headers stream flow control does
-      // not get in the way, and normal stream and connection level flow
-      // control are active anyway. This is really only for the client
-      // side (and mainly there just in tests and toys), where
-      // autotuning and/or large buffers are not enabled by default.
-      headers_stream_->flow_controller()->set_auto_tune_receive_window(true);
-    } else {
-      // Since all streams are tunneled through the headers stream, it
-      // is important that headers stream never flow control blocks.
-      // Otherwise, busy-loop behaviour can ensue where data streams
-      // data try repeatedly to write data not realizing that the
-      // tunnel through the headers stream is blocked.
-      headers_stream_->flow_controller()->UpdateReceiveWindowSize(
-          kStreamReceiveWindowLimit);
-      headers_stream_->flow_controller()->UpdateSendWindowOffset(
-          kStreamReceiveWindowLimit);
-    }
+    // Since all streams are tunneled through the headers stream, it
+    // is important that headers stream never flow control blocks.
+    // Otherwise, busy-loop behaviour can ensue where data streams
+    // data try repeatedly to write data not realizing that the
+    // tunnel through the headers stream is blocked.
+    headers_stream_->flow_controller()->UpdateReceiveWindowSize(
+        kStreamReceiveWindowLimit);
+    headers_stream_->flow_controller()->UpdateSendWindowOffset(
+        kStreamReceiveWindowLimit);
   }
 
   if (version > QUIC_VERSION_34) {
diff --git a/net/quic/core/quic_spdy_stream.cc b/net/quic/core/quic_spdy_stream.cc
index bb8b172..278cbba 100644
--- a/net/quic/core/quic_spdy_stream.cc
+++ b/net/quic/core/quic_spdy_stream.cc
@@ -47,18 +47,6 @@
   }
 }
 
-void QuicSpdyStream::CloseWriteSide() {
-  if (!fin_received() && !rst_received() && sequencer()->ignore_read_data() &&
-      !rst_sent()) {
-    DCHECK(fin_sent());
-    // Tell the peer to stop sending further data.
-    DVLOG(1) << ENDPOINT << "Send QUIC_STREAM_NO_ERROR on stream " << id();
-    Reset(QUIC_STREAM_NO_ERROR);
-  }
-
-  QuicStream::CloseWriteSide();
-}
-
 void QuicSpdyStream::StopReading() {
   if (!fin_received() && !rst_received() && write_side_closed() &&
       !rst_sent()) {
diff --git a/net/quic/core/quic_spdy_stream.h b/net/quic/core/quic_spdy_stream.h
index 19bcddc..97374e53 100644
--- a/net/quic/core/quic_spdy_stream.h
+++ b/net/quic/core/quic_spdy_stream.h
@@ -67,9 +67,6 @@
   QuicSpdyStream(QuicStreamId id, QuicSpdySession* spdy_session);
   ~QuicSpdyStream() override;
 
-  // Override the base class to send QUIC_STREAM_NO_ERROR to the peer
-  // when the stream has not received all the data.
-  void CloseWriteSide() override;
   void StopReading() override;
 
   // QuicStream implementation
@@ -183,6 +180,8 @@
 
   bool allow_bidirectional_data() const { return allow_bidirectional_data_; }
 
+  using QuicStream::CloseWriteSide;
+
  protected:
   virtual void OnInitialHeadersComplete(bool fin,
                                         size_t frame_len,
diff --git a/net/quic/test_tools/simulator/packet_filter.cc b/net/quic/test_tools/simulator/packet_filter.cc
new file mode 100644
index 0000000..a988d9b
--- /dev/null
+++ b/net/quic/test_tools/simulator/packet_filter.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/simulator/packet_filter.h"
+
+namespace net {
+namespace simulator {
+
+PacketFilter::PacketFilter(Simulator* simulator,
+                           std::string name,
+                           Endpoint* input)
+    : Endpoint(simulator, name), input_(input) {
+  input_->SetTxPort(this);
+}
+
+PacketFilter::~PacketFilter() {}
+
+void PacketFilter::AcceptPacket(std::unique_ptr<Packet> packet) {
+  if (FilterPacket(*packet)) {
+    output_tx_port_->AcceptPacket(std::move(packet));
+  }
+}
+
+QuicTime::Delta PacketFilter::TimeUntilAvailable() {
+  return output_tx_port_->TimeUntilAvailable();
+}
+
+void PacketFilter::Act() {}
+
+UnconstrainedPortInterface* PacketFilter::GetRxPort() {
+  return input_->GetRxPort();
+}
+
+void PacketFilter::SetTxPort(ConstrainedPortInterface* port) {
+  output_tx_port_ = port;
+}
+
+}  // namespace simulator
+}  // namespace net
diff --git a/net/quic/test_tools/simulator/packet_filter.h b/net/quic/test_tools/simulator/packet_filter.h
new file mode 100644
index 0000000..f8caa2f0
--- /dev/null
+++ b/net/quic/test_tools/simulator/packet_filter.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
+#define NET_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
+
+#include "net/quic/test_tools/simulator/port.h"
+
+namespace net {
+namespace simulator {
+
+// Packet filter allows subclasses to filter out the packets that enter the
+// input port and exit the output port.  Packets in the other direction are
+// always passed through.
+//
+// The filter wraps around the input endpoint, and exposes the resulting
+// filtered endpoint via the output() method.  For example, if initially there
+// are two endpoints, A and B, connected via a symmetric link:
+//
+//   QuicEndpoint endpoint_a;
+//   QuicEndpoint endpoint_b;
+//
+//   [...]
+//
+//   SymmetricLink a_b_link(&endpoint_a, &endpoint_b, ...);
+//
+// and the goal is to filter the traffic from A to B, then the new invocation
+// would be as follows:
+//
+//   PacketFilter filter(&simulator, "A-to-B packet filter", endpoint_a);
+//   SymmetricLink a_b_link(&filter, &endpoint_b, ...);
+//
+// Note that the filter drops the packet instanteneously, without it ever
+// reaching the output wire.  This means that in a direct endpoint-to-endpoint
+// scenario, whenever the packet is dropped, the link would become immediately
+// available for the next packet.
+class PacketFilter : public Endpoint, public ConstrainedPortInterface {
+ public:
+  // Initialize the filter by wrapping around |input|.  Does not take the
+  // ownership of |input|.
+  PacketFilter(Simulator* simulator, std::string name, Endpoint* input);
+  ~PacketFilter() override;
+
+  Endpoint* input() { return input_; }
+
+  // Implementation of ConstrainedPortInterface.
+  void AcceptPacket(std::unique_ptr<Packet> packet) override;
+  QuicTime::Delta TimeUntilAvailable() override;
+
+  // Implementation of Endpoint interface methods.
+  UnconstrainedPortInterface* GetRxPort() override;
+  void SetTxPort(ConstrainedPortInterface* port) override;
+
+  // Implementation of Actor interface methods.
+  void Act() override;
+
+ protected:
+  // Returns true if the packet should be passed through, and false if it should
+  // be dropped.  The function is called once per packet, in the order that the
+  // packets arrive, so it is safe for the function to alter the internal state
+  // of the filter.
+  virtual bool FilterPacket(const Packet& packet) = 0;
+
+ private:
+  // The port onto which the filtered packets are egressed.
+  ConstrainedPortInterface* output_tx_port_;
+
+  // The original network endpoint wrapped by the class.
+  Endpoint* input_;
+
+  DISALLOW_COPY_AND_ASSIGN(PacketFilter);
+};
+
+}  // namespace simulator
+}  // namespace net
+#endif  // NET_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
diff --git a/net/quic/test_tools/simulator/simulator_test.cc b/net/quic/test_tools/simulator/simulator_test.cc
index 4c85bead..07f19117 100644
--- a/net/quic/test_tools/simulator/simulator_test.cc
+++ b/net/quic/test_tools/simulator/simulator_test.cc
@@ -8,6 +8,7 @@
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "net/quic/test_tools/simulator/alarm_factory.h"
 #include "net/quic/test_tools/simulator/link.h"
+#include "net/quic/test_tools/simulator/packet_filter.h"
 #include "net/quic/test_tools/simulator/queue.h"
 #include "net/quic/test_tools/simulator/switch.h"
 #include "net/quic/test_tools/simulator/traffic_policer.h"
@@ -15,6 +16,10 @@
 #include "net/test/gtest_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
 namespace net {
 namespace simulator {
 
@@ -564,6 +569,52 @@
   EXPECT_EQ(33, counter.get_value());
 }
 
+class MockPacketFilter : public PacketFilter {
+ public:
+  MockPacketFilter(Simulator* simulator, std::string name, Endpoint* endpoint)
+      : PacketFilter(simulator, name, endpoint) {}
+  MOCK_METHOD1(FilterPacket, bool(const Packet&));
+};
+
+// Set up two trivial packet filters, one allowing any packets, and one dropping
+// all of them.
+TEST(SimulatorTest, PacketFilter) {
+  const QuicBandwidth bandwidth =
+      QuicBandwidth::FromBytesPerSecond(1024 * 1024);
+  const QuicTime::Delta base_propagation_delay =
+      QuicTime::Delta::FromMilliseconds(5);
+
+  Simulator simulator;
+  LinkSaturator saturator_a(&simulator, "Saturator A", 1000, "Saturator B");
+  LinkSaturator saturator_b(&simulator, "Saturator B", 1000, "Saturator A");
+
+  // Attach packets to the switch to create a delay between the point at which
+  // the packet is generated and the point at which it is filtered.  Note that
+  // if the saturators were connected directly, the link would be always
+  // available for the endpoint which has all of its packets dropped, resulting
+  // in saturator looping infinitely.
+  Switch network_switch(&simulator, "Switch", 8,
+                        bandwidth * base_propagation_delay * 10);
+  StrictMock<MockPacketFilter> a_to_b_filter(&simulator, "A -> B filter",
+                                             network_switch.port(1));
+  StrictMock<MockPacketFilter> b_to_a_filter(&simulator, "B -> A filter",
+                                             network_switch.port(2));
+  SymmetricLink link_a(&a_to_b_filter, &saturator_b, bandwidth,
+                       base_propagation_delay);
+  SymmetricLink link_b(&b_to_a_filter, &saturator_a, bandwidth,
+                       base_propagation_delay);
+
+  // Allow packets from A to B, but not from B to A.
+  EXPECT_CALL(a_to_b_filter, FilterPacket(_)).WillRepeatedly(Return(true));
+  EXPECT_CALL(b_to_a_filter, FilterPacket(_)).WillRepeatedly(Return(false));
+
+  // Run the simulation for a while, and expect that only B will receive any
+  // packets.
+  simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+  EXPECT_GE(saturator_b.counter()->packets(), 1u);
+  EXPECT_EQ(saturator_a.counter()->packets(), 0u);
+}
+
 // Set up a traffic policer in one direction that throttles at 25% of link
 // bandwidth, and put two link saturators at each endpoint.
 TEST(SimulatorTest, TrafficPolicer) {
@@ -587,8 +638,7 @@
 
   SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
                       base_propagation_delay);
-  SymmetricLink link2(&saturator2, policer.output(), bandwidth,
-                      base_propagation_delay);
+  SymmetricLink link2(&saturator2, &policer, bandwidth, base_propagation_delay);
 
   // Ensure the initial burst passes without being dropped at all.
   bool simulator_result = simulator.RunUntilOrTimeout(
@@ -646,8 +696,7 @@
 
   SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
                       base_propagation_delay);
-  SymmetricLink link2(&saturator2, policer.output(), bandwidth,
-                      base_propagation_delay);
+  SymmetricLink link2(&saturator2, &policer, bandwidth, base_propagation_delay);
 
   // Ensure at least one packet is sent on each side.
   bool simulator_result = simulator.RunUntilOrTimeout(
diff --git a/net/quic/test_tools/simulator/traffic_policer.cc b/net/quic/test_tools/simulator/traffic_policer.cc
index 84ea4db..93e1c8d3 100644
--- a/net/quic/test_tools/simulator/traffic_policer.cc
+++ b/net/quic/test_tools/simulator/traffic_policer.cc
@@ -15,20 +15,14 @@
                                QuicByteCount max_bucket_size,
                                QuicBandwidth target_bandwidth,
                                Endpoint* input)
-    : Actor(simulator, name),
+    : PacketFilter(simulator, name, input),
       initial_bucket_size_(initial_bucket_size),
       max_bucket_size_(max_bucket_size),
       target_bandwidth_(target_bandwidth),
-      last_refill_time_(clock_->Now()),
-      input_(input),
-      output_(this) {
-  input_->SetTxPort(this);
-}
+      last_refill_time_(clock_->Now()) {}
 
 TrafficPolicer::~TrafficPolicer() {}
 
-void TrafficPolicer::Act() {}
-
 void TrafficPolicer::Refill() {
   QuicTime::Delta time_passed = clock_->Now() - last_refill_time_;
   QuicByteCount refill_size = time_passed * target_bandwidth_;
@@ -40,47 +34,27 @@
   last_refill_time_ = clock_->Now();
 }
 
-void TrafficPolicer::AcceptPacket(std::unique_ptr<Packet> packet) {
+bool TrafficPolicer::FilterPacket(const Packet& packet) {
   // Refill existing buckets.
   Refill();
 
   // Create a new bucket if one does not exist.
-  if (token_buckets_.count(packet->destination) == 0) {
+  if (token_buckets_.count(packet.destination) == 0) {
     token_buckets_.insert(
-        std::make_pair(packet->destination, initial_bucket_size_));
+        std::make_pair(packet.destination, initial_bucket_size_));
   }
 
-  auto bucket = token_buckets_.find(packet->destination);
+  auto bucket = token_buckets_.find(packet.destination);
   DCHECK(bucket != token_buckets_.end());
 
   // Silently drop the packet on the floor if out of tokens
-  if (bucket->second < packet->size) {
-    return;
+  if (bucket->second < packet.size) {
+    return false;
   }
 
-  bucket->second -= packet->size;
-  output_tx_port_->AcceptPacket(std::move(packet));
+  bucket->second -= packet.size;
+  return true;
 }
 
-QuicTime::Delta TrafficPolicer::TimeUntilAvailable() {
-  return output_tx_port_->TimeUntilAvailable();
-}
-
-TrafficPolicer::Output::Output(TrafficPolicer* policer)
-    : Endpoint(policer->simulator(), policer->name() + " (output port)"),
-      policer_(policer) {}
-
-TrafficPolicer::Output::~Output() {}
-
-UnconstrainedPortInterface* TrafficPolicer::Output::GetRxPort() {
-  return policer_->input_->GetRxPort();
-}
-
-void TrafficPolicer::Output::SetTxPort(ConstrainedPortInterface* port) {
-  policer_->output_tx_port_ = port;
-}
-
-void TrafficPolicer::Output::Act() {}
-
 }  // namespace simulator
 }  // namespace net
diff --git a/net/quic/test_tools/simulator/traffic_policer.h b/net/quic/test_tools/simulator/traffic_policer.h
index caa5855..3a218ae0 100644
--- a/net/quic/test_tools/simulator/traffic_policer.h
+++ b/net/quic/test_tools/simulator/traffic_policer.h
@@ -7,6 +7,7 @@
 
 #include <unordered_map>
 
+#include "net/quic/test_tools/simulator/packet_filter.h"
 #include "net/quic/test_tools/simulator/port.h"
 
 namespace net {
@@ -16,8 +17,8 @@
 // passing through.  It wraps around an input port and exposes an output port.
 // Only the traffic from input to the output is policed, so in case when
 // bidirectional policing is desired, two policers have to be used.  The flows
-// are hashed by the source only.
-class TrafficPolicer : public Actor, public ConstrainedPortInterface {
+// are hashed by the destination only.
+class TrafficPolicer : public PacketFilter {
  public:
   TrafficPolicer(Simulator* simulator,
                  std::string name,
@@ -27,28 +28,10 @@
                  Endpoint* input);
   ~TrafficPolicer() override;
 
-  Endpoint* input() { return input_; }
-  Endpoint* output() { return &output_; }
-
-  void AcceptPacket(std::unique_ptr<Packet> packet) override;
-  QuicTime::Delta TimeUntilAvailable() override;
-
-  void Act() override;
+ protected:
+  bool FilterPacket(const Packet& packet) override;
 
  private:
-  class Output : public Endpoint {
-   public:
-    explicit Output(TrafficPolicer* policer);
-    ~Output() override;
-
-    UnconstrainedPortInterface* GetRxPort() override;
-    void SetTxPort(ConstrainedPortInterface* port) override;
-    void Act() override;
-
-   private:
-    TrafficPolicer* policer_;
-  };
-
   // Refill the token buckets with all the tokens that have been granted since
   // |last_refill_time_|.
   void Refill();
@@ -60,11 +43,6 @@
   // The time at which the token buckets were last refilled.
   QuicTime last_refill_time_;
 
-  ConstrainedPortInterface* output_tx_port_;
-
-  Endpoint* input_;
-  Output output_;
-
   // Maps each destination to the number of tokens it has left.
   std::unordered_map<std::string, QuicByteCount> token_buckets_;
 
diff --git a/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp b/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
index 0cf3500c..a2466559 100644
--- a/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
+++ b/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
@@ -371,7 +371,9 @@
   //   We try both variations, zero length item and empty string,
   //   without giving a user prompt when trying the different empty password
   //   flavors.
-  if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) {
+  if ((rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD ||
+       rv == net::ERR_PKCS12_IMPORT_INVALID_MAC) &&
+      password.empty()) {
     rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
                                    is_extractable, true, slot, imported_certs);
   }
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index a8f0909..06f5399 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -378,6 +378,10 @@
     // TODO(nimia): Consider setting the congestion control algorithm for the
     // client as well according to the test parameter.
     copt.push_back(GetParam().congestion_control_tag);
+    if (GetParam().congestion_control_tag == kQBIC &&
+        FLAGS_quic_fix_cubic_convex_mode) {
+      copt.push_back(kCCVX);
+    }
     if (support_server_push_) {
       copt.push_back(kSPSH);
     }
diff --git a/net/tools/quic/quic_http_response_cache_test.cc b/net/tools/quic/quic_http_response_cache_test.cc
index 4979d4c..f87f262 100644
--- a/net/tools/quic/quic_http_response_cache_test.cc
+++ b/net/tools/quic/quic_http_response_cache_test.cc
@@ -98,7 +98,7 @@
 TEST_F(QuicHttpResponseCacheTest, ReadsCacheDir) {
   cache_.InitializeFromDirectory(CacheDirectory());
   const QuicHttpResponseCache::Response* response =
-      cache_.GetResponse("quic.test.url", "/index.html");
+      cache_.GetResponse("www.example.com", "/index.html");
   ASSERT_TRUE(response);
   ASSERT_TRUE(ContainsKey(response->headers(), ":status"));
   EXPECT_EQ("200", response->headers().find(":status")->second);
@@ -109,22 +109,22 @@
 
 TEST_F(QuicHttpResponseCacheTest, ReadsCacheDirWithServerPushResource) {
   cache_.InitializeFromDirectory(CacheDirectory() + "_with_push");
-  list<ServerPushInfo> resources =
-      cache_.GetServerPushResources("quic.test.url/");
+  std::list<ServerPushInfo> resources =
+      cache_.GetServerPushResources("www.example.com/");
   ASSERT_EQ(1UL, resources.size());
 }
 
 TEST_F(QuicHttpResponseCacheTest, ReadsCacheDirWithServerPushResources) {
   cache_.InitializeFromDirectory(CacheDirectory() + "_with_push");
-  list<ServerPushInfo> resources =
-      cache_.GetServerPushResources("quic.test.url/index2.html");
+  std::list<ServerPushInfo> resources =
+      cache_.GetServerPushResources("www.example.com/index2.html");
   ASSERT_EQ(2UL, resources.size());
 }
 
 TEST_F(QuicHttpResponseCacheTest, UsesOriginalUrl) {
   cache_.InitializeFromDirectory(CacheDirectory());
   const QuicHttpResponseCache::Response* response =
-      cache_.GetResponse("quic.test.url", "/index.html");
+      cache_.GetResponse("www.example.com", "/site_map.html");
   ASSERT_TRUE(response);
   ASSERT_TRUE(ContainsKey(response->headers(), ":status"));
   EXPECT_EQ("200", response->headers().find(":status")->second);
diff --git a/net/tools/quic/quic_simple_server_stream.cc b/net/tools/quic/quic_simple_server_stream.cc
index 1d32f9c0..89f262e 100644
--- a/net/tools/quic/quic_simple_server_stream.cc
+++ b/net/tools/quic/quic_simple_server_stream.cc
@@ -30,7 +30,7 @@
     QuicStreamId id,
     QuicSpdySession* session,
     QuicHttpResponseCache* response_cache)
-    : QuicSpdyStream(id, session),
+    : QuicSpdyServerStreamBase(id, session),
       content_length_(-1),
       response_cache_(response_cache) {}
 
diff --git a/net/tools/quic/quic_simple_server_stream.h b/net/tools/quic/quic_simple_server_stream.h
index 7cb1819..2aa7b759 100644
--- a/net/tools/quic/quic_simple_server_stream.h
+++ b/net/tools/quic/quic_simple_server_stream.h
@@ -14,6 +14,7 @@
 #include "net/quic/core/quic_spdy_stream.h"
 #include "net/spdy/spdy_framer.h"
 #include "net/tools/quic/quic_http_response_cache.h"
+#include "net/tools/quic/quic_spdy_server_stream_base.h"
 
 namespace net {
 
@@ -24,7 +25,7 @@
 
 // All this does right now is aggregate data, and on fin, send an HTTP
 // response.
-class QuicSimpleServerStream : public QuicSpdyStream {
+class QuicSimpleServerStream : public QuicSpdyServerStreamBase {
  public:
   QuicSimpleServerStream(QuicStreamId id,
                          QuicSpdySession* session,
diff --git a/net/tools/quic/quic_spdy_server_stream_base.cc b/net/tools/quic/quic_spdy_server_stream_base.cc
new file mode 100644
index 0000000..4b68127
--- /dev/null
+++ b/net/tools/quic/quic_spdy_server_stream_base.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_spdy_server_stream_base.h"
+
+#include "net/quic/core/quic_error_codes.h"
+
+namespace net {
+
+QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(QuicStreamId id,
+                                                   QuicSpdySession* session)
+    : QuicSpdyStream(id, session) {}
+
+void QuicSpdyServerStreamBase::CloseWriteSide() {
+  if (!fin_received() && !rst_received() && sequencer()->ignore_read_data() &&
+      !rst_sent()) {
+    // Early cancel the stream if it has stopped reading before receiving FIN
+    // or RST.
+    DCHECK(fin_sent());
+    // Tell the peer to stop sending further data.
+    DVLOG(0) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id();
+    Reset(QUIC_STREAM_NO_ERROR);
+  }
+
+  QuicSpdyStream::CloseWriteSide();
+}
+
+}  // namespace net
diff --git a/net/tools/quic/quic_spdy_server_stream_base.h b/net/tools/quic/quic_spdy_server_stream_base.h
new file mode 100644
index 0000000..ebadee1
--- /dev/null
+++ b/net/tools/quic/quic_spdy_server_stream_base.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CORE_QUIC_SPDY_SERVER_STREAM_BASE_H_
+#define NET_QUIC_CORE_QUIC_SPDY_SERVER_STREAM_BASE_H_
+
+#include "net/quic/core/quic_spdy_stream.h"
+
+namespace net {
+
+class QuicSpdyServerStreamBase : public QuicSpdyStream {
+ public:
+  QuicSpdyServerStreamBase(QuicStreamId id, QuicSpdySession* session);
+
+  // Override the base class to send QUIC_STREAM_NO_ERROR to the peer
+  // when the stream has not received all the data.
+  void CloseWriteSide() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStreamBase);
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_CORE_QUIC_SPDY_SERVER_STREAM_BASE_H_
diff --git a/net/tools/quic/quic_spdy_server_stream_base_test.cc b/net/tools/quic/quic_spdy_server_stream_base_test.cc
new file mode 100644
index 0000000..32c9f87
--- /dev/null
+++ b/net/tools/quic/quic_spdy_server_stream_base_test.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_spdy_server_stream_base.h"
+
+#include "base/memory/ptr_util.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+class TestQuicSpdyServerStream : public QuicSpdyServerStreamBase {
+ public:
+  TestQuicSpdyServerStream(QuicStreamId id, QuicSpdySession* session)
+      : QuicSpdyServerStreamBase(id, session) {}
+
+  void OnDataAvailable() override {}
+};
+
+class QuicSpdyServerStreamBaseTest : public ::testing::Test {
+ protected:
+  QuicSpdyServerStreamBaseTest()
+      : session_(new MockQuicConnection(&helper_,
+                                        &alarm_factory_,
+                                        Perspective::IS_SERVER)) {
+    stream_ = new TestQuicSpdyServerStream(kClientDataStreamId1, &session_);
+    session_.ActivateStream(base::WrapUnique(stream_));
+  }
+
+  QuicSpdyServerStreamBase* stream_ = nullptr;
+  MockQuicConnectionHelper helper_;
+  MockAlarmFactory alarm_factory_;
+  MockQuicSpdySession session_;
+};
+
+TEST_F(QuicSpdyServerStreamBaseTest,
+       SendQuicRstStreamNoErrorWithEarlyResponse) {
+  stream_->StopReading();
+  EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1);
+  stream_->set_fin_sent(true);
+  stream_->CloseWriteSide();
+}
+
+TEST_F(QuicSpdyServerStreamBaseTest,
+       DoNotSendQuicRstStreamNoErrorWithRstReceived) {
+  EXPECT_FALSE(stream_->reading_stopped());
+
+  EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+  EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _)).Times(1);
+  QuicRstStreamFrame rst_frame(stream_->id(), QUIC_STREAM_CANCELLED, 1234);
+  stream_->OnStreamReset(rst_frame);
+
+  EXPECT_TRUE(stream_->reading_stopped());
+  EXPECT_TRUE(stream_->write_side_closed());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/services/ui/display/BUILD.gn b/services/ui/display/BUILD.gn
index 76a50667..aef68ee1 100644
--- a/services/ui/display/BUILD.gn
+++ b/services/ui/display/BUILD.gn
@@ -7,9 +7,9 @@
 
 source_set("display") {
   sources = [
-    "platform_screen.cc",
-    "platform_screen.h",
-    "platform_screen_delegate.h",
+    "screen_manager.cc",
+    "screen_manager.h",
+    "screen_manager_delegate.h",
     "viewport_metrics.cc",
     "viewport_metrics.h",
   ]
@@ -23,8 +23,8 @@
 
   if (use_ozone && is_chromeos) {
     sources += [
-      "platform_screen_ozone.cc",
-      "platform_screen_ozone.h",
+      "screen_manager_ozone.cc",
+      "screen_manager_ozone.h",
     ]
 
     deps += [
@@ -35,8 +35,8 @@
     ]
   } else {
     sources += [
-      "platform_screen_stub.cc",
-      "platform_screen_stub.h",
+      "screen_manager_stub.cc",
+      "screen_manager_stub.h",
     ]
   }
 }
@@ -44,7 +44,7 @@
 if (use_ozone && is_chromeos) {
   test("display_service_unittests") {
     sources = [
-      "platform_screen_ozone_unittests.cc",
+      "screen_manager_ozone_unittests.cc",
     ]
 
     deps = [
diff --git a/services/ui/display/platform_screen_stub.h b/services/ui/display/platform_screen_stub.h
deleted file mode 100644
index 787f497..0000000
--- a/services/ui/display/platform_screen_stub.h
+++ /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.
-
-#ifndef SERVICES_UI_DISPLAY_PLATFORM_SCREEN_STUB_H_
-#define SERVICES_UI_DISPLAY_PLATFORM_SCREEN_STUB_H_
-
-#include <stdint.h>
-
-#include "base/memory/weak_ptr.h"
-#include "services/ui/display/platform_screen.h"
-#include "services/ui/display/viewport_metrics.h"
-
-namespace display {
-
-// PlatformScreenStub provides the necessary functionality to configure a fixed
-// 1024x768 display for non-ozone platforms.
-class PlatformScreenStub : public PlatformScreen {
- public:
-  PlatformScreenStub();
-  ~PlatformScreenStub() override;
-
- private:
-  // Fake creation of a single 1024x768 display.
-  void FixedSizeScreenConfiguration();
-
-  // PlatformScreen.
-  void AddInterfaces(service_manager::InterfaceRegistry* registry) override;
-  void Init(PlatformScreenDelegate* delegate) override;
-  void RequestCloseDisplay(int64_t display_id) override;
-  int64_t GetPrimaryDisplayId() const override;
-
-  // Sample display information.
-  int64_t display_id_ = 1;
-  ViewportMetrics display_metrics_;
-
-  PlatformScreenDelegate* delegate_ = nullptr;
-
-  base::WeakPtrFactory<PlatformScreenStub> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(PlatformScreenStub);
-};
-
-}  // namespace display
-
-#endif  // SERVICES_UI_DISPLAY_PLATFORM_SCREEN_STUB_H_
diff --git a/services/ui/display/platform_screen.cc b/services/ui/display/screen_manager.cc
similarity index 64%
rename from services/ui/display/platform_screen.cc
rename to services/ui/display/screen_manager.cc
index 153331d..9fb4746 100644
--- a/services/ui/display/platform_screen.cc
+++ b/services/ui/display/screen_manager.cc
@@ -2,27 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/display/platform_screen.h"
+#include "services/ui/display/screen_manager.h"
 
 #include "base/logging.h"
 
 namespace display {
 
 // static
-PlatformScreen* PlatformScreen::instance_ = nullptr;
+ScreenManager* ScreenManager::instance_ = nullptr;
 
-PlatformScreen::PlatformScreen() {
+ScreenManager::ScreenManager() {
   DCHECK(!instance_);
   instance_ = this;
 }
 
-PlatformScreen::~PlatformScreen() {
+ScreenManager::~ScreenManager() {
   DCHECK_EQ(instance_, this);
   instance_ = nullptr;
 }
 
 // static
-PlatformScreen* PlatformScreen::GetInstance() {
+ScreenManager* ScreenManager::GetInstance() {
   DCHECK(instance_);
   return instance_;
 }
diff --git a/services/ui/display/platform_screen.h b/services/ui/display/screen_manager.h
similarity index 60%
rename from services/ui/display/platform_screen.h
rename to services/ui/display/screen_manager.h
index c266a57..3a0679cb 100644
--- a/services/ui/display/platform_screen.h
+++ b/services/ui/display/screen_manager.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_UI_DISPLAY_PLATFORM_SCREEN_H_
-#define SERVICES_UI_DISPLAY_PLATFORM_SCREEN_H_
+#ifndef SERVICES_UI_DISPLAY_SCREEN_MANAGER_H_
+#define SERVICES_UI_DISPLAY_SCREEN_MANAGER_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "services/ui/display/platform_screen_delegate.h"
+#include "services/ui/display/screen_manager_delegate.h"
 
 namespace service_manager {
 class InterfaceRegistry;
@@ -16,16 +16,16 @@
 
 namespace display {
 
-// PlatformScreen provides the necessary functionality to configure all
+// ScreenManager provides the necessary functionality to configure all
 // attached physical displays.
-class PlatformScreen {
+class ScreenManager {
  public:
-  PlatformScreen();
-  virtual ~PlatformScreen();
+  ScreenManager();
+  virtual ~ScreenManager();
 
-  // Creates a singleton PlatformScreen instance.
-  static std::unique_ptr<PlatformScreen> Create();
-  static PlatformScreen* GetInstance();
+  // Creates a singleton ScreenManager instance.
+  static std::unique_ptr<ScreenManager> Create();
+  static ScreenManager* GetInstance();
 
   // Registers Mojo interfaces provided.
   virtual void AddInterfaces(service_manager::InterfaceRegistry* registry) = 0;
@@ -35,7 +35,7 @@
   // more fake displays and pretend to configure them. A non-null |delegate|
   // must be provided that will receive notifications when displays are added,
   // removed or modified.
-  virtual void Init(PlatformScreenDelegate* delegate) = 0;
+  virtual void Init(ScreenManagerDelegate* delegate) = 0;
 
   // Handle requests from the platform to close a display.
   virtual void RequestCloseDisplay(int64_t display_id) = 0;
@@ -43,11 +43,11 @@
   virtual int64_t GetPrimaryDisplayId() const = 0;
 
  private:
-  static PlatformScreen* instance_;  // Instance is not owned.
+  static ScreenManager* instance_;  // Instance is not owned.
 
-  DISALLOW_COPY_AND_ASSIGN(PlatformScreen);
+  DISALLOW_COPY_AND_ASSIGN(ScreenManager);
 };
 
 }  // namespace display
 
-#endif  // SERVICES_UI_DISPLAY_PLATFORM_SCREEN_H_
+#endif  // SERVICES_UI_DISPLAY_SCREEN_MANAGER_H_
diff --git a/services/ui/display/platform_screen_delegate.h b/services/ui/display/screen_manager_delegate.h
similarity index 70%
rename from services/ui/display/platform_screen_delegate.h
rename to services/ui/display/screen_manager_delegate.h
index 7ad956df..c6ed387 100644
--- a/services/ui/display/platform_screen_delegate.h
+++ b/services/ui/display/screen_manager_delegate.h
@@ -2,24 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_UI_DISPLAY_PLATFORM_SCREEN_DELEGATE_H_
-#define SERVICES_UI_DISPLAY_PLATFORM_SCREEN_DELEGATE_H_
+#ifndef SERVICES_UI_DISPLAY_SCREEN_MANAGER_DELEGATE_H_
+#define SERVICES_UI_DISPLAY_SCREEN_MANAGER_DELEGATE_H_
 
 #include <stdint.h>
 
-namespace gfx {
-class Rect;
-class Size;
-}
-
 namespace display {
 
-class PlatformScreen;
 struct ViewportMetrics;
 
-// The PlatformScreenDelegate will be informed of changes to the physical
-// and/or virtual displays by PlatformScreen.
-class PlatformScreenDelegate {
+// The ScreenManagerDelegate will be informed of changes to the display or
+// screen state by ScreenManager.
+class ScreenManagerDelegate {
  public:
   // Called when a display is added. |id| is the display id of the new display
   // and |metrics| contains display viewport information.
@@ -38,9 +32,9 @@
   virtual void OnPrimaryDisplayChanged(int64_t primary_display_id) = 0;
 
  protected:
-  virtual ~PlatformScreenDelegate() {}
+  virtual ~ScreenManagerDelegate() {}
 };
 
 }  // namespace display
 
-#endif  // SERVICES_UI_DISPLAY_PLATFORM_SCREEN_DELEGATE_H_
+#endif  // SERVICES_UI_DISPLAY_SCREEN_MANAGER_DELEGATE_H_
diff --git a/services/ui/display/platform_screen_ozone.cc b/services/ui/display/screen_manager_ozone.cc
similarity index 83%
rename from services/ui/display/platform_screen_ozone.cc
rename to services/ui/display/screen_manager_ozone.cc
index 6ffb7aa..fc2d4bc 100644
--- a/services/ui/display/platform_screen_ozone.cc
+++ b/services/ui/display/screen_manager_ozone.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/display/platform_screen_ozone.h"
+#include "services/ui/display/screen_manager_ozone.h"
 
 #include <memory>
 #include <utility>
@@ -55,25 +55,25 @@
 }  // namespace
 
 // static
-std::unique_ptr<PlatformScreen> PlatformScreen::Create() {
-  return base::MakeUnique<PlatformScreenOzone>();
+std::unique_ptr<ScreenManager> ScreenManager::Create() {
+  return base::MakeUnique<ScreenManagerOzone>();
 }
 
-PlatformScreenOzone::PlatformScreenOzone() {}
+ScreenManagerOzone::ScreenManagerOzone() {}
 
-PlatformScreenOzone::~PlatformScreenOzone() {
+ScreenManagerOzone::~ScreenManagerOzone() {
   // We are shutting down and don't want to make anymore display changes.
   fake_display_controller_ = nullptr;
   display_configurator_.RemoveObserver(this);
 }
 
-void PlatformScreenOzone::AddInterfaces(
+void ScreenManagerOzone::AddInterfaces(
     service_manager::InterfaceRegistry* registry) {
   registry->AddInterface<mojom::DisplayController>(this);
   registry->AddInterface<mojom::TestDisplayController>(this);
 }
 
-void PlatformScreenOzone::Init(PlatformScreenDelegate* delegate) {
+void ScreenManagerOzone::Init(ScreenManagerDelegate* delegate) {
   DCHECK(delegate);
   delegate_ = delegate;
 
@@ -96,24 +96,24 @@
   display_configurator_.ForceInitialConfigure(kChromeOsBootColor);
 }
 
-void PlatformScreenOzone::RequestCloseDisplay(int64_t display_id) {
+void ScreenManagerOzone::RequestCloseDisplay(int64_t display_id) {
   if (!fake_display_controller_ || wait_for_display_config_update_)
     return;
 
   CachedDisplayIterator iter = GetCachedDisplayIterator(display_id);
   if (iter != cached_displays_.end()) {
-    // Tell the NDD to remove the display. PlatformScreen will get an update
+    // Tell the NDD to remove the display. ScreenManager will get an update
     // that the display configuration has changed and the display will be gone.
     wait_for_display_config_update_ =
         fake_display_controller_->RemoveDisplay(iter->id);
   }
 }
 
-int64_t PlatformScreenOzone::GetPrimaryDisplayId() const {
+int64_t ScreenManagerOzone::GetPrimaryDisplayId() const {
   return primary_display_id_;
 }
 
-void PlatformScreenOzone::ToggleAddRemoveDisplay() {
+void ScreenManagerOzone::ToggleAddRemoveDisplay() {
   if (!fake_display_controller_ || wait_for_display_config_update_)
     return;
 
@@ -129,7 +129,7 @@
   }
 }
 
-void PlatformScreenOzone::ToggleDisplayResolution() {
+void ScreenManagerOzone::ToggleDisplayResolution() {
   DisplayInfo& display = cached_displays_[0];
 
   // Toggle the display size to use.
@@ -147,7 +147,23 @@
   display_configurator_.OnConfigurationChanged();
 }
 
-void PlatformScreenOzone::SwapPrimaryDisplay() {
+void ScreenManagerOzone::IncreaseInternalDisplayZoom() {
+  NOTIMPLEMENTED();
+}
+
+void ScreenManagerOzone::DecreaseInternalDisplayZoom() {
+  NOTIMPLEMENTED();
+}
+
+void ScreenManagerOzone::ResetInternalDisplayZoom() {
+  NOTIMPLEMENTED();
+}
+
+void ScreenManagerOzone::RotateCurrentDisplayCW() {
+  NOTIMPLEMENTED();
+}
+
+void ScreenManagerOzone::SwapPrimaryDisplay() {
   const size_t num_displays = cached_displays_.size();
   if (num_displays <= 1)
     return;
@@ -171,9 +187,13 @@
   delegate_->OnPrimaryDisplayChanged(primary_display_id_);
 }
 
-void PlatformScreenOzone::SetDisplayWorkArea(int64_t display_id,
-                                             const gfx::Size& size,
-                                             const gfx::Insets& insets) {
+void ScreenManagerOzone::ToggleMirrorMode() {
+  NOTIMPLEMENTED();
+}
+
+void ScreenManagerOzone::SetDisplayWorkArea(int64_t display_id,
+                                            const gfx::Size& size,
+                                            const gfx::Insets& insets) {
   CachedDisplayIterator iter = GetCachedDisplayIterator(display_id);
   if (iter == cached_displays_.end()) {
     NOTREACHED() << display_id;
@@ -194,12 +214,12 @@
   }
 }
 
-PlatformScreenOzone::DisplayInfo::DisplayInfo() = default;
-PlatformScreenOzone::DisplayInfo::DisplayInfo(const DisplayInfo& other) =
+ScreenManagerOzone::DisplayInfo::DisplayInfo() = default;
+ScreenManagerOzone::DisplayInfo::DisplayInfo(const DisplayInfo& other) =
     default;
-PlatformScreenOzone::DisplayInfo::~DisplayInfo() = default;
+ScreenManagerOzone::DisplayInfo::~DisplayInfo() = default;
 
-void PlatformScreenOzone::ProcessRemovedDisplays(
+void ScreenManagerOzone::ProcessRemovedDisplays(
     const ui::DisplayConfigurator::DisplayStateList& snapshots) {
   std::vector<int64_t> current_ids;
   for (ui::DisplaySnapshot* snapshot : snapshots)
@@ -216,7 +236,7 @@
   }
 }
 
-void PlatformScreenOzone::ProcessModifiedDisplays(
+void ScreenManagerOzone::ProcessModifiedDisplays(
     const ui::DisplayConfigurator::DisplayStateList& snapshots) {
   for (ui::DisplaySnapshot* snapshot : snapshots) {
     auto iter = GetCachedDisplayIterator(snapshot->display_id());
@@ -234,7 +254,7 @@
   }
 }
 
-void PlatformScreenOzone::UpdateCachedDisplays() {
+void ScreenManagerOzone::UpdateCachedDisplays() {
   // Walk through cached displays after processing the snapshots to find any
   // removed or modified displays. This ensures that we only send one update per
   // display to the delegate.
@@ -264,7 +284,7 @@
   }
 }
 
-void PlatformScreenOzone::AddNewDisplays(
+void ScreenManagerOzone::AddNewDisplays(
     const ui::DisplayConfigurator::DisplayStateList& snapshots) {
   for (ui::DisplaySnapshot* snapshot : snapshots) {
     const int64_t id = snapshot->display_id();
@@ -298,15 +318,15 @@
   }
 }
 
-PlatformScreenOzone::CachedDisplayIterator
-PlatformScreenOzone::GetCachedDisplayIterator(int64_t display_id) {
+ScreenManagerOzone::CachedDisplayIterator
+ScreenManagerOzone::GetCachedDisplayIterator(int64_t display_id) {
   return std::find_if(cached_displays_.begin(), cached_displays_.end(),
                       [display_id](const DisplayInfo& display_info) {
                         return display_info.id == display_id;
                       });
 }
 
-ViewportMetrics PlatformScreenOzone::MetricsFromSnapshot(
+ViewportMetrics ScreenManagerOzone::MetricsFromSnapshot(
     const ui::DisplaySnapshot& snapshot,
     const gfx::Point& origin) {
   const ui::DisplayMode* current_mode = snapshot.current_mode();
@@ -325,7 +345,7 @@
   return metrics;
 }
 
-void PlatformScreenOzone::OnDisplayModeChanged(
+void ScreenManagerOzone::OnDisplayModeChanged(
     const ui::DisplayConfigurator::DisplayStateList& displays) {
   ProcessRemovedDisplays(displays);
   ProcessModifiedDisplays(displays);
@@ -348,28 +368,28 @@
   wait_for_display_config_update_ = false;
 }
 
-void PlatformScreenOzone::OnDisplayModeChangeFailed(
+void ScreenManagerOzone::OnDisplayModeChangeFailed(
     const ui::DisplayConfigurator::DisplayStateList& displays,
     ui::MultipleDisplayState failed_new_state) {
   LOG(ERROR) << "OnDisplayModeChangeFailed from DisplayConfigurator";
   wait_for_display_config_update_ = false;
 }
 
-void PlatformScreenOzone::Create(
+void ScreenManagerOzone::Create(
     const service_manager::Identity& remote_identity,
     mojom::DisplayControllerRequest request) {
   controller_bindings_.AddBinding(this, std::move(request));
 }
 
-ui::MultipleDisplayState PlatformScreenOzone::GetStateForDisplayIds(
+ui::MultipleDisplayState ScreenManagerOzone::GetStateForDisplayIds(
     const ui::DisplayConfigurator::DisplayStateList& display_states) const {
   return (display_states.size() == 1
               ? ui::MULTIPLE_DISPLAY_STATE_SINGLE
               : ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED);
 }
 
-bool PlatformScreenOzone::GetResolutionForDisplayId(int64_t display_id,
-                                                    gfx::Size* size) const {
+bool ScreenManagerOzone::GetResolutionForDisplayId(int64_t display_id,
+                                                   gfx::Size* size) const {
   for (const DisplayInfo& display : cached_displays_) {
     if (display.id == display_id) {
       *size = display.requested_size;
@@ -380,7 +400,7 @@
   return false;
 }
 
-void PlatformScreenOzone::Create(
+void ScreenManagerOzone::Create(
     const service_manager::Identity& remote_identity,
     mojom::TestDisplayControllerRequest request) {
   test_bindings_.AddBinding(this, std::move(request));
diff --git a/services/ui/display/platform_screen_ozone.h b/services/ui/display/screen_manager_ozone.h
similarity index 87%
rename from services/ui/display/platform_screen_ozone.h
rename to services/ui/display/screen_manager_ozone.h
index 8e6bf27..7b3a28f 100644
--- a/services/ui/display/platform_screen_ozone.h
+++ b/services/ui/display/screen_manager_ozone.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_UI_DISPLAY_PLATFORM_SCREEN_OZONE_H_
-#define SERVICES_UI_DISPLAY_PLATFORM_SCREEN_OZONE_H_
+#ifndef SERVICES_UI_DISPLAY_SCREEN_MANAGER_OZONE_H_
+#define SERVICES_UI_DISPLAY_SCREEN_MANAGER_OZONE_H_
 
 #include <stdint.h>
 
@@ -15,7 +15,7 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/service_manager/public/cpp/connection.h"
 #include "services/service_manager/public/cpp/interface_factory.h"
-#include "services/ui/display/platform_screen.h"
+#include "services/ui/display/screen_manager.h"
 #include "services/ui/display/viewport_metrics.h"
 #include "services/ui/public/interfaces/display/display_controller.mojom.h"
 #include "services/ui/public/interfaces/display/test_display_controller.mojom.h"
@@ -26,10 +26,10 @@
 
 namespace display {
 
-// PlatformScreenOzone provides the necessary functionality to configure all
+// ScreenManagerOzone provides the necessary functionality to configure all
 // attached physical displays on the ozone platform.
-class PlatformScreenOzone
-    : public PlatformScreen,
+class ScreenManagerOzone
+    : public ScreenManager,
       public ui::DisplayConfigurator::Observer,
       public ui::DisplayConfigurator::StateController,
       public service_manager::InterfaceFactory<mojom::DisplayController>,
@@ -37,12 +37,12 @@
       public mojom::DisplayController,
       public mojom::TestDisplayController {
  public:
-  PlatformScreenOzone();
-  ~PlatformScreenOzone() override;
+  ScreenManagerOzone();
+  ~ScreenManagerOzone() override;
 
-  // PlatformScreen:
+  // ScreenManager:
   void AddInterfaces(service_manager::InterfaceRegistry* registry) override;
-  void Init(PlatformScreenDelegate* delegate) override;
+  void Init(ScreenManagerDelegate* delegate) override;
   void RequestCloseDisplay(int64_t display_id) override;
   int64_t GetPrimaryDisplayId() const override;
 
@@ -51,13 +51,18 @@
   void ToggleDisplayResolution() override;
 
   // mojom::DisplayController:
+  void IncreaseInternalDisplayZoom() override;
+  void DecreaseInternalDisplayZoom() override;
+  void ResetInternalDisplayZoom() override;
+  void RotateCurrentDisplayCW() override;
   void SwapPrimaryDisplay() override;
+  void ToggleMirrorMode() override;
   void SetDisplayWorkArea(int64_t display_id,
                           const gfx::Size& size,
                           const gfx::Insets& insets) override;
 
  private:
-  friend class PlatformScreenOzoneTest;
+  friend class ScreenManagerOzoneTest;
 
   // TODO(kylechar): This struct is just temporary until we migrate
   // DisplayManager code out of ash so it can be used here.
@@ -140,7 +145,7 @@
               mojom::TestDisplayControllerRequest request) override;
 
   ui::DisplayConfigurator display_configurator_;
-  PlatformScreenDelegate* delegate_ = nullptr;
+  ScreenManagerDelegate* delegate_ = nullptr;
 
   // If not null it provides a way to modify the display state when running off
   // device (eg. running mustash on Linux).
@@ -158,9 +163,9 @@
   mojo::BindingSet<mojom::DisplayController> controller_bindings_;
   mojo::BindingSet<mojom::TestDisplayController> test_bindings_;
 
-  DISALLOW_COPY_AND_ASSIGN(PlatformScreenOzone);
+  DISALLOW_COPY_AND_ASSIGN(ScreenManagerOzone);
 };
 
 }  // namespace display
 
-#endif  // SERVICES_UI_DISPLAY_PLATFORM_SCREEN_OZONE_H_
+#endif  // SERVICES_UI_DISPLAY_SCREEN_MANAGER_OZONE_H_
diff --git a/services/ui/display/platform_screen_ozone_unittests.cc b/services/ui/display/screen_manager_ozone_unittests.cc
similarity index 84%
rename from services/ui/display/platform_screen_ozone_unittests.cc
rename to services/ui/display/screen_manager_ozone_unittests.cc
index 87d40ed..6525b110 100644
--- a/services/ui/display/platform_screen_ozone_unittests.cc
+++ b/services/ui/display/screen_manager_ozone_unittests.cc
@@ -9,8 +9,8 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "services/ui/display/platform_screen.h"
-#include "services/ui/display/platform_screen_ozone.h"
+#include "services/ui/display/screen_manager.h"
+#include "services/ui/display/screen_manager_ozone.h"
 #include "services/ui/display/viewport_metrics.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -63,15 +63,15 @@
 }
 
 // Test delegate to track what functions calls the delegate receives.
-class TestPlatformScreenDelegate : public PlatformScreenDelegate {
+class TestScreenManagerDelegate : public ScreenManagerDelegate {
  public:
-  TestPlatformScreenDelegate() {}
-  ~TestPlatformScreenDelegate() override {}
+  TestScreenManagerDelegate() {}
+  ~TestScreenManagerDelegate() override {}
 
   const std::vector<DisplayState>& added() const { return added_; }
   const std::vector<DisplayState>& modified() const { return modified_; }
 
-  // Returns a string containing the function calls that PlatformScreenDelegate
+  // Returns a string containing the function calls that ScreenManagerDelegate
   // has received in the order they occured. Each function call will be in the
   // form "<action>(<id>)" and multiple function calls will be separated by ";".
   // For example, if display 2 was added then display 1 was modified, changes()
@@ -113,20 +113,20 @@
   std::vector<DisplayState> modified_;
   std::string changes_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestPlatformScreenDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestScreenManagerDelegate);
 };
 
 }  // namespace
 
 // Test fixture with helpers to act like ui::DisplayConfigurator and send
-// OnDisplayModeChanged() to PlatformScreenOzone.
-class PlatformScreenOzoneTest : public testing::Test {
+// OnDisplayModeChanged() to ScreenManagerOzone.
+class ScreenManagerOzoneTest : public testing::Test {
  public:
-  PlatformScreenOzoneTest() {}
-  ~PlatformScreenOzoneTest() override {}
+  ScreenManagerOzoneTest() {}
+  ~ScreenManagerOzoneTest() override {}
 
-  PlatformScreenOzone* platform_screen() { return platform_screen_.get(); }
-  TestPlatformScreenDelegate* delegate() { return &delegate_; }
+  ScreenManagerOzone* screen_manager() { return screen_manager_.get(); }
+  TestScreenManagerDelegate* delegate() { return &delegate_; }
 
   // Adds a display snapshot with specified ID and default size.
   void AddDisplay(int64_t id) { return AddDisplay(id, gfx::Size(1024, 768)); }
@@ -174,7 +174,7 @@
     for (auto& snapshot : snapshots_) {
       snapshots_ptrs.push_back(snapshot.get());
     }
-    platform_screen_->OnDisplayModeChanged(snapshots_ptrs);
+    screen_manager_->OnDisplayModeChanged(snapshots_ptrs);
   }
 
  private:
@@ -193,8 +193,8 @@
 
     testing::Test::SetUp();
     ui::OzonePlatform::InitializeForUI();
-    platform_screen_ = base::MakeUnique<PlatformScreenOzone>();
-    platform_screen_->Init(&delegate_);
+    screen_manager_ = base::MakeUnique<ScreenManagerOzone>();
+    screen_manager_->Init(&delegate_);
 
     // Have all tests start with a 1024x768 display by default.
     AddDisplay(1, gfx::Size(1024, 768));
@@ -212,22 +212,22 @@
   void TearDown() override {
     snapshots_.clear();
     delegate_.Reset();
-    platform_screen_.reset();
+    screen_manager_.reset();
   }
 
-  TestPlatformScreenDelegate delegate_;
-  std::unique_ptr<PlatformScreenOzone> platform_screen_;
+  TestScreenManagerDelegate delegate_;
+  std::unique_ptr<ScreenManagerOzone> screen_manager_;
   std::vector<std::unique_ptr<DisplaySnapshot>> snapshots_;
 };
 
-TEST_F(PlatformScreenOzoneTest, AddDisplay) {
+TEST_F(ScreenManagerOzoneTest, AddDisplay) {
   AddDisplay(2);
 
   // Check that display 2 was added.
   EXPECT_EQ("Added(2)", delegate()->changes());
 }
 
-TEST_F(PlatformScreenOzoneTest, RemoveDisplay) {
+TEST_F(ScreenManagerOzoneTest, RemoveDisplay) {
   AddDisplay(2);
   delegate()->Reset();
 
@@ -237,7 +237,7 @@
   EXPECT_EQ("Removed(2)", delegate()->changes());
 }
 
-TEST_F(PlatformScreenOzoneTest, RemoveFirstDisplay) {
+TEST_F(ScreenManagerOzoneTest, RemoveFirstDisplay) {
   AddDisplay(2);
   delegate()->Reset();
 
@@ -251,7 +251,7 @@
   EXPECT_THAT(delegate()->modified()[0], DisplayOrigin("0,0"));
 }
 
-TEST_F(PlatformScreenOzoneTest, RemoveMultipleDisplay) {
+TEST_F(ScreenManagerOzoneTest, RemoveMultipleDisplay) {
   AddDisplay(2);
   AddDisplay(3);
   delegate()->Reset();
@@ -264,7 +264,7 @@
   EXPECT_EQ("Removed(2);Modified(3);Removed(3)", delegate()->changes());
 }
 
-TEST_F(PlatformScreenOzoneTest, ModifyDisplaySize) {
+TEST_F(ScreenManagerOzoneTest, ModifyDisplaySize) {
   const gfx::Size size1(1920, 1200);
   const gfx::Size size2(1680, 1050);
 
@@ -286,7 +286,7 @@
   EXPECT_EQ("Modified(2)", delegate()->changes());
 }
 
-TEST_F(PlatformScreenOzoneTest, ModifyFirstDisplaySize) {
+TEST_F(ScreenManagerOzoneTest, ModifyFirstDisplaySize) {
   const gfx::Size size(1920, 1200);
 
   AddDisplay(2, size);
@@ -309,7 +309,7 @@
   EXPECT_THAT(delegate()->modified()[1], DisplayOrigin("1920,0"));
 }
 
-TEST_F(PlatformScreenOzoneTest, RemovePrimaryDisplay) {
+TEST_F(ScreenManagerOzoneTest, RemovePrimaryDisplay) {
   AddDisplay(2);
   delegate()->Reset();
 
@@ -319,7 +319,7 @@
   EXPECT_EQ("Primary(2);Removed(1);Modified(2)", delegate()->changes());
 }
 
-TEST_F(PlatformScreenOzoneTest, RemoveLastDisplay) {
+TEST_F(ScreenManagerOzoneTest, RemoveLastDisplay) {
   RemoveDisplay(1);
 
   // Check that display 1 is removed and no updates for the primary display are
@@ -327,23 +327,23 @@
   EXPECT_EQ("Removed(1)", delegate()->changes());
 }
 
-TEST_F(PlatformScreenOzoneTest, SwapPrimaryDisplay) {
+TEST_F(ScreenManagerOzoneTest, SwapPrimaryDisplay) {
   AddDisplay(2);
   delegate()->Reset();
 
-  platform_screen()->SwapPrimaryDisplay();
+  screen_manager()->SwapPrimaryDisplay();
   EXPECT_EQ("Primary(2)", delegate()->changes());
 }
 
-TEST_F(PlatformScreenOzoneTest, SwapPrimaryThreeDisplays) {
+TEST_F(ScreenManagerOzoneTest, SwapPrimaryThreeDisplays) {
   AddDisplay(2);
   AddDisplay(3);
   EXPECT_EQ("Added(2);Added(3)", delegate()->changes());
   delegate()->Reset();
 
-  platform_screen()->SwapPrimaryDisplay();
-  platform_screen()->SwapPrimaryDisplay();
-  platform_screen()->SwapPrimaryDisplay();
+  screen_manager()->SwapPrimaryDisplay();
+  screen_manager()->SwapPrimaryDisplay();
+  screen_manager()->SwapPrimaryDisplay();
   EXPECT_EQ("Primary(2);Primary(3);Primary(1)", delegate()->changes());
 }
 
diff --git a/services/ui/display/platform_screen_stub.cc b/services/ui/display/screen_manager_stub.cc
similarity index 69%
rename from services/ui/display/platform_screen_stub.cc
rename to services/ui/display/screen_manager_stub.cc
index 8a90a4d2..c664c84 100644
--- a/services/ui/display/platform_screen_stub.cc
+++ b/services/ui/display/screen_manager_stub.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/display/platform_screen_stub.h"
+#include "services/ui/display/screen_manager_stub.h"
 
 #include <memory>
 
@@ -39,40 +39,39 @@
 }  // namespace
 
 // static
-std::unique_ptr<PlatformScreen> PlatformScreen::Create() {
-  return base::MakeUnique<PlatformScreenStub>();
+std::unique_ptr<ScreenManager> ScreenManager::Create() {
+  return base::MakeUnique<ScreenManagerStub>();
 }
 
-PlatformScreenStub::PlatformScreenStub()
-    : weak_ptr_factory_(this) {}
+ScreenManagerStub::ScreenManagerStub() : weak_ptr_factory_(this) {}
 
-PlatformScreenStub::~PlatformScreenStub() {}
+ScreenManagerStub::~ScreenManagerStub() {}
 
-void PlatformScreenStub::FixedSizeScreenConfiguration() {
+void ScreenManagerStub::FixedSizeScreenConfiguration() {
   delegate_->OnDisplayAdded(display_id_, display_metrics_);
 }
 
-void PlatformScreenStub::AddInterfaces(
+void ScreenManagerStub::AddInterfaces(
     service_manager::InterfaceRegistry* registry) {}
 
-void PlatformScreenStub::Init(PlatformScreenDelegate* delegate) {
+void ScreenManagerStub::Init(ScreenManagerDelegate* delegate) {
   DCHECK(delegate);
   delegate_ = delegate;
   display_metrics_ = DefaultViewportMetrics();
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&PlatformScreenStub::FixedSizeScreenConfiguration,
+      FROM_HERE, base::Bind(&ScreenManagerStub::FixedSizeScreenConfiguration,
                             weak_ptr_factory_.GetWeakPtr()));
 }
 
-void PlatformScreenStub::RequestCloseDisplay(int64_t display_id) {
+void ScreenManagerStub::RequestCloseDisplay(int64_t display_id) {
   if (display_id == display_id_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&PlatformScreenDelegate::OnDisplayRemoved,
+        FROM_HERE, base::Bind(&ScreenManagerDelegate::OnDisplayRemoved,
                               base::Unretained(delegate_), display_id));
   }
 }
 
-int64_t PlatformScreenStub::GetPrimaryDisplayId() const {
+int64_t ScreenManagerStub::GetPrimaryDisplayId() const {
   return display_id_;
 }
 
diff --git a/services/ui/display/screen_manager_stub.h b/services/ui/display/screen_manager_stub.h
new file mode 100644
index 0000000..b780bb4d
--- /dev/null
+++ b/services/ui/display/screen_manager_stub.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_UI_DISPLAY_SCREEN_MANAGER_STUB_H_
+#define SERVICES_UI_DISPLAY_SCREEN_MANAGER_STUB_H_
+
+#include <stdint.h>
+
+#include "base/memory/weak_ptr.h"
+#include "services/ui/display/screen_manager.h"
+#include "services/ui/display/viewport_metrics.h"
+
+namespace display {
+
+// ScreenManagerStub provides the necessary functionality to configure a fixed
+// 1024x768 display for non-ozone platforms.
+class ScreenManagerStub : public ScreenManager {
+ public:
+  ScreenManagerStub();
+  ~ScreenManagerStub() override;
+
+ private:
+  // Fake creation of a single 1024x768 display.
+  void FixedSizeScreenConfiguration();
+
+  // ScreenManager.
+  void AddInterfaces(service_manager::InterfaceRegistry* registry) override;
+  void Init(ScreenManagerDelegate* delegate) override;
+  void RequestCloseDisplay(int64_t display_id) override;
+  int64_t GetPrimaryDisplayId() const override;
+
+  // Sample display information.
+  int64_t display_id_ = 1;
+  ViewportMetrics display_metrics_;
+
+  ScreenManagerDelegate* delegate_ = nullptr;
+
+  base::WeakPtrFactory<ScreenManagerStub> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScreenManagerStub);
+};
+
+}  // namespace display
+
+#endif  // SERVICES_UI_DISPLAY_SCREEN_MANAGER_STUB_H_
diff --git a/services/ui/gpu/gpu_main.cc b/services/ui/gpu/gpu_main.cc
index 7b14ea5e..5be189b 100644
--- a/services/ui/gpu/gpu_main.cc
+++ b/services/ui/gpu/gpu_main.cc
@@ -10,6 +10,7 @@
 #include "gpu/ipc/gpu_in_process_thread_service.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
+#include "services/ui/common/mus_gpu_memory_buffer_manager.h"
 #include "services/ui/gpu/gpu_service_internal.h"
 
 namespace {
diff --git a/services/ui/public/interfaces/display/display_controller.mojom b/services/ui/public/interfaces/display/display_controller.mojom
index c4e126d..0d991ae 100644
--- a/services/ui/public/interfaces/display/display_controller.mojom
+++ b/services/ui/public/interfaces/display/display_controller.mojom
@@ -10,9 +10,29 @@
 // state.
 interface DisplayController {
 
+  // =========================== Keyboard Shortcuts ===========================
+
+  // Increase zoom on internal display.
+  IncreaseInternalDisplayZoom();
+
+  // Decrease zoom on internal display.
+  DecreaseInternalDisplayZoom();
+
+  // Reset zoom on internal display to default level.
+  ResetInternalDisplayZoom();
+
+  // Rotate the current display by 90° CW. The current display is the display
+  // closest to the mouse cursor.
+  RotateCurrentDisplayCW();
+
   // Swap the primary display and the next display.
   SwapPrimaryDisplay();
 
+  // Toggles between mirroring and extended mode.
+  ToggleMirrorMode();
+
+  // ========================= Configuration Changes ==========================
+
   // Sets the display work area with the provided insets. The display size is
   // included to ensure that the insets are for the current display size.
   SetDisplayWorkArea(int64 display_id, 
diff --git a/services/ui/service.cc b/services/ui/service.cc
index 03de291..16dcc4be 100644
--- a/services/ui/service.cc
+++ b/services/ui/service.cc
@@ -24,7 +24,7 @@
 #include "services/tracing/public/cpp/provider.h"
 #include "services/ui/clipboard/clipboard_impl.h"
 #include "services/ui/common/switches.h"
-#include "services/ui/display/platform_screen.h"
+#include "services/ui/display/screen_manager.h"
 #include "services/ui/ime/ime_registrar_impl.h"
 #include "services/ui/ime/ime_server_impl.h"
 #include "services/ui/ws/accessibility_manager.h"
@@ -87,7 +87,7 @@
 
 Service::Service()
     : test_config_(false),
-      platform_screen_(display::PlatformScreen::Create()),
+      screen_manager_(display::ScreenManager::Create()),
       ime_registrar_(&ime_server_) {}
 
 Service::~Service() {
@@ -187,7 +187,7 @@
   // so keep this line below both of those.
   input_device_server_.RegisterAsObserver();
 
-  // Gpu must be running before the PlatformScreen can be initialized.
+  // Gpu must be running before the ScreenManager can be initialized.
   window_server_.reset(new ws::WindowServer(this));
 
   // DeviceDataManager must be initialized before TouchController. On non-Linux
@@ -220,7 +220,7 @@
   if (input_device_server_.IsRegisteredAsObserver())
     input_device_server_.AddInterface(registry);
 
-  platform_screen_->AddInterfaces(registry);
+  screen_manager_->AddInterfaces(registry);
 
 #if defined(USE_OZONE)
   ui::OzonePlatform::GetInstance()->AddInterfaces(registry);
@@ -230,7 +230,7 @@
 }
 
 void Service::StartDisplayInit() {
-  platform_screen_->Init(window_server_->display_manager());
+  screen_manager_->Init(window_server_->display_manager());
 }
 
 void Service::OnFirstDisplayReady() {
diff --git a/services/ui/service.h b/services/ui/service.h
index 6b1a18a0..40cd1b1 100644
--- a/services/ui/service.h
+++ b/services/ui/service.h
@@ -41,7 +41,7 @@
 #endif
 
 namespace display {
-class PlatformScreen;
+class ScreenManager;
 }
 
 namespace gfx {
@@ -181,7 +181,7 @@
 
   // Manages display hardware and handles display management. May register Mojo
   // interfaces and must outlive service_manager::InterfaceRegistry.
-  std::unique_ptr<display::PlatformScreen> platform_screen_;
+  std::unique_ptr<display::ScreenManager> screen_manager_;
 
   std::unique_ptr<ws::TouchController> touch_controller_;
   IMERegistrarImpl ime_registrar_;
diff --git a/services/ui/surfaces/BUILD.gn b/services/ui/surfaces/BUILD.gn
index c4f8e46..7817fc5c 100644
--- a/services/ui/surfaces/BUILD.gn
+++ b/services/ui/surfaces/BUILD.gn
@@ -30,9 +30,6 @@
     "//gpu/ipc/common",
     "//services/service_manager/public/cpp",
     "//services/tracing/public/cpp",
-    "//services/ui/common:server_gpu",
-    "//services/ui/gpu/interfaces",
-    "//services/ui/public/interfaces",
     "//ui/display/types",
     "//ui/gfx",
     "//ui/gfx/geometry/mojo",
diff --git a/services/ui/surfaces/display_compositor.cc b/services/ui/surfaces/display_compositor.cc
index ab1508a..97cc327 100644
--- a/services/ui/surfaces/display_compositor.cc
+++ b/services/ui/surfaces/display_compositor.cc
@@ -9,7 +9,6 @@
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/ipc/gpu_in_process_thread_service.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/ui/common/mus_gpu_memory_buffer_manager.h"
 #include "services/ui/surfaces/gpu_compositor_frame_sink.h"
 
 namespace ui {
diff --git a/services/ui/surfaces/display_compositor.h b/services/ui/surfaces/display_compositor.h
index 950250d..0b3e67fb 100644
--- a/services/ui/surfaces/display_compositor.h
+++ b/services/ui/surfaces/display_compositor.h
@@ -20,7 +20,6 @@
 #include "gpu/ipc/in_process_command_buffer.h"
 #include "ipc/ipc_channel_handle.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/ui/common/mus_gpu_memory_buffer_manager.h"
 
 namespace gpu {
 class GpuMemoryBufferManager;
diff --git a/services/ui/ws/cursor_unittest.cc b/services/ui/ws/cursor_unittest.cc
index 8e38e2b..bfd4008 100644
--- a/services/ui/ws/cursor_unittest.cc
+++ b/services/ui/ws/cursor_unittest.cc
@@ -48,8 +48,8 @@
  protected:
   // testing::Test:
   void SetUp() override {
-    platform_screen_.Init(window_server()->display_manager());
-    platform_screen_.AddDisplay();
+    screen_manager_.Init(window_server()->display_manager());
+    screen_manager_.AddDisplay();
 
     // As a side effect, this allocates Displays.
     AddWindowManager(window_server(), kTestId1);
@@ -103,7 +103,7 @@
 
  private:
   WindowServerTestHelper ws_test_helper_;
-  TestPlatformScreen platform_screen_;
+  TestScreenManager screen_manager_;
   DISALLOW_COPY_AND_ASSIGN(CursorTest);
 };
 
diff --git a/services/ui/ws/display_manager.cc b/services/ui/ws/display_manager.cc
index 3607d60..2d9abf3 100644
--- a/services/ui/ws/display_manager.cc
+++ b/services/ui/ws/display_manager.cc
@@ -8,7 +8,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
-#include "services/ui/display/platform_screen.h"
 #include "services/ui/ws/display.h"
 #include "services/ui/ws/display_binding.h"
 #include "services/ui/ws/event_dispatcher.h"
diff --git a/services/ui/ws/display_manager.h b/services/ui/ws/display_manager.h
index 1915aaa1..00eeebb 100644
--- a/services/ui/ws/display_manager.h
+++ b/services/ui/ws/display_manager.h
@@ -10,7 +10,7 @@
 #include <set>
 
 #include "base/macros.h"
-#include "services/ui/display/platform_screen_delegate.h"
+#include "services/ui/display/screen_manager_delegate.h"
 #include "services/ui/ws/ids.h"
 #include "services/ui/ws/user_id.h"
 #include "services/ui/ws/user_id_tracker_observer.h"
@@ -29,7 +29,7 @@
 // between displays that do yet have an accelerated widget (pending), vs
 // those that do.
 class DisplayManager : public UserIdTrackerObserver,
-                       public display::PlatformScreenDelegate {
+                       public display::ScreenManagerDelegate {
  public:
   DisplayManager(WindowServer* window_server, UserIdTracker* user_id_tracker);
   ~DisplayManager() override;
@@ -81,7 +81,7 @@
   void OnActiveUserIdChanged(const UserId& previously_active_id,
                              const UserId& active_id) override;
 
-  // display::PlatformScreenDelegate:
+  // display::ScreenManagerDelegate:
   void OnDisplayAdded(int64_t id,
                       const display::ViewportMetrics& metrics) override;
   void OnDisplayRemoved(int64_t id) override;
diff --git a/services/ui/ws/display_unittest.cc b/services/ui/ws/display_unittest.cc
index 46f07aa..f2041f1 100644
--- a/services/ui/ws/display_unittest.cc
+++ b/services/ui/ws/display_unittest.cc
@@ -103,19 +103,19 @@
   TestWindowServerDelegate* window_server_delegate() {
     return ws_test_helper_.window_server_delegate();
   }
-  TestPlatformScreen& platform_screen() { return platform_screen_; }
+  TestScreenManager& screen_manager() { return screen_manager_; }
 
  protected:
   // testing::Test:
   void SetUp() override {
-    platform_screen_.Init(window_server()->display_manager());
+    screen_manager_.Init(window_server()->display_manager());
     window_server()->user_id_tracker()->AddUserId(kTestId1);
     window_server()->user_id_tracker()->AddUserId(kTestId2);
   }
 
  private:
   WindowServerTestHelper ws_test_helper_;
-  TestPlatformScreen platform_screen_;
+  TestScreenManager screen_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(DisplayTest);
 };
@@ -123,7 +123,7 @@
 TEST_F(DisplayTest, CreateDisplay) {
   AddWindowManager(window_server(), kTestId1);
   const int64_t display_id =
-      platform_screen().AddDisplay(MakeViewportMetrics(0, 0, 1024, 768, 1.0f));
+      screen_manager().AddDisplay(MakeViewportMetrics(0, 0, 1024, 768, 1.0f));
 
   ASSERT_EQ(1u, display_manager()->displays().size());
   Display* display = display_manager()->GetDisplayById(display_id);
@@ -144,7 +144,7 @@
 TEST_F(DisplayTest, CreateDisplayBeforeWM) {
   // Add one display, no WM exists yet.
   const int64_t display_id =
-      platform_screen().AddDisplay(MakeViewportMetrics(0, 0, 1024, 768, 1.0f));
+      screen_manager().AddDisplay(MakeViewportMetrics(0, 0, 1024, 768, 1.0f));
   EXPECT_EQ(1u, display_manager()->displays().size());
 
   Display* display = display_manager()->GetDisplayById(display_id);
@@ -170,7 +170,7 @@
 
 TEST_F(DisplayTest, CreateDisplayWithTwoWindowManagers) {
   AddWindowManager(window_server(), kTestId1);
-  const int64_t display_id = platform_screen().AddDisplay();
+  const int64_t display_id = screen_manager().AddDisplay();
   Display* display = display_manager()->GetDisplayById(display_id);
 
   // There should be only be one WM at this point.
@@ -201,7 +201,7 @@
   EXPECT_EQ("0,0 512x384", metrics.bounds.ToString());
   EXPECT_EQ("1024x768", metrics.pixel_size.ToString());
 
-  const int64_t display_id = platform_screen().AddDisplay(metrics);
+  const int64_t display_id = screen_manager().AddDisplay(metrics);
   Display* display = display_manager()->GetDisplayById(display_id);
 
   // The root ServerWindow bounds should be in PP.
@@ -209,7 +209,7 @@
 
   ViewportMetrics modified_metrics = metrics;
   modified_metrics.work_area.set_height(metrics.work_area.height() - 48);
-  platform_screen().ModifyDisplay(display_id, modified_metrics);
+  screen_manager().ModifyDisplay(display_id, modified_metrics);
 
   // The root ServerWindow should still be in PP after updating the work area.
   EXPECT_EQ("0,0 1024x768", display->root_window()->bounds().ToString());
@@ -218,7 +218,7 @@
 TEST_F(DisplayTest, Destruction) {
   AddWindowManager(window_server(), kTestId1);
 
-  int64_t display_id = platform_screen().AddDisplay();
+  int64_t display_id = screen_manager().AddDisplay();
 
   // Add a second WM.
   AddWindowManager(window_server(), kTestId2);
@@ -240,14 +240,14 @@
   }
 
   EXPECT_FALSE(window_server_delegate()->got_on_no_more_displays());
-  platform_screen().RemoveDisplay(display_id);
+  screen_manager().RemoveDisplay(display_id);
   // There is still one tree left.
   EXPECT_EQ(1u, window_server()->num_trees());
   EXPECT_TRUE(window_server_delegate()->got_on_no_more_displays());
 }
 
 TEST_F(DisplayTest, EventStateResetOnUserSwitch) {
-  const int64_t display_id = platform_screen().AddDisplay();
+  const int64_t display_id = screen_manager().AddDisplay();
 
   AddWindowManager(window_server(), kTestId1);
   AddWindowManager(window_server(), kTestId2);
@@ -290,7 +290,7 @@
 
 // Verifies capture fails when wm is inactive and succeeds when wm is active.
 TEST_F(DisplayTest, SetCaptureFromWindowManager) {
-  const int64_t display_id = platform_screen().AddDisplay();
+  const int64_t display_id = screen_manager().AddDisplay();
   AddWindowManager(window_server(), kTestId1);
   AddWindowManager(window_server(), kTestId2);
   window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
@@ -318,7 +318,7 @@
 }
 
 TEST_F(DisplayTest, FocusFailsForInactiveUser) {
-  const int64_t display_id = platform_screen().AddDisplay();
+  const int64_t display_id = screen_manager().AddDisplay();
   AddWindowManager(window_server(), kTestId1);
   TestWindowTreeClient* window_tree_client1 =
       window_server_delegate()->last_client();
@@ -353,8 +353,8 @@
 
 // Verifies a single tree is used for multiple displays.
 TEST_F(DisplayTest, MultipleDisplays) {
-  platform_screen().AddDisplay();
-  platform_screen().AddDisplay();
+  screen_manager().AddDisplay();
+  screen_manager().AddDisplay();
   AddWindowManager(window_server(), kTestId1);
   window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
   ASSERT_EQ(1u, window_server_delegate()->bindings()->size());
@@ -382,8 +382,8 @@
 // Assertions around destroying a secondary display.
 TEST_F(DisplayTest, DestroyingDisplayDoesntDelete) {
   AddWindowManager(window_server(), kTestId1);
-  platform_screen().AddDisplay();
-  const int64_t secondary_display_id = platform_screen().AddDisplay();
+  screen_manager().AddDisplay();
+  const int64_t secondary_display_id = screen_manager().AddDisplay();
   window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
   ASSERT_EQ(1u, window_server_delegate()->bindings()->size());
   WindowTree* tree = (*window_server_delegate()->bindings())[0]->tree();
@@ -404,7 +404,7 @@
   TestWindowManager* test_window_manager =
       window_server_delegate()->last_binding()->window_manager();
   EXPECT_FALSE(test_window_manager->got_display_removed());
-  platform_screen().RemoveDisplay(secondary_display_id);
+  screen_manager().RemoveDisplay(secondary_display_id);
 
   // Destroying the display should result in the following:
   // . The WindowManager should be told it was removed with the right id.
diff --git a/services/ui/ws/platform_display_default.cc b/services/ui/ws/platform_display_default.cc
index 41d40ca..11f105d 100644
--- a/services/ui/ws/platform_display_default.cc
+++ b/services/ui/ws/platform_display_default.cc
@@ -5,7 +5,7 @@
 #include "services/ui/ws/platform_display_default.h"
 
 #include "gpu/ipc/client/gpu_channel_host.h"
-#include "services/ui/display/platform_screen.h"
+#include "services/ui/display/screen_manager.h"
 #include "services/ui/ws/platform_display_init_params.h"
 #include "services/ui/ws/server_window.h"
 #include "ui/base/cursor/cursor_loader.h"
@@ -212,7 +212,7 @@
 }
 
 void PlatformDisplayDefault::OnCloseRequest() {
-  display::PlatformScreen::GetInstance()->RequestCloseDisplay(GetId());
+  display::ScreenManager::GetInstance()->RequestCloseDisplay(GetId());
 }
 
 void PlatformDisplayDefault::OnClosed() {}
diff --git a/services/ui/ws/test_utils.cc b/services/ui/ws/test_utils.cc
index 4e797f3c..c30f4472 100644
--- a/services/ui/ws/test_utils.cc
+++ b/services/ui/ws/test_utils.cc
@@ -86,18 +86,17 @@
 
 }  // namespace
 
-// TestPlatformScreen  -------------------------------------------------
+// TestScreenManager  -------------------------------------------------
 
-TestPlatformScreen::TestPlatformScreen() {}
+TestScreenManager::TestScreenManager() {}
 
-TestPlatformScreen::~TestPlatformScreen() {}
+TestScreenManager::~TestScreenManager() {}
 
-int64_t TestPlatformScreen::AddDisplay() {
+int64_t TestScreenManager::AddDisplay() {
   return AddDisplay(MakeViewportMetrics(0, 0, 100, 100, 1.0f));
 }
 
-int64_t TestPlatformScreen::AddDisplay(
-    const display::ViewportMetrics& metrics) {
+int64_t TestScreenManager::AddDisplay(const display::ViewportMetrics& metrics) {
   // Generate a unique display id.
   int64_t display_id = display_ids_.empty() ? 1 : *display_ids_.rbegin() + 1;
   display_ids_.insert(display_id);
@@ -113,27 +112,26 @@
   return display_id;
 }
 
-void TestPlatformScreen::ModifyDisplay(
-    int64_t id,
-    const display::ViewportMetrics& metrics) {
+void TestScreenManager::ModifyDisplay(int64_t id,
+                                      const display::ViewportMetrics& metrics) {
   DCHECK(display_ids_.count(id) == 1);
   delegate_->OnDisplayModified(id, metrics);
 }
 
-void TestPlatformScreen::RemoveDisplay(int64_t id) {
+void TestScreenManager::RemoveDisplay(int64_t id) {
   DCHECK(display_ids_.count(id) == 1);
   delegate_->OnDisplayRemoved(id);
   display_ids_.erase(id);
 }
 
-void TestPlatformScreen::Init(display::PlatformScreenDelegate* delegate) {
+void TestScreenManager::Init(display::ScreenManagerDelegate* delegate) {
   // Reset
   delegate_ = delegate;
   display_ids_.clear();
   primary_display_id_ = display::kInvalidDisplayId;
 }
 
-int64_t TestPlatformScreen::GetPrimaryDisplayId() const {
+int64_t TestScreenManager::GetPrimaryDisplayId() const {
   return primary_display_id_;
 }
 
diff --git a/services/ui/ws/test_utils.h b/services/ui/ws/test_utils.h
index bf2bc0e..9cafaa9 100644
--- a/services/ui/ws/test_utils.h
+++ b/services/ui/ws/test_utils.h
@@ -13,7 +13,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
-#include "services/ui/display/platform_screen.h"
+#include "services/ui/display/screen_manager.h"
 #include "services/ui/display/viewport_metrics.h"
 #include "services/ui/public/interfaces/display_manager.mojom.h"
 #include "services/ui/public/interfaces/window_tree.mojom.h"
@@ -41,14 +41,14 @@
 
 // Collection of utilities useful in creating mus tests.
 
-// Test PlatformDisplay instance that allows adding/modifying/removing displays.
+// Test ScreenManager instance that allows adding/modifying/removing displays.
 // Tracks display ids to perform some basic verification that no duplicates are
 // added and display was added before being modified or removed. Display ids
 // reset when Init() is called.
-class TestPlatformScreen : public display::PlatformScreen {
+class TestScreenManager : public display::ScreenManager {
  public:
-  TestPlatformScreen();
-  ~TestPlatformScreen() override;
+  TestScreenManager();
+  ~TestScreenManager() override;
 
   // Adds a new display with default metrics, generates a unique display id and
   // returns it. Calls OnDisplayAdded() on delegate.
@@ -64,18 +64,18 @@
   // Calls OnDisplayRemoved() on delegate.
   void RemoveDisplay(int64_t id);
 
-  // display::PlatformScreen:
+  // display::ScreenManager:
   void AddInterfaces(service_manager::InterfaceRegistry* registry) override {}
-  void Init(display::PlatformScreenDelegate* delegate) override;
+  void Init(display::ScreenManagerDelegate* delegate) override;
   void RequestCloseDisplay(int64_t display_id) override {}
   int64_t GetPrimaryDisplayId() const override;
 
  private:
-  display::PlatformScreenDelegate* delegate_;
+  display::ScreenManagerDelegate* delegate_;
   int64_t primary_display_id_ = display::kInvalidDisplayId;
   std::set<int64_t> display_ids_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestPlatformScreen);
+  DISALLOW_COPY_AND_ASSIGN(TestScreenManager);
 };
 
 // -----------------------------------------------------------------------------
diff --git a/services/ui/ws/user_display_manager.cc b/services/ui/ws/user_display_manager.cc
index d70d246..4b9129c 100644
--- a/services/ui/ws/user_display_manager.cc
+++ b/services/ui/ws/user_display_manager.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "services/ui/display/platform_screen.h"
+#include "services/ui/display/screen_manager.h"
 #include "services/ui/ws/display.h"
 #include "services/ui/ws/display_manager.h"
 #include "services/ui/ws/user_display_manager_delegate.h"
@@ -158,7 +158,7 @@
   // TODO(kylechar): Pass internal display id to clients here.
   observer->OnDisplays(
       GetAllDisplays().PassStorage(),
-      display::PlatformScreen::GetInstance()->GetPrimaryDisplayId(),
+      display::ScreenManager::GetInstance()->GetPrimaryDisplayId(),
       display::kInvalidDisplayId);
 }
 
diff --git a/services/ui/ws/user_display_manager_unittest.cc b/services/ui/ws/user_display_manager_unittest.cc
index afb1c70e..174886f 100644
--- a/services/ui/ws/user_display_manager_unittest.cc
+++ b/services/ui/ws/user_display_manager_unittest.cc
@@ -17,7 +17,7 @@
 #include "services/ui/common/task_runner_test_base.h"
 #include "services/ui/common/types.h"
 #include "services/ui/common/util.h"
-#include "services/ui/display/platform_screen.h"
+#include "services/ui/display/screen_manager.h"
 #include "services/ui/ws/display_manager.h"
 #include "services/ui/ws/ids.h"
 #include "services/ui/ws/server_window.h"
@@ -110,22 +110,22 @@
     return ws_test_helper_.window_server_delegate();
   }
 
-  TestPlatformScreen& platform_screen() { return platform_screen_; }
+  TestScreenManager& screen_manager() { return screen_manager_; }
 
  private:
   // testing::Test:
   void SetUp() override {
     TaskRunnerTestBase::SetUp();
-    platform_screen_.Init(window_server()->display_manager());
+    screen_manager_.Init(window_server()->display_manager());
   }
 
   WindowServerTestHelper ws_test_helper_;
-  TestPlatformScreen platform_screen_;
+  TestScreenManager screen_manager_;
   DISALLOW_COPY_AND_ASSIGN(UserDisplayManagerTest);
 };
 
 TEST_F(UserDisplayManagerTest, OnlyNotifyWhenFrameDecorationsSet) {
-  platform_screen().AddDisplay();
+  screen_manager().AddDisplay();
 
   TestDisplayManagerObserver display_manager_observer1;
   DisplayManager* display_manager = window_server()->display_manager();
@@ -153,7 +153,7 @@
 }
 
 TEST_F(UserDisplayManagerTest, AddObserverAfterFrameDecorationsSet) {
-  platform_screen().AddDisplay();
+  screen_manager().AddDisplay();
 
   TestDisplayManagerObserver display_manager_observer1;
   DisplayManager* display_manager = window_server()->display_manager();
@@ -175,7 +175,7 @@
 }
 
 TEST_F(UserDisplayManagerTest, AddRemoveDisplay) {
-  platform_screen().AddDisplay();
+  screen_manager().AddDisplay();
 
   TestDisplayManagerObserver display_manager_observer1;
   DisplayManager* display_manager = window_server()->display_manager();
@@ -195,7 +195,7 @@
             display_manager_observer1.GetAndClearObserverCalls());
 
   // Add another display.
-  const int64_t second_display_id = platform_screen().AddDisplay();
+  const int64_t second_display_id = screen_manager().AddDisplay();
   RunUntilIdle();
 
   // Observer should be notified immediately as frame decorations were set.
@@ -203,7 +203,7 @@
             display_manager_observer1.GetAndClearObserverCalls());
 
   // Remove the display and verify observer is notified.
-  platform_screen().RemoveDisplay(second_display_id);
+  screen_manager().RemoveDisplay(second_display_id);
   RunUntilIdle();
 
   EXPECT_EQ("OnDisplayRemoved 2",
@@ -211,7 +211,7 @@
 }
 
 TEST_F(UserDisplayManagerTest, NegativeCoordinates) {
-  platform_screen().AddDisplay();
+  screen_manager().AddDisplay();
 
   TestDisplayManagerObserver display_manager_observer1;
   DisplayManager* display_manager = window_server()->display_manager();
diff --git a/services/ui/ws/window_manager_state_unittest.cc b/services/ui/ws/window_manager_state_unittest.cc
index eebde14..a67f0bf 100644
--- a/services/ui/ws/window_manager_state_unittest.cc
+++ b/services/ui/ws/window_manager_state_unittest.cc
@@ -547,9 +547,9 @@
 TEST(WindowManagerStateShutdownTest, DestroyTreeBeforeDisplay) {
   WindowServerTestHelper ws_test_helper;
   WindowServer* window_server = ws_test_helper.window_server();
-  TestPlatformScreen platform_screen;
-  platform_screen.Init(window_server->display_manager());
-  platform_screen.AddDisplay();
+  TestScreenManager screen_manager;
+  screen_manager.Init(window_server->display_manager());
+  screen_manager.AddDisplay();
   const UserId kUserId1 = "2";
   AddWindowManager(window_server, kUserId1);
   ASSERT_EQ(1u, window_server->display_manager()->displays().size());
diff --git a/services/ui/ws/window_tree_unittest.cc b/services/ui/ws/window_tree_unittest.cc
index 708298c..2c0ded3 100644
--- a/services/ui/ws/window_tree_unittest.cc
+++ b/services/ui/ws/window_tree_unittest.cc
@@ -1355,10 +1355,10 @@
     // Create a tree with one window.
     WindowServerTestHelper ws_test_helper;
     WindowServer* window_server = ws_test_helper.window_server();
-    TestPlatformScreen platform_screen;
-    platform_screen.Init(window_server->display_manager());
+    TestScreenManager screen_manager;
+    screen_manager.Init(window_server->display_manager());
     window_server->user_id_tracker()->AddUserId(kTestUserId1);
-    platform_screen.AddDisplay();
+    screen_manager.AddDisplay();
 
     AddWindowManager(window_server, kTestUserId1);
     window_server->user_id_tracker()->SetActiveUserId(kTestUserId1);
diff --git a/testing/buildbot/README.md b/testing/buildbot/README.md
new file mode 100644
index 0000000..68cfcce5
--- /dev/null
+++ b/testing/buildbot/README.md
@@ -0,0 +1,67 @@
+# Buildbot Testing Configuration Files
+
+The files in this directory control how tests are run on the
+[Chromium buildbots](https://www.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot).
+In addition to specifying what tests run on which builders, they also specify
+special arguments and constraints for the tests.
+
+## A tour of the directory
+* <master_name\>.json -- buildbot configuration json files. These are used to
+configure what tests are run on what builders, in addition to specifying
+builder-specific arguments and parameters.
+* [gn_isolate_map.pyl](./gn_isolate_map.pyl) -- maps Ninja build target names
+to GN labels. Allows for certain overrides to get certain tests targets to work
+with GN (and properly run when isolated).
+* [trybot_analyze_config.json](./trybot_analyze_config.json) -- used to provide
+exclusions to
+[the analyze step](https://www.chromium.org/developers/testing/commit-queue/chromium_trybot-json)
+on trybots.
+* [filters/](./filters/) -- filters out tests that shouldn't be
+run in a particular mode.
+* [timeouts.py](./timeouts.py) -- calculates acceptable timeouts for tests by
+analyzing their execution on
+[swarming](https://github.com/luci/luci-py/tree/master/appengine/swarming).
+* [manage.py](./manage.py) -- makes sure the buildbot configuration json is in
+a standardized format.
+
+## How the files are consumed
+### Buildbot configuration json
+Logic in the
+[Chromium recipe](https://chromium.googlesource.com/chromium/tools/build/+/refs/heads/master/scripts/slave/recipes/chromium.py)
+looks up each builder for each master and test generators in
+[chromium_tests/steps.py](https://chromium.googlesource.com/chromium/tools/build/+/refs/heads/master/scripts/slave/recipe_modules/chromium_tests/steps.py)
+parse the data. For example, as of
+[a6e11220](https://chromium.googlesource.com/chromium/tools/build/+/a6e11220d97d578d6ba091abd68beba28a004722)
+[generate_gtest](https://chromium.googlesource.com/chromium/tools/build/+/a6e11220d97d578d6ba091abd68beba28a004722/scripts/slave/recipe_modules/chromium_tests/steps.py#416)
+parses any entry in a builder's
+['gtest_tests'](https://chromium.googlesource.com/chromium/src/+/5750756522296b2a9a08009d8d2cc90db3b88f56/testing/buildbot/chromium.android.json#1243)
+entry.
+
+## How to edit
+### Making the changes
+#### Buildbot configuration json
+After editing any buildbot json, run `./manage.py -w` to load and write in the
+canonical format. Then commit as normal.
+
+Note that trybots mirror regular waterfall bots, with the mapping defined in
+[trybots.py](https://chromium.googlesource.com/chromium/tools/build/+/refs/heads/master/scripts/slave/recipe_modules/chromium_tests/trybots.py).
+This means that, as of
+[81fcc4bc](https://chromium.googlesource.com/chromium/src/+/81fcc4bc6123ace8dd37db74fd2592e3e15ea46a/testing/buildbot/),
+if you want to edit
+[linux_android_rel_ng](https://chromium.googlesource.com/chromium/tools/build/+/59a2653d5f143213f4f166714657808b0c646bd7/scripts/slave/recipe_modules/chromium_tests/trybots.py#142),
+you actually need to edit
+[Android Tests](https://chromium.googlesource.com/chromium/src/+/81fcc4bc6123ace8dd37db74fd2592e3e15ea46a/testing/buildbot/chromium.linux.json#23).
+
+### Trying the changes on trybots
+You should be able to try build changes that affect the trybots directly (for
+example, adding a test to linux_android_rel_ng should show up immediately in
+your tryjob). Non-trybot changes have to be landed manually :(.
+
+## Capacity considerations when editing the buildbot configuration json
+When adding tests or bumping timeouts in the buildbot configuration json, care
+must be taken to ensure the infrastructure has capacity to handle the extra
+load.  This is especially true for the established
+[Chromium CQ builders](https://chromium.googlesource.com/chromium/src/+/master/infra/config/cq.cfg),
+as they operate under strict execution requirements. Make sure to get an
+infrastructure engineer on the Crossover Team to sign off that there is both
+buildbot and swarming capacity available.
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index b94763bd..b5968ca 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -29,12 +29,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "ash_content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "aura_unittests"
       },
       {
@@ -541,12 +535,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "ash_content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "aura_unittests"
       },
       {
@@ -995,12 +983,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "ash_content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "aura_unittests"
       },
       {
@@ -1467,12 +1449,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "ash_content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "aura_unittests"
       },
       {
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index ab95d59..07ff7fe 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -535,7 +535,10 @@
 crbug.com/498539 [ Mac ] inspector/sources/debugger/live-edit-no-reveal.html [ Crash Pass Timeout ]
 crbug.com/498539 [ Win7 ] inspector/sources/debugger-pause/debugger-eval-on-call-frame-inside-iframe.html [ Failure Pass ]
 crbug.com/498539 [ Mac10.9 ] inspector/console/console-uncaught-exception.html [ Failure Pass ]
-crbug.com/498539 crbug.com/405845 [ Win7 ] inspector/console/console-dir-es6.html [ Failure Pass NeedsManualRebaseline ]
+
+# TODO(luoe): The old expectation before NeedsManualRebaseline was the following
+# crbug.com/498539 [ Win7 ] inspector/console/console-dir-es6.html [ Failure Pass ]
+crbug.com/405845 inspector/console/console-dir-es6.html [ NeedsManualRebaseline ]
 crbug.com/498539 [ Win7 ] inspector/elements/styles-4/styles-update-from-js.html [ Crash Pass ]
 
 crbug.com/596968 [ Win ] inspector-protocol/input/eventTimestamp.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-zero-size-readback.html b/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-zero-size-readback.html
new file mode 100644
index 0000000..c9acf3a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-zero-size-readback.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+promise_test(
+  t => { return zeroSizeReadback("width", "none", t); }, 
+  "Verify convertToBlob on a 0 width OffscreenCanvas with no context."
+);
+
+promise_test(
+  t => { return zeroSizeReadback("width", "2d", t); }, 
+  "Verify convertToBlob and getImageData on a 0 width OffscreenCanvas with a 2d context."
+);
+
+promise_test(
+  t => { return zeroSizeReadback("width", "webgl", t); }, 
+  "Verify convertToBlob on a 0 width OffscreenCanvas with a webgl context."
+);
+
+promise_test(
+  t => { return zeroSizeReadback("width", "webgl2", t); }, 
+  "Verify convertToBlob on a 0 width OffscreenCanvas with a webgl2 context."
+);
+
+promise_test(
+  t => { return zeroSizeReadback("height", "none", t); }, 
+  "Verify convertToBlob on a 0 height OffscreenCanvas with no context."
+);
+
+promise_test(
+  t => { return zeroSizeReadback("height", "2d", t); }, 
+  "Verify convertToBlob and getImageData on a 0 height OffscreenCanvas with a 2d context."
+);
+
+promise_test(
+  t => { return zeroSizeReadback("height", "webgl", t); }, 
+  "Verify convertToBlob on a 0 height OffscreenCanvas with a webgl context."
+);
+
+promise_test(
+  t => { return zeroSizeReadback("height", "webgl2", t); }, 
+  "Verify convertToBlob on a 0 height OffscreenCanvas with a webgl2 context."
+);
+
+function zeroSizeReadback(zeroDimension, contextType, t) {
+  var offscreen = new OffscreenCanvas(10, 10);
+  eval("offscreen." + zeroDimension + " = 0");
+  // Verify that one of the dimensions was indeed zeroed.
+  assert_equals(offscreen.width * offscreen.height, 0);
+
+  var ctx;
+  if (contextType != "none") {
+    ctx = offscreen.getContext(contextType);
+  }
+
+  if (contextType == '2d') {
+    var imgdata = ctx.getImageData(0, 0, 1, 1);
+    assert_equals(imgdata.width, 1);
+    assert_equals(imgdata.height, 1);
+  }
+
+  return promise_rejects(t, new DOMException('', 'IndexSizeError'), offscreen.convertToBlob());
+}
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.html b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.js
similarity index 70%
rename from third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.js
index 8c79670a..3d4a7ad 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.js
@@ -1,12 +1,7 @@
-<html>
-<head>
-<base href="/inspector-debug/"></base>
-<script src="/inspector-debug/Runtime.js"></script>
-<script src="/inspector-unit/inspector-unit-test.js"></script>
-<script>
-UnitTest.addDependency("ui_lazy");
-function test()
-{
+TestRunner.loadLazyModules(["ui_lazy"]).then(test);
+function test() {
+    TestRunner.addResult("Check to see that FilteredItemSelectionDialog uses proper regex to filter results.");
+
     var overridenInput = [];
     var overrideShowMatchingItems = true;
     var history = [];
@@ -22,7 +17,7 @@
         itemCount() { return overridenInput.length; }
         selectItem(itemIndex, promptValue)
         {
-            UnitTest.addResult("Selected item index: " + itemIndex);
+            TestRunner.addResult("Selected item index: " + itemIndex);
         }
         shouldShowMatchingItems () { return overrideShowMatchingItems; }
     };
@@ -34,34 +29,34 @@
         overridenInput = input;
         overrideShowMatchingItems = !hideMatchingItems;
 
-        UnitTest.addResult("Input:" + JSON.stringify(input));
+        TestRunner.addResult("Input:" + JSON.stringify(input));
 
         var filteredSelectionDialog = new UI.FilteredListWidget(delegate);
         filteredSelectionDialog.showAsDialog();
-        var promise = UnitTest.addSniffer(filteredSelectionDialog, "_itemsFilteredForTest").then(accept);
+        var promise = TestRunner.addSniffer(filteredSelectionDialog, "_itemsFilteredForTest").then(accept);
         filteredSelectionDialog.setQuery(query);
         filteredSelectionDialog._updateAfterItemsLoaded();
         return promise;
 
         function dump()
         {
-            UnitTest.addResult("Query:" + JSON.stringify(filteredSelectionDialog._value()));
+            TestRunner.addResult("Query:" + JSON.stringify(filteredSelectionDialog._value()));
             var items = filteredSelectionDialog._filteredItems;
             var output = [];
             for (var i = 0; i < items.length; ++i)
                 output.push(delegate.itemKeyAt(items[i]));
-            UnitTest.addResult("Output:" + JSON.stringify(output));
+            TestRunner.addResult("Output:" + JSON.stringify(output));
         }
 
         function accept()
         {
             dump();
-            filteredSelectionDialog._onEnter(UnitTest.createKeyEvent("Enter"));
-            UnitTest.addResult("History:" + JSON.stringify(history));
+            filteredSelectionDialog._onEnter(TestRunner.createKeyEvent("Enter"));
+            TestRunner.addResult("History:" + JSON.stringify(history));
         }
     }
 
-    UnitTest.runTests([
+    TestRunner.runTests([
         function emptyQueryMatchesEverything()
         {
             return checkQuery("", ["a", "bc"]);
@@ -98,12 +93,3 @@
         }
     ]);
 }
-
-</script>
-</head>
-
-<body>
-<p>Check to see that FilteredItemSelectionDialog uses proper regex to filter results.</p>
-</body>
-</html>
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/inspector-unit-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/inspector-unit-test.js
deleted file mode 100644
index bb1ca19..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/inspector-unit-test.js
+++ /dev/null
@@ -1,157 +0,0 @@
-var UnitTest = {};
-(function()
-{
-    var lazyModules = [];
-    var oldLoadResourcePromise = Runtime.loadResourcePromise;
-    Runtime.loadResourcePromise = function(url)
-    {
-        if (url.startsWith("/"))
-            return oldLoadResourcePromise(url);
-
-        if (!url.startsWith("http://"))
-            return oldLoadResourcePromise("/inspector-debug/" + url);
-
-        var parsedURL = new URL(url, location.href);
-        var parsedLocation = new URL(location.href);
-
-        // hosted devtools is confused.
-        parsedURL.pathname = parsedURL.pathname.replace('/inspector-unit/', '/inspector-debug/');
-        return oldLoadResourcePromise(parsedURL.toString());
-    }
-
-    if (window.testRunner) {
-        testRunner.dumpAsText();
-        testRunner.waitUntilDone();
-    }
-
-    var results = [];
-    UnitTest.completeTest = function()
-    {
-        if (!window.testRunner) {
-            console.log("Test Done");
-            return;
-        }
-
-        document.documentElement.childNodes.forEach(x => x.remove());
-        var outputElement = document.createElement("div");
-        // Support for svg - add to document, not body, check for style.
-        if (outputElement.style) {
-            outputElement.style.whiteSpace = "pre";
-            outputElement.style.height = "10px";
-            outputElement.style.overflow = "hidden";
-        }
-        document.documentElement.appendChild(outputElement);
-        for (var i = 0; i < results.length; i++) {
-            outputElement.appendChild(document.createTextNode(results[i]));
-            outputElement.appendChild(document.createElement("br"));
-        }
-        results = [];
-        testRunner.notifyDone();
-    };
-
-    UnitTest.addResult = function(text)
-    {
-        if (window.testRunner)
-            results.push(String(text));
-        else
-            console.log(text);
-    };
-
-    UnitTest.runTests = function(tests)
-    {
-        nextTest();
-
-        function nextTest()
-        {
-            var test = tests.shift();
-            if (!test) {
-                UnitTest.completeTest();
-                return;
-            }
-            UnitTest.addResult("\ntest: " + test.name);
-            var testPromise = test();
-            if (!(testPromise instanceof Promise))
-                testPromise = Promise.resolve();
-            testPromise.then(nextTest);
-        }
-    };
-
-    UnitTest.addSniffer = function(receiver, methodName)
-    {
-        return new Promise(function(resolve, reject)
-        {
-            var original = receiver[methodName];
-            if (typeof original !== "function") {
-                reject("Cannot find method to override: " + methodName);
-                return;
-            }
-
-            receiver[methodName] = function(var_args)
-            {
-                try {
-                    var result = original.apply(this, arguments);
-                } finally {
-                    receiver[methodName] = original;
-                }
-                // In case of exception the override won't be called.
-                try {
-                    Array.prototype.push.call(arguments, result);
-                    resolve.apply(this, arguments);
-                } catch (e) {
-                    reject("Exception in overriden method '" + methodName + "': " + e);
-                    UnitTest.completeTest();
-                }
-                return result;
-            };
-        });
-    };
-
-    UnitTest.addDependency = function(lazyModule)
-    {
-        lazyModules.push(lazyModule);
-    };
-
-    UnitTest.createKeyEvent = function(key, ctrlKey, altKey, shiftKey, metaKey)
-    {
-        return new KeyboardEvent("keydown", { key: key, bubbles: true, cancelable: true, ctrlKey: ctrlKey, altKey: altKey, shiftKey: shiftKey, metaKey: metaKey });
-    };
-
-    function completeTestOnError(message, source, lineno, colno, error)
-    {
-        UnitTest.addResult("TEST ENDED IN ERROR: " + error.stack);
-        UnitTest.completeTest();
-    }
-    window.onerror = completeTestOnError;
-
-    Runtime.startApplication("/inspector-unit/inspector-unit-test").then(runTest);
-
-    function runTest()
-    {
-        var description = document.body.textContent.trim();
-        if (description)
-            UnitTest.addResult(description);
-
-        Common.settings = new Common.Settings(new Common.SettingsStorage(
-            {},
-            InspectorFrontendHost.setPreference,
-            InspectorFrontendHost.removePreference,
-            InspectorFrontendHost.clearPreferences));
-
-
-        UI.viewManager = new UI.ViewManager();
-        UI.initializeUIUtils(document, Common.settings.createSetting("uiTheme", "default"));
-        UI.installComponentRootStyles(document.body);
-
-        UI.zoomManager = new UI.ZoomManager(window, InspectorFrontendHost);
-        UI.inspectorView = UI.InspectorView.instance();
-        UI.ContextMenu.initialize();
-        UI.ContextMenu.installHandler(document);
-        UI.Tooltip.installHandler(document);
-
-        var rootView = new UI.RootView();
-        UI.inspectorView.show(rootView.element);
-        rootView.attachToDocument(document);
-        Promise.all(lazyModules.map(lazyModule => window.runtime.loadModulePromise(lazyModule))).then(test);
-    }
-})();
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/inspector-unit-test.json b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/inspector-unit-test.json
deleted file mode 100644
index a5a7695..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/inspector-unit-test.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-    "modules" : [
-        { "name": "platform", "type": "autostart" },
-        { "name": "ui", "type": "autostart" },
-        { "name": "host", "type": "autostart" },
-        { "name": "common", "type": "autostart" },
-        { "name": "workspace", "type": "autostart" },
-        { "name": "source_frame", "type": "autostart" },
-        { "name": "text_editor", "type": "autostart" },
-        { "name": "cm_modes", "type": "autostart" },
-        { "name": "diff", "type": "autostart" },
-        { "name": "ui_lazy" }
-    ],
-
-    "has_html": true
-}
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/static-viewport-control.html b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/static-viewport-control.html
deleted file mode 100644
index 493b6805..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/static-viewport-control.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<html>
-<head>
-<base href="/inspector-debug/"></base>
-<script src="/inspector-debug/Runtime.js"></script>
-<script src="/inspector-unit/inspector-unit-test.js"></script>
-<script>
-function test() {
-    var items = [];
-    var heights = [];
-    for (var i = 0; i < 100; i++){
-        items[i] = document.createElement("div");
-        items[i].style.height = (heights[i] = (i % 4) ? 50 : 28) + "px";
-        items[i].textContent = i;
-    }
-    var viewport = new UI.StaticViewportControl({
-        fastItemHeight: i => heights[i],
-        itemCount: _ => items.length,
-        itemElement: i => items[i]
-    });
-    viewport.element.style.height = "300px";
-    UI.inspectorView.element.appendChild(viewport.element);
-
-    viewport.refresh();
-    dumpViewport();
-
-    viewport.forceScrollItemToBeFirst(26);
-    dumpViewport();
-
-    viewport.scrollItemIntoView(33);
-    dumpViewport();
-
-    viewport.scrollItemIntoView(30);
-    dumpViewport();
-
-    viewport.forceScrollItemToBeFirst(30);
-    dumpViewport();
-
-    viewport.forceScrollItemToBeLast(88);
-    dumpViewport();
-
-    for (var i = 0; i < 100; i++)
-        items[i].style.height = (heights[i] = (i % 2) ? 55 : 63) + "px";
-    viewport.refresh();
-    viewport.forceScrollItemToBeLast(88);
-    dumpViewport();
-
-    UnitTest.completeTest();
-
-    function dumpViewport()
-    {
-        UnitTest.addResult("First:" + viewport.firstVisibleIndex());
-        UnitTest.addResult("Last:" + viewport.lastVisibleIndex());
-        UnitTest.addResult("Active Items:" + viewport._innerElement.children.length);
-        UnitTest.addResult("");
-    }
-}
-</script>
-</head>
-<body>
-This tests if the StaticViewportControl works properly.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/static-viewport-control.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/static-viewport-control.js
new file mode 100644
index 0000000..0b333830
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/static-viewport-control.js
@@ -0,0 +1,50 @@
+TestRunner.addResult("This tests if the StaticViewportControl works properly.");
+
+var items = [];
+var heights = [];
+for (var i = 0; i < 100; i++){
+    items[i] = document.createElement("div");
+    items[i].style.height = (heights[i] = (i % 4) ? 50 : 28) + "px";
+    items[i].textContent = i;
+}
+var viewport = new UI.StaticViewportControl({
+    fastItemHeight: i => heights[i],
+    itemCount: _ => items.length,
+    itemElement: i => items[i]
+});
+viewport.element.style.height = "300px";
+UI.inspectorView.element.appendChild(viewport.element);
+
+viewport.refresh();
+dumpViewport();
+
+viewport.forceScrollItemToBeFirst(26);
+dumpViewport();
+
+viewport.scrollItemIntoView(33);
+dumpViewport();
+
+viewport.scrollItemIntoView(30);
+dumpViewport();
+
+viewport.forceScrollItemToBeFirst(30);
+dumpViewport();
+
+viewport.forceScrollItemToBeLast(88);
+dumpViewport();
+
+for (var i = 0; i < 100; i++)
+    items[i].style.height = (heights[i] = (i % 2) ? 55 : 63) + "px";
+viewport.refresh();
+viewport.forceScrollItemToBeLast(88);
+dumpViewport();
+
+TestRunner.completeTest();
+
+function dumpViewport()
+{
+    TestRunner.addResult("First:" + viewport.firstVisibleIndex());
+    TestRunner.addResult("Last:" + viewport.lastVisibleIndex());
+    TestRunner.addResult("Active Items:" + viewport._innerElement.children.length);
+    TestRunner.addResult("");
+}
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/suggest-box.html b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/suggest-box.html
deleted file mode 100644
index 590cdb5..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/suggest-box.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<html>
-<head>
-<base href="/inspector-debug/"></base>
-<script src="/inspector-debug/Runtime.js"></script>
-<script src="/inspector-unit/inspector-unit-test.js"></script>
-<script>
-function test() {
-  var delegate = {
-    applySuggestion: function(suggestion, isIntermediateSuggestion) {
-      UnitTest.addResult((isIntermediateSuggestion ? "Intermediate " : "") + "Suggestion Applied: " + suggestion);
-    },
-    acceptSuggestion: function() {
-      UnitTest.addResult("Suggestion accepted");
-    }
-  };
-  var div = document.createElement("div");
-  UI.inspectorView.element.appendChild(div);
-  var suggestBox = new UI.SuggestBox(delegate);
-
-  UnitTest.addResult("");
-  UnitTest.addResult("Testing that the first item is selected.");
-  suggestBox.updateSuggestions(new AnchorBox(50, 50, 400, 400), [
-    {title: "First"},
-    {title: "Hello"},
-    {title: "The best suggestion"}], true, true, "e");
-
-  UnitTest.addResult("");
-  UnitTest.addResult("Testing that no item is selected.");
-  suggestBox.updateSuggestions(new AnchorBox(50, 50, 400, 400), [
-    {title: "First"},
-    {title: "Hello", priority: 2},
-    {title: "The best suggestion", priority: 5}], false, true, "e");
-
-  UnitTest.addResult("");
-  UnitTest.addResult("Testing that highest priority item is selected.");
-  suggestBox.updateSuggestions(new AnchorBox(50, 50, 400, 400), [
-    {title: "First"},
-    {title: "Hello", priority: 2},
-    {title: "The best suggestion", priority: 5}], true, true, "e");
-
-  UnitTest.addResult("");
-  UnitTest.addResult("Testing that arrow keys can be used for selection.");
-  suggestBox.keyPressed(UnitTest.createKeyEvent("ArrowUp"));
-  suggestBox.keyPressed(UnitTest.createKeyEvent("ArrowUp"));
-  suggestBox.keyPressed(UnitTest.createKeyEvent("ArrowUp"));
-  suggestBox.keyPressed(UnitTest.createKeyEvent("ArrowDown"));
-  suggestBox.keyPressed(UnitTest.createKeyEvent("ArrowDown"));
-
-  UnitTest.addResult("");
-  UnitTest.addResult("Testing that enter can be used to accept a suggestion.");
-  suggestBox.keyPressed(UnitTest.createKeyEvent("Enter"));
-
-  UnitTest.addResult("");
-  UnitTest.addResult("Testing that highest priority item is selected.");
-  suggestBox.updateSuggestions(new AnchorBox(50, 50, 400, 400), [
-    {title: "First"},
-    {title: "Hello", priority: 2},
-    {title: "The best suggestion", priority: 5}], true, true, "e");
-
-  UnitTest.completeTest();
-}
-</script>
-</head>
-<body>
-This tests if the SuggestBox works properly.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/suggest-box.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/suggest-box.js
new file mode 100644
index 0000000..0e784ca
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/suggest-box.js
@@ -0,0 +1,55 @@
+TestRunner.addResult("This tests if the SuggestBox works properly.");
+
+var delegate = {
+    applySuggestion: function(suggestion, isIntermediateSuggestion) {
+        TestRunner.addResult((isIntermediateSuggestion ? "Intermediate " : "") + "Suggestion Applied: " + suggestion);
+    },
+    acceptSuggestion: function() {
+        TestRunner.addResult("Suggestion accepted");
+    }
+};
+var div = document.createElement("div");
+UI.inspectorView.element.appendChild(div);
+var suggestBox = new UI.SuggestBox(delegate);
+
+TestRunner.addResult("");
+TestRunner.addResult("Testing that the first item is selected.");
+suggestBox.updateSuggestions(new AnchorBox(50, 50, 400, 400), [
+    {title: "First"},
+    {title: "Hello"},
+    {title: "The best suggestion"}], true, true, "e");
+
+TestRunner.addResult("");
+TestRunner.addResult("Testing that no item is selected.");
+suggestBox.updateSuggestions(new AnchorBox(50, 50, 400, 400), [
+    {title: "First"},
+    {title: "Hello", priority: 2},
+    {title: "The best suggestion", priority: 5}], false, true, "e");
+
+TestRunner.addResult("");
+TestRunner.addResult("Testing that highest priority item is selected.");
+suggestBox.updateSuggestions(new AnchorBox(50, 50, 400, 400), [
+    {title: "First"},
+    {title: "Hello", priority: 2},
+    {title: "The best suggestion", priority: 5}], true, true, "e");
+
+TestRunner.addResult("");
+TestRunner.addResult("Testing that arrow keys can be used for selection.");
+suggestBox.keyPressed(TestRunner.createKeyEvent("ArrowUp"));
+suggestBox.keyPressed(TestRunner.createKeyEvent("ArrowUp"));
+suggestBox.keyPressed(TestRunner.createKeyEvent("ArrowUp"));
+suggestBox.keyPressed(TestRunner.createKeyEvent("ArrowDown"));
+suggestBox.keyPressed(TestRunner.createKeyEvent("ArrowDown"));
+
+TestRunner.addResult("");
+TestRunner.addResult("Testing that enter can be used to accept a suggestion.");
+suggestBox.keyPressed(TestRunner.createKeyEvent("Enter"));
+
+TestRunner.addResult("");
+TestRunner.addResult("Testing that highest priority item is selected.");
+suggestBox.updateSuggestions(new AnchorBox(50, 50, 400, 400), [
+    {title: "First"},
+    {title: "Hello", priority: 2},
+    {title: "The best suggestion", priority: 5}], true, true, "e");
+
+TestRunner.completeTest();
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/test-failure.html b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/test-failure.html
deleted file mode 100644
index 1a0bc248..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/test-failure.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<html>
-<head>
-<base href="/inspector-debug/"></base>
-<script src="/inspector-debug/Runtime.js"></script>
-<script src="/inspector-unit/inspector-unit-test.js"></script>
-<script>
-function test()
-{
-    setTimeout(_ => { throw {stack: "This error is expected"} }, 0);
-}
-</script>
-</head>
-<body>
-Tests that a test will properly exit if it has an asynchronous error.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/test-failure.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/test-failure.js
new file mode 100644
index 0000000..7db1257
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/test-failure.js
@@ -0,0 +1,2 @@
+TestRunner.addResult("Tests that a test will properly exit if it has an asynchronous error.");
+setTimeout(_ => { throw {stack: "This error is expected"} }, 0);
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt-hint.html b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt-hint.html
deleted file mode 100644
index ac8a2c1..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt-hint.html
+++ /dev/null
@@ -1,124 +0,0 @@
-<html>
-<head>
-<base href="/inspector-debug/"></base>
-<script src="/inspector-debug/Runtime.js"></script>
-<script src="/inspector-unit/inspector-unit-test.js"></script>
-<script type="text/javascript">
-
-function test()
-{
-    var suggestions = [{title:"testTextPrompt"}];
-    var waitingForAutocomplete = null;
-    var completionsDone = function () {
-        console.error("completionsDone called too early!");
-        UnitTest.completeTest();
-    }
-    var prompt = new UI.TextPrompt();
-    prompt.initialize(completions);
-    var element = createElement("div");
-    UI.inspectorView.element.appendChild(element);
-    var proxy = prompt.attachAndStartEditing(element);
-    prompt.setText("testT");
-    waitForAutocomplete().then(step1);
-    prompt.complete();
-    dumpTextPrompt();
-
-    function step1() {
-        dumpTextPrompt();
-
-        typeCharacter("e");
-        dumpTextPrompt();
-
-        waitForAutocomplete().then(step2);
-    }
-    function step2()
-    {
-        dumpTextPrompt();
-
-        typeCharacter("z");
-        waitForAutocomplete().then(step3);
-    }
-
-    function step3()
-    {
-        dumpTextPrompt();
-        typeCharacter(null);
-        waitForAutocomplete().then(step4);
-    }
-
-    function step4()
-    {
-        dumpTextPrompt();
-        typeCharacter(null);
-        waitForAutocomplete().then(step5);
-    }
-    function step5()
-    {
-        dumpTextPrompt();
-        prompt.setText("something_before test");
-        prompt.complete();
-        completionsDone().then(()=>{
-            dumpTextPrompt();
-            typeCharacter("T");
-            dumpTextPrompt();
-            UnitTest.completeTest();
-        });
-    }
-
-    function completions(expression, query)
-    {
-        var callback;
-        var promise = new Promise(x => callback = x);
-        UnitTest.addResult("Requesting completions");
-        completionsDone = () => {
-            callback(suggestions.filter(s => s.title.startsWith(query.toString())))
-            return Promise.resolve();
-        };
-        var temp = waitingForAutocomplete;
-        waitingForAutocomplete = null;
-        if (temp)
-            temp();
-        return promise;
-    }
-
-    function waitForAutocomplete()
-    {
-        return new Promise(x => waitingForAutocomplete = x).then(() => completionsDone());
-    }
-
-    function dumpTextPrompt()
-    {
-        UnitTest.addResult("Text:" + prompt.text());
-        UnitTest.addResult("TextWithCurrentSuggestion:" + prompt.textWithCurrentSuggestion());
-        UnitTest.addResult("");
-    }
-
-    function typeCharacter(character)
-    {
-        var keyboardEvent = new KeyboardEvent("keydown", {
-            key: character || "Backspace",
-            charCode: character ? character.charCodeAt(0) : ""
-        });
-        element.dispatchEvent(keyboardEvent);
-
-        var selection = element.getComponentSelection();
-        var range = selection.getRangeAt(0);
-        var textNode = prompt._ghostTextElement.parentNode ? prompt._ghostTextElement.previousSibling : element.childTextNodes()[element.childTextNodes().length - 1];
-        if (!character)
-            textNode.textContent = textNode.textContent.substring(0,textNode.textContent.length-1);
-        else
-            textNode.textContent += character;
-        range.setStart(range.startContainer, range.startContainer.textContent.length);
-        selection.removeAllRanges();
-        selection.addRange(range);
-        element.dispatchEvent(new Event("input", {bubbles: true, cancelable: false}));
-    }
-}
-
-
-</script>
-</head>
-<body>
-<p>Tests that the hint displays properly on a UI.TextPrompt with autocomplete.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt-hint.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt-hint.js
new file mode 100644
index 0000000..cb309dc7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt-hint.js
@@ -0,0 +1,108 @@
+TestRunner.addResult("Tests that the hint displays properly on a UI.TextPrompt with autocomplete.");
+
+var suggestions = [{title:"testTextPrompt"}];
+var waitingForAutocomplete = null;
+var completionsDone = function () {
+    console.error("completionsDone called too early!");
+    TestRunner.completeTest();
+}
+var prompt = new UI.TextPrompt();
+prompt.initialize(completions);
+var element = createElement("div");
+UI.inspectorView.element.appendChild(element);
+var proxy = prompt.attachAndStartEditing(element);
+prompt.setText("testT");
+waitForAutocomplete().then(step1);
+prompt.complete();
+dumpTextPrompt();
+
+function step1() {
+    dumpTextPrompt();
+
+    typeCharacter("e");
+    dumpTextPrompt();
+
+    waitForAutocomplete().then(step2);
+}
+function step2()
+{
+    dumpTextPrompt();
+
+    typeCharacter("z");
+    waitForAutocomplete().then(step3);
+}
+
+function step3()
+{
+    dumpTextPrompt();
+    typeCharacter(null);
+    waitForAutocomplete().then(step4);
+}
+
+function step4()
+{
+    dumpTextPrompt();
+    typeCharacter(null);
+    waitForAutocomplete().then(step5);
+}
+function step5()
+{
+    dumpTextPrompt();
+    prompt.setText("something_before test");
+    prompt.complete();
+    completionsDone().then(()=>{
+        dumpTextPrompt();
+        typeCharacter("T");
+        dumpTextPrompt();
+        TestRunner.completeTest();
+    });
+}
+
+function completions(expression, query)
+{
+    var callback;
+    var promise = new Promise(x => callback = x);
+    TestRunner.addResult("Requesting completions");
+    completionsDone = () => {
+        callback(suggestions.filter(s => s.title.startsWith(query.toString())))
+        return Promise.resolve();
+    };
+    var temp = waitingForAutocomplete;
+    waitingForAutocomplete = null;
+    if (temp)
+        temp();
+    return promise;
+}
+
+function waitForAutocomplete()
+{
+    return new Promise(x => waitingForAutocomplete = x).then(() => completionsDone());
+}
+
+function dumpTextPrompt()
+{
+    TestRunner.addResult("Text:" + prompt.text());
+    TestRunner.addResult("TextWithCurrentSuggestion:" + prompt.textWithCurrentSuggestion());
+    TestRunner.addResult("");
+}
+
+function typeCharacter(character)
+{
+    var keyboardEvent = new KeyboardEvent("keydown", {
+        key: character || "Backspace",
+        charCode: character ? character.charCodeAt(0) : ""
+    });
+    element.dispatchEvent(keyboardEvent);
+
+    var selection = element.getComponentSelection();
+    var range = selection.getRangeAt(0);
+    var textNode = prompt._ghostTextElement.parentNode ? prompt._ghostTextElement.previousSibling : element.childTextNodes()[element.childTextNodes().length - 1];
+    if (!character)
+        textNode.textContent = textNode.textContent.substring(0,textNode.textContent.length-1);
+    else
+        textNode.textContent += character;
+    range.setStart(range.startContainer, range.startContainer.textContent.length);
+    selection.removeAllRanges();
+    selection.addRange(range);
+    element.dispatchEvent(new Event("input", {bubbles: true, cancelable: false}));
+}
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt.html b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt.html
deleted file mode 100644
index ef50f84..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<html>
-<head>
-<base href="/inspector-debug/"></base>
-<script src="/inspector-debug/Runtime.js"></script>
-<script src="/inspector-unit/inspector-unit-test.js"></script>
-<script>
-function test() {
-    var suggestions = ["heyoo", "hey it's a suggestion", "hey another suggestion"].map(s => ({title: s}));
-    var prompt = new UI.TextPrompt();
-    prompt.initialize(() => Promise.resolve(suggestions));
-    var div = document.createElement("div");
-    UI.inspectorView.element.appendChild(div);
-    prompt.attachAndStartEditing(div);
-    prompt.setText("hey");
-    UnitTest.addSniffer(prompt, "_completionsReady").then(step2);
-    prompt.complete();
-    function step2() {
-        UnitTest.addResult("Text:" + prompt.text());
-        UnitTest.addResult("TextWithCurrentSuggestion:" + prompt.textWithCurrentSuggestion());
-
-        UnitTest.addResult("Test with inexact match:");
-        prompt.clearAutocomplete();
-        prompt.setText("inexactmatch");
-        UnitTest.addSniffer(prompt, "_completionsReady").then(step3);
-        prompt.complete();
-    }
-    function step3() {
-        UnitTest.addResult("Text:" + prompt.text());
-        UnitTest.addResult("TextWithCurrentSuggestion:" + prompt.textWithCurrentSuggestion());
-        UnitTest.completeTest();
-    }
-}
-</script>
-</head>
-<body>
-This tests if the TextPrompt autocomplete works properly.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt.js
new file mode 100644
index 0000000..9712fe2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/text-prompt.js
@@ -0,0 +1,26 @@
+TestRunner.addResult("This tests if the TextPrompt autocomplete works properly.");
+
+var suggestions = ["heyoo", "hey it's a suggestion", "hey another suggestion"].map(s => ({title: s}));
+var prompt = new UI.TextPrompt();
+prompt.initialize(() => Promise.resolve(suggestions));
+var div = document.createElement("div");
+UI.inspectorView.element.appendChild(div);
+prompt.attachAndStartEditing(div);
+prompt.setText("hey");
+TestRunner.addSniffer(prompt, "_completionsReady").then(step2);
+prompt.complete();
+function step2() {
+    TestRunner.addResult("Text:" + prompt.text());
+    TestRunner.addResult("TextWithCurrentSuggestion:" + prompt.textWithCurrentSuggestion());
+
+    TestRunner.addResult("Test with inexact match:");
+    prompt.clearAutocomplete();
+    prompt.setText("inexactmatch");
+    TestRunner.addSniffer(prompt, "_completionsReady").then(step3);
+    prompt.complete();
+}
+function step3() {
+    TestRunner.addResult("Text:" + prompt.text());
+    TestRunner.addResult("TextWithCurrentSuggestion:" + prompt.textWithCurrentSuggestion());
+    TestRunner.completeTest();
+}
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/trie.html b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/trie.html
deleted file mode 100644
index c6f5582..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/trie.html
+++ /dev/null
@@ -1,183 +0,0 @@
-<html>
-<head>
-<base href="/inspector-debug/"></base>
-<script src="/inspector-debug/Runtime.js"></script>
-<script src="/inspector-unit/inspector-unit-test.js"></script>
-<script>
-function test() {
-    var trie;
-
-    UnitTest.runTests([
-        function testAddWord()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "hello");
-            hasWord(trie, "he");
-            hasWord(trie, "hello");
-            hasWord(trie, "helloo");
-        },
-
-        function testAddWords()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "foo");
-            addWord(trie, "bar");
-            addWord(trie, "bazz");
-            hasWord(trie, "f");
-            hasWord(trie, "ba");
-            hasWord(trie, "baz");
-            hasWord(trie, "bar");
-            hasWord(trie, "bazz");
-        },
-
-        function testRemoveWord()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "foo");
-            removeWord(trie, "f");
-            removeWord(trie, "fo");
-            removeWord(trie, "fooo");
-            hasWord(trie, "foo");
-            removeWord(trie, "foo");
-            hasWord(trie, "foo");
-        },
-
-        function testAddAfterRemove()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "foo");
-            removeWord(trie, "foo");
-            addWord(trie, "bar");
-            hasWord(trie, "foo");
-            hasWord(trie, "bar");
-        },
-
-        function testWordOverwrite()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "foo");
-            addWord(trie, "foo");
-            removeWord(trie, "foo");
-            hasWord(trie, "foo");
-        },
-
-        function testRemoveNonExisting()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "foo");
-            removeWord(trie, "bar");
-            removeWord(trie, "baz");
-            hasWord(trie, "foo");
-        },
-
-        function testEmptyWord()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "");
-            hasWord(trie, "");
-            removeWord(trie, "");
-            hasWord(trie, "");
-        },
-
-        function testAllWords()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "foo");
-            addWord(trie, "bar");
-            addWord(trie, "bazzz");
-            words(trie);
-            words(trie, "f");
-            words(trie, "g");
-            words(trie, "b");
-            words(trie, "ba");
-            words(trie, "bar");
-            words(trie, "barz");
-            words(trie, "baz");
-        },
-
-        function testOneCharWords()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "a");
-            addWord(trie, "b");
-            addWord(trie, "c");
-            words(trie);
-        },
-
-        function testChainWords()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "f");
-            addWord(trie, "fo");
-            addWord(trie, "foo");
-            addWord(trie, "foo");
-            words(trie);
-        },
-
-        function testClearTrie()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "foo");
-            addWord(trie, "bar");
-            words(trie);
-            clear(trie);
-            words(trie);
-        },
-
-        function testLongestPrefix()
-        {
-            var trie = new Common.Trie();
-            addWord(trie, "fo");
-            addWord(trie, "food");
-            longestPrefix(trie, "fear", false);
-            longestPrefix(trie, "fear", true);
-            longestPrefix(trie, "football", false);
-            longestPrefix(trie, "football", true);
-            longestPrefix(trie, "bar", false);
-            longestPrefix(trie, "bar", true);
-            longestPrefix(trie, "foo", false);
-            longestPrefix(trie, "foo", true);
-        },
-    ]);
-
-    function hasWord(trie, word)
-    {
-        UnitTest.addResult(`trie.has("${word}") = ${trie.has(word)}`);
-    }
-
-    function addWord(trie, word)
-    {
-        UnitTest.addResult(`trie.add("${word}")`);
-        trie.add(word);
-    }
-
-    function removeWord(trie, word)
-    {
-        UnitTest.addResult(`trie.remove("${word}") = ${trie.remove(word)}`);
-    }
-
-    function words(trie, prefix)
-    {
-        var title = prefix ? `trie.words("${prefix}")` : `trie.words()`;
-        var words = trie.words(prefix);
-        var text = words.length ? `[\n    ${words.join(",\n    ")}\n]` : "[]";
-        UnitTest.addResult(title + " = " + text);
-    }
-
-    function clear(trie)
-    {
-        trie.clear();
-        UnitTest.addResult("trie.clear()");
-    }
-
-    function longestPrefix(trie, word, fullWordOnly)
-    {
-        UnitTest.addResult(`trie.longestPrefix("${word}", ${fullWordOnly}) = "${trie.longestPrefix(word, fullWordOnly)}"`);
-    }
-}
-</script>
-</head>
-<body>
-Verify "trie" functionality.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/trie.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/trie.js
new file mode 100644
index 0000000..bcc354f2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/trie.js
@@ -0,0 +1,170 @@
+var trie;
+TestRunner.addResult(`Verify "trie" functionality.`);
+
+TestRunner.runTests([
+    function testAddWord()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "hello");
+        hasWord(trie, "he");
+        hasWord(trie, "hello");
+        hasWord(trie, "helloo");
+    },
+
+    function testAddWords()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "foo");
+        addWord(trie, "bar");
+        addWord(trie, "bazz");
+        hasWord(trie, "f");
+        hasWord(trie, "ba");
+        hasWord(trie, "baz");
+        hasWord(trie, "bar");
+        hasWord(trie, "bazz");
+    },
+
+    function testRemoveWord()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "foo");
+        removeWord(trie, "f");
+        removeWord(trie, "fo");
+        removeWord(trie, "fooo");
+        hasWord(trie, "foo");
+        removeWord(trie, "foo");
+        hasWord(trie, "foo");
+    },
+
+    function testAddAfterRemove()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "foo");
+        removeWord(trie, "foo");
+        addWord(trie, "bar");
+        hasWord(trie, "foo");
+        hasWord(trie, "bar");
+    },
+
+    function testWordOverwrite()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "foo");
+        addWord(trie, "foo");
+        removeWord(trie, "foo");
+        hasWord(trie, "foo");
+    },
+
+    function testRemoveNonExisting()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "foo");
+        removeWord(trie, "bar");
+        removeWord(trie, "baz");
+        hasWord(trie, "foo");
+    },
+
+    function testEmptyWord()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "");
+        hasWord(trie, "");
+        removeWord(trie, "");
+        hasWord(trie, "");
+    },
+
+    function testAllWords()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "foo");
+        addWord(trie, "bar");
+        addWord(trie, "bazzz");
+        words(trie);
+        words(trie, "f");
+        words(trie, "g");
+        words(trie, "b");
+        words(trie, "ba");
+        words(trie, "bar");
+        words(trie, "barz");
+        words(trie, "baz");
+    },
+
+    function testOneCharWords()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "a");
+        addWord(trie, "b");
+        addWord(trie, "c");
+        words(trie);
+    },
+
+    function testChainWords()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "f");
+        addWord(trie, "fo");
+        addWord(trie, "foo");
+        addWord(trie, "foo");
+        words(trie);
+    },
+
+    function testClearTrie()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "foo");
+        addWord(trie, "bar");
+        words(trie);
+        clear(trie);
+        words(trie);
+    },
+
+    function testLongestPrefix()
+    {
+        var trie = new Common.Trie();
+        addWord(trie, "fo");
+        addWord(trie, "food");
+        longestPrefix(trie, "fear", false);
+        longestPrefix(trie, "fear", true);
+        longestPrefix(trie, "football", false);
+        longestPrefix(trie, "football", true);
+        longestPrefix(trie, "bar", false);
+        longestPrefix(trie, "bar", true);
+        longestPrefix(trie, "foo", false);
+        longestPrefix(trie, "foo", true);
+    },
+]);
+
+function hasWord(trie, word)
+{
+    TestRunner.addResult(`trie.has("${word}") = ${trie.has(word)}`);
+}
+
+function addWord(trie, word)
+{
+    TestRunner.addResult(`trie.add("${word}")`);
+    trie.add(word);
+}
+
+function removeWord(trie, word)
+{
+    TestRunner.addResult(`trie.remove("${word}") = ${trie.remove(word)}`);
+}
+
+function words(trie, prefix)
+{
+    var title = prefix ? `trie.words("${prefix}")` : `trie.words()`;
+    var words = trie.words(prefix);
+    var text = words.length ? `[\n    ${words.join(",\n    ")}\n]` : "[]";
+    TestRunner.addResult(title + " = " + text);
+}
+
+function clear(trie)
+{
+    trie.clear();
+    TestRunner.addResult("trie.clear()");
+}
+
+function longestPrefix(trie, word, fullWordOnly)
+{
+    TestRunner.addResult(`trie.longestPrefix("${word}", ${fullWordOnly}) = "${trie.longestPrefix(word, fullWordOnly)}"`);
+}
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/payments/payment-request-interface.html b/third_party/WebKit/LayoutTests/payments/payment-request-interface.html
index 094f5e8..dcab558c 100644
--- a/third_party/WebKit/LayoutTests/payments/payment-request-interface.html
+++ b/third_party/WebKit/LayoutTests/payments/payment-request-interface.html
@@ -368,51 +368,51 @@
     generate_tests(assert_throws, [
         // Invalid currency code formats.
         ['Undefined currency code should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'currency': undefined}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'currency': undefined}), {requestShipping: true})
         }],
 
         // Invalid amount formats.
         ['Invalid amount "-" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-'}), {requestShipping: true})
         }],
         ['Invalid amount "notdigits" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': 'notdigits'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': 'notdigits'}), {requestShipping: true})
         }],
         ['Invalid amount "ALSONOTDIGITS" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': 'ALSONOTDIGITS'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': 'ALSONOTDIGITS'}), {requestShipping: true})
         }],
         ['Invalid amount "10." should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '10.'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '10.'}), {requestShipping: true})
         }],
         ['Invalid amount ".99" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '.99'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '.99'}), {requestShipping: true})
         }],
         ['Invalid amount "-10." should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-10.'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-10.'}), {requestShipping: true})
         }],
         ['Invalid amount "-.99" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-.99'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '-.99'}), {requestShipping: true})
         }],
         ['Invalid amount "10-" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '10-'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '10-'}), {requestShipping: true})
         }],
         ['Invalid amount "1-0" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1-0'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1-0'}), {requestShipping: true})
         }],
         ['Invalid amount "1.0.0" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1.0.0'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1.0.0'}), {requestShipping: true})
         }],
         ['Invalid amount "1/3" should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1/3'}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': '1/3'}), {requestShipping: true})
         }],
         ['Empty amount should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': ''}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': ''}), {requestShipping: true})
         }],
         ['Null amount should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': null}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': null}), {requestShipping: true})
         }],
         ['Undefined amount should throw', null, function() {
-            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': undefined}))
+            new PaymentRequest([{'supportedMethods': ['foo']}], buildDetails(detailNames[i], {'value': undefined}), {requestShipping: true})
         }],
     ]);
 }
diff --git a/third_party/WebKit/Source/core/dom/DocumentLifecycle.cpp b/third_party/WebKit/Source/core/dom/DocumentLifecycle.cpp
index 5c8e371..f3c7d40 100644
--- a/third_party/WebKit/Source/core/dom/DocumentLifecycle.cpp
+++ b/third_party/WebKit/Source/core/dom/DocumentLifecycle.cpp
@@ -33,6 +33,10 @@
 #include "platform/RuntimeEnabledFeatures.h"
 #include "wtf/Assertions.h"
 
+#if DCHECK_IS_ON()
+#include "wtf/text/WTFString.h"
+#endif
+
 namespace blink {
 
 static DocumentLifecycle::DeprecatedTransition* s_deprecatedTransitionStack = 0;
@@ -288,40 +292,12 @@
          m_state == PrePaintClean || m_state == PaintClean;
 }
 
-#endif
-
-void DocumentLifecycle::advanceTo(LifecycleState nextState) {
-#if DCHECK_IS_ON()
-  DCHECK(canAdvanceTo(nextState)) << "Cannot advance document lifecycle from "
-                                  << stateAsDebugString(m_state) << " to "
-                                  << stateAsDebugString(nextState) << ".";
-#endif
-  m_state = nextState;
-}
-
-void DocumentLifecycle::ensureStateAtMost(LifecycleState state) {
-  DCHECK(state == VisualUpdatePending || state == StyleClean ||
-         state == LayoutClean);
-  if (m_state <= state)
-    return;
-#if DCHECK_IS_ON()
-  DCHECK(canRewindTo(state)) << "Cannot rewind document lifecycle from "
-                             << stateAsDebugString(m_state) << " to "
-                             << stateAsDebugString(state) << ".";
-#endif
-  m_state = state;
-}
-
-bool DocumentLifecycle::throttlingAllowed() const {
-  return s_allowThrottlingCount;
-}
-
-#if DCHECK_IS_ON()
 #define DEBUG_STRING_CASE(StateName) \
-  case StateName:                    \
+  case DocumentLifecycle::StateName: \
     return #StateName
 
-const char* DocumentLifecycle::stateAsDebugString(const LifecycleState state) {
+static WTF::String stateAsDebugString(
+    const DocumentLifecycle::LifecycleState& state) {
   switch (state) {
     DEBUG_STRING_CASE(Uninitialized);
     DEBUG_STRING_CASE(Inactive);
@@ -349,6 +325,36 @@
   NOTREACHED();
   return "Unknown";
 }
+
+WTF::String DocumentLifecycle::toString() const {
+  return stateAsDebugString(m_state);
+}
 #endif
 
+void DocumentLifecycle::advanceTo(LifecycleState nextState) {
+#if DCHECK_IS_ON()
+  DCHECK(canAdvanceTo(nextState)) << "Cannot advance document lifecycle from "
+                                  << stateAsDebugString(m_state) << " to "
+                                  << stateAsDebugString(nextState) << ".";
+#endif
+  m_state = nextState;
+}
+
+void DocumentLifecycle::ensureStateAtMost(LifecycleState state) {
+  DCHECK(state == VisualUpdatePending || state == StyleClean ||
+         state == LayoutClean);
+  if (m_state <= state)
+    return;
+#if DCHECK_IS_ON()
+  DCHECK(canRewindTo(state)) << "Cannot rewind document lifecycle from "
+                             << stateAsDebugString(m_state) << " to "
+                             << stateAsDebugString(state) << ".";
+#endif
+  m_state = state;
+}
+
+bool DocumentLifecycle::throttlingAllowed() const {
+  return s_allowThrottlingCount;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/DocumentLifecycle.h b/third_party/WebKit/Source/core/dom/DocumentLifecycle.h
index 8a05f66..e2ef48bb 100644
--- a/third_party/WebKit/Source/core/dom/DocumentLifecycle.h
+++ b/third_party/WebKit/Source/core/dom/DocumentLifecycle.h
@@ -36,6 +36,10 @@
 #include "wtf/Assertions.h"
 #include "wtf/Noncopyable.h"
 
+#if DCHECK_IS_ON()
+#include "wtf/Forward.h"
+#endif
+
 namespace blink {
 
 class CORE_EXPORT DocumentLifecycle {
@@ -206,9 +210,11 @@
 
   bool throttlingAllowed() const;
 
+#if DCHECK_IS_ON()
+  WTF::String toString() const;
+#endif
  private:
 #if DCHECK_IS_ON()
-  static const char* stateAsDebugString(const LifecycleState);
   bool canAdvanceTo(LifecycleState) const;
   bool canRewindTo(LifecycleState) const;
 #endif
diff --git a/third_party/WebKit/Source/core/frame/BUILD.gn b/third_party/WebKit/Source/core/frame/BUILD.gn
index eceddee..d75dc18 100644
--- a/third_party/WebKit/Source/core/frame/BUILD.gn
+++ b/third_party/WebKit/Source/core/frame/BUILD.gn
@@ -89,6 +89,8 @@
     "RootFrameViewport.h",
     "Screen.cpp",
     "Screen.h",
+    "ScreenOrientationController.cpp",
+    "ScreenOrientationController.h",
     "Settings.cpp",
     "SettingsDelegate.cpp",
     "SettingsDelegate.h",
diff --git a/third_party/WebKit/Source/core/frame/ScreenOrientationController.cpp b/third_party/WebKit/Source/core/frame/ScreenOrientationController.cpp
new file mode 100644
index 0000000..e8104123
--- /dev/null
+++ b/third_party/WebKit/Source/core/frame/ScreenOrientationController.cpp
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/frame/ScreenOrientationController.h"
+
+namespace blink {
+
+// static
+ScreenOrientationController* ScreenOrientationController::from(
+    LocalFrame& frame) {
+  return static_cast<ScreenOrientationController*>(
+      Supplement<LocalFrame>::from(frame, supplementName()));
+}
+
+DEFINE_TRACE(ScreenOrientationController) {
+  Supplement<LocalFrame>::trace(visitor);
+}
+
+// static
+void ScreenOrientationController::provideTo(
+    LocalFrame& frame,
+    ScreenOrientationController* controller) {
+  Supplement<LocalFrame>::provideTo(frame, supplementName(), controller);
+}
+
+// static
+const char* ScreenOrientationController::supplementName() {
+  return "ScreenOrientationController";
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/ScreenOrientationController.h b/third_party/WebKit/Source/core/frame/ScreenOrientationController.h
new file mode 100644
index 0000000..378fd153
--- /dev/null
+++ b/third_party/WebKit/Source/core/frame/ScreenOrientationController.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ScreenOrientationController_h
+#define ScreenOrientationController_h
+
+#include "core/CoreExport.h"
+#include "core/frame/LocalFrame.h"
+#include "platform/Supplementable.h"
+#include "public/platform/modules/screen_orientation/WebScreenOrientationLockType.h"
+
+namespace blink {
+
+class WebLockOrientationCallback;
+
+// ScreenOrientationController allows to manipulate screen orientation in Blink
+// outside of the screen_orientation/ modules. It is an interface that the
+// module will implement and add a provider for.
+// Callers of ScreenOrientationController::from() should always assume the
+// returned pointer can be nullptr.
+class CORE_EXPORT ScreenOrientationController : public Supplement<LocalFrame> {
+ public:
+  virtual ~ScreenOrientationController() = default;
+
+  static ScreenOrientationController* from(LocalFrame&);
+
+  virtual void lock(WebScreenOrientationLockType,
+                    std::unique_ptr<WebLockOrientationCallback>) = 0;
+  virtual void unlock() = 0;
+
+  DECLARE_VIRTUAL_TRACE();
+
+ protected:
+  // To be called by an ScreenOrientationController to register its
+  // implementation.
+  static void provideTo(LocalFrame&, ScreenOrientationController*);
+
+ private:
+  static const char* supplementName();
+};
+
+}  // namespace blink
+
+#endif  // ScreenOrientationController_h
diff --git a/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp b/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
index 9dc16fe..da83239 100644
--- a/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
+++ b/third_party/WebKit/Source/core/offscreencanvas/OffscreenCanvas.cpp
@@ -175,7 +175,7 @@
 bool OffscreenCanvas::isPaintable() const {
   if (!m_context)
     return ImageBuffer::canCreateImageBuffer(m_size);
-  return m_context->isPaintable();
+  return m_context->isPaintable() && m_size.width() && m_size.height();
 }
 
 bool OffscreenCanvas::isAccelerated() const {
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
index 33e635c..185a3b53 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
@@ -1101,22 +1101,6 @@
   notifyGeometryChanged();
 }
 
-#if OS(MACOSX)
-void ScrollingCoordinator::handleWheelEventPhase(
-    PlatformWheelEventPhase phase) {
-  DCHECK(isMainThread());
-
-  if (!m_page)
-    return;
-
-  FrameView* frameView = m_page->deprecatedLocalMainFrame()->view();
-  if (!frameView)
-    return;
-
-  frameView->scrollAnimator().handleWheelEventPhase(phase);
-}
-#endif
-
 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(
     FrameView* frameView) const {
   const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects =
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
index 3b63359..b5775ab 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
@@ -94,12 +94,6 @@
   // Should be called whenever the root layer for the given frame view changes.
   void frameViewRootLayerDidChange(FrameView*);
 
-#if OS(MACOSX)
-  // Dispatched by the scrolling tree during handleWheelEvent. This is required
-  // as long as scrollbars are painted on the main thread.
-  void handleWheelEventPhase(PlatformWheelEventPhase);
-#endif
-
   MainThreadScrollingReasons mainThreadScrollingReasons() const;
   bool shouldUpdateScrollLayerPositionOnMainThread() const {
     return mainThreadScrollingReasons() != 0;
diff --git a/third_party/WebKit/Source/devtools/.gitignore b/third_party/WebKit/Source/devtools/.gitignore
index eb6f6b0..456908cd 100644
--- a/third_party/WebKit/Source/devtools/.gitignore
+++ b/third_party/WebKit/Source/devtools/.gitignore
@@ -13,5 +13,6 @@
 /.test_cache
 /release
 /scripts/local_node/runtimes
+/front_end/protocol_externs.js
 /front_end/InspectorBackendCommands.js
 /front_end/SupportedCSSProperties.js
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index cff1ae0..0025228 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -21,6 +21,8 @@
   "front_end/heap_snapshot_worker.json",
   "front_end/utility_shared_worker.js",
   "front_end/utility_shared_worker.json",
+  "front_end/unit_test_runner.js",
+  "front_end/unit_test_runner.json",
   "front_end/toolbox.js",
   "front_end/toolbox.json",
   "front_end/Runtime.js",
@@ -334,10 +336,12 @@
   "front_end/formatter_worker/module.json",
   "front_end/sdk/module.json",
   "front_end/settings/module.json",
+  "front_end/shell/module.json",
   "front_end/source_frame/module.json",
   "front_end/sources/module.json",
   "front_end/snippets/module.json",
   "front_end/utility_shared_worker/module.json",
+  "front_end/test_runner/module.json",
   "front_end/text_editor/module.json",
   "front_end/timeline_model/module.json",
   "front_end/timeline/module.json",
@@ -710,6 +714,8 @@
   "front_end/terminal/xterm.js/build/xterm.css",
   "front_end/terminal/xterm.js/build/xterm.js",
 ]
+devtools_shell_js_files = [ "front_end/shell/TestShell.js" ]
+devtools_test_runner_js_files = [ "front_end/test_runner/TestRunner.js" ]
 devtools_timeline_model_js_files = [
   "front_end/timeline_model/TracingLayerTree.js",
   "front_end/timeline_model/TimelineFrameModel.js",
@@ -888,9 +894,10 @@
     devtools_resources_js_files + devtools_sass_js_files +
     devtools_security_js_files + devtools_screencast_js_files +
     devtools_formatter_worker_js_files + devtools_settings_js_files +
-    devtools_services_js_files + devtools_snippets_js_files +
-    devtools_source_frame_js_files + devtools_sources_js_files +
-    devtools_utility_shared_worker_js_files + devtools_text_editor_js_files +
+    devtools_services_js_files + devtools_shell_js_files +
+    devtools_snippets_js_files + devtools_source_frame_js_files +
+    devtools_sources_js_files + devtools_utility_shared_worker_js_files +
+    devtools_text_editor_js_files + devtools_test_runner_js_files +
     devtools_terminal_js_files + devtools_timeline_model_js_files +
     devtools_timeline_js_files + devtools_ui_lazy_js_files +
     devtools_layer_viewer_js_files + devtools_worker_service_js_files
@@ -911,6 +918,8 @@
   "$resources_out_dir/inspector.js",
   "$resources_out_dir/toolbox.html",
   "$resources_out_dir/toolbox.js",
+  "$resources_out_dir/unit_test_runner.html",
+  "$resources_out_dir/unit_test_runner.js",
 ]
 
 generated_workers = [
@@ -957,6 +966,7 @@
 devtools_applications = [
   "inspector",
   "toolbox",
+  "unit_test_runner",
   "formatter_worker",
   "heap_snapshot_worker",
   "utility_shared_worker",
@@ -1103,6 +1113,7 @@
   inputs = helper_scripts + all_devtools_files + generated_scripts + [
              "front_end/inspector.html",
              "front_end/toolbox.html",
+             "front_end/unit_test_runner.html",
            ]
 
   outputs = generated_entry_files + generated_workers +
@@ -1129,11 +1140,13 @@
     inputs = all_devtools_files + [
                "front_end/inspector.html",
                "front_end/toolbox.html",
+               "front_end/unit_test_runner.html",
              ]
 
     outputs = [
       "$resources_out_debug_dir/inspector.html",
       "$resources_out_debug_dir/toolbox.html",
+      "$resources_out_debug_dir/unit_test_runner.html",
     ]
 
     args = devtools_applications + [
diff --git a/third_party/WebKit/Source/devtools/PRESUBMIT.py b/third_party/WebKit/Source/devtools/PRESUBMIT.py
index c7ebd1a7..bd9c111 100644
--- a/third_party/WebKit/Source/devtools/PRESUBMIT.py
+++ b/third_party/WebKit/Source/devtools/PRESUBMIT.py
@@ -32,10 +32,13 @@
 for more details about the presubmit API built into gcl.
 """
 
+from collections import namedtuple
 import sys
 
 compile_note = "Be sure to run your patch by the compile_frontend.py script prior to committing!"
 
+CheckOutput = namedtuple('CheckOutput', ['results', 'has_errors'])
+
 
 def _CheckNodeAndNPMModules(input_api, output_api):
     node_script_path = input_api.os_path.join(input_api.PresubmitLocalPath(), "scripts", "install_node_deps.py")
@@ -45,13 +48,14 @@
         stderr=input_api.subprocess.STDOUT)
     out, _ = process.communicate()
     if process.returncode != 0:
-        return [output_api.PresubmitError(out)]
-    return [output_api.PresubmitNotifyResult(out)]
+        return CheckOutput([output_api.PresubmitError(out)], has_errors=True)
+    return CheckOutput([output_api.PresubmitNotifyResult(out)], has_errors=False)
+
 
 def _FormatDevtools(input_api, output_api):
     affected_front_end_files = _getAffectedFrontEndFiles(input_api)
     if len(affected_front_end_files) == 0:
-        return []
+        return CheckOutput([], has_errors=False)
     original_sys_path = sys.path
     try:
         sys.path = sys.path + [input_api.os_path.join(input_api.PresubmitLocalPath(), "scripts")]
@@ -66,9 +70,9 @@
         args=[node_path, format_path] + [glob_arg, "--output-replacements-xml"])
     check_formatting_out, _ = check_formatting_process.communicate()
     if check_formatting_process.returncode != 0:
-        return [output_api.PresubmitError(check_formatting_out)]
+        return CheckOutput([output_api.PresubmitError(check_formatting_out)], has_errors=True)
     if "</replacement>" not in check_formatting_out:
-        return [output_api.PresubmitNotifyResult("CL is properly formatted")]
+        return CheckOutput([output_api.PresubmitNotifyResult("CL is properly formatted")], has_errors=False)
 
     format_args = [node_path, format_path] + [glob_arg]
     format_process = _inputPopen(input_api, format_args)
@@ -86,10 +90,10 @@
     reformat_process = _inputPopen(input_api, format_args)
     reformat_process.communicate()
 
-    return [output_api.PresubmitError("ERROR: Found formatting violations.\n"
+    return CheckOutput([output_api.PresubmitError("ERROR: Found formatting violations.\n"
                                       "Ran clang-format on files changed in CL\n"
                                       "Use git status to check the formatting changes"),
-            output_api.PresubmitError(format_process_out)]
+            output_api.PresubmitError(format_process_out)], has_errors=True)
 
 
 def _CheckDevtoolsStyle(input_api, output_api):
@@ -202,8 +206,19 @@
 
 def CheckChangeOnUpload(input_api, output_api):
     results = []
-    results.extend(_CheckNodeAndNPMModules(input_api, output_api))
-    results.extend(_FormatDevtools(input_api, output_api))
+
+    (node_results, has_errors) = _CheckNodeAndNPMModules(input_api, output_api)
+    results.extend(node_results)
+    if has_errors:
+        results.append(output_api.PresubmitError("ERROR: Bailed out early because error using node.js/npm"))
+        return results
+
+    (format_results, has_errors) = _FormatDevtools(input_api, output_api)
+    results.extend(format_results)
+    if has_errors:
+        results.append(output_api.PresubmitError("ERROR: Bailed out early because formatting errors were found"))
+        return results
+
     results.extend(_CheckDevtoolsStyle(input_api, output_api))
     results.extend(_CompileDevtoolsFrontend(input_api, output_api))
     results.extend(_CheckConvertSVGToPNGHashes(input_api, output_api))
@@ -215,6 +230,7 @@
 def CheckChangeOnCommit(input_api, output_api):
     return []
 
+
 def _getAffectedFrontEndFiles(input_api):
     local_paths = [f.AbsoluteLocalPath() for f in input_api.AffectedFiles() if f.Action() != "D"]
     devtools_root = input_api.PresubmitLocalPath()
@@ -222,6 +238,7 @@
     affected_front_end_files = [file_name for file_name in local_paths if devtools_front_end in file_name and file_name.endswith(".js")]
     return [input_api.os_path.relpath(file_name, devtools_root) for file_name in affected_front_end_files]
 
+
 def _inputPopen(input_api, args):
     return input_api.subprocess.Popen(
         args,
diff --git a/third_party/WebKit/Source/devtools/front_end/externs.js b/third_party/WebKit/Source/devtools/front_end/externs.js
index ce5dd2e..e05c8f4 100644
--- a/third_party/WebKit/Source/devtools/front_end/externs.js
+++ b/third_party/WebKit/Source/devtools/front_end/externs.js
@@ -840,10 +840,12 @@
 var Security = {};
 var Services = {};
 var Settings = {};
+var Shell = {};
 var Snippets = {};
 var SourceFrame = {};
 var Sources = {};
 var Terminal = {};
+var TestRunner = {};
 var TextEditor = {};
 var Timeline = {};
 var TimelineModel = {};
diff --git a/third_party/WebKit/Source/devtools/front_end/shell/TestShell.js b/third_party/WebKit/Source/devtools/front_end/shell/TestShell.js
new file mode 100644
index 0000000..fa84429
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/shell/TestShell.js
@@ -0,0 +1,41 @@
+// 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.
+
+Shell.TestShell = class {
+  /**
+   * @suppressGlobalPropertiesCheck
+   */
+  constructor() {
+    runOnWindowLoad(this.initializeUnitTest.bind(this));
+  }
+
+  /**
+   * @suppressGlobalPropertiesCheck
+   */
+  initializeUnitTest() {
+    var globalStorage = new Common.SettingsStorage(
+        {}, InspectorFrontendHost.setPreference, InspectorFrontendHost.removePreference,
+        InspectorFrontendHost.clearPreferences);
+    var storagePrefix = '';
+    var localStorage = new Common.SettingsStorage({}, undefined, undefined, undefined, storagePrefix);
+    Common.settings = new Common.Settings(globalStorage, localStorage);
+
+    UI.viewManager = new UI.ViewManager();
+    UI.initializeUIUtils(document, Common.settings.createSetting('uiTheme', 'default'));
+    UI.installComponentRootStyles(/** @type {!Element} */ (document.body));
+
+    UI.zoomManager = new UI.ZoomManager(self, InspectorFrontendHost);
+    UI.inspectorView = UI.InspectorView.instance();
+    UI.ContextMenu.initialize();
+    UI.ContextMenu.installHandler(document);
+    UI.Tooltip.installHandler(document);
+
+    var rootView = new UI.RootView();
+    UI.inspectorView.show(rootView.element);
+    rootView.attachToDocument(document);
+    TestRunner.executeTestScript();
+  }
+};
+
+new Shell.TestShell();
diff --git a/third_party/WebKit/Source/devtools/front_end/shell/module.json b/third_party/WebKit/Source/devtools/front_end/shell/module.json
new file mode 100644
index 0000000..0b2846b4
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/shell/module.json
@@ -0,0 +1,10 @@
+{
+  "dependencies": [
+    "common",
+    "test_runner",
+    "ui"
+  ],
+  "scripts": [
+    "TestShell.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/test_runner/TestRunner.js b/third_party/WebKit/Source/devtools/front_end/test_runner/TestRunner.js
new file mode 100644
index 0000000..e45f29df
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/test_runner/TestRunner.js
@@ -0,0 +1,151 @@
+// 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.
+
+/** @type {!{notifyDone: function()}|undefined} */
+self.testRunner;
+
+TestRunner.executeTestScript = function() {
+  fetch(`${Runtime.queryParam('test')}`)
+      .then((data) => data.text())
+      .then((testScript) => eval(`(function(){${testScript}})()`))
+      .catch((error) => {
+        TestRunner.addResult(`Unable to execute test script because of error: ${error}`);
+        TestRunner.completeTest();
+      });
+};
+
+/** @type {!Array<string>} */
+TestRunner._results = [];
+
+/**
+ * @suppressGlobalPropertiesCheck
+ */
+TestRunner.completeTest = function() {
+  if (!self.testRunner) {
+    console.log('Test Done');
+    return;
+  }
+
+  Array.prototype.forEach.call(document.documentElement.childNodes, x => x.remove());
+  var outputElement = document.createElement('div');
+  // Support for svg - add to document, not body, check for style.
+  if (outputElement.style) {
+    outputElement.style.whiteSpace = 'pre';
+    outputElement.style.height = '10px';
+    outputElement.style.overflow = 'hidden';
+  }
+  document.documentElement.appendChild(outputElement);
+  for (var i = 0; i < TestRunner._results.length; i++) {
+    outputElement.appendChild(document.createTextNode(TestRunner._results[i]));
+    outputElement.appendChild(document.createElement('br'));
+  }
+  TestRunner._results = [];
+  self.testRunner.notifyDone();
+};
+
+/**
+ * @param {*} text
+ */
+TestRunner.addResult = function(text) {
+  if (self.testRunner)
+    TestRunner._results.push(String(text));
+  else
+    console.log(text);
+};
+
+/**
+ * @param {!Array<function()>} tests
+ */
+TestRunner.runTests = function(tests) {
+  nextTest();
+
+  function nextTest() {
+    var test = tests.shift();
+    if (!test) {
+      TestRunner.completeTest();
+      return;
+    }
+    TestRunner.addResult('\ntest: ' + test.name);
+    var testPromise = test();
+    if (!(testPromise instanceof Promise))
+      testPromise = Promise.resolve();
+    testPromise.then(nextTest);
+  }
+};
+
+/**
+ * @param {!Object} receiver
+ * @param {string} methodName
+ * @return {!Promise<*>}
+ */
+TestRunner.addSniffer = function(receiver, methodName) {
+  return new Promise(function(resolve, reject) {
+    var original = receiver[methodName];
+    if (typeof original !== 'function') {
+      reject('Cannot find method to override: ' + methodName);
+      return;
+    }
+
+    receiver[methodName] = function(var_args) {
+      try {
+        var result = original.apply(this, arguments);
+      } finally {
+        receiver[methodName] = original;
+      }
+      // In case of exception the override won't be called.
+      try {
+        Array.prototype.push.call(arguments, result);
+        resolve.apply(this, arguments);
+      } catch (e) {
+        reject('Exception in overridden method \'' + methodName + '\': ' + e);
+        TestRunner.completeTest();
+      }
+      return result;
+    };
+  });
+};
+
+/**
+ * @param {!Array<string>} lazyModules
+ * @return {!Promise<!Array<undefined>>}
+ */
+TestRunner.loadLazyModules = function(lazyModules) {
+  return Promise.all(lazyModules.map(lazyModule => self.runtime.loadModulePromise(lazyModule)));
+};
+
+/**
+ * @param {string} key
+ * @param {boolean} ctrlKey
+ * @param {boolean} altKey
+ * @param {boolean} shiftKey
+ * @param {boolean} metaKey
+ * @return {!KeyboardEvent}
+ */
+TestRunner.createKeyEvent = function(key, ctrlKey, altKey, shiftKey, metaKey) {
+  return new KeyboardEvent('keydown', {
+    key: key,
+    bubbles: true,
+    cancelable: true,
+    ctrlKey: ctrlKey,
+    altKey: altKey,
+    shiftKey: shiftKey,
+    metaKey: metaKey
+  });
+};
+
+(function() {
+  /**
+   * @param {string|!Event} message
+   * @param {string} source
+   * @param {number} lineno
+   * @param {number} colno
+   * @param {!Error} error
+   */
+  function completeTestOnError(message, source, lineno, colno, error) {
+    TestRunner.addResult('TEST ENDED IN ERROR: ' + error.stack);
+    TestRunner.completeTest();
+  }
+
+  self['onerror'] = completeTestOnError;
+})();
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/front_end/test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/test_runner/module.json
new file mode 100644
index 0000000..ebd476c4
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/test_runner/module.json
@@ -0,0 +1,5 @@
+{
+  "scripts": [
+    "TestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/unit_test_runner.html b/third_party/WebKit/Source/devtools/front_end/unit_test_runner.html
new file mode 100644
index 0000000..b4ebe421
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/unit_test_runner.html
@@ -0,0 +1,15 @@
+<!--
+ * 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.
+-->
+<!doctype html>
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval' https://chrome-devtools-frontend.appspot.com">
+    <script type="text/javascript" src="Runtime.js"></script>
+    <script type="text/javascript" src="unit_test_runner.js"></script>
+</head>
+<body></body>
+</html>
diff --git a/third_party/WebKit/Source/devtools/front_end/unit_test_runner.js b/third_party/WebKit/Source/devtools/front_end/unit_test_runner.js
new file mode 100644
index 0000000..68b2da8
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/unit_test_runner.js
@@ -0,0 +1,10 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+if (self.testRunner) {
+  testRunner.dumpAsText();
+  testRunner.waitUntilDone();
+}
+
+Runtime.startApplication('unit_test_runner');
diff --git a/third_party/WebKit/Source/devtools/front_end/unit_test_runner.json b/third_party/WebKit/Source/devtools/front_end/unit_test_runner.json
new file mode 100644
index 0000000..7356125
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/unit_test_runner.json
@@ -0,0 +1,18 @@
+{
+  "modules" : [
+    { "name": "platform", "type": "autostart" },
+    { "name": "ui", "type": "autostart" },
+    { "name": "host", "type": "autostart" },
+    { "name": "common", "type": "autostart" },
+    { "name": "workspace", "type": "autostart" },
+    { "name": "source_frame", "type": "autostart" },
+    { "name": "text_editor", "type": "autostart" },
+    { "name": "cm_modes", "type": "autostart" },
+    { "name": "diff", "type": "autostart" },
+    { "name": "shell", "type": "autostart" },
+    { "name": "test_runner", "type": "autostart" },
+    { "name": "ui_lazy" }
+  ],
+
+  "has_html": true
+}
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/scripts/compile_frontend.py b/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
index c94eaf6..afc5667 100755
--- a/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
+++ b/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
@@ -123,7 +123,14 @@
     sys.__excepthook__(exctype, value, traceback)
 sys.excepthook = error_excepthook
 
-application_descriptors = ['inspector.json', 'toolbox.json', 'formatter_worker.json', 'heap_snapshot_worker.json', 'utility_shared_worker.json']
+application_descriptors = [
+    'inspector.json',
+    'toolbox.json',
+    'unit_test_runner.json',
+    'formatter_worker.json',
+    'heap_snapshot_worker.json',
+    'utility_shared_worker.json',
+]
 loader = modular_build.DescriptorLoader(devtools_frontend_path)
 descriptors = loader.load_applications(application_descriptors)
 modules_by_name = descriptors.modules
diff --git a/third_party/WebKit/Source/modules/payments/PaymentDetailsModifier.idl b/third_party/WebKit/Source/modules/payments/PaymentDetailsModifier.idl
index 14657ee..e58c21c 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentDetailsModifier.idl
+++ b/third_party/WebKit/Source/modules/payments/PaymentDetailsModifier.idl
@@ -8,4 +8,5 @@
     required sequence<DOMString> supportedMethods;
     PaymentItem total;
     sequence<PaymentItem> additionalDisplayItems;
+    [RuntimeEnabled=PaymentDetailsModifierData] object data;
 };
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
index f57a1ee5..239a7a14 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
@@ -35,26 +35,20 @@
 #include "public/platform/Platform.h"
 #include "public/platform/WebTraceLocation.h"
 #include "wtf/HashSet.h"
+#include <stddef.h>
 #include <utility>
 
 using payments::mojom::blink::ActivePaymentQueryResult;
 using payments::mojom::blink::PaymentAddressPtr;
 using payments::mojom::blink::PaymentCurrencyAmount;
 using payments::mojom::blink::PaymentCurrencyAmountPtr;
-using payments::mojom::blink::PaymentDetails;
-using payments::mojom::blink::PaymentDetailsModifier;
 using payments::mojom::blink::PaymentDetailsModifierPtr;
 using payments::mojom::blink::PaymentDetailsPtr;
-using payments::mojom::blink::PaymentDetailsPtr;
 using payments::mojom::blink::PaymentErrorReason;
-using payments::mojom::blink::PaymentItem;
 using payments::mojom::blink::PaymentItemPtr;
-using payments::mojom::blink::PaymentMethodData;
 using payments::mojom::blink::PaymentMethodDataPtr;
-using payments::mojom::blink::PaymentOptions;
 using payments::mojom::blink::PaymentOptionsPtr;
 using payments::mojom::blink::PaymentResponsePtr;
-using payments::mojom::blink::PaymentShippingOption;
 using payments::mojom::blink::PaymentShippingOptionPtr;
 using payments::mojom::blink::PaymentShippingType;
 
@@ -76,7 +70,7 @@
 template <>
 struct TypeConverter<PaymentItemPtr, blink::PaymentItem> {
   static PaymentItemPtr Convert(const blink::PaymentItem& input) {
-    PaymentItemPtr output = PaymentItem::New();
+    PaymentItemPtr output = payments::mojom::blink::PaymentItem::New();
     output->label = input.label();
     output->amount = PaymentCurrencyAmount::From(input.amount());
     output->pending = input.pending();
@@ -88,7 +82,8 @@
 struct TypeConverter<PaymentShippingOptionPtr, blink::PaymentShippingOption> {
   static PaymentShippingOptionPtr Convert(
       const blink::PaymentShippingOption& input) {
-    PaymentShippingOptionPtr output = PaymentShippingOption::New();
+    PaymentShippingOptionPtr output =
+        payments::mojom::blink::PaymentShippingOption::New();
     output->id = input.id();
     output->label = input.label();
     output->amount = PaymentCurrencyAmount::From(input.amount());
@@ -98,65 +93,9 @@
 };
 
 template <>
-struct TypeConverter<PaymentDetailsModifierPtr, blink::PaymentDetailsModifier> {
-  static PaymentDetailsModifierPtr Convert(
-      const blink::PaymentDetailsModifier& input) {
-    PaymentDetailsModifierPtr output = PaymentDetailsModifier::New();
-    output->supported_methods = input.supportedMethods();
-
-    if (input.hasTotal())
-      output->total = PaymentItem::From(input.total());
-
-    if (input.hasAdditionalDisplayItems()) {
-      for (size_t i = 0; i < input.additionalDisplayItems().size(); ++i) {
-        output->additional_display_items.append(
-            PaymentItem::From(input.additionalDisplayItems()[i]));
-      }
-    }
-    return output;
-  }
-};
-
-template <>
-struct TypeConverter<PaymentDetailsPtr, blink::PaymentDetails> {
-  static PaymentDetailsPtr Convert(const blink::PaymentDetails& input) {
-    PaymentDetailsPtr output = PaymentDetails::New();
-    output->total = PaymentItem::From(input.total());
-
-    if (input.hasDisplayItems()) {
-      for (size_t i = 0; i < input.displayItems().size(); ++i) {
-        output->display_items.append(
-            PaymentItem::From(input.displayItems()[i]));
-      }
-    }
-
-    if (input.hasShippingOptions()) {
-      for (size_t i = 0; i < input.shippingOptions().size(); ++i) {
-        output->shipping_options.append(
-            PaymentShippingOption::From(input.shippingOptions()[i]));
-      }
-    }
-
-    if (input.hasModifiers()) {
-      for (size_t i = 0; i < input.modifiers().size(); ++i) {
-        output->modifiers.append(
-            PaymentDetailsModifier::From(input.modifiers()[i]));
-      }
-    }
-
-    if (input.hasError())
-      output->error = input.error();
-    else
-      output->error = emptyString();
-
-    return output;
-  }
-};
-
-template <>
 struct TypeConverter<PaymentOptionsPtr, blink::PaymentOptions> {
   static PaymentOptionsPtr Convert(const blink::PaymentOptions& input) {
-    PaymentOptionsPtr output = PaymentOptions::New();
+    PaymentOptionsPtr output = payments::mojom::blink::PaymentOptions::New();
     output->request_payer_name = input.requestPayerName();
     output->request_payer_email = input.requestPayerEmail();
     output->request_payer_phone = input.requestPayerPhone();
@@ -240,128 +179,67 @@
   }
 }
 
-void validateDisplayItems(const HeapVector<PaymentItem>& items,
-                          ExceptionState& exceptionState) {
-  for (const auto& item : items) {
+void validateAndConvertDisplayItems(const HeapVector<PaymentItem>& input,
+                                    Vector<PaymentItemPtr>& output,
+                                    ExceptionState& exceptionState) {
+  for (const PaymentItem& item : input) {
     validateShippingOptionOrPaymentItem(item, exceptionState);
     if (exceptionState.hadException())
       return;
+    output.append(payments::mojom::blink::PaymentItem::From(item));
   }
 }
 
-// Returns false if |options| should be ignored, even if an exception was not
-// thrown. TODO(rouslan): Clear shipping options instead of ignoring them when
-// http://crbug.com/601193 is fixed.
-bool validateShippingOptions(const HeapVector<PaymentShippingOption>& options,
-                             ExceptionState& exceptionState) {
+// Validates and converts |input| shipping options into |output|. Throws an
+// exception if the data is not valid, except for duplicate identifiers, which
+// returns an empty |output| instead of throwing an exception. There's no need
+// to clear |output| when an exception is thrown, because the caller takes care
+// of deleting |output|.
+void validateAndConvertShippingOptions(
+    const HeapVector<PaymentShippingOption>& input,
+    Vector<PaymentShippingOptionPtr>& output,
+    ExceptionState& exceptionState) {
   HashSet<String> uniqueIds;
-  for (const auto& option : options) {
+  for (const PaymentShippingOption& option : input) {
     if (!option.hasId() || option.id().isEmpty()) {
       exceptionState.throwTypeError("ShippingOption id required");
-      return false;
+      return;
     }
 
-    if (uniqueIds.contains(option.id()))
-      return false;
+    if (uniqueIds.contains(option.id())) {
+      // Clear |output| instead of throwing an exception.
+      output.clear();
+      return;
+    }
 
     uniqueIds.add(option.id());
 
     validateShippingOptionOrPaymentItem(option, exceptionState);
     if (exceptionState.hadException())
-      return false;
-  }
+      return;
 
-  return true;
+    output.append(payments::mojom::blink::PaymentShippingOption::From(option));
+  }
 }
 
-void validatePaymentDetailsModifiers(
-    const HeapVector<PaymentDetailsModifier>& modifiers,
-    ExceptionState& exceptionState) {
-  if (modifiers.isEmpty()) {
-    exceptionState.throwTypeError(
-        "Must specify at least one payment details modifier");
+void validateAndConvertTotal(const PaymentItem& input,
+                             PaymentItemPtr& output,
+                             ExceptionState& exceptionState) {
+  validateShippingOptionOrPaymentItem(input, exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  if (input.amount().value()[0] == '-') {
+    exceptionState.throwTypeError("Total amount value should be non-negative");
     return;
   }
 
-  for (const auto& modifier : modifiers) {
-    if (modifier.supportedMethods().isEmpty()) {
-      exceptionState.throwTypeError(
-          "Must specify at least one payment method identifier");
-      return;
-    }
-
-    if (modifier.hasTotal()) {
-      validateShippingOptionOrPaymentItem(modifier.total(), exceptionState);
-      if (exceptionState.hadException())
-        return;
-
-      if (modifier.total().amount().value()[0] == '-') {
-        exceptionState.throwTypeError(
-            "Total amount value should be non-negative");
-        return;
-      }
-    }
-
-    if (modifier.hasAdditionalDisplayItems()) {
-      validateDisplayItems(modifier.additionalDisplayItems(), exceptionState);
-      if (exceptionState.hadException())
-        return;
-    }
-  }
+  output = payments::mojom::blink::PaymentItem::From(input);
 }
 
-// Returns false if the shipping options should be ignored without throwing an
-// exception.
-bool validatePaymentDetails(const PaymentDetails& details,
-                            ExceptionState& exceptionState) {
-  bool keepShippingOptions = true;
-  if (!details.hasTotal()) {
-    exceptionState.throwTypeError("Must specify total");
-    return keepShippingOptions;
-  }
-
-  validateShippingOptionOrPaymentItem(details.total(), exceptionState);
-  if (exceptionState.hadException())
-    return keepShippingOptions;
-
-  if (details.total().amount().value()[0] == '-') {
-    exceptionState.throwTypeError("Total amount value should be non-negative");
-    return keepShippingOptions;
-  }
-
-  if (details.hasDisplayItems()) {
-    validateDisplayItems(details.displayItems(), exceptionState);
-    if (exceptionState.hadException())
-      return keepShippingOptions;
-  }
-
-  if (details.hasShippingOptions()) {
-    keepShippingOptions =
-        validateShippingOptions(details.shippingOptions(), exceptionState);
-
-    if (exceptionState.hadException())
-      return keepShippingOptions;
-  }
-
-  if (details.hasModifiers()) {
-    validatePaymentDetailsModifiers(details.modifiers(), exceptionState);
-    if (exceptionState.hadException())
-      return keepShippingOptions;
-  }
-
-  String errorMessage;
-  if (!PaymentsValidators::isValidErrorMsgFormat(details.error(),
-                                                 &errorMessage)) {
-    exceptionState.throwTypeError(errorMessage);
-  }
-
-  return keepShippingOptions;
-}
-
-void maybeSetAndroidPayMethodData(
-    const ScriptValue& input,
-    payments::mojom::blink::PaymentMethodDataPtr& output,
-    ExceptionState& exceptionState) {
+void maybeSetAndroidPayMethodData(const ScriptValue& input,
+                                  PaymentMethodDataPtr& output,
+                                  ExceptionState& exceptionState) {
   AndroidPayMethodData androidPay;
   DummyExceptionStateForTesting dummyExceptionState;
   V8AndroidPayMethodData::toImpl(input.isolate(), input.v8Value(), androidPay,
@@ -376,19 +254,14 @@
   output->merchant_id = androidPay.merchantId();
 
   if (androidPay.hasAllowedCardNetworks()) {
-    output->allowed_card_networks.resize(
-        androidPay.allowedCardNetworks().size());
-    size_t numberOfNetworks = 0;
-    for (size_t i = 0; i < androidPay.allowedCardNetworks().size(); ++i) {
-      for (size_t j = 0; j < arraysize(kAndroidPayNetwork); ++j) {
-        if (androidPay.allowedCardNetworks()[i] == kAndroidPayNetwork[j].name) {
-          output->allowed_card_networks[numberOfNetworks++] =
-              kAndroidPayNetwork[j].code;
+    for (const String& allowedCardNetwork : androidPay.allowedCardNetworks()) {
+      for (size_t i = 0; i < arraysize(kAndroidPayNetwork); ++i) {
+        if (allowedCardNetwork == kAndroidPayNetwork[i].name) {
+          output->allowed_card_networks.append(kAndroidPayNetwork[i].code);
           break;
         }
       }
     }
-    output->allowed_card_networks.resize(numberOfNetworks);
   }
 
   if (androidPay.hasPaymentMethodTokenizationParameters()) {
@@ -397,10 +270,10 @@
     output->tokenization_type =
         payments::mojom::blink::AndroidPayTokenization::UNSPECIFIED;
     if (tokenization.hasTokenizationType()) {
-      for (size_t j = 0; j < arraysize(kAndroidPayTokenization); ++j) {
+      for (size_t i = 0; i < arraysize(kAndroidPayTokenization); ++i) {
         if (tokenization.tokenizationType() ==
-            kAndroidPayTokenization[j].name) {
-          output->tokenization_type = kAndroidPayTokenization[j].code;
+            kAndroidPayTokenization[i].name) {
+          output->tokenization_type = kAndroidPayTokenization[i].code;
           break;
         }
       }
@@ -411,23 +284,147 @@
           tokenization.parameters().getPropertyNames(exceptionState);
       if (exceptionState.hadException())
         return;
-      output->parameters.resize(keys.size());
-      size_t numberOfParameters = 0;
       String value;
-      for (size_t i = 0; i < keys.size(); ++i) {
-        if (!DictionaryHelper::get(tokenization.parameters(), keys[i], value))
+      for (const String& key : keys) {
+        if (!DictionaryHelper::get(tokenization.parameters(), key, value))
           continue;
-        output->parameters[numberOfParameters] =
-            payments::mojom::blink::AndroidPayTokenizationParameter::New();
-        output->parameters[numberOfParameters]->key = keys[i];
-        output->parameters[numberOfParameters]->value = value;
-        ++numberOfParameters;
+        output->parameters.append(
+            payments::mojom::blink::AndroidPayTokenizationParameter::New());
+        output->parameters.back()->key = key;
+        output->parameters.back()->value = value;
       }
-      output->parameters.resize(numberOfParameters);
     }
   }
 }
 
+void stringifyAndParseMethodSpecificData(const ScriptValue& input,
+                                         PaymentMethodDataPtr& output,
+                                         ExceptionState& exceptionState) {
+  DCHECK(!input.isEmpty());
+  if (!input.v8Value()->IsObject() || input.v8Value()->IsArray()) {
+    exceptionState.throwTypeError("Data should be a JSON-serializable object");
+    return;
+  }
+
+  v8::Local<v8::String> value;
+  if (!v8::JSON::Stringify(input.context(), input.v8Value().As<v8::Object>())
+           .ToLocal(&value)) {
+    exceptionState.throwTypeError(
+        "Unable to parse payment method specific data");
+    return;
+  }
+
+  output->stringified_data =
+      v8StringToWebCoreString<String>(value, DoNotExternalize);
+  maybeSetAndroidPayMethodData(input, output, exceptionState);
+}
+
+void validateAndConvertPaymentDetailsModifiers(
+    const HeapVector<PaymentDetailsModifier>& input,
+    Vector<PaymentDetailsModifierPtr>& output,
+    ExceptionState& exceptionState) {
+  if (input.isEmpty()) {
+    exceptionState.throwTypeError(
+        "Must specify at least one payment details modifier");
+    return;
+  }
+
+  for (const PaymentDetailsModifier& modifier : input) {
+    output.append(payments::mojom::blink::PaymentDetailsModifier::New());
+    if (modifier.hasTotal()) {
+      validateAndConvertTotal(modifier.total(), output.back()->total,
+                              exceptionState);
+      if (exceptionState.hadException())
+        return;
+    }
+
+    if (modifier.hasAdditionalDisplayItems()) {
+      validateAndConvertDisplayItems(modifier.additionalDisplayItems(),
+                                     output.back()->additional_display_items,
+                                     exceptionState);
+      if (exceptionState.hadException())
+        return;
+    }
+
+    if (modifier.supportedMethods().isEmpty()) {
+      exceptionState.throwTypeError(
+          "Must specify at least one payment method identifier");
+      return;
+    }
+
+    output.back()->method_data =
+        payments::mojom::blink::PaymentMethodData::New();
+    output.back()->method_data->supported_methods = modifier.supportedMethods();
+
+    if (modifier.hasData() && !modifier.data().isEmpty()) {
+      stringifyAndParseMethodSpecificData(
+          modifier.data(), output.back()->method_data, exceptionState);
+    } else {
+      output.back()->method_data->stringified_data = "";
+    }
+  }
+}
+
+String getSelectedShippingOption(
+    const Vector<PaymentShippingOptionPtr>& shippingOptions) {
+  String result;
+  for (const PaymentShippingOptionPtr& shippingOption : shippingOptions) {
+    if (shippingOption->selected)
+      result = shippingOption->id;
+  }
+  return result;
+}
+
+void validateAndConvertPaymentDetails(const PaymentDetails& input,
+                                      bool requestShipping,
+                                      PaymentDetailsPtr& output,
+                                      String& shippingOptionOutput,
+                                      ExceptionState& exceptionState) {
+  if (!input.hasTotal()) {
+    exceptionState.throwTypeError("Must specify total");
+    return;
+  }
+
+  validateAndConvertTotal(input.total(), output->total, exceptionState);
+  if (exceptionState.hadException())
+    return;
+
+  if (input.hasDisplayItems()) {
+    validateAndConvertDisplayItems(input.displayItems(), output->display_items,
+                                   exceptionState);
+    if (exceptionState.hadException())
+      return;
+  }
+
+  if (input.hasShippingOptions() && requestShipping) {
+    validateAndConvertShippingOptions(input.shippingOptions(),
+                                      output->shipping_options, exceptionState);
+    if (exceptionState.hadException())
+      return;
+  }
+
+  shippingOptionOutput = getSelectedShippingOption(output->shipping_options);
+
+  if (input.hasModifiers()) {
+    validateAndConvertPaymentDetailsModifiers(
+        input.modifiers(), output->modifiers, exceptionState);
+    if (exceptionState.hadException())
+      return;
+  }
+
+  if (input.hasError() && !input.error().isNull()) {
+    String errorMessage;
+    if (!PaymentsValidators::isValidErrorMsgFormat(input.error(),
+                                                   &errorMessage)) {
+      exceptionState.throwTypeError(errorMessage);
+      return;
+    }
+    output->error = input.error();
+  } else {
+    output->error = "";
+  }
+}
+
 void validateAndConvertPaymentMethodData(
     const HeapVector<PaymentMethodData>& input,
     Vector<payments::mojom::blink::PaymentMethodDataPtr>& output,
@@ -438,81 +435,36 @@
     return;
   }
 
-  output.resize(input.size());
-  for (size_t i = 0; i < input.size(); ++i) {
-    const auto& paymentMethodData = input[i];
+  for (const PaymentMethodData paymentMethodData : input) {
     if (paymentMethodData.supportedMethods().isEmpty()) {
       exceptionState.throwTypeError(
           "Must specify at least one payment method identifier");
       return;
     }
 
-    String stringifiedData = "";
+    output.append(payments::mojom::blink::PaymentMethodData::New());
+    output.back()->supported_methods = paymentMethodData.supportedMethods();
+
     if (paymentMethodData.hasData() && !paymentMethodData.data().isEmpty()) {
-      if (!paymentMethodData.data().v8Value()->IsObject() ||
-          paymentMethodData.data().v8Value()->IsArray()) {
-        exceptionState.throwTypeError(
-            "Data should be a JSON-serializable object");
-        return;
-      }
-
-      v8::Local<v8::String> value;
-      if (!v8::JSON::Stringify(
-               paymentMethodData.data().context(),
-               paymentMethodData.data().v8Value().As<v8::Object>())
-               .ToLocal(&value)) {
-        exceptionState.throwTypeError(
-            "Unable to parse payment method specific data");
-        return;
-      }
-      stringifiedData =
-          v8StringToWebCoreString<String>(value, DoNotExternalize);
-    }
-
-    output[i] = payments::mojom::blink::PaymentMethodData::New();
-    output[i]->supported_methods = paymentMethodData.supportedMethods();
-    output[i]->stringified_data = stringifiedData;
-    maybeSetAndroidPayMethodData(paymentMethodData.data(), output[i],
-                                 exceptionState);
-    if (exceptionState.hadException())
-      return;
-  }
-}
-
-String getSelectedShippingOption(const PaymentDetails& details) {
-  String result;
-  if (!details.hasShippingOptions())
-    return result;
-
-  for (int i = details.shippingOptions().size() - 1; i >= 0; --i) {
-    if (details.shippingOptions()[i].hasSelected() &&
-        details.shippingOptions()[i].selected()) {
-      return details.shippingOptions()[i].id();
+      stringifyAndParseMethodSpecificData(paymentMethodData.data(),
+                                          output.back(), exceptionState);
+    } else {
+      output.back()->stringified_data = "";
     }
   }
-
-  return result;
 }
 
 String getValidShippingType(const String& shippingType) {
   static const char* const validValues[] = {
       "shipping", "delivery", "pickup",
   };
-  for (size_t i = 0; i < WTF_ARRAY_LENGTH(validValues); i++) {
+  for (size_t i = 0; i < arraysize(validValues); i++) {
     if (shippingType == validValues[i])
       return shippingType;
   }
   return validValues[0];
 }
 
-PaymentDetailsPtr maybeKeepShippingOptions(PaymentDetailsPtr details,
-                                           bool keep) {
-  if (!keep)
-    details->shipping_options.resize(0);
-
-  return details;
-}
-
 bool allowedToUsePaymentRequest(const Frame* frame) {
   // To determine whether a Document object |document| is allowed to use the
   // feature indicated by attribute name |allowpaymentrequest|, run these steps:
@@ -672,7 +624,11 @@
     return;
   }
 
-  bool keepShippingOptions = validatePaymentDetails(details, exceptionState);
+  PaymentDetailsPtr validatedDetails =
+      payments::mojom::blink::PaymentDetails::New();
+  validateAndConvertPaymentDetails(details, m_options.requestShipping(),
+                                   validatedDetails, m_shippingOption,
+                                   exceptionState);
   if (exceptionState.hadException()) {
     m_showResolver->reject(
         DOMException::create(SyntaxError, exceptionState.message()));
@@ -680,16 +636,7 @@
     return;
   }
 
-  if (m_options.requestShipping()) {
-    if (keepShippingOptions)
-      m_shippingOption = getSelectedShippingOption(details);
-    else
-      m_shippingOption = String();
-  }
-
-  m_paymentProvider->UpdateWith(maybeKeepShippingOptions(
-      payments::mojom::blink::PaymentDetails::From(details),
-      keepShippingOptions));
+  m_paymentProvider->UpdateWith(std::move(validatedDetails));
 }
 
 void PaymentRequest::onUpdatePaymentDetailsFailure(const String& error) {
@@ -746,7 +693,11 @@
     return;
   }
 
-  bool keepShippingOptions = validatePaymentDetails(details, exceptionState);
+  PaymentDetailsPtr validatedDetails =
+      payments::mojom::blink::PaymentDetails::New();
+  validateAndConvertPaymentDetails(details, m_options.requestShipping(),
+                                   validatedDetails, m_shippingOption,
+                                   exceptionState);
   if (exceptionState.hadException())
     return;
 
@@ -755,11 +706,8 @@
     return;
   }
 
-  if (m_options.requestShipping()) {
-    if (keepShippingOptions)
-      m_shippingOption = getSelectedShippingOption(details);
+  if (m_options.requestShipping())
     m_shippingType = getValidShippingType(m_options.shippingType());
-  }
 
   scriptState->domWindow()->frame()->interfaceProvider()->getInterface(
       mojo::GetProxy(&m_paymentProvider));
@@ -768,10 +716,7 @@
                 PaymentErrorReason::UNKNOWN)));
   m_paymentProvider->Init(
       m_clientBinding.CreateInterfacePtrAndBind(),
-      std::move(validatedMethodData),
-      maybeKeepShippingOptions(
-          payments::mojom::blink::PaymentDetails::From(details),
-          keepShippingOptions && m_options.requestShipping()),
+      std::move(validatedMethodData), std::move(validatedDetails),
       payments::mojom::blink::PaymentOptions::From(m_options));
 }
 
@@ -813,8 +758,7 @@
   ALLOW_UNUSED_LOCAL(success);
 }
 
-void PaymentRequest::OnPaymentResponse(
-    payments::mojom::blink::PaymentResponsePtr response) {
+void PaymentRequest::OnPaymentResponse(PaymentResponsePtr response) {
   DCHECK(m_showResolver);
   DCHECK(!m_completeResolver);
   DCHECK(!m_completeTimer.isActive());
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestDetailsTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestDetailsTest.cpp
index 988948e..374e6b77 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestDetailsTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestDetailsTest.cpp
@@ -10,6 +10,7 @@
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
 #include "modules/payments/PaymentDetails.h"
+#include "modules/payments/PaymentOptions.h"
 #include "modules/payments/PaymentTestHelper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include <ostream>  // NOLINT
@@ -129,9 +130,11 @@
   V8TestingScope scope;
   scope.document().setSecurityOrigin(
       SecurityOrigin::create(KURL(KURL(), "https://www.example.com/")));
-  PaymentRequest::create(scope.getScriptState(),
-                         buildPaymentMethodDataForTest(),
-                         GetParam().buildDetails(), scope.getExceptionState());
+  PaymentOptions options;
+  options.setRequestShipping(true);
+  PaymentRequest::create(
+      scope.getScriptState(), buildPaymentMethodDataForTest(),
+      GetParam().buildDetails(), options, scope.getExceptionState());
 
   EXPECT_EQ(GetParam().expectException(),
             scope.getExceptionState().hadException());
diff --git a/third_party/WebKit/Source/modules/screen_orientation/BUILD.gn b/third_party/WebKit/Source/modules/screen_orientation/BUILD.gn
index e06285b3..863fe41 100644
--- a/third_party/WebKit/Source/modules/screen_orientation/BUILD.gn
+++ b/third_party/WebKit/Source/modules/screen_orientation/BUILD.gn
@@ -10,8 +10,8 @@
     "LockOrientationCallback.h",
     "ScreenOrientation.cpp",
     "ScreenOrientation.h",
-    "ScreenOrientationController.cpp",
-    "ScreenOrientationController.h",
+    "ScreenOrientationControllerImpl.cpp",
+    "ScreenOrientationControllerImpl.h",
     "ScreenOrientationDispatcher.cpp",
     "ScreenOrientationDispatcher.h",
     "ScreenScreenOrientation.cpp",
diff --git a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.cpp b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.cpp
index aa75279..937f9dbf 100644
--- a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.cpp
+++ b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.cpp
@@ -12,7 +12,7 @@
 #include "core/frame/LocalFrame.h"
 #include "modules/EventTargetModules.h"
 #include "modules/screen_orientation/LockOrientationCallback.h"
-#include "modules/screen_orientation/ScreenOrientationController.h"
+#include "modules/screen_orientation/ScreenOrientationControllerImpl.h"
 #include "public/platform/modules/screen_orientation/WebScreenOrientationType.h"
 
 // This code assumes that WebScreenOrientationType values are included in
@@ -98,7 +98,7 @@
   // Check if the ScreenOrientationController is supported for the
   // frame. It will not be for all LocalFrames, or the frame may
   // have been detached.
-  if (!ScreenOrientationController::from(*frame))
+  if (!ScreenOrientationControllerImpl::from(*frame))
     return nullptr;
 
   ScreenOrientation* orientation = new ScreenOrientation(frame);
@@ -182,11 +182,11 @@
   controller()->unlock();
 }
 
-ScreenOrientationController* ScreenOrientation::controller() {
+ScreenOrientationControllerImpl* ScreenOrientation::controller() {
   if (!frame())
     return 0;
 
-  return ScreenOrientationController::from(*frame());
+  return ScreenOrientationControllerImpl::from(*frame());
 }
 
 DEFINE_TRACE(ScreenOrientation) {
diff --git a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.h b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.h
index 3fe30b7..9d24694 100644
--- a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.h
+++ b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientation.h
@@ -19,7 +19,7 @@
 class LocalFrame;
 class ScriptPromise;
 class ScriptState;
-class ScreenOrientationController;
+class ScreenOrientationControllerImpl;
 
 class ScreenOrientation final : public EventTargetWithInlineData,
                                 public DOMWindowProperty {
@@ -54,7 +54,7 @@
  private:
   explicit ScreenOrientation(LocalFrame*);
 
-  ScreenOrientationController* controller();
+  ScreenOrientationControllerImpl* controller();
 
   WebScreenOrientationType m_type;
   unsigned short m_angle;
diff --git a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationController.cpp b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationControllerImpl.cpp
similarity index 75%
rename from third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationController.cpp
rename to third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationControllerImpl.cpp
index af8642e..342b15f 100644
--- a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationController.cpp
+++ b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationControllerImpl.cpp
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "modules/screen_orientation/ScreenOrientationController.h"
+#include "modules/screen_orientation/ScreenOrientationControllerImpl.h"
 
 #include "core/events/Event.h"
 #include "core/frame/FrameHost.h"
@@ -21,23 +21,22 @@
 
 namespace blink {
 
-ScreenOrientationController::~ScreenOrientationController() {}
+ScreenOrientationControllerImpl::~ScreenOrientationControllerImpl() = default;
 
-void ScreenOrientationController::provideTo(
+void ScreenOrientationControllerImpl::provideTo(
     LocalFrame& frame,
     WebScreenOrientationClient* client) {
-  ScreenOrientationController* controller =
-      new ScreenOrientationController(frame, client);
-  Supplement<LocalFrame>::provideTo(frame, supplementName(), controller);
+  ScreenOrientationController::provideTo(
+      frame, new ScreenOrientationControllerImpl(frame, client));
 }
 
-ScreenOrientationController* ScreenOrientationController::from(
+ScreenOrientationControllerImpl* ScreenOrientationControllerImpl::from(
     LocalFrame& frame) {
-  return static_cast<ScreenOrientationController*>(
-      Supplement<LocalFrame>::from(frame, supplementName()));
+  return static_cast<ScreenOrientationControllerImpl*>(
+      ScreenOrientationController::from(frame));
 }
 
-ScreenOrientationController::ScreenOrientationController(
+ScreenOrientationControllerImpl::ScreenOrientationControllerImpl(
     LocalFrame& frame,
     WebScreenOrientationClient* client)
     : DOMWindowProperty(&frame),
@@ -45,15 +44,11 @@
       m_client(client),
       m_dispatchEventTimer(
           this,
-          &ScreenOrientationController::dispatchEventTimerFired) {}
-
-const char* ScreenOrientationController::supplementName() {
-  return "ScreenOrientationController";
-}
+          &ScreenOrientationControllerImpl::dispatchEventTimerFired) {}
 
 // Compute the screen orientation using the orientation angle and the screen
 // width / height.
-WebScreenOrientationType ScreenOrientationController::computeOrientation(
+WebScreenOrientationType ScreenOrientationControllerImpl::computeOrientation(
     const IntRect& rect,
     uint16_t rotation) {
   // Bypass orientation detection in layout tests to get consistent results.
@@ -78,15 +73,15 @@
       return isTallDisplay ? WebScreenOrientationLandscapeSecondary
                            : WebScreenOrientationPortraitPrimary;
     default:
-      ASSERT_NOT_REACHED();
+      NOTREACHED();
       return WebScreenOrientationPortraitPrimary;
   }
 }
 
-void ScreenOrientationController::updateOrientation() {
-  ASSERT(m_orientation);
-  ASSERT(frame());
-  ASSERT(frame()->host());
+void ScreenOrientationControllerImpl::updateOrientation() {
+  DCHECK(m_orientation);
+  DCHECK(frame());
+  DCHECK(frame()->host());
 
   ChromeClient& chromeClient = frame()->host()->chromeClient();
   WebScreenInfo screenInfo = chromeClient.screenInfo();
@@ -97,17 +92,17 @@
     orientationType = computeOrientation(chromeClient.screenInfo().rect,
                                          screenInfo.orientationAngle);
   }
-  ASSERT(orientationType != WebScreenOrientationUndefined);
+  DCHECK(orientationType != WebScreenOrientationUndefined);
 
   m_orientation->setType(orientationType);
   m_orientation->setAngle(screenInfo.orientationAngle);
 }
 
-bool ScreenOrientationController::isActiveAndVisible() const {
+bool ScreenOrientationControllerImpl::isActiveAndVisible() const {
   return m_orientation && m_client && page() && page()->isPageVisible();
 }
 
-void ScreenOrientationController::pageVisibilityChanged() {
+void ScreenOrientationControllerImpl::pageVisibilityChanged() {
   notifyDispatcher();
 
   if (!isActiveAndVisible())
@@ -131,7 +126,7 @@
     notifyOrientationChanged();
 }
 
-void ScreenOrientationController::notifyOrientationChanged() {
+void ScreenOrientationControllerImpl::notifyOrientationChanged() {
   if (!isActiveAndVisible())
     return;
 
@@ -151,15 +146,15 @@
   if (!m_dispatchEventTimer.isActive())
     m_dispatchEventTimer.startOneShot(0, BLINK_FROM_HERE);
 
-  // ... and child frames, if they have a ScreenOrientationController.
+  // ... and child frames, if they have a ScreenOrientationControllerImpl.
   for (size_t i = 0; i < childFrames.size(); ++i) {
-    if (ScreenOrientationController* controller =
-            ScreenOrientationController::from(*childFrames[i]))
+    if (ScreenOrientationControllerImpl* controller =
+            ScreenOrientationControllerImpl::from(*childFrames[i]))
       controller->notifyOrientationChanged();
   }
 }
 
-void ScreenOrientationController::setOrientation(
+void ScreenOrientationControllerImpl::setOrientation(
     ScreenOrientation* orientation) {
   m_orientation = orientation;
   if (m_orientation)
@@ -167,7 +162,7 @@
   notifyDispatcher();
 }
 
-void ScreenOrientationController::lock(
+void ScreenOrientationControllerImpl::lock(
     WebScreenOrientationLockType orientation,
     std::unique_ptr<WebLockOrientationCallback> callback) {
   // When detached, the client is no longer valid.
@@ -176,14 +171,14 @@
   m_client->lockOrientation(orientation, std::move(callback));
 }
 
-void ScreenOrientationController::unlock() {
+void ScreenOrientationControllerImpl::unlock() {
   // When detached, the client is no longer valid.
   if (!m_client)
     return;
   m_client->unlockOrientation();
 }
 
-void ScreenOrientationController::dispatchEventTimerFired(TimerBase*) {
+void ScreenOrientationControllerImpl::dispatchEventTimerFired(TimerBase*) {
   if (!m_orientation)
     return;
 
@@ -191,35 +186,35 @@
   m_orientation->dispatchEvent(Event::create(EventTypeNames::change));
 }
 
-void ScreenOrientationController::didUpdateData() {
+void ScreenOrientationControllerImpl::didUpdateData() {
   // Do nothing.
 }
 
-void ScreenOrientationController::registerWithDispatcher() {
+void ScreenOrientationControllerImpl::registerWithDispatcher() {
   ScreenOrientationDispatcher::instance().addController(this);
 }
 
-void ScreenOrientationController::unregisterWithDispatcher() {
+void ScreenOrientationControllerImpl::unregisterWithDispatcher() {
   ScreenOrientationDispatcher::instance().removeController(this);
 }
 
-bool ScreenOrientationController::hasLastData() {
+bool ScreenOrientationControllerImpl::hasLastData() {
   return true;
 }
 
-void ScreenOrientationController::frameDestroyed() {
+void ScreenOrientationControllerImpl::frameDestroyed() {
   m_client = nullptr;
   DOMWindowProperty::frameDestroyed();
 }
 
-void ScreenOrientationController::notifyDispatcher() {
+void ScreenOrientationControllerImpl::notifyDispatcher() {
   if (m_orientation && page()->isPageVisible())
     startUpdating();
   else
     stopUpdating();
 }
 
-DEFINE_TRACE(ScreenOrientationController) {
+DEFINE_TRACE(ScreenOrientationControllerImpl) {
   visitor->trace(m_orientation);
   DOMWindowProperty::trace(visitor);
   Supplement<LocalFrame>::trace(visitor);
diff --git a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationController.h b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationControllerImpl.h
similarity index 67%
rename from third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationController.h
rename to third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationControllerImpl.h
index 2fb4e89..b054aaf 100644
--- a/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationController.h
+++ b/third_party/WebKit/Source/modules/screen_orientation/ScreenOrientationControllerImpl.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ScreenOrientationController_h
-#define ScreenOrientationController_h
+#ifndef ScreenOrientationControllerImpl_h
+#define ScreenOrientationControllerImpl_h
 
 #include "core/frame/DOMWindowProperty.h"
 #include "core/frame/PlatformEventController.h"
+#include "core/frame/ScreenOrientationController.h"
 #include "modules/ModulesExport.h"
-#include "platform/Supplementable.h"
 #include "platform/Timer.h"
 #include "public/platform/modules/screen_orientation/WebLockOrientationCallback.h"
 #include "public/platform/modules/screen_orientation/WebScreenOrientationLockType.h"
@@ -20,32 +20,32 @@
 class ScreenOrientation;
 class WebScreenOrientationClient;
 
-class MODULES_EXPORT ScreenOrientationController final
-    : public GarbageCollectedFinalized<ScreenOrientationController>,
-      public Supplement<LocalFrame>,
+class MODULES_EXPORT ScreenOrientationControllerImpl final
+    : public GarbageCollectedFinalized<ScreenOrientationControllerImpl>,
+      public ScreenOrientationController,
       public DOMWindowProperty,
       public PlatformEventController {
-  USING_GARBAGE_COLLECTED_MIXIN(ScreenOrientationController);
-  WTF_MAKE_NONCOPYABLE(ScreenOrientationController);
+  USING_GARBAGE_COLLECTED_MIXIN(ScreenOrientationControllerImpl);
+  WTF_MAKE_NONCOPYABLE(ScreenOrientationControllerImpl);
 
  public:
-  ~ScreenOrientationController() override;
+  ~ScreenOrientationControllerImpl() override;
 
   void setOrientation(ScreenOrientation*);
   void notifyOrientationChanged();
 
+  // Implementation of ScreenOrientationController.
   void lock(WebScreenOrientationLockType,
-            std::unique_ptr<WebLockOrientationCallback>);
-  void unlock();
+            std::unique_ptr<WebLockOrientationCallback>) override;
+  void unlock() override;
 
   static void provideTo(LocalFrame&, WebScreenOrientationClient*);
-  static ScreenOrientationController* from(LocalFrame&);
-  static const char* supplementName();
+  static ScreenOrientationControllerImpl* from(LocalFrame&);
 
   DECLARE_VIRTUAL_TRACE();
 
  private:
-  ScreenOrientationController(LocalFrame&, WebScreenOrientationClient*);
+  ScreenOrientationControllerImpl(LocalFrame&, WebScreenOrientationClient*);
 
   static WebScreenOrientationType computeOrientation(const IntRect&, uint16_t);
 
@@ -69,9 +69,9 @@
 
   Member<ScreenOrientation> m_orientation;
   WebScreenOrientationClient* m_client;
-  Timer<ScreenOrientationController> m_dispatchEventTimer;
+  Timer<ScreenOrientationControllerImpl> m_dispatchEventTimer;
 };
 
 }  // namespace blink
 
-#endif  // ScreenOrientationController_h
+#endif  // ScreenOrientationControllerImpl_h
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index 091e628..48237e05 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -4101,9 +4101,14 @@
     synthesizeGLError(GL_INVALID_FRAMEBUFFER_OPERATION, "readPixels", reason);
     return;
   }
-  if (!validateReadPixelsFuncParameters(
-          width, height, format, type, pixels,
-          (pixels->byteLength() - offsetInBytes).ValueOrDie())) {
+  CheckedNumeric<GLuint> bufferSize = pixels->byteLength() - offsetInBytes;
+  if (!bufferSize.IsValid()) {
+    synthesizeGLError(GL_INVALID_VALUE, "readPixels",
+                      "destination offset out of range");
+    return;
+  }
+  if (!validateReadPixelsFuncParameters(width, height, format, type, pixels,
+                                        bufferSize.ValueOrDie())) {
     return;
   }
   clearIfComposited();
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index 7efe2b45..a1494f30 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -173,6 +173,7 @@
 PassPaintVisualRectToCompositor
 PaymentApp status=experimental
 PaymentDetailsError status=experimental
+PaymentDetailsModifierData status=experimental
 // PaymentRequest is enabled by default on Android
 PaymentRequest status=experimental
 PaymentRequestIFrame status=experimental
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp b/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp
index 88ae543..06f0a0d 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp
@@ -2660,7 +2660,11 @@
   }
 
   unsigned padding = 0;
-  unsigned residual = (checkedValue % params.alignment).ValueOrDie();
+  CheckedNumeric<uint32_t> checkedResidual = checkedValue % params.alignment;
+  if (!checkedResidual.IsValid()) {
+    return GL_INVALID_VALUE;
+  }
+  unsigned residual = checkedResidual.ValueOrDie();
   if (residual) {
     padding = params.alignment - residual;
     checkedValue += padding;
diff --git a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.h b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.h
index 800a1221..05e5c9a 100644
--- a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.h
+++ b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.h
@@ -92,8 +92,6 @@
                           const ScrollOffset& delta) override;
   void scrollToOffsetWithoutAnimation(const ScrollOffset&) override;
 
-  void handleWheelEventPhase(PlatformWheelEventPhase) override;
-
   void cancelAnimation() override;
 
   void contentAreaWillPaint() const override;
@@ -105,9 +103,6 @@
   void contentsResized() const override;
   void contentAreaDidShow() const override;
   void contentAreaDidHide() const override;
-  void didBeginScrollGesture() const;
-  void didEndScrollGesture() const;
-  void mayBeginScrollGesture() const;
 
   void finishCurrentScrollAnimations() override;
 
diff --git a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm
index 0a52e4f..fa1161c 100644
--- a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm
+++ b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm
@@ -871,25 +871,6 @@
   [m_scrollbarPainterController.get() windowOrderedOut];
 }
 
-void ScrollAnimatorMac::didBeginScrollGesture() const {
-  if (!getScrollableArea()->scrollbarsCanBeActive())
-    return;
-  [m_scrollbarPainterController.get() beginScrollGesture];
-}
-
-void ScrollAnimatorMac::didEndScrollGesture() const {
-  if (!getScrollableArea()->scrollbarsCanBeActive())
-    return;
-  [m_scrollbarPainterController.get() endScrollGesture];
-}
-
-void ScrollAnimatorMac::mayBeginScrollGesture() const {
-  if (!getScrollableArea()->scrollbarsCanBeActive())
-    return;
-  [m_scrollbarPainterController.get() beginScrollGesture];
-  [m_scrollbarPainterController.get() contentAreaScrolled];
-}
-
 void ScrollAnimatorMac::finishCurrentScrollAnimations() {
   [m_scrollbarPainterController.get() hideOverlayScrollers];
 }
@@ -972,21 +953,6 @@
   m_haveScrolledSincePageLoad = false;
 }
 
-void ScrollAnimatorMac::handleWheelEventPhase(PlatformWheelEventPhase phase) {
-  // This may not have been set to true yet if the wheel event was handled by
-  // the ScrollingTree,
-  // So set it to true here.
-  m_haveScrolledSincePageLoad = true;
-
-  if (phase == PlatformWheelEventPhaseBegan)
-    didBeginScrollGesture();
-  else if (phase == PlatformWheelEventPhaseEnded ||
-           phase == PlatformWheelEventPhaseCancelled)
-    didEndScrollGesture();
-  else if (phase == PlatformWheelEventPhaseMayBegin)
-    mayBeginScrollGesture();
-}
-
 void ScrollAnimatorMac::updateScrollerStyle() {
   if (!getScrollableArea()->scrollbarsCanBeActive()) {
     m_needsScrollerStyleUpdate = true;
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollAnimatorBase.h b/third_party/WebKit/Source/platform/scroll/ScrollAnimatorBase.h
index 42d431d..6a81c42b 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollAnimatorBase.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollAnimatorBase.h
@@ -63,10 +63,6 @@
 
   virtual void scrollToOffsetWithoutAnimation(const ScrollOffset&);
 
-#if OS(MACOSX)
-  virtual void handleWheelEventPhase(PlatformWheelEventPhase) {}
-#endif
-
   void setCurrentOffset(const ScrollOffset&);
   ScrollOffset currentOffset() const;
   virtual ScrollOffset desiredTargetOffset() const { return currentOffset(); }
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
index 52377c6..ec3508f 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -60,7 +60,7 @@
 #include "modules/mediastream/UserMediaController.h"
 #include "modules/presentation/PresentationController.h"
 #include "modules/push_messaging/PushController.h"
-#include "modules/screen_orientation/ScreenOrientationController.h"
+#include "modules/screen_orientation/ScreenOrientationControllerImpl.h"
 #include "modules/vr/VRController.h"
 #include "platform/Cursor.h"
 #include "platform/FileChooser.h"
@@ -1165,8 +1165,8 @@
   if (RuntimeEnabledFeatures::webBluetoothEnabled())
     BluetoothSupplement::provideTo(frame, client->bluetooth());
 
-  ScreenOrientationController::provideTo(frame,
-                                         client->webScreenOrientationClient());
+  ScreenOrientationControllerImpl::provideTo(
+      frame, client->webScreenOrientationClient());
   if (RuntimeEnabledFeatures::presentationEnabled())
     PresentationController::provideTo(frame, client->presentationClient());
   if (RuntimeEnabledFeatures::audioOutputDevicesEnabled())
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index 9887dd8..e0108fad 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -157,7 +157,7 @@
 #include "core/timing/DOMWindowPerformance.h"
 #include "core/timing/Performance.h"
 #include "modules/app_banner/AppBannerController.h"
-#include "modules/screen_orientation/ScreenOrientationController.h"
+#include "modules/screen_orientation/ScreenOrientationControllerImpl.h"
 #include "platform/ScriptForbiddenScope.h"
 #include "platform/UserGestureIndicator.h"
 #include "platform/clipboard/ClipboardUtilities.h"
@@ -2057,8 +2057,8 @@
     return;
 
   // Screen Orientation API
-  if (ScreenOrientationController::from(*frame()))
-    ScreenOrientationController::from(*frame())->notifyOrientationChanged();
+  if (ScreenOrientationControllerImpl::from(*frame()))
+    ScreenOrientationControllerImpl::from(*frame())->notifyOrientationChanged();
 
   // Legacy window.orientation API
   if (RuntimeEnabledFeatures::orientationEventEnabled() && frame()->domWindow())
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
index db8be0a..c85cd20 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -785,6 +785,8 @@
             else:
                 path_in_wpt = filename
             return self._manifest_items_for_path(path_in_wpt) is not None
+        if 'inspector-unit' in dirname:
+            return filesystem.splitext(filename)[1] == '.js'
         return Port._has_supported_extension(
             filesystem, filename) and not Port.is_reference_html_file(filesystem, dirname, filename)
 
@@ -1093,9 +1095,6 @@
     def inspector_build_directory(self):
         return self._build_path('resources', 'inspector')
 
-    def inspector_debug_directory(self):
-        return self.path_from_webkit_base('Source', 'devtools', 'front_end')
-
     def apache_config_directory(self):
         return self.path_from_webkit_base('Tools', 'Scripts', 'apache_config')
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
index 36d4dd3..c508704 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
@@ -355,6 +355,9 @@
         self.assertTrue(is_test_file('', 'foo.html'))
         self.assertTrue(is_test_file('', 'foo.svg'))
         self.assertTrue(is_test_file('', 'test-ref-test.html'))
+        self.assertTrue(is_test_file('inspector-unit', 'trie.js'))
+        self.assertFalse(is_test_file('inspector-unit', 'foo.html'))
+        self.assertFalse(is_test_file('inspector', 'devtools.js'))
         self.assertFalse(is_test_file('', 'foo.png'))
         self.assertFalse(is_test_file('', 'foo-expected.html'))
         self.assertFalse(is_test_file('', 'foo-expected.svg'))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http.py
index d2a658a9..d1b3e58 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http.py
@@ -63,7 +63,6 @@
         mime_types_path = self._filesystem.join(self._port_obj.apache_config_directory(), "mime.types")
         cert_file = self._filesystem.join(self._port_obj.apache_config_directory(), "webkit-httpd.pem")
         inspector_sources_dir = self._port_obj.inspector_build_directory()
-        inspector_debug_dir = self._port_obj.inspector_debug_directory()
 
         self._access_log_path = self._filesystem.join(output_dir, "access_log.txt")
         self._error_log_path = self._filesystem.join(output_dir, "error_log.txt")
@@ -87,7 +86,6 @@
             '-c', 'PidFile %s' % self._pid_file,
             '-c', 'SSLCertificateFile "%s"' % cert_file,
             '-c', 'Alias /inspector-sources "%s"' % inspector_sources_dir,
-            '-c', 'Alias /inspector-debug "%s"' % inspector_debug_dir,
             '-c', 'DefaultType None',
         ]
 
diff --git a/third_party/polymer/v1_0/chromium.patch b/third_party/polymer/v1_0/chromium.patch
index 7649e0d..d6a80e8 100644
--- a/third_party/polymer/v1_0/chromium.patch
+++ b/third_party/polymer/v1_0/chromium.patch
@@ -13,7 +13,7 @@
 index 2eaa665..7416f94 100644
 --- a/components-chromium/paper-icon-button/paper-icon-button-light.html
 +++ b/components-chromium/paper-icon-button/paper-icon-button-light.html
-@@ -45,9 +45,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
+@@ +45,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
        paper-ripple {
          opacity: 0.6;
          color: currentColor;
@@ -21,6 +21,4 @@
        }
      </style>
      <content></content>
-   </template>
-   </dom-module>
 
diff --git a/third_party/polymer/v1_0/find_unused_elements.py b/third_party/polymer/v1_0/find_unused_elements.py
old mode 100755
new mode 100644
index a93b177..5dbacdb
--- a/third_party/polymer/v1_0/find_unused_elements.py
+++ b/third_party/polymer/v1_0/find_unused_elements.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python2
-
 # 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.
diff --git a/third_party/polymer/v1_0/reproduce.sh b/third_party/polymer/v1_0/reproduce.sh
index 9f388c7..8fb85b4 100755
--- a/third_party/polymer/v1_0/reproduce.sh
+++ b/third_party/polymer/v1_0/reproduce.sh
@@ -112,4 +112,4 @@
 popd > /dev/null
 
 echo 'Searching for unused elements...'
-"$(dirname "$0")"/find_unused_elements.py
+python "$(dirname "$0")"/find_unused_elements.py
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 46579b9..8c611e9 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -9837,6 +9837,14 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="MobileSadTabFeedback">
+  <owner>jwanda@chromium.org</owner>
+  <description>
+    The user pressed the Send Feedback button on a tab that is displaying the
+    sad view.
+  </description>
+</action>
+
 <action name="MobileSettingsStorageClearAll">
   <owner>dmurph@chromium.org</owner>
   <description>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ae162cf..b6cb3d29 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -40913,6 +40913,17 @@
   </summary>
 </histogram>
 
+<histogram name="Omnibox.HistoryQuickHistoryIDSetFromWords" units="ms">
+  <owner>mpearson@chromium.org</owner>
+  <summary>
+    Times URLIndexPrivateData::HistoryIDSetFromWords(), which is called by the
+    omnibox's HistoryQuick provider.
+    URLIndexPrivateData::HistoryIDSetFromWords() can be called multiple times
+    per keystroke due to, for example, the cursor being in the middle of the
+    input string or SearchProvider's calls to Classify().
+  </summary>
+</histogram>
+
 <histogram name="Omnibox.InputType" enum="OmniboxInputType">
   <owner>mpearson@chromium.org</owner>
   <summary>
@@ -80908,9 +80919,9 @@
 <enum name="DownEventDestination" type="int">
   <int value="0" label="Others, everything except browser and apps"/>
   <int value="1" label="Inside the browser frame"/>
-  <int value="2" label="Regular chrome app, except default note-taking app"/>
-  <int value="3" label="ARC app (android app on Chrome)"/>
-  <int value="4" label="Default note-taking app"/>
+  <int value="2" label="Regular Chrome app"/>
+  <int value="3" label="ARC app (Android app on Chrome)"/>
+  <int value="4" label="Default note-taking app (deprecated)"/>
 </enum>
 
 <enum name="DownEventFormFactor" type="int">
@@ -101084,9 +101095,28 @@
   <int value="1" label="Recovered">
     Failed to open the existing db, deleted it, and created a new empty db.
   </int>
-  <int value="2" label="Total Fail">
+  <int value="2" label="Reopen: Total Fail">
+    <obsolete>
+      Deprecated 2016/11. Replaced by other &quot;Reopen:&quot; errors.
+    </obsolete>
     Failed to open the database and also failed to delete and start over.
   </int>
+  <int value="3" label="Reopen: not found">
+    Reopen attempt failed with (not found) after successful database delete.
+  </int>
+  <int value="4" label="Reopen: not supported">
+    Reopen attempt failed with (not supported) after successful database delete.
+  </int>
+  <int value="5" label="Reopen: corruption">
+    Reopen attempt failed with (corruption) after successful database delete.
+  </int>
+  <int value="6" label="Reopen: invalid argument">
+    Reopen attempt failed with (invalid argument) after successful database
+    delete.
+  </int>
+  <int value="7" label="Reopen: I/O error">
+    Reopen attempt failed with (I/O error) after successful database delete.
+  </int>
 </enum>
 
 <enum name="SetDefaultAttemptResult" type="int">
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 52985fe..d5a64e8 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -58,6 +58,9 @@
 }
 
 void WindowPortMus::SetPredefinedCursor(ui::mojom::Cursor cursor_id) {
+  if (cursor_id == predefined_cursor_)
+    return;
+
   window_tree_client_->SetPredefinedCursor(this, predefined_cursor_, cursor_id);
   predefined_cursor_ = cursor_id;
 }
diff --git a/ui/login/account_picker/user_pod_row.js b/ui/login/account_picker/user_pod_row.js
index 71e88f2..58ffddfc 100644
--- a/ui/login/account_picker/user_pod_row.js
+++ b/ui/login/account_picker/user_pod_row.js
@@ -2729,10 +2729,10 @@
     },
 
     /**
-     * Enables or disables transitions on the user pod.
+     * Enables or disables transitions on every pod instance.
      * @param {boolean} enable
      */
-    togglePinTransitions: function(enable) {
+    toggleTransitions: function(enable) {
       for (var i = 0; i < this.pods.length; ++i)
         this.pods[i].toggleTransitions(enable);
     },
@@ -2773,15 +2773,6 @@
     },
 
     /**
-     * Function that hides the pin keyboard. Meant to be called when the virtual
-     * keyboard is enabled and being toggled.
-     * @param {boolean} hidden
-     */
-    setPinHidden: function(hidden) {
-      this.setFocusedPodPinVisibility(!hidden);
-    },
-
-    /**
      * Remove the pin keyboard from the pod with the given |username|.
      * @param {!user} username
      * @param {boolean} visible
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index fb08fb3..a3b5210c 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -266,7 +266,7 @@
 
     set pinHidden(hidden) {
       this.virtualKeyboardShown = hidden;
-      $('pod-row').setPinHidden(hidden);
+      $('pod-row').setFocusedPodPinVisibility(!hidden);
     },
 
     /**
diff --git a/ui/views/bubble/tray_bubble_view.cc b/ui/views/bubble/tray_bubble_view.cc
index 61f1f7e..53a0506 100644
--- a/ui/views/bubble/tray_bubble_view.cc
+++ b/ui/views/bubble/tray_bubble_view.cc
@@ -192,6 +192,7 @@
     : BubbleDialogDelegateView(anchor,
                                GetArrowAlignment(init_params.anchor_alignment)),
       params_(init_params),
+      layout_(new BottomAlignedBoxLayout(this)),
       delegate_(delegate),
       preferred_width_(init_params.min_width),
       bubble_border_(new BubbleBorder(arrow(),
@@ -211,6 +212,9 @@
 
   bubble_content_mask_.reset(
       new TrayBubbleContentMask(bubble_border_->GetBorderCornerRadius()));
+
+  layout_->SetDefaultFlex(1);
+  SetLayoutManager(layout_);
 }
 
 TrayBubbleView::~TrayBubbleView() {
@@ -243,6 +247,10 @@
     SizeToContents();
 }
 
+void TrayBubbleView::SetBottomPadding(int padding) {
+  layout_->set_inside_border_insets(gfx::Insets(0, 0, padding, 0));
+}
+
 void TrayBubbleView::SetWidth(int width) {
   width = std::max(std::min(width, params_.max_width), params_.min_width);
   if (preferred_width_ == width)
@@ -260,12 +268,6 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
-void TrayBubbleView::Init() {
-  BoxLayout* layout = new BottomAlignedBoxLayout(this);
-  layout->SetDefaultFlex(1);
-  SetLayoutManager(layout);
-}
-
 void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params,
                                               Widget* bubble_widget) const {
   if (delegate_)
diff --git a/ui/views/bubble/tray_bubble_view.h b/ui/views/bubble/tray_bubble_view.h
index a50791d9..8de9c30 100644
--- a/ui/views/bubble/tray_bubble_view.h
+++ b/ui/views/bubble/tray_bubble_view.h
@@ -13,6 +13,7 @@
 #include "ui/views/views_export.h"
 
 namespace views {
+class BoxLayout;
 class View;
 class Widget;
 }
@@ -102,6 +103,9 @@
   // Sets the maximum bubble height and resizes the bubble.
   void SetMaxHeight(int height);
 
+  // Sets the bottom padding that child views will be laid out within.
+  void SetBottomPadding(int padding);
+
   // Sets the bubble width.
   void SetWidth(int width);
 
@@ -145,7 +149,6 @@
 
   // Overridden from views::BubbleDialogDelegateView.
   int GetDialogButtons() const override;
-  void Init() override;
 
   // Overridden from views::View.
   void ChildPreferredSizeChanged(View* child) override;
@@ -154,6 +157,7 @@
 
  private:
   InitParams params_;
+  BoxLayout* layout_;
   Delegate* delegate_;
   int preferred_width_;
   // |bubble_border_| and |owned_bubble_border_| point to the same thing, but
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc
index 29197e63..8d441d91 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.cc
+++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -6,9 +6,11 @@
 
 #include "base/memory/ptr_util.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/env.h"
+#include "ui/aura/mus/window_port_mus.h"
 #include "ui/aura/mus/window_tree_host_mus.h"
 #include "ui/aura/window.h"
 #include "ui/base/hit_test.h"
@@ -20,6 +22,8 @@
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 #include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget_delegate.h"
+#include "ui/wm/core/cursor_manager.h"
+#include "ui/wm/core/native_cursor_manager.h"
 #include "ui/wm/core/window_util.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -106,6 +110,62 @@
   DISALLOW_COPY_AND_ASSIGN(ClientSideNonClientFrameView);
 };
 
+class NativeCursorManagerMus : public wm::NativeCursorManager {
+ public:
+  explicit NativeCursorManagerMus(aura::Window* window) : window_(window) {}
+  ~NativeCursorManagerMus() override {}
+
+  // wm::NativeCursorManager:
+  void SetDisplay(const display::Display& display,
+                  wm::NativeCursorManagerDelegate* delegate) override {
+    // We ignore this entirely, as cursor are set on the client.
+  }
+
+  void SetCursor(gfx::NativeCursor cursor,
+                 wm::NativeCursorManagerDelegate* delegate) override {
+    aura::WindowPortMus::Get(window_)->SetPredefinedCursor(
+        ui::mojom::Cursor(cursor.native_type()));
+    delegate->CommitCursor(cursor);
+  }
+
+  void SetVisibility(bool visible,
+                     wm::NativeCursorManagerDelegate* delegate) override {
+    delegate->CommitVisibility(visible);
+
+    if (visible) {
+      SetCursor(delegate->GetCursor(), delegate);
+    } else {
+      aura::WindowPortMus::Get(window_)->SetPredefinedCursor(
+          ui::mojom::Cursor::NONE);
+    }
+  }
+
+  void SetCursorSet(ui::CursorSetType cursor_set,
+                    wm::NativeCursorManagerDelegate* delegate) override {
+    // TODO(erg): For now, ignore the difference between SET_NORMAL and
+    // SET_LARGE here. This feels like a thing that mus should decide instead.
+    //
+    // Also, it's NOTIMPLEMENTED() in the desktop version!? Including not
+    // acknowledging the call in the delegate.
+    NOTIMPLEMENTED();
+  }
+
+  void SetMouseEventsEnabled(
+      bool enabled,
+      wm::NativeCursorManagerDelegate* delegate) override {
+    // TODO(erg): How do we actually implement this?
+    //
+    // Mouse event dispatch is potentially done in a different process,
+    // definitely in a different mojo service. Each app is fairly locked down.
+    delegate->CommitMouseEventsEnabled(enabled);
+    NOTIMPLEMENTED();
+  }
+
+ private:
+  aura::Window* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeCursorManagerMus);
+};
 }  // namespace
 
 DesktopWindowTreeHostMus::DesktopWindowTreeHostMus(
@@ -180,6 +240,10 @@
   content_window->layer()->SetFillsBoundsOpaquely(false);
   if (!params.bounds.IsEmpty())
     SetBoundsInDIP(params.bounds);
+
+  cursor_manager_ = base::MakeUnique<wm::CursorManager>(
+      base::MakeUnique<NativeCursorManagerMus>(window()));
+  aura::client::SetCursorClient(window(), cursor_manager_.get());
 }
 
 void DesktopWindowTreeHostMus::OnNativeWidgetCreated(
@@ -546,6 +610,11 @@
   return false;
 }
 
+bool DesktopWindowTreeHostMus::ShouldUseDesktopNativeCursorManager() const {
+  // We manage the cursor ourself.
+  return false;
+}
+
 void DesktopWindowTreeHostMus::OnWindowManagerFrameValuesChanged() {
   NonClientView* non_client_view =
       native_widget_delegate_->AsWidget()->non_client_view();
diff --git a/ui/views/mus/desktop_window_tree_host_mus.h b/ui/views/mus/desktop_window_tree_host_mus.h
index eb0e6930..b988aee 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.h
+++ b/ui/views/mus/desktop_window_tree_host_mus.h
@@ -16,6 +16,10 @@
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
 #include "ui/views/widget/widget.h"
 
+namespace wm {
+class CursorManager;
+}
+
 namespace views {
 
 class VIEWS_MUS_EXPORT DesktopWindowTreeHostMus
@@ -102,6 +106,7 @@
   bool IsTranslucentWindowOpacitySupported() const override;
   void SizeConstraintsChanged() override;
   bool ShouldUpdateWindowTransparency() const override;
+  bool ShouldUseDesktopNativeCursorManager() const override;
 
   // MusClientObserver:
   void OnWindowManagerFrameValuesChanged() override;
@@ -131,6 +136,8 @@
 
   bool is_active_ = false;
 
+  std::unique_ptr<wm::CursorManager> cursor_manager_;
+
   // Used so that Close() isn't immediate.
   base::WeakPtrFactory<DesktopWindowTreeHostMus> close_widget_factory_;
 
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 91698f4..e50108f 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -326,14 +326,16 @@
 
 void DesktopNativeWidgetAura::OnDesktopWindowTreeHostDestroyed(
     aura::WindowTreeHost* host) {
-  // We explicitly do NOT clear the cursor client property. Since the cursor
-  // manager is a singleton, it can outlive any window hierarchy, and it's
-  // important that objects attached to this destroying window hierarchy have
-  // an opportunity to deregister their observers from the cursor manager.
-  // They may want to do this when they are notified that they're being
-  // removed from the window hierarchy, which happens soon after this
-  // function when DesktopWindowTreeHost* calls DestroyDispatcher().
-  native_cursor_manager_->RemoveHost(host);
+  if (use_desktop_native_cursor_manager_) {
+    // We explicitly do NOT clear the cursor client property. Since the cursor
+    // manager is a singleton, it can outlive any window hierarchy, and it's
+    // important that objects attached to this destroying window hierarchy have
+    // an opportunity to deregister their observers from the cursor manager.
+    // They may want to do this when they are notified that they're being
+    // removed from the window hierarchy, which happens soon after this
+    // function when DesktopWindowTreeHost* calls DestroyDispatcher().
+    native_cursor_manager_->RemoveHost(host);
+  }
 
   aura::client::SetScreenPositionClient(host->window(), NULL);
   position_client_.reset();
@@ -447,17 +449,21 @@
   root_window_event_filter_.reset(new wm::CompoundEventFilter);
   host_->window()->AddPreTargetHandler(root_window_event_filter_.get());
 
-  // The host's dispatcher must be added to |native_cursor_manager_| before
-  // OnNativeWidgetCreated() is called.
-  cursor_reference_count_++;
-  if (!native_cursor_manager_)
-    native_cursor_manager_ = new DesktopNativeCursorManager();
-  if (!cursor_manager_) {
-    cursor_manager_ = new wm::CursorManager(
-        std::unique_ptr<wm::NativeCursorManager>(native_cursor_manager_));
+  use_desktop_native_cursor_manager_ =
+      desktop_window_tree_host_->ShouldUseDesktopNativeCursorManager();
+  if (use_desktop_native_cursor_manager_) {
+    // The host's dispatcher must be added to |native_cursor_manager_| before
+    // OnNativeWidgetCreated() is called.
+    cursor_reference_count_++;
+    if (!native_cursor_manager_)
+      native_cursor_manager_ = new DesktopNativeCursorManager();
+    if (!cursor_manager_) {
+      cursor_manager_ = new wm::CursorManager(
+          std::unique_ptr<wm::NativeCursorManager>(native_cursor_manager_));
+    }
+    native_cursor_manager_->AddHost(host());
+    aura::client::SetCursorClient(host_->window(), cursor_manager_);
   }
-  native_cursor_manager_->AddHost(host());
-  aura::client::SetCursorClient(host_->window(), cursor_manager_);
 
   desktop_window_tree_host_->OnNativeWidgetCreated(params);
 
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
index 3b81ddf3..c12b6ff 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -307,6 +307,9 @@
   // See class documentation for Widget in widget.h for a note about type.
   Widget::InitParams::Type widget_type_;
 
+  // See DesktopWindowTreeHost::ShouldUseDesktopNativeCursorManager().
+  bool use_desktop_native_cursor_manager_ = false;
+
   // The following factory is used for calls to close the NativeWidgetAura
   // instance.
   base::WeakPtrFactory<DesktopNativeWidgetAura> close_widget_factory_;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host.h b/ui/views/widget/desktop_aura/desktop_window_tree_host.h
index e8bfb9d..6528cf5 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host.h
@@ -162,6 +162,11 @@
   // Returns true if the transparency of the DesktopNativeWidgetAura's
   // |content_window_| should change.
   virtual bool ShouldUpdateWindowTransparency() const = 0;
+
+  // A return value of true indicates DesktopNativeCursorManager should be
+  // used, a return value of false indicates the DesktopWindowTreeHost manages
+  // cursors itself.
+  virtual bool ShouldUseDesktopNativeCursorManager() const = 0;
 };
 
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 8f030fb..2c3c254 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -488,6 +488,10 @@
   return true;
 }
 
+bool DesktopWindowTreeHostWin::ShouldUseDesktopNativeCursorManager() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostWin, WindowTreeHost implementation:
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index 8935db5..8de99c58 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -105,6 +105,7 @@
   bool IsTranslucentWindowOpacitySupported() const override;
   void SizeConstraintsChanged() override;
   bool ShouldUpdateWindowTransparency() const override;
+  bool ShouldUseDesktopNativeCursorManager() const override;
 
   // Overridden from aura::WindowTreeHost:
   ui::EventSource* GetEventSource() override;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index d4a5387..8c566fa 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -1171,6 +1171,10 @@
   return true;
 }
 
+bool DesktopWindowTreeHostX11::ShouldUseDesktopNativeCursorManager() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostX11, aura::WindowTreeHost implementation:
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
index fc227c35..7dfc750 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -153,6 +153,7 @@
   bool IsTranslucentWindowOpacitySupported() const override;
   void SizeConstraintsChanged() override;
   bool ShouldUpdateWindowTransparency() const override;
+  bool ShouldUseDesktopNativeCursorManager() const override;
 
   // Overridden from aura::WindowTreeHost:
   gfx::Transform GetRootTransform() const override;