diff --git a/BUILD.gn b/BUILD.gn
index a310f481..3c4534f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -38,19 +38,11 @@
   assert(!is_component_build)
 }
 
-# This file defines the following five main targets:
-#
-# "both_gn_and_gyp" should list every root target (target that nothing else
-# depends on) built by GN that is also built in the GYP build.
+# This file defines the following three main targets:
 #
 # "gn_all" should (transitively) cause everything to be built; if you run
 # 'ninja gn_all' and then 'ninja all', the second build should do no work.
 #
-# "gn_only" should list every root target that is *not* intended to be built in
-# a GYP build. Because GN has different rules for deciding what an 'all' build
-# is, this may end up including targets that are actually defined in a GYP
-# build but not dependencies of GYP's "all" (and so not actually built).
-#
 # "gn_visibility": targets that are normally not visible to top-level targets,
 # but are built anyway by "all". Since we don't want any such targets, we have
 # this placeholder to make sure hidden targets that aren't otherwise depended
@@ -63,8 +55,8 @@
 # wildcards.
 #
 # Lastly, none of these targets are guaranteed to be the same as what ninja
-# will build with "all". For more on how "all" works and the differences in how
-# GYP and GN determine "all", see crbug.com/503241.
+# will build with "all". For more on how "all" works and how GN determines
+# "all", see crbug.com/503241.
 #
 # TODO(GYP_GONE): crbug.com/481694. Make sure that the above is true and there
 # are scripts run on the bots that enforce this. Once the GYP migration is
@@ -155,8 +147,6 @@
   }
 
   if (!is_ios) {
-    # TODO(GYP): Figure out which of these should actually build on iOS,
-    # and whether there should be other targets that are iOS-only and missing.
     deps += [
       "//cc:cc_unittests",
       "//chrome/test:telemetry_perf_unittests",
@@ -549,10 +539,6 @@
       "//third_party/tcmalloc:addr2line-pdb",
       "//tools/win/chromeexts:chromeexts",
     ]
-    deps -= [
-      "//crypto:crypto_unittests",  # TODO(GYP)
-      "//net:net_unittests",  # TODO(GYP)
-    ]
 
     if (!(is_component_build && is_debug && target_cpu == "x86")) {
       deps +=
@@ -625,7 +611,6 @@
     # TODO(GYP): Figure out if any of these should be in gn_all
     # and figure out how cross-platform they are
     deps += [
-      ":gn_mojo_targets",
       "//chrome/installer/util:strings",
       "//chrome/tools/convert_dict",
       "//components/constrained_window:unit_tests",
@@ -828,19 +813,6 @@
   }
 }
 
-group("gn_mojo_targets") {
-  testonly = true
-  if (is_linux && !is_chromeos) {
-    # TODO(GYP): Figure out if any of these should be in gn_all
-    # and figure out how cross-platform they are
-    deps = [
-      "//ipc:ipc_tests",
-      "//mojo:tests",
-      "//services:service_unittests",
-    ]
-  }
-}
-
 group("gn_visibility") {
   deps = [
     "//build/config/sanitizers:options_sources",
diff --git a/DEPS b/DEPS
index 86df34d..a26d3020 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': 'ec6ae52168ae4803c2312c362280f7f79e0d380e',
+  'skia_revision': 'a2fc16e677c213d9efcc9e857effde0ed249b54d',
   # 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': 'ceafe3118264d8a896412f5e7a0affe1a62de046',
+  'v8_revision': 'fa3a1c7570ad09f941c31714d7d54c87439f4c39',
   # 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': '00ab92f4c6cd045904a0f26d7e2c227217758f02',
+  'pdfium_revision': '1fe7d8c3819f6c6fdbf34a92146587a2eee8296a',
   # 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': 'b2c22b39ba7df300abaa789fb7ea0292d62044d3',
+  'catapult_revision': '380124f4ad9d972449c81462ec99a4c916fcd2d8',
   # 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/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index 28e7c26..5c824d2 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -32,8 +32,8 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.parameter.ParameterizedTest;
 import org.chromium.content.browser.BindingManager;
-import org.chromium.content.browser.ChildProcessConnection;
 import org.chromium.content.browser.ChildProcessLauncher;
+import org.chromium.content.browser.ManagedChildProcessConnection;
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.net.test.util.TestWebServer;
@@ -585,7 +585,7 @@
         }
 
         @Override
-        public void addNewConnection(int pid, ChildProcessConnection connection) {
+        public void addNewConnection(int pid, ManagedChildProcessConnection connection) {
             mIsChildProcessCreated = true;
         }
 
@@ -607,12 +607,7 @@
         public void onBroughtToForeground() {}
 
         @Override
-        public boolean isOomProtected(int pid) {
-            return false;
-        }
-
-        @Override
-        public void clearConnection(int pid) {}
+        public void removeConnection(int pid) {}
 
         @Override
         public void startModerateBindingManagement(Context context, int maxSize) {}
diff --git a/ash/app_list/app_list_presenter_delegate.cc b/ash/app_list/app_list_presenter_delegate.cc
index 3a1f918c..dbc28e6 100644
--- a/ash/app_list/app_list_presenter_delegate.cc
+++ b/ash/app_list/app_list_presenter_delegate.cc
@@ -99,13 +99,11 @@
   aura::Window* root_window = wm_root_window->aura_window();
   aura::Window* container = GetRootWindowController(root_window)
                                 ->GetContainer(kShellWindowId_AppListContainer);
+  view->InitAsBubble(container, current_apps_page);
+  // The app list is centered over the display.
+  view->SetAnchorPoint(GetCenterOfDisplayForWindow(
+      wm_root_window, GetMinimumBoundsHeightForAppList(view)));
 
-  view->Initialize(container, current_apps_page);
-
-  if (!app_list::switches::IsFullscreenAppListEnabled()) {
-    view->MaybeSetAnchorPoint(GetCenterOfDisplayForWindow(
-        wm_root_window, GetMinimumBoundsHeightForAppList(view)));
-  }
   keyboard::KeyboardController* keyboard_controller =
       keyboard::KeyboardController::GetInstance();
   if (keyboard_controller)
@@ -150,7 +148,7 @@
     return;
 
   view_->UpdateBounds();
-  view_->MaybeSetAnchorPoint(GetCenterOfDisplayForWindow(
+  view_->SetAnchorPoint(GetCenterOfDisplayForWindow(
       WmWindow::Get(view_->GetWidget()->GetNativeWindow()),
       GetMinimumBoundsHeightForAppList(view_)));
 }
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 00525dc..d554816 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -4,7 +4,6 @@
 
 #include <memory>
 
-#include "ash/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
@@ -12,9 +11,7 @@
 #include "ash/test/test_app_list_view_presenter_impl.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm_window.h"
-#include "base/command_line.h"
 #include "base/macros.h"
-#include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
@@ -29,15 +26,9 @@
   return display::Screen::GetScreen()->GetPrimaryDisplay().id();
 }
 
-void SetFullscreenAppListSwitch() {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      app_list::switches::kEnableFullscreenAppList);
-}
-
 }  // namespace
 
-class AppListPresenterDelegateTest : public test::AshTestBase,
-                                     public testing::WithParamInterface<bool> {
+class AppListPresenterDelegateTest : public test::AshTestBase {
  public:
   AppListPresenterDelegateTest() {}
   ~AppListPresenterDelegateTest() override {}
@@ -50,29 +41,18 @@
   void SetUp() override {
     AshTestBase::SetUp();
 
-    // If the current test is parameterized.
-    if (testing::UnitTest::GetInstance()->current_test_info()->value_param()) {
-      test_with_fullscreen_ = GetParam();
-      if (test_with_fullscreen_)
-        SetFullscreenAppListSwitch();
-    }
     // Make the display big enough to hold the app list.
     UpdateDisplay("1024x768");
   }
 
  private:
   test::TestAppListViewPresenterImpl app_list_presenter_impl_;
-  bool test_with_fullscreen_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListPresenterDelegateTest);
 };
 
-// Instantiate the Boolean which is used to toggle the Fullscreen app list in
-// the parameterized tests.
-INSTANTIATE_TEST_CASE_P(, AppListPresenterDelegateTest, testing::Bool());
-
 // Tests that app launcher hides when focus moves to a normal window.
-TEST_P(AppListPresenterDelegateTest, HideOnFocusOut) {
+TEST_F(AppListPresenterDelegateTest, HideOnFocusOut) {
   app_list_presenter_impl()->Show(GetPrimaryDisplayId());
   EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
 
@@ -84,7 +64,7 @@
 
 // Tests that app launcher remains visible when focus is moved to a different
 // window in kShellWindowId_AppListContainer.
-TEST_P(AppListPresenterDelegateTest,
+TEST_F(AppListPresenterDelegateTest,
        RemainVisibleWhenFocusingToApplistContainer) {
   app_list_presenter_impl()->Show(GetPrimaryDisplayId());
   EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
@@ -142,7 +122,7 @@
 
 // Tests opening the app launcher on a non-primary display, then deleting the
 // display.
-TEST_P(AppListPresenterDelegateTest, NonPrimaryDisplay) {
+TEST_F(AppListPresenterDelegateTest, NonPrimaryDisplay) {
   // Set up a screen with two displays (horizontally adjacent).
   UpdateDisplay("1024x768,1024x768");
 
@@ -176,12 +156,10 @@
   // from the anchor (center) and height. There isn't a bounds rect that gives
   // the actual app list position (the widget bounds include the bubble border
   // which is much bigger than the actual app list size).
-
   app_list::AppListView* app_list = app_list_presenter_impl()->GetView();
   int app_list_view_top =
       app_list->anchor_rect().y() - app_list->bounds().height() / 2;
   const int kMinimalAppListMargin = 10;
-
   EXPECT_GE(app_list_view_top, kMinimalAppListMargin);
 }
 
diff --git a/ash/system/audio/audio_detailed_view.cc b/ash/system/audio/audio_detailed_view.cc
index 2bcfc8b..bf378b6 100644
--- a/ash/system/audio/audio_detailed_view.cc
+++ b/ash/system/audio/audio_detailed_view.cc
@@ -13,10 +13,7 @@
 #include "chromeos/audio/cras_audio_handler.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/native_theme/native_theme.h"
 #include "ui/views/controls/image_view.h"
-#include "ui/views/controls/label.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
 
@@ -73,47 +70,10 @@
   Layout();
 }
 
-void AudioDetailedView::AddInputHeader() {
-  AddScrollListInfoItem(IDS_ASH_STATUS_TRAY_AUDIO_INPUT,
-                        kSystemMenuAudioInputIcon);
-}
-
-void AudioDetailedView::AddOutputHeader() {
-  AddScrollListInfoItem(IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT,
-                        kSystemMenuAudioOutputIcon);
-}
-
-void AudioDetailedView::AddScrollListInfoItem(int text_id,
-                                              const gfx::VectorIcon& icon) {
-  TriView* header = TrayPopupUtils::CreateDefaultRowView();
-  TrayPopupUtils::ConfigureAsStickyHeader(header);
-  views::ImageView* image_view = TrayPopupUtils::CreateMainImageView();
-  image_view->SetImage(gfx::CreateVectorIcon(
-      icon, GetNativeTheme()->GetSystemColor(
-                ui::NativeTheme::kColorId_ProminentButtonColor)));
-  header->AddView(TriView::Container::START, image_view);
-
-  views::Label* label = TrayPopupUtils::CreateDefaultLabel();
-  label->SetText(l10n_util::GetStringUTF16(text_id));
-  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::SUB_HEADER);
-  style.SetupLabel(label);
-  header->AddView(TriView::Container::CENTER, label);
-
+void AudioDetailedView::AddAudioSubHeader(const gfx::VectorIcon& icon,
+                                          int text_id) {
+  TriView* header = AddScrollListSubHeader(icon, text_id);
   header->SetContainerVisible(TriView::Container::END, false);
-  scroll_content()->AddChildView(header);
-}
-
-HoverHighlightView* AudioDetailedView::AddScrollListItem(
-    const base::string16& text,
-    bool highlight,
-    bool checked) {
-  HoverHighlightView* container = new HoverHighlightView(this);
-
-  container->AddLabelRow(text);
-  TrayPopupUtils::InitializeAsCheckableRow(container, checked);
-
-  scroll_content()->AddChildView(container);
-  return container;
 }
 
 void AudioDetailedView::CreateItems() {
@@ -129,21 +89,20 @@
   audio_handler->GetAudioDevices(&devices);
   bool has_dual_internal_mic = audio_handler->HasDualInternalMic();
   bool is_front_or_rear_mic_active = false;
-  for (size_t i = 0; i < devices.size(); ++i) {
+  for (const auto& device : devices) {
     // Don't display keyboard mic or aokr type.
-    if (!devices[i].is_for_simple_usage())
+    if (!device.is_for_simple_usage())
       continue;
-    if (devices[i].is_input) {
+    if (device.is_input) {
       // Do not expose the internal front and rear mic to UI.
-      if (has_dual_internal_mic &&
-          audio_handler->IsFrontOrRearMic(devices[i])) {
-        if (devices[i].active)
+      if (has_dual_internal_mic && audio_handler->IsFrontOrRearMic(device)) {
+        if (device.active)
           is_front_or_rear_mic_active = true;
         continue;
       }
-      input_devices_.push_back(devices[i]);
+      input_devices_.push_back(device);
     } else {
-      output_devices_.push_back(devices[i]);
+      output_devices_.push_back(device);
     }
   }
 
@@ -168,14 +127,15 @@
 
   // Add audio output devices.
   const bool has_output_devices = output_devices_.size() > 0;
-  if (has_output_devices)
-    AddOutputHeader();
+  if (has_output_devices) {
+    AddAudioSubHeader(kSystemMenuAudioOutputIcon,
+                      IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT);
+  }
 
-  for (size_t i = 0; i < output_devices_.size(); ++i) {
-    HoverHighlightView* container = AddScrollListItem(
-        GetAudioDeviceName(output_devices_[i]), false /* highlight */,
-        output_devices_[i].active); /* checkmark if active */
-    device_map_[container] = output_devices_[i];
+  for (const auto& device : output_devices_) {
+    HoverHighlightView* container =
+        AddScrollListCheckableItem(GetAudioDeviceName(device), device.active);
+    device_map_[container] = device;
   }
 
   if (has_output_devices) {
@@ -185,14 +145,15 @@
 
   // Add audio input devices.
   const bool has_input_devices = input_devices_.size() > 0;
-  if (has_input_devices)
-    AddInputHeader();
+  if (has_input_devices) {
+    AddAudioSubHeader(kSystemMenuAudioInputIcon,
+                      IDS_ASH_STATUS_TRAY_AUDIO_INPUT);
+  }
 
-  for (size_t i = 0; i < input_devices_.size(); ++i) {
-    HoverHighlightView* container = AddScrollListItem(
-        GetAudioDeviceName(input_devices_[i]), false /* highlight */,
-        input_devices_[i].active); /* checkmark if active */
-    device_map_[container] = input_devices_[i];
+  for (const auto& device : input_devices_) {
+    HoverHighlightView* container =
+        AddScrollListCheckableItem(GetAudioDeviceName(device), device.active);
+    device_map_[container] = device;
   }
 
   scroll_content()->SizeToPreferredSize();
diff --git a/ash/system/audio/audio_detailed_view.h b/ash/system/audio/audio_detailed_view.h
index 5e2d3fd..7478ba8 100644
--- a/ash/system/audio/audio_detailed_view.h
+++ b/ash/system/audio/audio_detailed_view.h
@@ -15,13 +15,7 @@
 struct VectorIcon;
 }
 
-namespace views {
-class View;
-}
-
 namespace ash {
-class HoverHighlightView;
-
 namespace tray {
 
 class AudioDetailedView : public TrayDetailsView {
@@ -33,15 +27,9 @@
   void Update();
 
  private:
-  // Helper functions to add non-clickable header rows within the scrollable
+  // Helper function to add non-clickable header rows within the scrollable
   // list.
-  void AddInputHeader();
-  void AddOutputHeader();
-  void AddScrollListInfoItem(int text_id, const gfx::VectorIcon& icon);
-
-  HoverHighlightView* AddScrollListItem(const base::string16& text,
-                                        bool highlight,
-                                        bool checked);
+  void AddAudioSubHeader(const gfx::VectorIcon& icon, int text_id);
 
   void CreateItems();
 
diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc
index 962a3ff..26625a6 100644
--- a/ash/system/bluetooth/tray_bluetooth.cc
+++ b/ash/system/bluetooth/tray_bluetooth.cc
@@ -24,10 +24,10 @@
 #include "ash/system/tray/tri_view.h"
 #include "device/bluetooth/bluetooth_common.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/vector_icon_types.h"
 #include "ui/views/controls/button/toggle_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
@@ -119,11 +119,10 @@
   void Update() {
     TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
     if (helper->GetBluetoothAvailable()) {
-      ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-      const base::string16 label =
-          rb.GetLocalizedString(helper->GetBluetoothEnabled()
-                                    ? IDS_ASH_STATUS_TRAY_BLUETOOTH_ENABLED
-                                    : IDS_ASH_STATUS_TRAY_BLUETOOTH_DISABLED);
+      const base::string16 label = l10n_util::GetStringUTF16(
+          helper->GetBluetoothEnabled()
+              ? IDS_ASH_STATUS_TRAY_BLUETOOTH_ENABLED
+              : IDS_ASH_STATUS_TRAY_BLUETOOTH_DISABLED);
       SetLabel(label);
       SetAccessibleName(label);
       SetVisible(true);
@@ -163,8 +162,8 @@
 
     bool has_connected_device = false;
     BluetoothDeviceList list = helper->GetAvailableBluetoothDevices();
-    for (size_t i = 0; i < list.size(); ++i) {
-      if (list[i].connected) {
+    for (const auto& device : list) {
+      if (device.connected) {
         has_connected_device = true;
         break;
       }
@@ -235,21 +234,20 @@
 
     BluetoothDeviceList list =
         Shell::Get()->tray_bluetooth_helper()->GetAvailableBluetoothDevices();
-    for (size_t i = 0; i < list.size(); ++i) {
-      if (list[i].connecting) {
-        new_connecting_devices.insert(list[i].address);
-        UpdateBluetoothDeviceListHelper(&connecting_devices_, list[i]);
-      } else if (list[i].connected && list[i].paired) {
-        new_connected_devices.insert(list[i].address);
-        UpdateBluetoothDeviceListHelper(&connected_devices_, list[i]);
-      } else if (list[i].paired) {
-        new_paired_not_connected_devices.insert(list[i].address);
-        UpdateBluetoothDeviceListHelper(&paired_not_connected_devices_,
-                                        list[i]);
+    for (const auto& device : list) {
+      if (device.connecting) {
+        new_connecting_devices.insert(device.address);
+        UpdateBluetoothDeviceListHelper(&connecting_devices_, device);
+      } else if (device.connected && device.paired) {
+        new_connected_devices.insert(device.address);
+        UpdateBluetoothDeviceListHelper(&connected_devices_, device);
+      } else if (device.paired) {
+        new_paired_not_connected_devices.insert(device.address);
+        UpdateBluetoothDeviceListHelper(&paired_not_connected_devices_, device);
       } else {
-        new_discovered_not_paired_devices.insert(list[i].address);
+        new_discovered_not_paired_devices.insert(device.address);
         UpdateBluetoothDeviceListHelper(&discovered_not_paired_devices_,
-                                        list[i]);
+                                        device);
       }
     }
     RemoveObsoleteBluetoothDevicesFromList(&connecting_devices_,
@@ -293,7 +291,7 @@
                                 connecting_devices_.size() +
                                 paired_not_connected_devices_.size();
     if (num_paired_devices > 0) {
-      AddSubHeader(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED_DEVICES);
+      AddScrollListSubHeader(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED_DEVICES);
       AppendSameTypeDevicesToScrollList(connected_devices_, true, true,
                                         bluetooth_enabled);
       AppendSameTypeDevicesToScrollList(connecting_devices_, true, false,
@@ -306,7 +304,7 @@
     // present, also add a section header above the unpaired devices.
     if (discovered_not_paired_devices_.size() > 0) {
       if (num_paired_devices > 0)
-        AddSubHeader(IDS_ASH_STATUS_TRAY_BLUETOOTH_UNPAIRED_DEVICES);
+        AddScrollListSubHeader(IDS_ASH_STATUS_TRAY_BLUETOOTH_UNPAIRED_DEVICES);
       AppendSameTypeDevicesToScrollList(discovered_not_paired_devices_, false,
                                         false, bluetooth_enabled);
     }
@@ -328,83 +326,41 @@
                                          bool highlight,
                                          bool checked,
                                          bool enabled) {
-    for (size_t i = 0; i < list.size(); ++i) {
-      HoverHighlightView* container = nullptr;
-      gfx::ImageSkia icon_image = CreateVectorIcon(
-          GetBluetoothDeviceIcon(list[i].device_type, list[i].connected),
-          kMenuIconColor);
-      container = AddScrollListItem(list[i].display_name, icon_image,
-                                    list[i].connected, list[i].connecting);
-      device_map_[container] = list[i].address;
+    for (const auto& device : list) {
+      const gfx::VectorIcon& icon =
+          GetBluetoothDeviceIcon(device.device_type, device.connected);
+      HoverHighlightView* container =
+          AddScrollListItem(icon, device.display_name);
+      if (device.connected)
+        SetupConnectedItem(container);
+      else if (device.connecting)
+        SetupConnectingItem(container);
+      device_map_[container] = device.address;
     }
   }
 
-  HoverHighlightView* AddScrollListItem(const base::string16& text,
-                                        const gfx::ImageSkia& image,
-                                        bool connected,
-                                        bool connecting) {
-    HoverHighlightView* container = new HoverHighlightView(this);
-    if (connected) {
-      SetupConnectedItem(container, text, image);
-    } else if (connecting) {
-      SetupConnectingItem(container, text, image);
-    } else {
-      container->AddIconAndLabel(image, text);
-    }
-    scroll_content()->AddChildView(container);
-    return container;
-  }
-
-  void AddSubHeader(int message_id) {
-    TriView* header = TrayPopupUtils::CreateSubHeaderRowView();
-    TrayPopupUtils::ConfigureAsStickyHeader(header);
-
-    views::Label* label = TrayPopupUtils::CreateDefaultLabel();
-    label->SetText(l10n_util::GetStringUTF16(message_id));
-    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::SUB_HEADER);
-    style.SetupLabel(label);
-    header->AddView(TriView::Container::CENTER, label);
-
-    scroll_content()->AddChildView(header);
-  }
-
-  void SetupConnectedItem(HoverHighlightView* container,
-                          const base::string16& text,
-                          const gfx::ImageSkia& image) {
-    container->AddIconAndLabels(
-        image, text, l10n_util::GetStringUTF16(
-                         IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED));
+  void SetupConnectedItem(HoverHighlightView* container) {
+    container->SetSubText(l10n_util::GetStringUTF16(
+        IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED));
     TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::CAPTION);
     style.set_color_style(TrayPopupItemStyle::ColorStyle::CONNECTED);
     style.SetupLabel(container->sub_text_label());
   }
 
-  void SetupConnectingItem(HoverHighlightView* container,
-                           const base::string16& text,
-                           const gfx::ImageSkia& image) {
-    container->AddIconAndLabels(
-        image, text, l10n_util::GetStringUTF16(
-                         IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING));
+  void SetupConnectingItem(HoverHighlightView* container) {
+    container->SetSubText(l10n_util::GetStringUTF16(
+        IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING));
     ThrobberView* throbber = new ThrobberView;
     throbber->Start();
     container->AddRightView(throbber);
   }
 
-  // Returns true if the device with |device_id| is found in |device_list|,
-  // and the display_name of the device will be returned in |display_name| if
-  // it's not NULL.
+  // Returns true if the device with |device_id| is found in |device_list|.
   bool FoundDevice(const std::string& device_id,
-                   const BluetoothDeviceList& device_list,
-                   base::string16* display_name,
-                   device::BluetoothDeviceType* device_type) {
-    for (size_t i = 0; i < device_list.size(); ++i) {
-      if (device_list[i].address == device_id) {
-        if (display_name)
-          *display_name = device_list[i].display_name;
-        if (device_type)
-          *device_type = device_list[i].device_type;
+                   const BluetoothDeviceList& device_list) {
+    for (const auto& device : device_list) {
+      if (device.address == device_id)
         return true;
-      }
     }
     return false;
   }
@@ -413,18 +369,10 @@
   // or disconnected if such an operation is going to be performed underway.
   void UpdateClickedDevice(const std::string& device_id,
                            views::View* item_container) {
-    base::string16 display_name;
-    device::BluetoothDeviceType device_type;
-    if (FoundDevice(device_id, paired_not_connected_devices_, &display_name,
-                    &device_type)) {
-      item_container->RemoveAllChildViews(true);
+    if (FoundDevice(device_id, paired_not_connected_devices_)) {
       HoverHighlightView* container =
           static_cast<HoverHighlightView*>(item_container);
-      TrayPopupItemStyle style(
-          TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
-      gfx::ImageSkia icon_image = CreateVectorIcon(
-          GetBluetoothDeviceIcon(device_type, false), style.GetIconColor());
-      SetupConnectingItem(container, display_name, icon_image);
+      SetupConnectingItem(container);
       scroll_content()->SizeToPreferredSize();
       scroller()->Layout();
     }
@@ -442,7 +390,7 @@
       return;
 
     const std::string device_id = find->second;
-    if (FoundDevice(device_id, connecting_devices_, nullptr, nullptr))
+    if (FoundDevice(device_id, connecting_devices_))
       return;
 
     UpdateClickedDevice(device_id, view);
@@ -536,8 +484,7 @@
     container->AddChildView(image_view);
 
     views::Label* label = new views::Label(
-        ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
-            IDS_ASH_STATUS_TRAY_BLUETOOTH_DISABLED));
+        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISABLED));
     style.SetupLabel(label);
     label->SetBorder(views::CreateEmptyBorder(
         kDisabledPanelLabelBaselineY - label->GetBaseline(), 0, 0, 0));
diff --git a/ash/system/cast/tray_cast.cc b/ash/system/cast/tray_cast.cc
index 899b4d0..70678868 100644
--- a/ash/system/cast/tray_cast.cc
+++ b/ash/system/cast/tray_cast.cc
@@ -315,7 +315,6 @@
   void CreateItems();
 
   void UpdateReceiverListFromCachedData();
-  views::View* AddToReceiverList(const mojom::SinkAndRoutePtr& sink_route);
 
   // TrayDetailsView:
   void HandleViewClicked(views::View* view) override;
@@ -388,7 +387,8 @@
   // Add a view for each receiver.
   for (auto& it : sinks_and_routes_) {
     const ash::mojom::SinkAndRoutePtr& sink_route = it.second;
-    views::View* container = AddToReceiverList(sink_route);
+    const base::string16 name = base::UTF8ToUTF16(sink_route->sink->name);
+    views::View* container = AddScrollListItem(kSystemMenuCastDeviceIcon, name);
     view_to_sink_map_[container] = sink_route->sink.Clone();
   }
 
@@ -396,18 +396,6 @@
   scroller()->Layout();
 }
 
-views::View* CastDetailedView::AddToReceiverList(
-    const ash::mojom::SinkAndRoutePtr& sink_route) {
-  const gfx::ImageSkia image =
-      gfx::CreateVectorIcon(kSystemMenuCastDeviceIcon, kMenuIconColor);
-
-  HoverHighlightView* container = new HoverHighlightView(this);
-  container->AddIconAndLabel(image, base::UTF8ToUTF16(sink_route->sink->name));
-
-  scroll_content()->AddChildView(container);
-  return container;
-}
-
 void CastDetailedView::HandleViewClicked(views::View* view) {
   // Find the receiver we are going to cast to.
   auto it = view_to_sink_map_.find(view);
diff --git a/ash/system/ime_menu/ime_list_view.cc b/ash/system/ime_menu/ime_list_view.cc
index 334b42e..4d9cf07a 100644
--- a/ash/system/ime_menu/ime_list_view.cc
+++ b/ash/system/ime_menu/ime_list_view.cc
@@ -112,8 +112,8 @@
 
   void OnFocus() override {
     ActionableView::OnFocus();
-    if (ime_list_view_ && ime_list_view_->scroll_content())
-      ime_list_view_->scroll_content()->ScrollRectToVisible(bounds());
+    if (ime_list_view_)
+      ime_list_view_->ScrollItemToVisible(this);
   }
 
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
@@ -221,7 +221,7 @@
       last_item_selected_with_keyboard_) {
     FocusCurrentImeIfNeeded();
   } else if (current_ime_view_) {
-    scroll_content()->ScrollRectToVisible(current_ime_view_->bounds());
+    ScrollItemToVisible(current_ime_view_);
   }
 }
 
@@ -232,6 +232,11 @@
   current_ime_view_ = nullptr;
 }
 
+void ImeListView::ScrollItemToVisible(views::View* item_view) {
+  if (scroll_content())
+    scroll_content()->ScrollRectToVisible(item_view->bounds());
+}
+
 void ImeListView::CloseImeListView() {
   last_selected_item_id_.clear();
   current_ime_view_ = nullptr;
@@ -324,7 +329,7 @@
     return;
   }
 
-  scroll_content()->ScrollRectToVisible(current_ime_view_->bounds());
+  ScrollItemToVisible(current_ime_view_);
 }
 
 void ImeListView::FocusCurrentImeIfNeeded() {
diff --git a/ash/system/ime_menu/ime_list_view.h b/ash/system/ime_menu/ime_list_view.h
index 2acb4e0b..b0c9d9f 100644
--- a/ash/system/ime_menu/ime_list_view.h
+++ b/ash/system/ime_menu/ime_list_view.h
@@ -44,6 +44,9 @@
   // Closes the view.
   void CloseImeListView();
 
+  // Scrolls contents such that |item_view| is visible.
+  void ScrollItemToVisible(views::View* item_view);
+
   void set_last_item_selected_with_keyboard(
       bool last_item_selected_with_keyboard) {
     last_item_selected_with_keyboard_ = last_item_selected_with_keyboard;
diff --git a/ash/system/network/network_list.cc b/ash/system/network/network_list.cc
index 735903f..f0f1a929 100644
--- a/ash/system/network/network_list.cc
+++ b/ash/system/network/network_list.cc
@@ -135,7 +135,7 @@
   void InitializeLayout() {
     TrayPopupUtils::ConfigureAsStickyHeader(this);
     SetLayoutManager(new views::FillLayout);
-    container_ = TrayPopupUtils::CreateSubHeaderRowView();
+    container_ = TrayPopupUtils::CreateSubHeaderRowView(false);
     AddChildView(container_);
 
     views::Label* label = TrayPopupUtils::CreateDefaultLabel();
diff --git a/ash/system/network/vpn_list_view.cc b/ash/system/network/vpn_list_view.cc
index 6b437ba7..e4c9df9 100644
--- a/ash/system/network/vpn_list_view.cc
+++ b/ash/system/network/vpn_list_view.cc
@@ -88,7 +88,7 @@
       : parent_(parent) {
     TrayPopupUtils::ConfigureAsStickyHeader(this);
     SetLayoutManager(new views::FillLayout);
-    TriView* tri_view = TrayPopupUtils::CreateSubHeaderRowView();
+    TriView* tri_view = TrayPopupUtils::CreateSubHeaderRowView(false);
     AddChildView(tri_view);
 
     views::Label* label = TrayPopupUtils::CreateDefaultLabel();
diff --git a/ash/system/tray/hover_highlight_view.cc b/ash/system/tray/hover_highlight_view.cc
index c3f7cfa3..e96272b6 100644
--- a/ash/system/tray/hover_highlight_view.cc
+++ b/ash/system/tray/hover_highlight_view.cc
@@ -55,6 +55,19 @@
   Layout();
 }
 
+void HoverHighlightView::SetSubText(const base::string16& sub_text) {
+  DCHECK(text_label_);
+
+  if (!sub_text_label_) {
+    sub_text_label_ = TrayPopupUtils::CreateDefaultLabel();
+    tri_view_->AddView(TriView::Container::CENTER, sub_text_label_);
+  }
+  TrayPopupItemStyle sub_style(TrayPopupItemStyle::FontStyle::CAPTION);
+  sub_style.set_color_style(TrayPopupItemStyle::ColorStyle::INACTIVE);
+  sub_style.SetupLabel(sub_text_label_);
+  sub_text_label_->SetText(sub_text);
+}
+
 void HoverHighlightView::AddIconAndLabel(const gfx::ImageSkia& image,
                                          const base::string16& text) {
   DoAddIconAndLabel(image, text,
@@ -102,16 +115,9 @@
   text_label_->SetEnabled(enabled());
   TrayPopupItemStyle style(font_style);
   style.SetupLabel(text_label_);
-
   tri_view_->AddView(TriView::Container::CENTER, text_label_);
-  if (!sub_text.empty()) {
-    sub_text_label_ = TrayPopupUtils::CreateDefaultLabel();
-    sub_text_label_->SetText(sub_text);
-    TrayPopupItemStyle sub_style(TrayPopupItemStyle::FontStyle::CAPTION);
-    sub_style.set_color_style(TrayPopupItemStyle::ColorStyle::INACTIVE);
-    sub_style.SetupLabel(sub_text_label_);
-    tri_view_->AddView(TriView::Container::CENTER, sub_text_label_);
-  }
+
+  SetSubText(sub_text);
 
   tri_view_->SetContainerVisible(TriView::Container::END, false);
 
diff --git a/ash/system/tray/hover_highlight_view.h b/ash/system/tray/hover_highlight_view.h
index bdaf647..4283c53 100644
--- a/ash/system/tray/hover_highlight_view.h
+++ b/ash/system/tray/hover_highlight_view.h
@@ -69,6 +69,10 @@
   // Hide or show the right view.
   void SetRightViewVisible(bool visible);
 
+  // Sets text for the sub label. Precondition for this function is that
+  // |text_label_| is non-null.
+  void SetSubText(const base::string16& sub_text);
+
   // Allows view to expand its height. Size of unexapandable view is fixed and
   // equals to kTrayPopupItemHeight.
   void SetExpandable(bool expandable);
diff --git a/ash/system/tray/tray_details_view.cc b/ash/system/tray/tray_details_view.cc
index fee3c72..56f24b1 100644
--- a/ash/system/tray/tray_details_view.cc
+++ b/ash/system/tray/tray_details_view.cc
@@ -6,6 +6,7 @@
 
 #include "ash/ash_view_ids.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/tray/hover_highlight_view.h"
 #include "ash/system/tray/system_menu_button.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_item.h"
@@ -16,13 +17,18 @@
 #include "base/containers/adapters.h"
 #include "base/memory/ptr_util.h"
 #include "third_party/skia/include/core/SkDrawLooper.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/compositor/paint_context.h"
 #include "ui/compositor/paint_recorder.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/skia_paint_util.h"
+#include "ui/gfx/vector_icon_types.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
+#include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/progress_bar.h"
 #include "ui/views/controls/scroll_view.h"
@@ -303,6 +309,60 @@
   box_layout_->SetFlexForView(scroller_, 1);
 }
 
+HoverHighlightView* TrayDetailsView::AddScrollListItem(
+    const gfx::VectorIcon& icon,
+    const base::string16& text) {
+  HoverHighlightView* item = new HoverHighlightView(this);
+  if (icon.is_empty())
+    item->AddLabelRow(text);
+  else
+    item->AddIconAndLabel(gfx::CreateVectorIcon(icon, kMenuIconColor), text);
+  scroll_content_->AddChildView(item);
+  return item;
+}
+
+HoverHighlightView* TrayDetailsView::AddScrollListCheckableItem(
+    const gfx::VectorIcon& icon,
+    const base::string16& text,
+    bool checked) {
+  HoverHighlightView* item = AddScrollListItem(icon, text);
+  TrayPopupUtils::InitializeAsCheckableRow(item, checked);
+  return item;
+}
+
+HoverHighlightView* TrayDetailsView::AddScrollListCheckableItem(
+    const base::string16& text,
+    bool checked) {
+  return AddScrollListCheckableItem(gfx::kNoneIcon, text, checked);
+}
+
+TriView* TrayDetailsView::AddScrollListSubHeader(const gfx::VectorIcon& icon,
+                                                 int text_id) {
+  TriView* header = TrayPopupUtils::CreateSubHeaderRowView(!icon.is_empty());
+  TrayPopupUtils::ConfigureAsStickyHeader(header);
+
+  views::Label* label = TrayPopupUtils::CreateDefaultLabel();
+  label->SetText(l10n_util::GetStringUTF16(text_id));
+  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::SUB_HEADER);
+  style.SetupLabel(label);
+  header->AddView(TriView::Container::CENTER, label);
+
+  if (!icon.is_empty()) {
+    views::ImageView* image_view = TrayPopupUtils::CreateMainImageView();
+    image_view->SetImage(gfx::CreateVectorIcon(
+        icon, GetNativeTheme()->GetSystemColor(
+                  ui::NativeTheme::kColorId_ProminentButtonColor)));
+    header->AddView(TriView::Container::START, image_view);
+  }
+
+  scroll_content_->AddChildView(header);
+  return header;
+}
+
+TriView* TrayDetailsView::AddScrollListSubHeader(int text_id) {
+  return AddScrollListSubHeader(gfx::kNoneIcon, text_id);
+}
+
 void TrayDetailsView::Reset() {
   RemoveAllChildViews(true);
   scroller_ = nullptr;
diff --git a/ash/system/tray/tray_details_view.h b/ash/system/tray/tray_details_view.h
index f7f30f4..1822067 100644
--- a/ash/system/tray/tray_details_view.h
+++ b/ash/system/tray/tray_details_view.h
@@ -15,6 +15,10 @@
 #include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
+namespace gfx {
+struct VectorIcon;
+}  // namespace gfx
+
 namespace views {
 class BoxLayout;
 class CustomButton;
@@ -27,6 +31,7 @@
 class TrayDetailsViewTest;
 }  // namespace test
 
+class HoverHighlightView;
 class ScrollBorder;
 class SystemTrayItem;
 class TriView;
@@ -47,8 +52,6 @@
   void ButtonPressed(views::Button* sender, const ui::Event& event) final;
 
   SystemTrayItem* owner() { return owner_; }
-  views::ScrollView* scroller() { return scroller_; }
-  views::View* scroll_content() { return scroll_content_; }
 
  protected:
   // views::View:
@@ -67,6 +70,29 @@
   // any other view between the list and the footer row at the bottom.
   void CreateScrollableList();
 
+  // Adds a targetable row to |scroll_content_| containing |icon| and |text|.
+  HoverHighlightView* AddScrollListItem(const gfx::VectorIcon& icon,
+                                        const base::string16& text);
+
+  // Adds a targetable row to |scroll_content_| containing |icon|, |text|, and a
+  // checkbox. |checked| determines whether the checkbox is checked or not.
+  HoverHighlightView* AddScrollListCheckableItem(const gfx::VectorIcon& icon,
+                                                 const base::string16& text,
+                                                 bool checked);
+
+  // Adds a targetable row to |scroll_content_| containing |text| and a
+  // checkbox. |checked| determines whether the checkbox is checked or not.
+  HoverHighlightView* AddScrollListCheckableItem(const base::string16& text,
+                                                 bool checked);
+
+  // Adds a sticky sub header to |scroll_content_| containing |icon| and a text
+  // represented by |text_id| resource id.
+  TriView* AddScrollListSubHeader(const gfx::VectorIcon& icon, int text_id);
+
+  // Adds a sticky sub header to |scroll_content_| containing a text represented
+  // by |text_id| resource id.
+  TriView* AddScrollListSubHeader(int text_id);
+
   // Removes (and destroys) all child views.
   void Reset();
 
@@ -82,6 +108,8 @@
   views::CustomButton* CreateHelpButton();
 
   TriView* tri_view() { return tri_view_; }
+  views::ScrollView* scroller() const { return scroller_; }
+  views::View* scroll_content() const { return scroll_content_; }
 
  private:
   friend class test::TrayDetailsViewTest;
diff --git a/ash/system/tray/tray_details_view_unittest.cc b/ash/system/tray/tray_details_view_unittest.cc
index 49d3f3a0..cd13603 100644
--- a/ash/system/tray/tray_details_view_unittest.cc
+++ b/ash/system/tray/tray_details_view_unittest.cc
@@ -43,6 +43,10 @@
 
   void CreateScrollerViews() { CreateScrollableList(); }
 
+  views::View* scroll_content() const {
+    return TrayDetailsView::scroll_content();
+  }
+
  private:
   TrayPopupHeaderButton* tray_popup_header_button_;
 
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc
index d5bdd0a6..d89c0d0 100644
--- a/ash/system/tray/tray_popup_utils.cc
+++ b/ash/system/tray/tray_popup_utils.cc
@@ -48,8 +48,8 @@
 // stretched horizontally and centered vertically.
 std::unique_ptr<views::LayoutManager> CreateDefaultCenterLayoutManager() {
   // TODO(bruthig): Use constants instead of magic numbers.
-  auto box_layout =
-      base::MakeUnique<views::BoxLayout>(views::BoxLayout::kVertical, 4, 8, 0);
+  auto box_layout = base::MakeUnique<views::BoxLayout>(
+      views::BoxLayout::kVertical, kTrayPopupLabelHorizontalPadding, 8, 0);
   box_layout->set_main_axis_alignment(
       views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
   box_layout->set_cross_axis_alignment(
@@ -174,13 +174,14 @@
   return tri_view;
 }
 
-TriView* TrayPopupUtils::CreateSubHeaderRowView() {
-  TriView* tri_view = CreateMultiTargetRowView();
-  tri_view->SetInsets(gfx::Insets(0, kTrayPopupPaddingHorizontal, 0, 0));
-  tri_view->SetContainerVisible(TriView::Container::START, false);
-  tri_view->SetContainerLayout(
-      TriView::Container::END,
-      CreateDefaultLayoutManager(TriView::Container::END));
+TriView* TrayPopupUtils::CreateSubHeaderRowView(bool start_visible) {
+  TriView* tri_view = CreateDefaultRowView();
+  if (!start_visible) {
+    tri_view->SetInsets(gfx::Insets(
+        0, kTrayPopupPaddingHorizontal - kTrayPopupLabelHorizontalPadding, 0,
+        0));
+    tri_view->SetContainerVisible(TriView::Container::START, false);
+  }
   return tri_view;
 }
 
diff --git a/ash/system/tray/tray_popup_utils.h b/ash/system/tray/tray_popup_utils.h
index d674034..db268693 100644
--- a/ash/system/tray/tray_popup_utils.h
+++ b/ash/system/tray/tray_popup_utils.h
@@ -54,13 +54,18 @@
   // Creates a container view to be used by system menu sub-section header rows.
   // The caller takes over ownership of the created view.
   //
-  // The returned view consists of 2 regions: CENTER, and END having the same
-  // properties as when using |CreateMultiTargetRowView|. The START container is
-  // hidden.
-  // The END container has a fixed minimum width but can grow into the CENTER
-  // container if space is required and available. The CENTER container has a
-  // flexible width.
-  static TriView* CreateSubHeaderRowView();
+  // The returned view contains at least CENTER and END regions having the same
+  // properties as when using |CreateMultiTargetRowView|. |start_visible|
+  // determines whether the START region should be visible or not. If START is
+  // not visible, extra padding is added to the left of the contents.
+  //
+  // The START (if visible) and END containers have a fixed minimum width but
+  // can grow into the CENTER container if space is required and available. The
+  // CENTER container has a flexible width.
+  //
+  // TODO(mohsen): Merge this into TrayDetailsView::AddScrollListSubHeader()
+  // once network and VPN alse use TrayDetailsView::AddScrollListSubHeader().
+  static TriView* CreateSubHeaderRowView(bool start_visible);
 
   // Creates a view containing only a label (corresponding to |message_id|),
   // which is to be inserted as a non-targetable row within a system menu
diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc
index bf12a2d..643ea49 100644
--- a/ash/system/tray_accessibility.cc
+++ b/ash/system/tray_accessibility.cc
@@ -215,58 +215,64 @@
   CreateScrollableList();
 
   AccessibilityDelegate* delegate = Shell::Get()->accessibility_delegate();
+
   spoken_feedback_enabled_ = delegate->IsSpokenFeedbackEnabled();
-  spoken_feedback_view_ = AddScrollListItem(
+  spoken_feedback_view_ = AddScrollListCheckableItem(
+      kSystemMenuAccessibilityChromevoxIcon,
       l10n_util::GetStringUTF16(
           IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SPOKEN_FEEDBACK),
-      spoken_feedback_enabled_, kSystemMenuAccessibilityChromevoxIcon);
+      spoken_feedback_enabled_);
 
   high_contrast_enabled_ = delegate->IsHighContrastEnabled();
-  high_contrast_view_ = AddScrollListItem(
+  high_contrast_view_ = AddScrollListCheckableItem(
+      kSystemMenuAccessibilityContrastIcon,
       l10n_util::GetStringUTF16(
           IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE),
-      high_contrast_enabled_, kSystemMenuAccessibilityContrastIcon);
+      high_contrast_enabled_);
+
   screen_magnifier_enabled_ = delegate->IsMagnifierEnabled();
-  screen_magnifier_view_ = AddScrollListItem(
+  screen_magnifier_view_ = AddScrollListCheckableItem(
+      kSystemMenuAccessibilityScreenMagnifierIcon,
       l10n_util::GetStringUTF16(
           IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER),
-      screen_magnifier_enabled_, kSystemMenuAccessibilityScreenMagnifierIcon);
+      screen_magnifier_enabled_);
 
   autoclick_enabled_ = delegate->IsAutoclickEnabled();
-  autoclick_view_ = AddScrollListItem(
+  autoclick_view_ = AddScrollListCheckableItem(
+      kSystemMenuAccessibilityAutoClickIcon,
       l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK),
-      autoclick_enabled_, kSystemMenuAccessibilityAutoClickIcon);
+      autoclick_enabled_);
 
   virtual_keyboard_enabled_ = delegate->IsVirtualKeyboardEnabled();
-  virtual_keyboard_view_ =
-      AddScrollListItem(l10n_util::GetStringUTF16(
-                            IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD),
-                        virtual_keyboard_enabled_, kSystemMenuKeyboardIcon);
+  virtual_keyboard_view_ = AddScrollListCheckableItem(
+      kSystemMenuKeyboardIcon,
+      l10n_util::GetStringUTF16(
+          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD),
+      virtual_keyboard_enabled_);
 
   scroll_content()->AddChildView(
       TrayPopupUtils::CreateListSubHeaderSeparator());
 
-  AddSubHeader(l10n_util::GetStringUTF16(
-      IDS_ASH_STATUS_TRAY_ACCESSIBILITY_ADDITIONAL_SETTINGS));
+  AddScrollListSubHeader(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_ADDITIONAL_SETTINGS);
 
   large_cursor_enabled_ = delegate->IsLargeCursorEnabled();
-  large_cursor_view_ = AddScrollListItemWithoutIcon(
+  large_cursor_view_ = AddScrollListCheckableItem(
       l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR),
       large_cursor_enabled_);
 
   mono_audio_enabled_ = delegate->IsMonoAudioEnabled();
-  mono_audio_view_ = AddScrollListItemWithoutIcon(
+  mono_audio_view_ = AddScrollListCheckableItem(
       l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_MONO_AUDIO),
       mono_audio_enabled_);
 
   caret_highlight_enabled_ = delegate->IsCaretHighlightEnabled();
-  caret_highlight_view_ = AddScrollListItemWithoutIcon(
+  caret_highlight_view_ = AddScrollListCheckableItem(
       l10n_util::GetStringUTF16(
           IDS_ASH_STATUS_TRAY_ACCESSIBILITY_CARET_HIGHLIGHT),
       caret_highlight_enabled_);
 
   highlight_mouse_cursor_enabled_ = delegate->IsCursorHighlightEnabled();
-  highlight_mouse_cursor_view_ = AddScrollListItemWithoutIcon(
+  highlight_mouse_cursor_view_ = AddScrollListCheckableItem(
       l10n_util::GetStringUTF16(
           IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGHLIGHT_MOUSE_CURSOR),
       highlight_mouse_cursor_enabled_);
@@ -275,59 +281,23 @@
   // ChromeVox does its own focus highlighting.
   if (!spoken_feedback_enabled_) {
     highlight_keyboard_focus_enabled_ = delegate->IsFocusHighlightEnabled();
-    highlight_keyboard_focus_view_ = AddScrollListItemWithoutIcon(
+    highlight_keyboard_focus_view_ = AddScrollListCheckableItem(
         l10n_util::GetStringUTF16(
             IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGHLIGHT_KEYBOARD_FOCUS),
         highlight_keyboard_focus_enabled_);
   }
 
   sticky_keys_enabled_ = delegate->IsStickyKeysEnabled();
-  sticky_keys_view_ = AddScrollListItemWithoutIcon(
+  sticky_keys_view_ = AddScrollListCheckableItem(
       l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_STICKY_KEYS),
       sticky_keys_enabled_);
 
   tap_dragging_enabled_ = delegate->IsTapDraggingEnabled();
-  tap_dragging_view_ = AddScrollListItemWithoutIcon(
+  tap_dragging_view_ = AddScrollListCheckableItem(
       l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TAP_DRAGGING),
       tap_dragging_enabled_);
 }
 
-HoverHighlightView* AccessibilityDetailedView::AddScrollListItem(
-    const base::string16& text,
-    bool checked,
-    const gfx::VectorIcon& icon) {
-  HoverHighlightView* container = new HoverHighlightView(this);
-  gfx::ImageSkia image = CreateVectorIcon(icon, kMenuIconColor);
-  container->AddIconAndLabel(image, text);
-  TrayPopupUtils::InitializeAsCheckableRow(container, checked);
-  scroll_content()->AddChildView(container);
-  return container;
-}
-
-HoverHighlightView* AccessibilityDetailedView::AddScrollListItemWithoutIcon(
-    const base::string16& text,
-    bool checked) {
-  HoverHighlightView* container = new HoverHighlightView(this);
-  container->AddLabelRow(text);
-  TrayPopupUtils::InitializeAsCheckableRow(container, checked);
-  scroll_content()->AddChildView(container);
-  return container;
-}
-
-void AccessibilityDetailedView::AddSubHeader(
-    const base::string16& header_text) {
-  TriView* header = TrayPopupUtils::CreateSubHeaderRowView();
-  TrayPopupUtils::ConfigureAsStickyHeader(header);
-
-  views::Label* label = TrayPopupUtils::CreateDefaultLabel();
-  label->SetText(header_text);
-  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::SUB_HEADER);
-  style.SetupLabel(label);
-  header->AddView(TriView::Container::CENTER, label);
-
-  scroll_content()->AddChildView(header);
-}
-
 void AccessibilityDetailedView::HandleViewClicked(views::View* view) {
   AccessibilityDelegate* delegate = Shell::Get()->accessibility_delegate();
   UserMetricsAction user_action;
diff --git a/ash/system/tray_accessibility.h b/ash/system/tray_accessibility.h
index e3a2e1a6..2e101f9 100644
--- a/ash/system/tray_accessibility.h
+++ b/ash/system/tray_accessibility.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include "ash/accessibility_delegate.h"
-#include "ash/shell_observer.h"
 #include "ash/system/accessibility_observer.h"
 #include "ash/system/tray/tray_details_view.h"
 #include "ash/system/tray/tray_image_item.h"
@@ -21,10 +20,6 @@
 class TrayAccessibilityTest;
 }
 
-namespace gfx {
-struct VectorIcon;
-}
-
 namespace views {
 class Button;
 class CustomButton;
@@ -33,7 +28,6 @@
 }
 
 namespace ash {
-class HoverHighlightView;
 class SystemTrayItem;
 
 namespace tray {
@@ -60,8 +54,7 @@
 };
 
 // Create the detailed view of accessibility tray.
-class AccessibilityDetailedView : public TrayDetailsView,
-                                  public ShellObserver {
+class AccessibilityDetailedView : public TrayDetailsView {
  public:
   explicit AccessibilityDetailedView(SystemTrayItem* owner);
   ~AccessibilityDetailedView() override {}
@@ -82,15 +75,6 @@
   // Add the accessibility feature list.
   void AppendAccessibilityList();
 
-  // Helper function to create entries in the detailed accessibility view.
-  HoverHighlightView* AddScrollListItem(const base::string16& text,
-                                        bool checked,
-                                        const gfx::VectorIcon& icon);
-  HoverHighlightView* AddScrollListItemWithoutIcon(const base::string16& text,
-                                                   bool checked);
-
-  void AddSubHeader(const base::string16& header_text);
-
   views::View* spoken_feedback_view_ = nullptr;
   views::View* high_contrast_view_ = nullptr;
   views::View* screen_magnifier_view_ = nullptr;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index d096f012..b284bec 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2116,7 +2116,6 @@
     "task_scheduler/task_scheduler_impl_unittest.cc",
     "task_scheduler/task_tracker_posix_unittest.cc",
     "task_scheduler/task_tracker_unittest.cc",
-    "task_scheduler/task_traits_unittest.cc",
     "task_scheduler/task_unittest.cc",
     "task_scheduler/test_task_factory.cc",
     "task_scheduler/test_task_factory.h",
diff --git a/base/containers/README.md b/base/containers/README.md
index 33c2f32..e64e8f3 100644
--- a/base/containers/README.md
+++ b/base/containers/README.md
@@ -1,5 +1,7 @@
 # base/containers library
 
+## What goes here
+
 This directory contains some STL-like containers.
 
 Things should be moved here that are generally applicable across the code base.
@@ -8,7 +10,7 @@
 your component's directory and we can promote them here later if we feel there
 is broad applicability.
 
-## Design and naming
+### Design and naming
 
 Containers should adhere as closely to STL as possible. Functions and behaviors
 not present in STL should only be added when they are related to the specific
@@ -18,3 +20,151 @@
 when it may conflict with the style guide. So functions and class names should
 be lower case with underscores. Non-STL-like classes and functions should use
 Google naming. Be sure to use the base namespace.
+
+## Map and set selection
+
+### Usage advice
+
+  * Generally avoid **std::unordered\_set** and **std::unordered\_map**. In the
+    common case, query performance is unlikely to be sufficiently higher than
+    std::map to make a difference, insert performance is slightly worse, and
+    the memory overhead is high. This makes sense mostly for large tables where
+    you expect a lot of lookups.
+
+  * Most maps and sets in Chrome are small and contain objects that can be
+    moved efficiently. In this case, consider **base::flat\_map** and
+    **base::flat\_set**. You need to be aware of the maximum expected size of
+    the container since individual inserts and deletes are O(n), giving O(n^2)
+    construction time for the entire map. But because it avoids mallocs in most
+    cases, inserts are better or comparable to other containers even for
+    several dozen items, and efficiently-moved types are unlikely to have
+    performance problems for most cases until you have hundreds of items. If
+    your container can be constructed in one shot, the constructor from vector
+    gives O(n log n) construction times and it should be strictly better than
+    a std::map.
+
+  * **base::small\_map** has better runtime memory usage without the poor
+    mutation performance of large containers that base::flat\_map has. But this
+    advantage is partially offset by additional code size. Prefer in cases
+    where you make many objects so that the code/heap tradeoff is good.
+
+  * Use **std::map** and **std::set** if you can't decide. Even if they're not
+    great, they're unlikely to be bad or surprising.
+
+### Map and set details
+
+Sizes are on 64-bit platforms. Stable iterators aren't invalidated when the
+container is mutated.
+
+| Container                                | Empty size            | Per-item overhead | Stable iterators? |
+|:---------------------------------------- |:--------------------- |:----------------- |:----------------- |
+| std::map, std::set                       | 16 bytes              | 32 bytes          | Yes               |
+| std::unordered\_map, std::unordered\_set | 128 bytes             | 16-24 bytes       | No                |
+| base::flat\_map and base::flat\_set      | 24 bytes              | 0 (see notes)     | No                |
+| base::small\_map                         | 24 bytes (see notes)  | 32 bytes          | No                |
+
+**Takeaways:** std::unordered\_map and std::unordered\_map have high
+overhead for small container sizes, prefer these only for larger workloads.
+
+Code size comparisons for a block of code (see appendix) on Windows using
+strings as keys.
+
+| Container           | Code size  |
+|:------------------- |:---------- |
+| std::unordered\_map | 1646 bytes |
+| std::map            | 1759 bytes |
+| base::flat\_map     | 1872 bytes |
+| base::small\_map    | 2410 bytes |
+
+**Takeaways:** base::small\_map generates more code because of the inlining of
+both brute-force and red-black tree searching. This makes it less attractive
+for random one-off uses. But if your code is called frequently, the runtime
+memory benefits will be more important. The code sizes of the other maps are
+close enough it's not worth worrying about.
+
+### std::map and std::set
+
+A red-black tree. Each inserted item requires the memory allocation of a node
+on the heap. Each node contains a left pointer, a right pointer, a parent
+pointer, and a "color" for the red-black tree (32-bytes per item on 64-bits).
+
+### std::unordered\_map and std::unordered\_set
+
+A hash table. Implemented on Windows as a std::vector + std::list and in libc++
+as the equivalent of a std::vector + a std::forward\_list. Both implementations
+allocate an 8-entry hash table (containing iterators into the list) on
+initialization, and grow to 64 entries once 8 items are inserted. Above 64
+items, the size doubles every time the load factor exceeds 1.
+
+The empty size is sizeof(std::unordered\_map) = 64 +
+the initial hash table size which is 8 pointers. The per-item overhead in the
+table above counts the list node (2 pointers on Windows, 1 pointer in libc++),
+plus amortizes the hash table assuming a 0.5 load factor on average.
+
+In a microbenchmark on Windows, inserts of 1M integers into a
+std::unordered\_set took 1.07x the time of std::set, and queries took 0.67x the
+time of std::set. For a typical 4-entry set (the statistical mode of map sizes
+in the browser), query performance is identical to std::set and base::flat\_set.
+On ARM, unordered\_set performance can be worse because integer division to
+compute the bucket is slow, and a few "less than" operations can be faster than
+computing a hash depending on the key type. The takeaway is that you should not
+default to using unordered maps because "they're faster."
+
+### base::flat\_map and base::flat\_set
+
+A sorted std::vector. Seached via binary search, inserts in the middle require
+moving elements to make room. Good cache locality. For large objects and large
+set sizes, std::vector's doubling-when-full strategy can waste memory.
+
+Supports efficient construction from a vector of items which avoids the O(n^2)
+insertion time of each element separately.
+
+The per-item overhead will depend on the underlying std::vector's reallocation
+strategy and the memory access pattern. Assuming items are being linearly added,
+one would expect it to be 3/4 full, so per-item overhead will be 0.25 *
+sizeof(T).
+
+### base::small\_map
+
+A small inline buffer that is brute-force searched that overflows into a full
+std::map or std::unordered\_map. This gives the memory benefit of
+base::flat\_map for small data sizes without the degenerate insertion
+performance for large container sizes.
+
+Since instantiations require both code for a std::map and a brute-force search
+of the inline container, plus a fancy iterator to cover both cases, code size
+is larger.
+
+The initial size in the above table is assuming a very small inline table. The
+actual size will be sizeof(int) + min(sizeof(std::map), sizeof(T) *
+inline\_size).
+
+## Appendix
+
+### Code for map code size comparison
+
+This just calls insert and query a number of times, with printfs that prevent
+things from being dead-code eliminated.
+
+```
+TEST(Foo, Bar) {
+  base::small_map<std::map<std::string, Flubber>> foo;
+  foo.insert(std::make_pair("foo", Flubber(8, "bar")));
+  foo.insert(std::make_pair("bar", Flubber(8, "bar")));
+  foo.insert(std::make_pair("foo1", Flubber(8, "bar")));
+  foo.insert(std::make_pair("bar1", Flubber(8, "bar")));
+  foo.insert(std::make_pair("foo", Flubber(8, "bar")));
+  foo.insert(std::make_pair("bar", Flubber(8, "bar")));
+  auto found = foo.find("asdf");
+  printf("Found is %d\n", (int)(found == foo.end()));
+  found = foo.find("foo");
+  printf("Found is %d\n", (int)(found == foo.end()));
+  found = foo.find("bar");
+  printf("Found is %d\n", (int)(found == foo.end()));
+  found = foo.find("asdfhf");
+  printf("Found is %d\n", (int)(found == foo.end()));
+  found = foo.find("bar1");
+  printf("Found is %d\n", (int)(found == foo.end()));
+}
+```
+
diff --git a/base/containers/flat_map.h b/base/containers/flat_map.h
index bd0b1265..225052b 100644
--- a/base/containers/flat_map.h
+++ b/base/containers/flat_map.h
@@ -14,7 +14,7 @@
 
 namespace internal {
 
-// An implementation of the flat_set GetKeyFromValue template parameter that
+// An implementation of the flat_tree GetKeyFromValue template parameter that
 // extracts the key as the first element of a pair.
 template <class Key, class Mapped>
 struct GetKeyFromValuePairFirst {
@@ -25,14 +25,30 @@
 
 }  // namespace internal
 
-// OVERVIEW
+// flat_map is a container with a std::map-like interface that stores its
+// contents in a sorted vector.
 //
-// This file implements flat_map container. It is an alternative to standard
-// sorted containers that stores its elements in contiguous memory (a vector).
+// Please see //base/containers/README.md for an overview of which container
+// to select.
 //
-// Additional documentation and usage advice is in flat_set.h.
+// PROS
 //
-// DOCUMENTATION
+//  - Good memory locality.
+//  - Low overhead, especially for smaller maps.
+//  - Performance is good for more workloads than you might expect (see
+//    overview link above).
+//
+// CONS
+//
+//  - Inserts and removals are O(n).
+//
+// IMPORTANT NOTES
+//
+//  - Iterators are invalidated across mutations.
+//  - If possible, construct a flat_map in one operation by inserting into
+//    a std::vector and moving that vector into the flat_map constructor.
+//
+// QUICK REFERENCE
 //
 // Most of the core functionality is inherited from flat_tree. Please see
 // flat_tree.h for more details for most of these functions. As a quick
diff --git a/base/containers/flat_set.h b/base/containers/flat_set.h
index da19034f..13f44dd 100644
--- a/base/containers/flat_set.h
+++ b/base/containers/flat_set.h
@@ -9,70 +9,31 @@
 
 namespace base {
 
-// Overview:
-// This file implements flat_set container. It is an alternative to standard
-// sorted containers that stores it's elements in contiguous memory using a
-// std::vector.
+// flat_set is a container with a std::set-like interface that stores its
+// contents in a sorted vector.
 //
-// Discussion that preceded introduction of this container can be found here:
-// https://groups.google.com/a/chromium.org/forum/#!searchin/chromium-dev/vector$20based/chromium-dev/4uQMma9vj9w/HaQ-WvMOAwAJ
+// Please see //base/containers/README.md for an overview of which container
+// to select.
 //
-// Motivation:
-// Contiguous memory is very beneficial to iteration and copy speed at the cost
-// of worse algorithmic complexity of insertion/erasure operations. They can
-// be very fast for set operations and for small number of elements.
+// PROS
 //
-// Usage guidance:
-// Prefer base::flat_set for:
-//  * Very small sets, something that is an easy fit for cache. Consider
-//    "very small" to be under 100 32bit integers.
-//  * Sets that are built once (using flat_set::flat_set(first, last)). Consider
-//    collecting all data in a vector and then building flat_set out of it.
-//    Using the constructor that takes a moved vector allows you to re-use
-//    storage.
-//  * Sets where mutating happens in big bulks: to erase multiple elements, use
-//    base::EraseIf() rather than repeated single-element removal. Insertion is
-//    harder - consider set operations or building a new vector. Set operations
-//    can be slow if one of the sets is considerably bigger. Also be aware that
-//    beating performance of sort + unique (implementation of flat_set's
-//    constructor) is hard, clever merge of many sets might not win. Generally
-//    avoid inserting into flat set without benchmarks.
-//  * Copying and iterating.
-//  * Set operations (union/intersect etc).
+//  - Good memory locality.
+//  - Low overhead, especially for smaller sets.
+//  - Performance is good for more workloads than you might expect (see
+//    overview link above).
 //
-// Prefer to build a new flat_set from a std::vector (or similar) instead of
-// calling insert() repeatedly, which would have O(size^2) complexity. The
-// constructor that can accept a moved vector (not required to be sorted) is
-// the most efficient.
+// CONS
 //
-// TODO(dyaroshev): develop standalone benchmarks to find performance boundaries
-// for different types of sets crbug.com/682215.
+//  - Inserts and removals are O(n).
 //
-// If you do write a benchmark that significantly depends on using sets please
-// share your results at:
-// https://groups.google.com/a/chromium.org/forum/#!searchin/chromium-dev/vector$20based/chromium-dev/4uQMma9vj9w/HaQ-WvMOAwAJ
+// IMPORTANT NOTES
 //
-// Important usability aspects:
-//   * flat_set implements std::set interface from C++11 where possible. It
-//     also has reserve(), capacity() and shrink_to_fit() from std::vector.
-//   * iteration invalidation rules differ:
-//     - all cases of std::vector::iterator invalidation also apply.
-//     - we ask (for now) to assume that move operations invalidate iterators.
-//       TODO(dyaroshev): Research the possibility of using a small buffer
-//       optimization crbug.com/682240.
-//   * allocator support is not implemented.
-//   * insert(first, last) and insert(std::initializer_list) are not
-//     implemented (see Notes section).
+//  - Iterators are invalidated across mutations.
+//  - If possible, construct a flat_set in one operation by inserting into
+//    a std::vector and moving that vector into the flat_set constructor.
+//  - For multiple removals use base::EraseIf() which is O(n) rather than
+//    O(n * removed_items).
 //
-// Notes:
-// Current implementation is based on boost::containers::flat_set,
-// eastl::vector_set and folly::sorted_vector. All of these implementations do
-// insert(first, last) as insertion one by one (some implementations with hints
-// and/or reserve). Boost documentation claims this algorithm to be O(n*log(n))
-// but it seems to be a quadratic algorithm. For now we do not implement this
-// method.
-// TODO(dyaroshev): research an algorithm for range insertion crbug.com/682249.
-
 // QUICK REFERENCE
 //
 // Most of the core functionality is inherited from flat_tree. Please see
diff --git a/base/containers/small_map.h b/base/containers/small_map.h
index db27b88..0517ea6 100644
--- a/base/containers/small_map.h
+++ b/base/containers/small_map.h
@@ -17,39 +17,32 @@
 
 namespace base {
 
-// An STL-like associative container which starts out backed by a simple
-// array but switches to some other container type if it grows beyond a
-// fixed size.
+// small_map is a container with a std::map-like interface. It starts out
+// backed by a unsorted array but switches to some other container type if it
+// grows beyond this fixed size.
 //
-// WHAT TYPE OF MAP SHOULD YOU USE?
-// --------------------------------
+// Please see //base/containers/README.md for an overview of which container
+// to select.
 //
-//  - std::map should be the default if you're not sure, since it's the most
-//    difficult to mess up. Generally this is backed by a red-black tree. It
-//    will generate a lot of code (if you use a common key type like int or
-//    string the linker will probably emiminate the duplicates). It will
-//    do heap allocations for each element.
+// PROS
 //
-//  - If you only ever keep a couple of items and have very simple usage,
-//    use a base::flat_map.
+//  - Good memory locality and low overhead for smaller maps.
+//  - Handles large maps without the degenerate performance of flat_map.
 //
-//  - std::unordered_map should be used if you need O(1) lookups. It may waste
-//    space in the hash table, and it can be easy to write correct-looking
-//    code with the default hash function being wrong or poorly-behaving.
+// CONS
 //
-//  - base::small_map combines the performance benefits of the
-//    brute-force-searched vector for small cases (no extra heap allocations),
-//    but can efficiently fall back if you end up adding many items. It will
-//    generate more code than std::map (at least 160 bytes for operator[])
-//    which is bad if you have a "weird" key where map functions can't be
-//    duplicate-code-eliminated. If you have a one-off key and aren't in
-//    performance-critical code, this bloat may negate some of the benefits and
-//    you should consider on of the other options.
+//  - Larger code size than the alternatives.
+//
+// IMPORTANT NOTES
+//
+//  - Iterators are invalidated across mutations.
+//
+// DETAILS
 //
 // base::small_map will pick up the comparator from the underlying map type. In
-// std::map (and in MSVC additionally hash_map) only a "less" operator is
-// defined, which requires us to do two comparisons per element when doing the
-// brute-force search in the simple array.
+// std::map only a "less" operator is defined, which requires us to do two
+// comparisons per element when doing the brute-force search in the simple
+// array. std::unordered_map has a key_equal function which will be used.
 //
 // We define default overrides for the common map types to avoid this
 // double-compare, but you should be aware of this if you use your own
@@ -90,9 +83,6 @@
 //   days["thursday" ] = 4;
 //   days["friday"   ] = 5;
 //   days["saturday" ] = 6;
-//
-// You should assume that small_map might invalidate all the iterators
-// on any call to erase(), insert() and operator[].
 
 namespace internal {
 
@@ -147,7 +137,7 @@
 // If we switch to using std::unordered_map for base::hash_map, then the
 // hash_map specialization can be removed.
 template <typename KeyType, typename ValueType>
-struct select_equal_key< std::map<KeyType, ValueType>, false> {
+struct select_equal_key<std::map<KeyType, ValueType>, false> {
   struct equal_key {
     bool operator()(const KeyType& left, const KeyType& right) {
       return left == right;
@@ -155,7 +145,7 @@
   };
 };
 template <typename KeyType, typename ValueType>
-struct select_equal_key< base::hash_map<KeyType, ValueType>, false> {
+struct select_equal_key<base::hash_map<KeyType, ValueType>, false> {
   struct equal_key {
     bool operator()(const KeyType& left, const KeyType& right) {
       return left == right;
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index 6e5e796..06d4ba9 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -167,10 +167,20 @@
   bool GetCommittedAndWorkingSetKBytes(CommittedKBytes* usage,
                                        WorkingSetKBytes* ws_usage) const;
 
-  // Returns the physical footprint, only available on macOS 10.11+. This
-  // measures anonymous, non-discardable memory. Returns 0 on error, or if the
-  // measurement was unavailable.
-  size_t GetPhysicalFootprint() const;
+  struct TaskVMInfo {
+    // Only available on macOS 10.12+.
+    // Anonymous, non-discardable memory, including non-volatile IOKit.
+    // Measured in bytes.
+    uint64_t phys_footprint = 0;
+
+    // Anonymous, non-discardable, non-compressed memory, excluding IOKit.
+    // Measured in bytes.
+    uint64_t internal = 0;
+
+    // Compressed memory measured in bytes.
+    uint64_t compressed = 0;
+  };
+  TaskVMInfo GetTaskVMInfo() const;
 
   // Returns private, shared, and total resident bytes. |locked_bytes| refers to
   // bytes that must stay resident. |locked_bytes| only counts bytes locked by
diff --git a/base/process/process_metrics_mac.cc b/base/process/process_metrics_mac.cc
index 5eda0d0..ad751e3 100644
--- a/base/process/process_metrics_mac.cc
+++ b/base/process/process_metrics_mac.cc
@@ -287,18 +287,21 @@
   return true;
 }
 
-size_t ProcessMetrics::GetPhysicalFootprint() const {
-  if (mac::IsAtMostOS10_11())
-    return 0;
-
+ProcessMetrics::TaskVMInfo ProcessMetrics::GetTaskVMInfo() const {
+  TaskVMInfo info;
   ChromeTaskVMInfo task_vm_info;
   mach_msg_type_number_t count = ChromeTaskVMInfoCount;
   kern_return_t result =
       task_info(TaskForPid(process_), TASK_VM_INFO,
                 reinterpret_cast<task_info_t>(&task_vm_info), &count);
   if (result != KERN_SUCCESS)
-    return 0;
-  return task_vm_info.phys_footprint;
+    return info;
+
+  info.internal = task_vm_info.internal;
+  info.compressed = task_vm_info.compressed;
+  if (count == ChromeTaskVMInfoCount)
+    info.phys_footprint = task_vm_info.phys_footprint;
+  return info;
 }
 
 #define TIME_VALUE_TO_TIMEVAL(a, r) do {  \
diff --git a/base/task_scheduler/post_task.cc b/base/task_scheduler/post_task.cc
index 6f9adae2..6da11bd8 100644
--- a/base/task_scheduler/post_task.cc
+++ b/base/task_scheduler/post_task.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h"
 #include "base/task_scheduler/task_scheduler.h"
 #include "base/threading/post_task_and_reply_impl.h"
 
@@ -29,6 +30,15 @@
   const TaskTraits traits_;
 };
 
+// Returns TaskTraits based on |traits|. If TaskPriority hasn't been set
+// explicitly in |traits|, the returned TaskTraits have the current
+// TaskPriority.
+TaskTraits GetTaskTraitsWithExplicitPriority(const TaskTraits& traits) {
+  return traits.priority_set_explicitly()
+             ? traits
+             : TaskTraits(traits).WithPriority(
+                   internal::GetTaskPriorityForCurrentThread());
+}
 
 }  // namespace
 
@@ -62,7 +72,8 @@
   DCHECK(TaskScheduler::GetInstance())
       << "Ref. Prerequisite section of post_task.h";
   TaskScheduler::GetInstance()->PostDelayedTaskWithTraits(
-      from_here, traits, std::move(task), std::move(delay));
+      from_here, GetTaskTraitsWithExplicitPriority(traits), std::move(task),
+      std::move(delay));
 }
 
 void PostTaskWithTraitsAndReply(const tracked_objects::Location& from_here,
@@ -76,7 +87,8 @@
 scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits(const TaskTraits& traits) {
   DCHECK(TaskScheduler::GetInstance())
       << "Ref. Prerequisite section of post_task.h";
-  return TaskScheduler::GetInstance()->CreateTaskRunnerWithTraits(traits);
+  return TaskScheduler::GetInstance()->CreateTaskRunnerWithTraits(
+      GetTaskTraitsWithExplicitPriority(traits));
 }
 
 scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunnerWithTraits(
@@ -84,7 +96,7 @@
   DCHECK(TaskScheduler::GetInstance())
       << "Ref. Prerequisite section of post_task.h";
   return TaskScheduler::GetInstance()->CreateSequencedTaskRunnerWithTraits(
-      traits);
+      GetTaskTraitsWithExplicitPriority(traits));
 }
 
 scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunnerWithTraits(
@@ -92,7 +104,7 @@
   DCHECK(TaskScheduler::GetInstance())
       << "Ref. Prerequisite section of post_task.h";
   return TaskScheduler::GetInstance()->CreateSingleThreadTaskRunnerWithTraits(
-      traits);
+      GetTaskTraitsWithExplicitPriority(traits));
 }
 
 #if defined(OS_WIN)
@@ -100,7 +112,8 @@
     const TaskTraits& traits) {
   DCHECK(TaskScheduler::GetInstance())
       << "Ref. Prerequisite section of post_task.h";
-  return TaskScheduler::GetInstance()->CreateCOMSTATaskRunnerWithTraits(traits);
+  return TaskScheduler::GetInstance()->CreateCOMSTATaskRunnerWithTraits(
+      GetTaskTraitsWithExplicitPriority(traits));
 }
 #endif  // defined(OS_WIN)
 
diff --git a/base/task_scheduler/task_scheduler.cc b/base/task_scheduler/task_scheduler.cc
index c1eb432..80359c3 100644
--- a/base/task_scheduler/task_scheduler.cc
+++ b/base/task_scheduler/task_scheduler.cc
@@ -39,7 +39,7 @@
 
 #if !defined(OS_NACL)
 // static
-void TaskScheduler::CreateAndSetSimpleTaskScheduler(StringPiece name) {
+void TaskScheduler::CreateAndStartWithDefaultParams(StringPiece name) {
   using StandbyThreadPolicy = SchedulerWorkerPoolParams::StandbyThreadPolicy;
 
   // Values were chosen so that:
@@ -54,22 +54,32 @@
 
   constexpr TimeDelta kSuggestedReclaimTime = TimeDelta::FromSeconds(30);
 
-  CreateAndSetDefaultTaskScheduler(
-      name, {{StandbyThreadPolicy::LAZY, kBackgroundMaxThreads,
-              kSuggestedReclaimTime},
-             {StandbyThreadPolicy::LAZY, kBackgroundBlockingMaxThreads,
-              kSuggestedReclaimTime},
-             {StandbyThreadPolicy::LAZY, kForegroundMaxThreads,
-              kSuggestedReclaimTime},
-             {StandbyThreadPolicy::LAZY, kForegroundBlockingMaxThreads,
-              kSuggestedReclaimTime}});
+  Create(name);
+  GetInstance()->Start(
+      {{StandbyThreadPolicy::LAZY, kBackgroundMaxThreads,
+        kSuggestedReclaimTime},
+       {StandbyThreadPolicy::LAZY, kBackgroundBlockingMaxThreads,
+        kSuggestedReclaimTime},
+       {StandbyThreadPolicy::LAZY, kForegroundMaxThreads,
+        kSuggestedReclaimTime},
+       {StandbyThreadPolicy::LAZY, kForegroundBlockingMaxThreads,
+        kSuggestedReclaimTime}});
+}
+
+// static
+void TaskScheduler::CreateAndSetSimpleTaskScheduler(StringPiece name) {
+  CreateAndStartWithDefaultParams(name);
 }
 #endif  // !defined(OS_NACL)
 
+void TaskScheduler::Create(StringPiece name) {
+  SetInstance(MakeUnique<internal::TaskSchedulerImpl>(name));
+}
+
 void TaskScheduler::CreateAndSetDefaultTaskScheduler(
     StringPiece name,
     const InitParams& init_params) {
-  SetInstance(MakeUnique<internal::TaskSchedulerImpl>(name));
+  Create(name);
   GetInstance()->Start(init_params);
 }
 
diff --git a/base/task_scheduler/task_scheduler.h b/base/task_scheduler/task_scheduler.h
index 3d3d5eb0..0bbfd5c 100644
--- a/base/task_scheduler/task_scheduler.h
+++ b/base/task_scheduler/task_scheduler.h
@@ -134,28 +134,45 @@
   // after this call.
   virtual void JoinForTesting() = 0;
 
-  // CreateAndSetSimpleTaskScheduler(), CreateAndSetDefaultTaskScheduler(), and
-  // SetInstance() register a TaskScheduler to handle tasks posted through the
-  // post_task.h API for this process. The registered TaskScheduler will only be
-  // deleted when a new TaskScheduler is registered and is leaked on shutdown.
-  // The methods must not be called when TaskRunners created by the previous
-  // TaskScheduler are still alive. The methods are not thread-safe; proper
-  // synchronization is required to use the post_task.h API after registering a
-  // new TaskScheduler.
+// CreateAndStartWithDefaultParams(), Create(), and SetInstance() register a
+// TaskScheduler to handle tasks posted through the post_task.h API for this
+// process.
+//
+// Processes that need to initialize TaskScheduler with custom params or that
+// need to allow tasks to be posted before the TaskScheduler creates its
+// threads should use Create() followed by Start(). Other processes can use
+// CreateAndStartWithDefaultParams().
+//
+// A registered TaskScheduler is only deleted when a new TaskScheduler is
+// registered. The last registered TaskScheduler is leaked on shutdown. The
+// methods below must not be called when TaskRunners created by a previous
+// TaskScheduler are still alive. The methods are not thread-safe; proper
+// synchronization is required to use the post_task.h API after registering a
+// new TaskScheduler.
 
 #if !defined(OS_NACL)
-  // Creates and sets a task scheduler using default params. |name| is used to
+  // Creates and starts a task scheduler using default params. |name| is used to
   // label threads and histograms. It should identify the component that calls
-  // this. CHECKs on failure. For tests, prefer base::test::ScopedTaskScheduler
-  // (ensures isolation).
+  // this. Start() is called by this method; it is invalid to call it again
+  // afterwards. CHECKs on failure. For tests, prefer
+  // base::test::ScopedTaskEnvironment (ensures isolation).
+  static void CreateAndStartWithDefaultParams(StringPiece name);
+
+  // Deprecated. Use CreateAndStartWithDefaultParams() instead.
+  // TODO(fdoray): Redirect callers to CreateAndStartWithDefaultParams().
   static void CreateAndSetSimpleTaskScheduler(StringPiece name);
 #endif  // !defined(OS_NACL)
 
-  // Creates and sets a task scheduler using custom params. |name| is used to
-  // label threads and histograms. It should identify the component that creates
-  // the TaskScheduler. |init_params| is used to initialize the worker pools.
-  // CHECKs on failure. For tests, prefer base::test::ScopedTaskScheduler
+  // Creates a ready to start task scheduler. |name| is used to label threads
+  // and histograms. It should identify the component that creates the
+  // TaskScheduler. The task scheduler doesn't create threads until Start() is
+  // called. Tasks can be posted at any time but will not run until after
+  // Start() is called. For tests, prefer base::test::ScopedTaskEnvironment
   // (ensures isolation).
+  static void Create(StringPiece name);
+
+  // Deprecated. Use Create() and Start() instead.
+  // TODO(fdoray): Redirect callers to Create() and Start().
   static void CreateAndSetDefaultTaskScheduler(StringPiece name,
                                                const InitParams& init_params);
 
diff --git a/base/task_scheduler/task_traits.cc b/base/task_scheduler/task_traits.cc
index 6acf324..c00ae6c4 100644
--- a/base/task_scheduler/task_traits.cc
+++ b/base/task_scheduler/task_traits.cc
@@ -9,19 +9,10 @@
 #include <ostream>
 
 #include "base/logging.h"
-#include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h"
 
 namespace base {
 
-// Do not rely on defaults hard-coded below beyond the guarantees described in
-// the header; anything else is subject to change. Tasks should explicitly
-// request defaults if the behavior is critical to the task.
-TaskTraits::TaskTraits()
-    : may_block_(false),
-      with_base_sync_primitives_(false),
-      priority_(internal::GetTaskPriorityForCurrentThread()),
-      shutdown_behavior_(TaskShutdownBehavior::SKIP_ON_SHUTDOWN) {}
-
+TaskTraits::TaskTraits() = default;
 TaskTraits::~TaskTraits() = default;
 
 TaskTraits& TaskTraits::MayBlock() {
@@ -35,6 +26,7 @@
 }
 
 TaskTraits& TaskTraits::WithPriority(TaskPriority priority) {
+  priority_set_explicitly_ = true;
   priority_ = priority;
   return *this;
 }
diff --git a/base/task_scheduler/task_traits.h b/base/task_scheduler/task_traits.h
index 435fdac..04aea49 100644
--- a/base/task_scheduler/task_traits.h
+++ b/base/task_scheduler/task_traits.h
@@ -142,6 +142,9 @@
   // Returns true if tasks with these traits may use base/ sync primitives.
   bool with_base_sync_primitives() const { return with_base_sync_primitives_; }
 
+  // Returns true if the priority was set explicitly.
+  bool priority_set_explicitly() const { return priority_set_explicitly_; }
+
   // Returns the priority of tasks with these traits.
   TaskPriority priority() const { return priority_; }
 
@@ -149,10 +152,15 @@
   TaskShutdownBehavior shutdown_behavior() const { return shutdown_behavior_; }
 
  private:
-  bool may_block_;
-  bool with_base_sync_primitives_;
-  TaskPriority priority_;
-  TaskShutdownBehavior shutdown_behavior_;
+  // Do not rely on defaults hard-coded below beyond the guarantees described on
+  // the constructor; anything else is subject to change. Tasks should
+  // explicitly request defaults if the behavior is critical to the task.
+  bool may_block_ = false;
+  bool with_base_sync_primitives_ = false;
+  bool priority_set_explicitly_ = false;
+  TaskPriority priority_ = TaskPriority::USER_VISIBLE;
+  TaskShutdownBehavior shutdown_behavior_ =
+      TaskShutdownBehavior::SKIP_ON_SHUTDOWN;
 };
 
 // Returns string literals for the enums defined in this file. These methods
diff --git a/base/task_scheduler/task_traits_unittest.cc b/base/task_scheduler/task_traits_unittest.cc
deleted file mode 100644
index fed3f99..0000000
--- a/base/task_scheduler/task_traits_unittest.cc
+++ /dev/null
@@ -1,32 +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 "base/task_scheduler/task_traits.h"
-
-#include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-// Verify that TaskTraits is initialized with the priority of the task running
-// on the current thread.
-TEST(TaskSchedulerTaskTraitsTest, DefaultPriority) {
-  {
-    internal::ScopedSetTaskPriorityForCurrentThread scope(
-        TaskPriority::BACKGROUND);
-    EXPECT_EQ(TaskPriority::BACKGROUND, TaskTraits().priority());
-  }
-  {
-    internal::ScopedSetTaskPriorityForCurrentThread scope(
-        TaskPriority::USER_VISIBLE);
-    EXPECT_EQ(TaskPriority::USER_VISIBLE, TaskTraits().priority());
-  }
-  {
-    internal::ScopedSetTaskPriorityForCurrentThread scope(
-        TaskPriority::USER_BLOCKING);
-    EXPECT_EQ(TaskPriority::USER_BLOCKING, TaskTraits().priority());
-  }
-}
-
-}  // namespace base
diff --git a/base/trace_event/process_memory_totals.cc b/base/trace_event/process_memory_totals.cc
index de27ab3d..d3cfad9 100644
--- a/base/trace_event/process_memory_totals.cc
+++ b/base/trace_event/process_memory_totals.cc
@@ -14,8 +14,7 @@
 ProcessMemoryTotals::ProcessMemoryTotals()
     : resident_set_bytes_(0),
       peak_resident_set_bytes_(0),
-      is_peak_rss_resetable_(false) {
-}
+      is_peak_rss_resetable_(false) {}
 
 ProcessMemoryTotals::~ProcessMemoryTotals() {}
 
@@ -35,6 +34,7 @@
 
 void ProcessMemoryTotals::Clear() {
   resident_set_bytes_ = 0;
+  platform_private_footprint_ = PlatformPrivateFootprint();
 }
 
 void ProcessMemoryTotals::SetExtraFieldInBytes(const char* name,
diff --git a/base/trace_event/process_memory_totals.h b/base/trace_event/process_memory_totals.h
index 329967a6..207e398 100644
--- a/base/trace_event/process_memory_totals.h
+++ b/base/trace_event/process_memory_totals.h
@@ -32,6 +32,30 @@
   uint64_t resident_set_bytes() const { return resident_set_bytes_; }
   void set_resident_set_bytes(uint64_t value) { resident_set_bytes_ = value; }
 
+  // Platform-specific data that will be used to compute the
+  // PrivateMemoryFootprint.
+  struct PlatformPrivateFootprint {
+    // macOS 10.12+
+    uint64_t phys_footprint_bytes = 0;
+
+    // macOS [all versions]
+    uint64_t internal_bytes = 0;
+    uint64_t compressed_bytes = 0;
+
+    // Linux, Android, ChromeOS
+    // TODO(hjd): https://crbug.com/707019
+    uint64_t rss_anon_bytes = 0;
+    uint64_t vm_swap_bytes = 0;
+
+    // On Windows,
+    //   TBD: https://crbug.com/707022
+    // On iOS,
+    //   TBD: https://crbug.com/714961
+  };
+  PlatformPrivateFootprint& GetPlatformPrivateFootprint() {
+    return platform_private_footprint_;
+  }
+
   uint64_t peak_resident_set_bytes() const { return peak_resident_set_bytes_; }
   void set_peak_resident_set_bytes(uint64_t value) {
     peak_resident_set_bytes_ = value;
@@ -51,6 +75,10 @@
   uint64_t peak_resident_set_bytes_;
   bool is_peak_rss_resetable_;
 
+  // Not emitted in the trace as this is intended to be an intermediary in
+  // computation of private memory footprint.
+  PlatformPrivateFootprint platform_private_footprint_;
+
   // Extra metrics for OS-specific statistics.
   std::map<const char*, uint64_t> extra_fields_;
 
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 026215d..099410c 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -451,6 +451,5 @@
     <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java"/>
     <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java"/>
     <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/signin/SigninAndSyncView.java"/>
-    <ignore regexp="content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java"/>
   </issue>
 </lint>
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 6a495cc..092991d 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -512,12 +512,22 @@
       ldflags += [ "-Wl,-all_load" ]
     }
 
-    # Allows the linker to apply ICF to the LTO object file. Also, when
-    # targeting ARM, without this flag, LTO produces a .text section that is
-    # larger than the maximum call displacement, preventing the linker from
-    # relocating calls (http://llvm.org/PR22999).
+    # Allows the linker to apply --gc-sections and ICF to the LTO object file.
+    # Also, when targeting ARM, without this flag, LTO produces a .text section
+    # that is larger than the maximum call displacement, preventing the linker
+    # from relocating calls (http://llvm.org/PR22999).
     if (is_linux) {
-      ldflags += [ "-Wl,-plugin-opt,-function-sections" ]
+      if (use_lld) {
+        ldflags += [
+          "-Wl,-mllvm,-function-sections",
+          "-Wl,-mllvm,-data-sections",
+        ]
+      } else {
+        ldflags += [
+          "-Wl,-plugin-opt,-function-sections",
+          "-Wl,-plugin-opt,-data-sections",
+        ]
+      }
     }
   }
 
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index f0343310..78166cbd 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -277,12 +277,15 @@
   cflags_cc = []
 
   # Sanitizers need line table info for stack traces. They don't need type info
-  # or variable info, so we can leave that out to speed up the build.
+  # or variable info, so we can leave that out to speed up the build (unless
+  # it's explicitly asked for by setting |sanitizer_keep_symbols| to true).
   if (using_sanitizer) {
     assert(is_clang, "sanitizers only supported with clang")
-    cflags += [
-      "-gline-tables-only",
+    if (!sanitizer_keep_symbols) {
+      cflags += [ "-gline-tables-only" ]
+    }
 
+    cflags += [
       # Column info in debug data confuses Visual Studio's debugger, so don't
       # use this by default.  However, clusterfuzz needs it for good attribution
       # of reports to CLs, so turn it on there.
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index 6feef783..585a065 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -97,6 +97,12 @@
   # Default value when unset and use_sanitizer_coverage=true:
   #     edge,indirect-calls,8bit-counters
   sanitizer_coverage_flags = ""
+
+  # Keep symbol level when building with sanitizers. When sanitizers are
+  # enabled, the default is to compile with the minimum debug info level
+  # necessary, overriding any other symbol level arguments that may be set.
+  # Setting this to true prevents this.
+  sanitizer_keep_symbols = false
 }
 
 # Disable sanitizers for non-default toolchains.
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index c2b244d..f7be83ed 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -11,7 +11,7 @@
 import("//chrome/chrome_paks.gni")
 import("//chrome/common/features.gni")
 import("//chrome/process_version_rc_template.gni")  # For branding_file_path.
-import("//device/vr/features.gni")
+import("//device/vr/features/features.gni")
 import("//testing/test.gni")
 import("//third_party/icu/config.gni")
 import("//third_party/protobuf/proto_library.gni")
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 f39d09d..ca0009bde 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1253,7 +1253,7 @@
         WebContents webContents = currentTab.getWebContents();
 
         RecordHistogram.recordBooleanHistogram(
-                "OfflinePages.SharedPageWasOffline", currentTab.isOfflinePage());
+                "OfflinePages.SharedPageWasOffline", OfflinePageUtils.isOfflinePage(currentTab));
         boolean canShareOfflinePage = OfflinePageBridge.isPageSharingEnabled();
 
         // Share an empty blockingUri in place of screenshot file. The file ready notification is
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 93c8ddf..3417e128 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -31,10 +31,12 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
+import android.widget.PopupWindow.OnDismissListener;
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.Callback;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -63,6 +65,7 @@
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.document.DocumentUtils;
 import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.feature_engagement_tracker.FeatureEngagementTrackerFactory;
 import org.chromium.chrome.browser.firstrun.FirstRunActivity;
 import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer;
 import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
@@ -116,6 +119,9 @@
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.browser.widget.emptybackground.EmptyBackgroundViewWrapper;
 import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
+import org.chromium.chrome.browser.widget.textbubble.ViewAnchoredTextBubble;
+import org.chromium.components.feature_engagement_tracker.FeatureConstants;
+import org.chromium.components.feature_engagement_tracker.FeatureEngagementTracker;
 import org.chromium.content.browser.ContentVideoView;
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content.browser.crypto.CipherFactory;
@@ -688,6 +694,15 @@
             }
 
             mLayoutManager.hideOverview(false);
+            FeatureEngagementTracker tracker =
+                    FeatureEngagementTrackerFactory.getFeatureEngagementTrackerForProfile(
+                            Profile.getLastUsedProfile());
+            tracker.addOnInitializedCallback(new Callback<Boolean>() {
+                @Override
+                public void onResult(Boolean result) {
+                    showFeatureEngagementTextBubbleForDownloadHome();
+                }
+            });
 
             mUIInitialized = true;
         } finally {
@@ -695,6 +710,34 @@
         }
     }
 
+    private void showFeatureEngagementTextBubbleForDownloadHome() {
+        final FeatureEngagementTracker tracker =
+                FeatureEngagementTrackerFactory.getFeatureEngagementTrackerForProfile(
+                        Profile.getLastUsedProfile());
+        if (!tracker.shouldTriggerHelpUI(FeatureConstants.DOWNLOAD_HOME_FEATURE)) return;
+
+        ViewAnchoredTextBubble textBubble = new ViewAnchoredTextBubble(
+                this, getToolbarManager().getMenuAnchor(), R.string.iph_download_home_text);
+        textBubble.setDismissOnTouchInteraction(true);
+        textBubble.addOnDismissListener(new OnDismissListener() {
+            @Override
+            public void onDismiss() {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        tracker.dismissed();
+                        getAppMenuHandler().setMenuHighlight(null);
+                    }
+                });
+            }
+        });
+        getAppMenuHandler().setMenuHighlight(R.id.downloads_menu_id);
+        int yInsetPx =
+                getResources().getDimensionPixelOffset(R.dimen.text_bubble_menu_anchor_y_inset);
+        textBubble.setInsetPx(0, yInsetPx, 0, 0);
+        textBubble.show();
+    }
+
     private boolean isMainIntent(Intent intent) {
         return intent != null && TextUtils.equals(intent.getAction(), Intent.ACTION_MAIN);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 5fe243de..9fac8503 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -34,6 +34,10 @@
 import org.chromium.chrome.browser.download.ui.BackendProvider;
 import org.chromium.chrome.browser.download.ui.DownloadHistoryAdapter;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
+import org.chromium.chrome.browser.feature_engagement_tracker.FeatureEngagementTrackerFactory;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.feature_engagement_tracker.EventConstants;
+import org.chromium.components.feature_engagement_tracker.FeatureEngagementTracker;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
 import org.chromium.net.ConnectionType;
@@ -1332,6 +1336,13 @@
             mDownloadSnackbarController.onDownloadSucceeded(
                     info, notificationId, systemDownloadId, canResolve, false);
         }
+
+        Profile profile = info.isOffTheRecord()
+                ? Profile.getLastUsedProfile().getOffTheRecordProfile()
+                : Profile.getLastUsedProfile().getOriginalProfile();
+        FeatureEngagementTracker tracker =
+                FeatureEngagementTrackerFactory.getFeatureEngagementTrackerForProfile(profile);
+        tracker.notifyEvent(EventConstants.DOWNLOAD_COMPLETED);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 45fb196..f9313f625 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -42,7 +42,9 @@
 import org.chromium.chrome.browser.feature_engagement_tracker.FeatureEngagementTrackerFactory;
 import org.chromium.chrome.browser.offlinepages.DownloadUiActionFlags;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBridge;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
@@ -152,6 +154,11 @@
             }
         }
 
+        Profile profile = (tab == null ? Profile.getLastUsedProfile() : tab.getProfile());
+        FeatureEngagementTracker tracker =
+                FeatureEngagementTrackerFactory.getFeatureEngagementTrackerForProfile(profile);
+        tracker.notifyEvent(EventConstants.DOWNLOAD_HOME_OPENED);
+
         return true;
     }
 
@@ -251,7 +258,7 @@
         if (tab.isShowingInterstitialPage()) return false;
 
         // Don't allow re-downloading the currently displayed offline page.
-        if (tab.isOfflinePage()) return false;
+        if (OfflinePageUtils.isOfflinePage(tab)) return false;
 
         return true;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
index 8e9751e..f2f3542 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
@@ -233,8 +233,8 @@
     @VisibleForTesting
     void onSuccessNotificationShown(
             final PendingNotificationInfo notificationInfo, final int notificationId) {
-        if (notificationInfo.downloadInfo != null
-                && !notificationInfo.downloadInfo.getIsOpenable()) {
+        if (notificationInfo.downloadInfo == null
+                || !notificationInfo.downloadInfo.getIsOpenable()) {
             return;
         }
         DownloadManagerService.getDownloadManagerService().onSuccessNotificationShown(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index c73a508..8c09c2a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -19,6 +19,8 @@
 import java.util.List;
 import java.util.Set;
 
+import javax.annotation.Nullable;
+
 /**
  * Access gate to C++ side offline pages functionalities.
  */
@@ -468,6 +470,25 @@
         nativeScheduleDownload(mNativeOfflinePageBridge, webContents, nameSpace, url, uiAction);
     }
 
+    /**
+     * Checks if an offline page is shown for the webContents.
+     * @param webContents Web contents used to find the offline page.
+     * @return True if the offline page is opened.
+     */
+    public boolean isOfflinePage(WebContents webContents) {
+        return nativeIsOfflinePage(mNativeOfflinePageBridge, webContents);
+    }
+
+    /**
+     * Retrieves the offline page that is shown for the tab.
+     * @param webContents Web contents used to find the offline page.
+     * @return The offline page if tab currently displays it, null otherwise.
+     */
+    @Nullable
+    public OfflinePageItem getOfflinePage(WebContents webContents) {
+        return nativeGetOfflinePage(mNativeOfflinePageBridge, webContents);
+    }
+
     @VisibleForTesting
     static void setOfflineBookmarksEnabledForTesting(boolean enabled) {
         sOfflineBookmarksEnabled = enabled;
@@ -573,4 +594,8 @@
             long nativeOfflinePageBridge, WebContents webContents);
     private native void nativeScheduleDownload(long nativeOfflinePageBridge,
             WebContents webContents, String nameSpace, String url, int uiAction);
+    private native boolean nativeIsOfflinePage(
+            long nativeOfflinePageBridge, WebContents webContents);
+    private native OfflinePageItem nativeGetOfflinePage(
+            long nativeOfflinePageBridge, WebContents webContents);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserver.java
index 04015df1..c1dff32 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserver.java
@@ -171,7 +171,7 @@
     @Override
     public void onUrlUpdated(Tab tab) {
         Log.d(TAG, "onUrlUpdated");
-        if (!tab.isOfflinePage()) {
+        if (!isOfflinePage(tab)) {
             stopObservingTab(tab);
         } else {
             if (isObservingTab(tab)) {
@@ -184,7 +184,7 @@
     }
 
     void startObservingTab(Tab tab) {
-        if (!tab.isOfflinePage()) return;
+        if (!isOfflinePage(tab)) return;
 
         mCurrentTab = tab;
 
@@ -285,9 +285,14 @@
         return OfflinePageUtils.isShowingOfflinePreview(tab);
     }
 
+    @VisibleForTesting
+    boolean isOfflinePage(Tab tab) {
+        return OfflinePageUtils.isOfflinePage(tab);
+    }
+
     void maybeShowReloadSnackbar(Tab tab, boolean isNetworkEvent) {
         // Exclude Offline Previews, as there is a seperate UI for previews.
-        if (tab == null || tab.isFrozen() || tab.isHidden() || !tab.isOfflinePage()
+        if (tab == null || tab.isFrozen() || tab.isHidden() || !isOfflinePage(tab)
                 || isShowingOfflinePreview(tab) || !isConnected() || !isLoadedTab(tab)
                 || (wasSnackbarSeen(tab) && !isNetworkEvent)) {
             // Conditions to show a snackbar are not met.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
index 47709b4..39f86dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
@@ -283,7 +283,7 @@
             return;
         }
 
-        OfflinePageItem offlinePage = currentTab.getOfflinePage();
+        OfflinePageItem offlinePage = offlinePageBridge.getOfflinePage(currentTab.getWebContents());
         if (offlinePage != null) {
             // If we're currently on offline page get the saved file directly.
             prepareFileAndShare(shareDirectly, saveLastUsed, mainActivity, title, text,
@@ -564,6 +564,28 @@
     }
 
     /**
+     * Checks if an offline page is shown for the tab.
+     * @param tab The tab to be reloaded.
+     * @return True if the offline page is opened.
+     */
+    public static boolean isOfflinePage(Tab tab) {
+        OfflinePageBridge offlinePageBridge = getInstance().getOfflinePageBridge(tab.getProfile());
+        if (offlinePageBridge == null) return false;
+        return offlinePageBridge.isOfflinePage(tab.getWebContents());
+    }
+
+    /**
+     * Retrieves the offline page that is shown for the tab.
+     * @param tab The tab to be reloaded.
+     * @return The offline page if tab currently displays it, null otherwise.
+     */
+    public static OfflinePageItem getOfflinePage(Tab tab) {
+        OfflinePageBridge offlinePageBridge = getInstance().getOfflinePageBridge(tab.getProfile());
+        if (offlinePageBridge == null) return null;
+        return offlinePageBridge.getOfflinePage(tab.getWebContents());
+    }
+
+    /**
      * Reloads specified tab, which should allow to open an online version of the page.
      * @param tab The tab to be reloaded.
      */
@@ -698,7 +720,7 @@
             // We first compute the bitwise tab restore context.
             int tabRestoreContext = 0;
             if (isConnected()) tabRestoreContext |= BIT_ONLINE;
-            OfflinePageItem page = tab.getOfflinePage();
+            OfflinePageItem page = getOfflinePage(tab);
             if (page != null) {
                 tabRestoreContext |= BIT_OFFLINE_PAGE;
                 if (page.getClientId().getNamespace().equals(OfflinePageBridge.LAST_N_NAMESPACE)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 8ef70af..a949e84f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -60,6 +60,7 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPage.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
+import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.omnibox.AutocompleteController.OnSuggestionsReceivedListener;
 import org.chromium.chrome.browser.omnibox.OmniboxResultsAdapter.OmniboxResultItem;
 import org.chromium.chrome.browser.omnibox.OmniboxResultsAdapter.OmniboxSuggestionDelegate;
@@ -895,7 +896,8 @@
     }
 
     @LocationBarButtonType private int getLocationBarButtonToShow() {
-        boolean isOffline = getCurrentTab() != null && getCurrentTab().isOfflinePage();
+        boolean isOffline =
+                getCurrentTab() != null && OfflinePageUtils.isOfflinePage(getCurrentTab());
         boolean isTablet = DeviceFormFactor.isTablet(getContext());
 
         // The navigation icon type is only applicable on tablets.  While smaller form factors do
@@ -1342,7 +1344,8 @@
     @Override
     public void updateSecurityIcon(int securityLevel) {
         boolean isSmallDevice = !DeviceFormFactor.isTablet(getContext());
-        boolean isOfflinePage = getCurrentTab() != null && getCurrentTab().isOfflinePage();
+        boolean isOfflinePage =
+                getCurrentTab() != null && OfflinePageUtils.isOfflinePage(getCurrentTab());
         int id = getSecurityIconResource(securityLevel, isSmallDevice, isOfflinePage);
         if (id == 0) {
             mSecurityButton.setImageDrawable(null);
@@ -1432,7 +1435,7 @@
         // Because is offline page is cleared a bit slower, we also ensure that connection security
         // level is NONE or HTTP_SHOW_WARNING (http://crbug.com/671453).
         boolean verboseStatusVisible = !mUrlHasFocus && getCurrentTab() != null
-                && getCurrentTab().isOfflinePage()
+                && OfflinePageUtils.isOfflinePage(getCurrentTab())
                 && (getSecurityLevel() == ConnectionSecurityLevel.NONE
                            || getSecurityLevel() == ConnectionSecurityLevel.HTTP_SHOW_WARNING);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoPopup.java
index cf3cc43..c44e6f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoPopup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoPopup.java
@@ -995,7 +995,7 @@
 
         String offlinePageCreationDate = null;
 
-        OfflinePageItem offlinePage = tab.getOfflinePage();
+        OfflinePageItem offlinePage = OfflinePageUtils.getOfflinePage(tab);
         if (offlinePage != null) {
             // Get formatted creation date of the offline page.
             Date creationDate = new Date(offlinePage.getCreationTimeMs());
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 0775f97..6ffe223 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
@@ -66,7 +66,6 @@
 import org.chromium.chrome.browser.media.ui.MediaSessionTabHelper;
 import org.chromium.chrome.browser.ntp.NativePageAssassin;
 import org.chromium.chrome.browser.ntp.NativePageFactory;
-import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.policy.PolicyAuditor;
 import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler;
@@ -836,7 +835,7 @@
      */
     public void reload() {
         // TODO(dtrainor): Should we try to rebuild the ContentView if it's frozen?
-        if (isOfflinePage()) {
+        if (OfflinePageUtils.isOfflinePage(this)) {
             // If current page is an offline page, reload it with custom behavior defined in extra
             // header respected.
             OfflinePageUtils.reload(this);
@@ -2661,30 +2660,6 @@
     }
 
     /**
-     * @return True if the offline page is opened.
-     */
-    public boolean isOfflinePage() {
-        return isFrozen() ? false : nativeIsOfflinePage(mNativeTabAndroid);
-    }
-
-    /**
-     * @return The offline page if tab currently displays it, null otherwise.
-     */
-    public OfflinePageItem getOfflinePage() {
-        return isFrozen() ? null : nativeGetOfflinePage(mNativeTabAndroid);
-    }
-
-    /**
-     * Shows the list of offline pages. This should only be hit when offline pages feature is
-     * enabled.
-     */
-    @CalledByNative
-    public void showOfflinePages() {
-        // TODO(jianli): This is not currently used. Figure out what to do here.
-        // http://crbug.com/636574
-    }
-
-    /**
      * @return Original url of the tab, which is the original url from DOMDistiller.
      */
     public String getOriginalUrl() {
@@ -3094,8 +3069,6 @@
             long nativeTabAndroid, int constraints, int current, boolean animate);
     private native void nativeLoadOriginalImage(long nativeTabAndroid);
     private native long nativeGetBookmarkId(long nativeTabAndroid, boolean onlyEditable);
-    private native boolean nativeIsOfflinePage(long nativeTabAndroid);
-    private native OfflinePageItem nativeGetOfflinePage(long nativeTabAndroid);
     private native void nativeSetInterceptNavigationDelegate(long nativeTabAndroid,
             InterceptNavigationDelegate delegate);
     private native void nativeAttachToTabContentManager(long nativeTabAndroid,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
index 0e9393df..c5bae347 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -41,6 +41,7 @@
 import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils;
 import org.chromium.chrome.browser.ntp.NativePageFactory;
 import org.chromium.chrome.browser.ntp.NewTabPage;
+import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.omnibox.LocationBar;
 import org.chromium.chrome.browser.omnibox.LocationBarLayout;
 import org.chromium.chrome.browser.omnibox.UrlBar;
@@ -467,7 +468,8 @@
         mSecurityIconType = securityLevel;
 
         boolean isSmallDevice = !DeviceFormFactor.isTablet(getContext());
-        boolean isOfflinePage = getCurrentTab() != null && getCurrentTab().isOfflinePage();
+        boolean isOfflinePage =
+                getCurrentTab() != null && OfflinePageUtils.isOfflinePage(getCurrentTab());
 
         int id = LocationBarLayout.getSecurityIconResource(
                 securityLevel, isSmallDevice, isOfflinePage);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModelImpl.java
index cfd52f1..4ab12eac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModelImpl.java
@@ -115,7 +115,7 @@
                 displayText =
                         DomDistillerTabUtils.getFormattedUrlFromOriginalDistillerUrl(originalUrl);
             }
-        } else if (mTab.isOfflinePage()
+        } else if (OfflinePageUtils.isOfflinePage(mTab)
                 && mTab.getSecurityLevel() == ConnectionSecurityLevel.NONE) {
             String originalUrl = mTab.getOriginalUrl();
             displayText = OfflinePageUtils.stripSchemeFromOnlineUrl(
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 1160091..e948a05 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2946,6 +2946,9 @@
       <message name="IDS_IPH_DOWNLOAD_PAGE_FOR_OFFLINE_USAGE_TEXT" desc="The in-product-help message after a successful navigation prompting user to download the pages to view offline.">
         Download pages to use them offline
       </message>
+      <message name="IDS_IPH_DOWNLOAD_HOME_TEXT" desc="The in-product-help message to open download home after a restart.">
+        Find your files and pages in Downloads
+      </message>
 
       <!-- Search Widget strings -->
       <message name="IDS_SEARCH_WIDGET_DEFAULT" desc="Default text for the search widget">
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
index efa5df22..0441146 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
@@ -26,8 +26,8 @@
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.PrerenderTestHelper;
 import org.chromium.content.browser.BindingManager;
-import org.chromium.content.browser.ChildProcessConnection;
 import org.chromium.content.browser.ChildProcessLauncher;
+import org.chromium.content.browser.ManagedChildProcessConnection;
 import org.chromium.content.browser.test.ChildProcessAllocatorSettings;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
@@ -99,7 +99,7 @@
         }
 
         @Override
-        public void addNewConnection(int pid, ChildProcessConnection connection) {
+        public void addNewConnection(int pid, ManagedChildProcessConnection connection) {
             synchronized (mVisibilityCallsMap) {
                 mVisibilityCallsMap.put(pid, "");
             }
@@ -132,12 +132,7 @@
         public void onBroughtToForeground() {}
 
         @Override
-        public boolean isOomProtected(int pid) {
-            return false;
-        }
-
-        @Override
-        public void clearConnection(int pid) {}
+        public void removeConnection(int pid) {}
 
         @Override
         public void startModerateBindingManagement(Context context, int maxSize) {}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
index 42ae2e9a..6283b75 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
@@ -169,7 +169,7 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                isOffline[0] = tab.isOfflinePage();
+                isOffline[0] = OfflinePageUtils.isOfflinePage(tab);
             }
         });
         return isOffline[0];
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserverTest.java
index 6eb6186..a901d25 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserverTest.java
@@ -62,6 +62,7 @@
         doReturn(false).when(observer).isShowingOfflinePreview(any(Tab.class));
         // TODO(fgorski): This call has to be mocked out until we update OfflinePageUtils.
         doNothing().when(observer).showReloadSnackbar(any(Tab.class));
+        doReturn(true).when(observer).isOfflinePage(any(Tab.class));
         // Assert tab model observer was created.
         assertTrue(observer.getTabModelObserver() != null);
         return observer;
@@ -76,7 +77,6 @@
         doReturn(TAB_ID).when(mTab).getId();
         doReturn(false).when(mTab).isFrozen();
         doReturn(false).when(mTab).isHidden();
-        doReturn(true).when(mTab).isOfflinePage();
         doReturn(mActivity).when(mTab).getActivity();
 
         // Setting up mock snackbar manager.
@@ -130,14 +130,14 @@
     public void testStartObservingTab() {
         OfflinePageTabObserver observer = createObserver();
 
-        doReturn(false).when(mTab).isOfflinePage();
+        doReturn(false).when(observer).isOfflinePage(any(Tab.class));
         observer.startObservingTab(mTab);
 
         assertFalse(observer.isObservingNetworkChanges());
         assertFalse(observer.isObservingTab(mTab));
         verify(observer, times(0)).showReloadSnackbar(any(Tab.class));
 
-        doReturn(true).when(mTab).isOfflinePage();
+        doReturn(true).when(observer).isOfflinePage(any(Tab.class));
         observer.startObservingTab(mTab);
 
         assertTrue(observer.isObservingNetworkChanges());
@@ -400,7 +400,7 @@
         verify(mSnackbarManager, times(1)).dismissSnackbars(eq(mSnackbarController));
 
         // URL updated and tab no longer shows offline page.
-        doReturn(false).when(mTab).isOfflinePage();
+        doReturn(false).when(observer).isOfflinePage(any(Tab.class));
         observer.onUrlUpdated(mTab);
 
         assertFalse(observer.isObservingTab(mTab));
@@ -432,7 +432,7 @@
         observer.onPageLoadFinished(mTab);
 
         // URL updated and tab no longer shows offline page.
-        doReturn(false).when(mTab).isOfflinePage();
+        doReturn(false).when(observer).isOfflinePage(any(Tab.class));
         observer.onUrlUpdated(mTab);
 
         assertFalse(observer.isObservingTab(mTab));
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 8658bd43..9403dab 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6854,12 +6854,6 @@
         <message name="IDS_PRINT_PREVIEW_OFFLINE" desc="Shown when printer is currently offline.">
           Currently offline
         </message>
-        <message name="IDS_PRINT_PREVIEW_FEDEX_TOS" desc="Terms-of-service for using the FedEx Office printer.">
-          By printing to FedEx Office, you accept their <ph name="START_LINK">$1</ph>terms of use<ph name="END_LINK">$2</ph>.
-        </message>
-        <message name="IDS_PRINT_PREVIEW_TOS_CHECKBOX_LABEL" desc="Label of the terms-of-service agreement checkbox.">
-          I agree
-        </message>
         <message name="IDS_PRINT_PREVIEW_NO_DESTS_PROMO_TITLE" desc="Title of promotion shown when user has no Google Cloud Print printers.">
           Add Printers
         </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ae72f49f..2d402198 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -10,7 +10,7 @@
 import("//chrome/common/features.gni")
 import("//components/os_crypt/features.gni")
 import("//components/spellcheck/spellcheck_build_features.gni")
-import("//device/vr/features.gni")
+import("//device/vr/features/features.gni")
 import("//extensions/features/features.gni")
 import("//media/media_options.gni")
 import("//net/features.gni")
@@ -1608,7 +1608,7 @@
     "//device/power_save_blocker",
     "//device/usb/mojo",
     "//device/usb/public/interfaces",
-    "//device/vr:features",
+    "//device/vr/features",
     "//extensions/features",
     "//gin:gin_features",
     "//google_apis",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index ca33be37..cb576af5 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -22,7 +22,7 @@
   "+device/media_transfer_protocol",
   "+device/power_save_blocker",
   "+device/usb",
-  "+device/vr/features.h",
+  "+device/vr/features/features.h",
   "+extensions/browser",
   "+extensions/common",
   "+extensions/components/javascript_dialog_extensions_client",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index f0f52cf1..8b843be 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -76,7 +76,7 @@
 #include "content/public/common/feature_h264_with_openh264_ffmpeg.h"
 #include "content/public/common/features.h"
 #include "device/base/features.h"
-#include "device/vr/features.h"
+#include "device/vr/features/features.h"
 #include "extensions/features/features.h"
 #include "gin/public/gin_features.h"
 #include "gpu/config/gpu_switches.h"
@@ -706,6 +706,21 @@
 };
 #endif  // !defined(OS_ANDROID)
 
+const FeatureEntry::Choice kAutoplayPolicyChoices[] = {
+    {flags_ui::kGenericExperimentChoiceDefault, "", ""},
+    {flag_descriptions::kAutoplayPolicyNoUserGestureRequired,
+     switches::kAutoplayPolicy,
+     switches::autoplay::kNoUserGestureRequiredPolicy},
+#if defined(OS_ANDROID)
+    {flag_descriptions::kAutoplayPolicyUserGestureRequired,
+     switches::kAutoplayPolicy, switches::autoplay::kUserGestureRequiredPolicy},
+#else
+    {flag_descriptions::kAutoplayPolicyCrossOriginUserGestureRequired,
+     switches::kAutoplayPolicy,
+     switches::autoplay::kCrossOriginUserGestureRequiredPolicy},
+#endif
+};
+
 const FeatureEntry::FeatureParam kNoStatePrefetchEnabled[] = {
     {prerender::kNoStatePrefetchFeatureModeParameterName,
      prerender::kNoStatePrefetchFeatureModeParameterPrefetch}};
@@ -1437,15 +1452,6 @@
      flag_descriptions::kGestureRequirementForMediaPlaybackDescription, kOsAll,
      SINGLE_DISABLE_VALUE_TYPE(
          switches::kDisableGestureRequirementForMediaPlayback)},
-#if !defined(OS_ANDROID)
-    {"cross-origin-media-playback-requires-user-gesture",
-     flag_descriptions::kCrossOriginMediaPlaybackRequiresUserGestureName,
-     flag_descriptions::kCrossOriginMediaPlaybackRequiresUserGestureDescription,
-     kOsDesktop,
-     FEATURE_VALUE_TYPE(
-         features::kCrossOriginMediaPlaybackRequiresUserGesture)},
-#endif  // !defined(OS_ANDROID)
-
 #if defined(OS_CHROMEOS)
     {"enable-virtual-keyboard", flag_descriptions::kVirtualKeyboardName,
      flag_descriptions::kVirtualKeyboardDescription, kOsCrOS,
@@ -2763,6 +2769,10 @@
      FEATURE_VALUE_TYPE(omnibox::kEnableClipboardProvider)},
 #endif
 
+    {"autoplay-policy", flag_descriptions::kAutoplayPolicyName,
+     flag_descriptions::kAutoplayPolicyDescription, kOsAll,
+     MULTI_VALUE_TYPE(kAutoplayPolicyChoices)},
+
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms.xml. See note in
     // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test.
diff --git a/chrome/browser/android/DEPS b/chrome/browser/android/DEPS
index 9da1a1a6..bb55a8c 100644
--- a/chrome/browser/android/DEPS
+++ b/chrome/browser/android/DEPS
@@ -9,7 +9,7 @@
   "+components/sync/test/fake_server/android",
   "+components/toolbar",
   "+components/web_contents_delegate_android",
-  "+device/vr/features.h",
+  "+device/vr/features/features.h",
   "+sandbox/linux/seccomp-bpf/sandbox_bpf.h",
   "+sandbox/sandbox_features.h",
   "+third_party/gvr-android-sdk",
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index 9017b02..cdd7a13 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -190,7 +190,7 @@
 #include "components/url_formatter/android/component_jni_registrar.h"
 #include "components/variations/android/component_jni_registrar.h"
 #include "components/web_contents_delegate_android/component_jni_registrar.h"
-#include "device/vr/features.h"
+#include "device/vr/features/features.h"
 #include "printing/features/features.h"
 
 #if BUILDFLAG(ENABLE_PRINTING) && !BUILDFLAG(ENABLE_PRINT_PREVIEW)
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.cc b/chrome/browser/android/offline_pages/offline_page_bridge.cc
index 9819635..6f1dbe7 100644
--- a/chrome/browser/android/offline_pages/offline_page_bridge.cc
+++ b/chrome/browser/android/offline_pages/offline_page_bridge.cc
@@ -631,6 +631,30 @@
       static_cast<OfflinePageUtils::DownloadUIActionFlags>(ui_action));
 }
 
+jboolean OfflinePageBridge::IsOfflinePage(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jobject>& j_web_contents) {
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(j_web_contents);
+  return offline_pages::OfflinePageUtils::GetOfflinePageFromWebContents(
+             web_contents) != nullptr;
+}
+
+ScopedJavaLocalRef<jobject> OfflinePageBridge::GetOfflinePage(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jobject>& j_web_contents) {
+  const offline_pages::OfflinePageItem* offline_page =
+      offline_pages::OfflinePageUtils::GetOfflinePageFromWebContents(
+          content::WebContents::FromJavaWebContents(j_web_contents));
+  if (!offline_page)
+    return ScopedJavaLocalRef<jobject>();
+
+  return offline_pages::android::OfflinePageBridge::ConvertToJavaOfflinePage(
+      env, *offline_page);
+}
+
 void OfflinePageBridge::NotifyIfDoneLoading() const {
   if (!offline_page_model_->is_loaded())
     return;
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.h b/chrome/browser/android/offline_pages/offline_page_bridge.h
index c6a8b62..db898dfb 100644
--- a/chrome/browser/android/offline_pages/offline_page_bridge.h
+++ b/chrome/browser/android/offline_pages/offline_page_bridge.h
@@ -144,6 +144,16 @@
 
   base::android::ScopedJavaGlobalRef<jobject> java_ref() { return java_ref_; }
 
+  jboolean IsOfflinePage(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& j_web_contents);
+
+  base::android::ScopedJavaLocalRef<jobject> GetOfflinePage(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& j_web_contents);
+
  private:
   void NotifyIfDoneLoading() const;
 
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index e41f85a..0d71514 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -15,9 +15,6 @@
 #include "cc/layers/layer.h"
 #include "chrome/browser/android/compositor/tab_content_manager.h"
 #include "chrome/browser/android/metrics/uma_utils.h"
-#include "chrome/browser/android/offline_pages/offline_page_bridge.h"
-#include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
-#include "chrome/browser/android/offline_pages/offline_page_utils.h"
 #include "chrome/browser/android/tab_web_contents_delegate_android.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
@@ -57,9 +54,6 @@
 #include "components/favicon/content/content_favicon_driver.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "components/navigation_interception/navigation_params.h"
-#include "components/offline_pages/core/offline_page_feature.h"
-#include "components/offline_pages/core/offline_page_item.h"
-#include "components/offline_pages/core/offline_page_model.h"
 #include "components/sessions/content/content_live_tab.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/url_formatter/url_fixer.h"
@@ -695,30 +689,6 @@
   return -1;
 }
 
-void TabAndroid::ShowOfflinePages() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_Tab_showOfflinePages(env, weak_java_tab_.get(env));
-}
-
-jboolean TabAndroid::IsOfflinePage(JNIEnv* env,
-                                   const JavaParamRef<jobject>& obj) {
-  return offline_pages::OfflinePageUtils::GetOfflinePageFromWebContents(
-      web_contents()) != nullptr;
-}
-
-ScopedJavaLocalRef<jobject> TabAndroid::GetOfflinePage(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) {
-  const offline_pages::OfflinePageItem* offline_page =
-      offline_pages::OfflinePageUtils::GetOfflinePageFromWebContents(
-          web_contents());
-  if (!offline_page)
-    return ScopedJavaLocalRef<jobject>();
-
-  return offline_pages::android::OfflinePageBridge::ConvertToJavaOfflinePage(
-      env, *offline_page);
-}
-
 bool TabAndroid::HasPrerenderedUrl(JNIEnv* env,
                                    const JavaParamRef<jobject>& obj,
                                    const JavaParamRef<jstring>& url) {
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index dea06a6..1e3e4a8a 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -117,8 +117,6 @@
 
   bool HasPrerenderedUrl(GURL gurl);
 
-  void ShowOfflinePages();
-
   // Overridden from CoreTabHelperDelegate:
   void SwapTabContents(content::WebContents* old_contents,
                        content::WebContents* new_contents,
@@ -212,16 +210,6 @@
                       const base::android::JavaParamRef<jobject>& obj,
                       jboolean only_editable);
 
-  jboolean HasOfflineCopy(JNIEnv* env,
-                          const base::android::JavaParamRef<jobject>& obj);
-
-  jboolean IsOfflinePage(JNIEnv* env,
-                         const base::android::JavaParamRef<jobject>& obj);
-
-  base::android::ScopedJavaLocalRef<jobject> GetOfflinePage(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
-
   void SetInterceptNavigationDelegate(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index 5c9e0f87..dc166f9 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//build/config/android/rules.gni")
 import("//chrome/common/features.gni")
-import("//device/vr/features.gni")
+import("//device/vr/features/features.gni")
 import("//testing/test.gni")
 
 assert(enable_vr)
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index 4bfebfb2..431f3a5 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/test_timeouts.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -56,6 +57,51 @@
 using guest_view::TestGuestViewManager;
 using guest_view::TestGuestViewManagerFactory;
 
+#if defined(OS_MACOSX)
+// The original TextInputClientMessageFilter is added during the initialization
+// phase of RenderProcessHost. The only chance we have to add the test filter
+// (so that it can receive the TextInputClientMac incoming IPC messages) is
+// during the call to RenderProcessWillLaunch() on ContentBrowserClient. This
+// class provides that for testing. The class also replaces the current client
+// and will reset the client back to the original one upon destruction.
+class BrowserClientForTextInputClientMac : public ChromeContentBrowserClient {
+ public:
+  BrowserClientForTextInputClientMac()
+      : old_client_(content::SetBrowserClientForTesting(this)) {}
+  ~BrowserClientForTextInputClientMac() override {
+    content::SetBrowserClientForTesting(old_client_);
+  }
+
+  // ContentBrowserClient overrides.
+  void RenderProcessWillLaunch(
+      content::RenderProcessHost* process_host) override {
+    ChromeContentBrowserClient::RenderProcessWillLaunch(process_host);
+    filters_.push_back(
+        new content::TestTextInputClientMessageFilter(process_host));
+  }
+
+  // Retrieves the registered filter for the given RenderProcessHost. It will
+  // return false if the RenderProcessHost was initialized while a different
+  // instance of ContentBrowserClient was in action.
+  scoped_refptr<content::TestTextInputClientMessageFilter>
+  GetTextInputClientMessageFilterForProcess(
+      content::RenderProcessHost* process_host) const {
+    for (auto filter : filters_) {
+      if (filter->process() == process_host)
+        return filter;
+    }
+    return nullptr;
+  }
+
+ private:
+  content::ContentBrowserClient* old_client_;
+  std::vector<scoped_refptr<content::TestTextInputClientMessageFilter>>
+      filters_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserClientForTextInputClientMac);
+};
+#endif  // OS_MACOSX
+
 class WebViewInteractiveTestBase : public extensions::PlatformAppBrowserTest {
  public:
   WebViewInteractiveTestBase()
@@ -1363,6 +1409,37 @@
   ASSERT_TRUE(selected_text.size() >= 10u);
   ASSERT_EQ("AAAAAAAAAA", selected_text.substr(0, 10));
 }
+
+// Verifies that asking for a word lookup from a guest will lead to a returned
+// IPC from the renderer containing the right selected word.
+IN_PROC_BROWSER_TEST_P(WebViewInteractiveTest, WordLookup) {
+  // BrowserClientForTextInputClientMac needs to replace the
+  // ChromeContentBrowserClient after most things are initialized but before the
+  // WebContents is created.
+  BrowserClientForTextInputClientMac browser_client;
+
+  SetupTest("web_view/text_selection",
+            "/extensions/platform_apps/web_view/text_selection/guest.html");
+  ASSERT_TRUE(guest_web_contents());
+  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(GetPlatformAppWindow()));
+
+  auto guest_message_filter =
+      browser_client.GetTextInputClientMessageFilterForProcess(
+          guest_web_contents()->GetRenderProcessHost());
+  ASSERT_TRUE(guest_message_filter);
+
+  // Lookup some string through context menu.
+  ContextMenuNotificationObserver menu_observer(IDC_CONTENT_CONTEXT_LOOK_UP);
+  // Simulating a mouse click at a position to highlight text in guest and
+  // showing the context menu.
+  SimulateRWHMouseClick(guest_web_contents()->GetRenderViewHost()->GetWidget(),
+                        blink::WebMouseEvent::Button::kRight, 20, 20);
+  // Wait for the response form the guest renderer.
+  guest_message_filter->WaitForStringFromRange();
+
+  // Sanity check.
+  ASSERT_EQ("AAAA", guest_message_filter->string_from_range().substr(0, 4));
+}
 #endif
 
 IN_PROC_BROWSER_TEST_P(WebViewFocusInteractiveTest, FocusAndVisibility) {
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 20b9634..499d61d 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -434,8 +434,6 @@
                  file="resources\print_preview\images\pdf.png" type="BINDATA" />
         <include name="IDR_PRINT_PREVIEW_IMAGES_THIRD_PARTY"
                  file="resources\print_preview\images\third_party.png" type="BINDATA" />
-        <include name="IDR_PRINT_PREVIEW_IMAGES_THIRD_PARTY_FEDEX"
-                 file="resources\print_preview\images\third_party_fedex.png" type="BINDATA" />
         <include name="IDR_PRINT_PREVIEW_IMAGES_MOBILE"
                  file="resources\print_preview\images\mobile.png" type="BINDATA" />
         <include name="IDR_PRINT_PREVIEW_IMAGES_MOBILE_SHARED"
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 04b8048..7fdd922 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -660,17 +660,6 @@
     "User gesture requirement for playing media elements. Disabling this "
     "will allow autoplay to work.";
 
-#if !defined(OS_ANDROID)
-
-const char kCrossOriginMediaPlaybackRequiresUserGestureName[] =
-    "Media playback in cross-origin iframes requires user gesture";
-
-const char kCrossOriginMediaPlaybackRequiresUserGestureDescription[] =
-    "Playing media elements in cross-origin iframes requires user gesture. "
-    "Disabling this will allow autoplay in cross-origin iframes to work.";
-
-#endif  // !defined(OS_ANDROID)
-
 const char kPassiveDocumentEventListenersDescription[] =
     "Forces touchstart, and touchmove event listeners on document level "
     "targets (which haven't requested otherwise) to be treated as passive.";
@@ -3053,4 +3042,17 @@
 
 #endif  // defined(OS_ANDROID)
 
+const char kAutoplayPolicyName[] = "Autoplay policy";
+
+const char kAutoplayPolicyDescription[] =
+    "Policy used when deciding if audio or video is allowed to autoplay.";
+
+const char kAutoplayPolicyNoUserGestureRequired[] =
+    "No user gesture is required.";
+
+const char kAutoplayPolicyUserGestureRequired[] = "User gesture is required.";
+
+const char kAutoplayPolicyCrossOriginUserGestureRequired[] =
+    "User gesture is required for cross-origin iframes.";
+
 }  // namespace flag_descriptions
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index cc6bedd..2eeb915 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -721,16 +721,6 @@
 // Description for the flag for gesture requiment for media playback
 extern const char kGestureRequirementForMediaPlaybackDescription[];
 
-#if !defined(OS_ANDROID)
-
-// Title for the flag for gesture requiment for media playback
-extern const char kCrossOriginMediaPlaybackRequiresUserGestureName[];
-
-// Description for the flag for gesture requiment for media playback
-extern const char kCrossOriginMediaPlaybackRequiresUserGestureDescription[];
-
-#endif  // !defined(OS_ANDROID)
-
 // Description for the flag to adjust the default behaviour for document level
 // passive touch listeners.
 extern const char kPassiveDocumentEventListenersDescription[];
@@ -3320,6 +3310,22 @@
 
 #endif  // defined(OS_ANDROID)
 
+// Name of the autoplay policy flag.
+extern const char kAutoplayPolicyName[];
+
+// Description of the autoplay policy entry.
+extern const char kAutoplayPolicyDescription[];
+
+// Description of the autoplay policy that requires a user gesture on cross
+// origin iframes.
+extern const char kAutoplayPolicyCrossOriginUserGestureRequired[];
+
+// Description of the autoplay policy that has no user gesture requirements.
+extern const char kAutoplayPolicyNoUserGestureRequired[];
+
+// Description of the autoplay policy that requires a user gesture.
+extern const char kAutoplayPolicyUserGestureRequired[];
+
 }  // namespace flag_descriptions
 
 #endif  // CHROME_BROWSER_FLAG_DESCRIPTIONS_H_
diff --git a/chrome/browser/memory_details_mac.cc b/chrome/browser/memory_details_mac.cc
index 36b77ff..2a7292f8 100644
--- a/chrome/browser/memory_details_mac.cc
+++ b/chrome/browser/memory_details_mac.cc
@@ -62,7 +62,7 @@
       base::ProcessMetrics::CreateProcessMetrics(
           pid, content::BrowserChildProcessHost::GetPortProvider());
   metrics->GetCommittedAndWorkingSetKBytes(&info.committed, &info.working_set);
-  info.phys_footprint = metrics->GetPhysicalFootprint();
+  info.phys_footprint = metrics->GetTaskVMInfo().phys_footprint;
 
   processes->push_back(info);
 }
diff --git a/chrome/browser/resources/md_bookmarks/shared_style.html b/chrome/browser/resources/md_bookmarks/shared_style.html
index f242649..c8d75709 100644
--- a/chrome/browser/resources/md_bookmarks/shared_style.html
+++ b/chrome/browser/resources/md_bookmarks/shared_style.html
@@ -1,12 +1,9 @@
 <link rel="import" href="chrome://bookmarks/shared_vars.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 
 <dom-module id="shared-style">
   <template>
-    <style>
-      [hidden] {
-        display: none !important;
-      }
-
+    <style include="cr-hidden-style">
       button.more-vert-button {
         height: 36px;
         padding: 8px;
diff --git a/chrome/browser/resources/md_downloads/item.html b/chrome/browser/resources/md_downloads/item.html
index f057735..c4ed060 100644
--- a/chrome/browser/resources/md_downloads/item.html
+++ b/chrome/browser/resources/md_downloads/item.html
@@ -1,4 +1,5 @@
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/action_link_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
@@ -16,16 +17,12 @@
 
 <dom-module id="downloads-item">
   <template>
-    <style include="action-link">
+    <style include="action-link cr-hidden-style">
       :host {
         display: flex;
         flex-direction: column;
       }
 
-      [hidden] {
-        display: none !important;
-      }
-
       paper-button {
         font-weight: 500;
         margin: 0;
diff --git a/chrome/browser/resources/md_downloads/manager.html b/chrome/browser/resources/md_downloads/manager.html
index 34aa9e9..736d9d1 100644
--- a/chrome/browser/resources/md_downloads/manager.html
+++ b/chrome/browser/resources/md_downloads/manager.html
@@ -1,3 +1,4 @@
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/cr/ui.html">
 <link rel="import" href="chrome://resources/html/cr/ui/command.html">
@@ -13,7 +14,7 @@
 
 <dom-module id="downloads-manager">
   <template>
-    <style>
+    <style include="cr-hidden-style">
       :host {
         display: flex;
         flex: 1 0;
@@ -22,10 +23,6 @@
         z-index: 0;
       }
 
-      [hidden] {
-        display: none !important;
-      }
-
       @media screen and (max-width: 1024px) {
         :host {
           flex-basis: calc(
diff --git a/chrome/browser/resources/md_downloads/toolbar.html b/chrome/browser/resources/md_downloads/toolbar.html
index bda557e..2b6bca652 100644
--- a/chrome/browser/resources/md_downloads/toolbar.html
+++ b/chrome/browser/resources/md_downloads/toolbar.html
@@ -1,4 +1,5 @@
 <link rel="import" href="chrome://downloads/action_service.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
@@ -12,7 +13,7 @@
 
 <dom-module id="downloads-toolbar">
   <template>
-    <style>
+    <style include="cr-hidden-style">
       :host {
         align-items: center;
         background: var(--google-blue-700);
@@ -21,10 +22,6 @@
         min-height: 56px;
       }
 
-      [hidden] {
-        display: none !important;
-      }
-
       #toolbar {
         --cr-toolbar-field-width: var(--downloads-card-width);
         flex: 1;
diff --git a/chrome/browser/resources/md_history/shared_style.html b/chrome/browser/resources/md_history/shared_style.html
index f561acc..d428ea27 100644
--- a/chrome/browser/resources/md_history/shared_style.html
+++ b/chrome/browser/resources/md_history/shared_style.html
@@ -1,12 +1,9 @@
 <link rel="import" href="chrome://history/shared_vars.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 
 <dom-module id="shared-style">
   <template>
-    <style>
-      [hidden] {
-        display: none !important;
-      }
-
+    <style include="cr-hidden-style">
       a {
         color: var(--link-color);
       }
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface.js b/chrome/browser/resources/print_preview/cloud_print_interface.js
index abf3b96..e6c36722 100644
--- a/chrome/browser/resources/print_preview/cloud_print_interface.js
+++ b/chrome/browser/resources/print_preview/cloud_print_interface.js
@@ -105,8 +105,6 @@
     SEARCH_FAILED: 'cloudprint.CloudPrintInterface.SEARCH_FAILED',
     SUBMIT_DONE: 'cloudprint.CloudPrintInterface.SUBMIT_DONE',
     SUBMIT_FAILED: 'cloudprint.CloudPrintInterface.SUBMIT_FAILED',
-    UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED:
-        'cloudprint.CloudPrintInterface.UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED'
   };
 
   /**
@@ -339,27 +337,6 @@
     },
 
     /**
-     * Sends a Google Cloud Print update API request to accept (or reject) the
-     * terms-of-service of the given printer.
-     * @param {!print_preview.Destination} destination Destination to accept ToS
-     *     for.
-     * @param {boolean} isAccepted Whether the user accepted ToS or not.
-     */
-    updatePrinterTosAcceptance: function(destination, isAccepted) {
-      var params = [
-        new HttpParam('printerid', destination.id),
-        new HttpParam('is_tos_accepted', isAccepted)
-      ];
-      this.sendOrQueueRequest_(this.buildRequest_(
-          'POST',
-          'update',
-          params,
-          destination.origin,
-          destination.account,
-          this.onUpdatePrinterTosAcceptanceDone_.bind(this)));
-    },
-
-    /**
      * Adds event listeners to relevant events.
      * @private
      */
@@ -759,21 +736,6 @@
         this.dispatchEvent(errorEvent);
       }
     },
-
-    /**
-     * Called when the update printer TOS acceptance request completes.
-     * @param {!CloudPrintRequest} request Request that has been completed.
-     * @private
-     */
-    onUpdatePrinterTosAcceptanceDone_: function(request) {
-      if (request.xhr.status == 200 && request.result['success']) {
-        // Do nothing.
-      } else {
-        var errorEvent = this.createErrorEvent_(
-            CloudPrintInterface.EventType.SUBMIT_FAILED, request);
-        this.dispatchEvent(errorEvent);
-      }
-    }
   };
 
   /**
diff --git a/chrome/browser/resources/print_preview/data/cloud_parsers.js b/chrome/browser/resources/print_preview/data/cloud_parsers.js
index d4d032e4..dc6236a 100644
--- a/chrome/browser/resources/print_preview/data/cloud_parsers.js
+++ b/chrome/browser/resources/print_preview/data/cloud_parsers.js
@@ -19,7 +19,6 @@
     DESCRIPTION: 'description',
     DISPLAY_NAME: 'displayName',
     ID: 'id',
-    IS_TOS_ACCEPTED: 'isTosAccepted',
     LAST_ACCESS: 'accessTime',
     TAGS: 'tags',
     TYPE: 'type'
@@ -80,8 +79,6 @@
       isOwned: arrayContains(tags, CloudDestinationParser.OWNED_TAG_),
       lastAccessTime: parseInt(
           json[CloudDestinationParser.Field_.LAST_ACCESS], 10) || Date.now(),
-      isTosAccepted: (id == print_preview.Destination.GooglePromotedId.FEDEX) ?
-          json[CloudDestinationParser.Field_.IS_TOS_ACCEPTED] : null,
       cloudID: id,
       description: json[CloudDestinationParser.Field_.DESCRIPTION]
     };
diff --git a/chrome/browser/resources/print_preview/data/destination.js b/chrome/browser/resources/print_preview/data/destination.js
index 8eaa24f..5584e70 100644
--- a/chrome/browser/resources/print_preview/data/destination.js
+++ b/chrome/browser/resources/print_preview/data/destination.js
@@ -53,7 +53,6 @@
    *          isEnterprisePrinter: (boolean|undefined),
    *          account: (string|undefined),
    *          lastAccessTime: (number|undefined),
-   *          isTosAccepted: (boolean|undefined),
    *          cloudID: (string|undefined),
    *          provisionalType:
    *              (print_preview.Destination.ProvisionalType|undefined),
@@ -153,14 +152,6 @@
                            Date.now();
 
     /**
-     * Whether the user has accepted the terms-of-service for the print
-     * destination. Only applies to the FedEx Office cloud-based printer.
-     * {@code null} if terms-of-service does not apply to the print destination.
-     * @private {?boolean}
-     */
-    this.isTosAccepted_ = !!(opt_params && opt_params.isTosAccepted);
-
-    /**
      * Cloud ID for Privet printers.
      * @private {string}
      */
@@ -212,7 +203,6 @@
    */
   Destination.GooglePromotedId = {
     DOCS: '__google__docs',
-    FEDEX: '__google__fedex',
     SAVE_AS_PDF: 'Save as PDF'
   };
 
@@ -282,7 +272,6 @@
     THIRD_PARTY: 'images/third_party.png',
     PDF: 'images/pdf.png',
     DOCS: 'images/google_doc.png',
-    FEDEX: 'images/third_party_fedex.png',
     ENTERPRISE: 'images/business.svg'
   };
 
@@ -391,8 +380,7 @@
      *     destination.
      */
     get hint() {
-      if (this.id_ == Destination.GooglePromotedId.DOCS ||
-          this.id_ == Destination.GooglePromotedId.FEDEX) {
+      if (this.id_ == Destination.GooglePromotedId.DOCS) {
         return this.account_;
       }
       return this.location || this.extensionName || this.description;
@@ -492,9 +480,6 @@
       if (this.id_ == Destination.GooglePromotedId.DOCS) {
         return Destination.IconUrl_.DOCS;
       }
-      if (this.id_ == Destination.GooglePromotedId.FEDEX) {
-        return Destination.IconUrl_.FEDEX;
-      }
       if (this.id_ == Destination.GooglePromotedId.SAVE_AS_PDF) {
         return Destination.IconUrl_.PDF;
       }
@@ -517,24 +502,6 @@
     },
 
     /**
-     * @return {?boolean} Whether the user has accepted the terms-of-service of
-     *     the print destination or {@code null} if a terms-of-service does not
-     *     apply.
-     */
-    get isTosAccepted() {
-      return this.isTosAccepted_;
-    },
-
-    /**
-     * @param {?boolean} isTosAccepted Whether the user has accepted the
-     *     terms-of-service of the print destination or {@code null} if
-     *     a terms-of-service does not apply.
-     */
-    set isTosAccepted(isTosAccepted) {
-      this.isTosAccepted_ = isTosAccepted;
-    },
-
-    /**
      * @return {!Array<string>} Properties (besides display name) to match
      *     search queries against.
      */
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index 34021ccf..e26fd39 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -89,8 +89,7 @@
             destination.id);
       }
       return arrayContains(
-          [print_preview.Destination.GooglePromotedId.DOCS,
-           print_preview.Destination.GooglePromotedId.FEDEX],
+          [print_preview.Destination.GooglePromotedId.DOCS],
           destination.id);
     }
   };
@@ -1025,8 +1024,7 @@
       // TODO: Move the logic to print_preview.
       return this.destinations_.every(function(dest) {
         return dest.isLocal ||
-            dest.id == print_preview.Destination.GooglePromotedId.DOCS ||
-            dest.id == print_preview.Destination.GooglePromotedId.FEDEX;
+            dest.id == print_preview.Destination.GooglePromotedId.DOCS;
       });
     },
 
@@ -1056,13 +1054,6 @@
       // Update and persist selected destination.
       this.selectedDestination_ = destination;
       this.selectedDestination_.isRecent = true;
-      if (destination.id == print_preview.Destination.GooglePromotedId.FEDEX &&
-          !destination.isTosAccepted) {
-        assert(this.cloudPrintInterface_ != null,
-               'Selected FedEx destination, but GCP API is not available');
-        destination.isTosAccepted = true;
-        this.cloudPrintInterface_.updatePrinterTosAcceptance(destination, true);
-      }
       this.appState_.persistSelectedDestination(this.selectedDestination_);
       // Adjust metrics.
       if (destination.cloudID &&
diff --git a/chrome/browser/resources/print_preview/data/ticket_items/color.js b/chrome/browser/resources/print_preview/data/ticket_items/color.js
index b1cbe38..2740501 100644
--- a/chrome/browser/resources/print_preview/data/ticket_items/color.js
+++ b/chrome/browser/resources/print_preview/data/ticket_items/color.js
@@ -105,7 +105,6 @@
       var dest = this.getSelectedDestInternal();
       if (dest) {
         if (dest.id == print_preview.Destination.GooglePromotedId.DOCS ||
-            dest.id == print_preview.Destination.GooglePromotedId.FEDEX ||
             dest.type == print_preview.Destination.Type.MOBILE) {
           return true;
         }
diff --git a/chrome/browser/resources/print_preview/data/ticket_items/vendor_items.js b/chrome/browser/resources/print_preview/data/ticket_items/vendor_items.js
index f48948c6..af17ce0 100644
--- a/chrome/browser/resources/print_preview/data/ticket_items/vendor_items.js
+++ b/chrome/browser/resources/print_preview/data/ticket_items/vendor_items.js
@@ -66,8 +66,7 @@
           this.destinationStore_.selectedDestination : null;
       if (!destination)
         return null;
-      if (destination.id == print_preview.Destination.GooglePromotedId.FEDEX ||
-          destination.type == print_preview.Destination.Type.MOBILE) {
+      if (destination.type == print_preview.Destination.Type.MOBILE) {
         return null;
       }
       return (destination.capabilities &&
diff --git a/chrome/browser/resources/print_preview/images/third_party_fedex.png b/chrome/browser/resources/print_preview/images/third_party_fedex.png
deleted file mode 100644
index 03d7da8..0000000
--- a/chrome/browser/resources/print_preview/images/third_party_fedex.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/print_preview/print_preview.html b/chrome/browser/resources/print_preview/print_preview.html
index e32bbe93..30b3964 100644
--- a/chrome/browser/resources/print_preview/print_preview.html
+++ b/chrome/browser/resources/print_preview/print_preview.html
@@ -36,7 +36,6 @@
   <link rel="stylesheet" href="search/destination_list.css">
   <link rel="stylesheet" href="search/destination_list_item.css">
   <link rel="stylesheet" href="search/destination_search.css">
-  <link rel="stylesheet" href="search/fedex_tos.css">
   <link rel="stylesheet" href="search/provisional_destination_resolver.css">
 
   <script src="chrome://resources/js/action_link.js"></script>
@@ -108,7 +107,6 @@
   <include src="previewarea/margin_control.html">
   <include src="search/destination_list.html">
   <include src="search/destination_list_item.html">
-  <include src="search/fedex_tos.html">
   <include src="search/provisional_destination_resolver.html">
 
   <script src="chrome://resources/js/i18n_template.js"></script>
diff --git a/chrome/browser/resources/print_preview/print_preview.js b/chrome/browser/resources/print_preview/print_preview.js
index a4b96fc..64526ba 100644
--- a/chrome/browser/resources/print_preview/print_preview.js
+++ b/chrome/browser/resources/print_preview/print_preview.js
@@ -716,11 +716,6 @@
           this.cloudPrintInterface_,
           cloudprint.CloudPrintInterface.EventType.PRINTER_FAILED,
           this.onCloudPrintError_.bind(this));
-      this.tracker.add(
-          this.cloudPrintInterface_,
-          cloudprint.CloudPrintInterface.EventType.
-              UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED,
-          this.onCloudPrintError_.bind(this));
 
       this.destinationStore_.setCloudPrintInterface(this.cloudPrintInterface_);
       this.invitationStore_.setCloudPrintInterface(this.cloudPrintInterface_);
@@ -783,12 +778,6 @@
       assert(this.uiState_ == PrintPreview.UiState_.PRINTING,
              'Submited job to Google Cloud Print but not in printing state ' +
                  this.uiState_);
-      if (this.destinationStore_.selectedDestination.id ==
-              print_preview.Destination.GooglePromotedId.FEDEX) {
-        this.nativeLayer_.startForceOpenNewTab(
-            'https://www.google.com/cloudprint/fedexcode.html?jobid=' +
-            event.jobId);
-      }
       this.close_();
     },
 
@@ -799,12 +788,13 @@
      * @private
      */
     onCloudPrintError_: function(event) {
+      if (event.status == 0) {
+        return; // Ignore, the system does not have internet connectivity.
+      }
       if (event.status == 403) {
         if (!this.isInAppKioskMode_) {
           this.destinationSearch_.showCloudPrintPromo();
         }
-      } else if (event.status == 0) {
-        return; // Ignore, the system does not have internet connectivity.
       } else {
         this.printHeader_.setErrorMessage(event.message);
       }
@@ -1364,7 +1354,6 @@
 // <include src="search/recent_destination_list.js">
 // <include src="search/destination_list_item.js">
 // <include src="search/destination_search.js">
-// <include src="search/fedex_tos.js">
 // <include src="search/provisional_destination_resolver.js">
 
 window.addEventListener('DOMContentLoaded', function() {
diff --git a/chrome/browser/resources/print_preview/search/cloud_destination_list.js b/chrome/browser/resources/print_preview/search/cloud_destination_list.js
index e8c6710..71e5bc3 100644
--- a/chrome/browser/resources/print_preview/search/cloud_destination_list.js
+++ b/chrome/browser/resources/print_preview/search/cloud_destination_list.js
@@ -26,18 +26,11 @@
     /** @override */
     updateDestinations: function(destinations) {
       // Change the action link from "Manage..." to "Setup..." if user only has
-      // Docs and FedEx printers.
+      // Docs printers.
       var docsId = print_preview.Destination.GooglePromotedId.DOCS;
-      var fedexId = print_preview.Destination.GooglePromotedId.FEDEX;
-      if ((destinations.length == 1 && destinations[0].id == docsId) ||
-          (destinations.length == 2 &&
-           ((destinations[0].id == docsId && destinations[1].id == fedexId) ||
-            (destinations[0].id == fedexId && destinations[1].id == docsId)))) {
-        this.setActionLinkTextInternal(
-            loadTimeData.getString('setupCloudPrinters'));
-      } else {
-        this.setActionLinkTextInternal(loadTimeData.getString('manage'));
-      }
+      this.setActionLinkTextInternal(
+          destinations.length == 1 && destinations[0].id == docsId ?
+              'setupCloudPrinters' : 'manage');
       print_preview.DestinationList.prototype.updateDestinations.call(
           this, destinations);
     }
diff --git a/chrome/browser/resources/print_preview/search/destination_list_item.js b/chrome/browser/resources/print_preview/search/destination_list_item.js
index b13dc54..20e3c2d 100644
--- a/chrome/browser/resources/print_preview/search/destination_list_item.js
+++ b/chrome/browser/resources/print_preview/search/destination_list_item.js
@@ -38,14 +38,6 @@
      * @private
      */
     this.query_ = query;
-
-    /**
-     * FedEx terms-of-service widget or {@code null} if this list item does not
-     * render the FedEx Office print destination.
-     * @type {print_preview.FedexTos}
-     * @private
-     */
-    this.fedexTos_ = null;
   };
 
   /**
@@ -284,20 +276,8 @@
      * @private
      */
     onDestinationActivated_: function() {
-      if (this.destination_.id ==
-              print_preview.Destination.GooglePromotedId.FEDEX &&
-          !this.destination_.isTosAccepted) {
-        if (!this.fedexTos_) {
-          this.fedexTos_ = new print_preview.FedexTos();
-          this.fedexTos_.render(this.getElement());
-          this.tracker.add(
-              this.fedexTos_,
-              print_preview.FedexTos.EventType.AGREE,
-              this.onTosAgree_.bind(this));
-        }
-        this.fedexTos_.setIsVisible(true);
-      } else if (this.destination_.connectionStatus !=
-                     print_preview.Destination.ConnectionStatus.UNREGISTERED) {
+      if (this.destination_.connectionStatus !=
+              print_preview.Destination.ConnectionStatus.UNREGISTERED) {
         var selectEvt = new Event(DestinationListItem.EventType.SELECT);
         selectEvt.destination = this.destination_;
         this.eventTarget_.dispatchEvent(selectEvt);
@@ -325,17 +305,6 @@
     },
 
     /**
-     * Called when the user agrees to the print destination's terms-of-service.
-     * Selects the print destination that was agreed to.
-     * @private
-     */
-    onTosAgree_: function() {
-      var selectEvt = new Event(DestinationListItem.EventType.SELECT);
-      selectEvt.destination = this.destination_;
-      this.eventTarget_.dispatchEvent(selectEvt);
-    },
-
-    /**
      * Called when the registration promo is clicked.
      * @private
      */
diff --git a/chrome/browser/resources/print_preview/search/fedex_tos.css b/chrome/browser/resources/print_preview/search/fedex_tos.css
deleted file mode 100644
index d239b69..0000000
--- a/chrome/browser/resources/print_preview/search/fedex_tos.css
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-.fedex-tos {
-  overflow: hidden;
-  padding: 0 36px;
-  text-align: center;
-  transition: height 300ms;
-}
-
-.fedex-tos .tos-text {
-  padding: 8px 0;
-}
-
-.fedex-tos .agreement-box {
-  padding-bottom: 8px;
-}
diff --git a/chrome/browser/resources/print_preview/search/fedex_tos.html b/chrome/browser/resources/print_preview/search/fedex_tos.html
deleted file mode 100644
index f3dcffc..0000000
--- a/chrome/browser/resources/print_preview/search/fedex_tos.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<div id="fedex-tos-template" class="fedex-tos" aria-hidden="false"
-    aria-live="polite" hidden style="height: 0;">
-  <div class="height-helper">
-    <div class="tos-text"></div>
-    <div class="agreement-box">
-      <label>
-        <input class="agree-checkbox" type="checkbox">
-        <span>$i18n{tosCheckboxLabel}</span>
-      </label>
-    </div>
-  </div>
-</div>
diff --git a/chrome/browser/resources/print_preview/search/fedex_tos.js b/chrome/browser/resources/print_preview/search/fedex_tos.js
deleted file mode 100644
index 7aa45f2..0000000
--- a/chrome/browser/resources/print_preview/search/fedex_tos.js
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('print_preview', function() {
-  'use strict';
-
-  /**
-   * Widget that renders a terms-of-service agreement for using the FedEx Office
-   * print destination.
-   * @constructor
-   * @extends {print_preview.Component}
-   */
-  function FedexTos() {
-    print_preview.Component.call(this);
-  };
-
-  /**
-   * Enumeration of event types dispatched by the widget.
-   * @enum {string}
-   */
-  FedexTos.EventType = {
-    // Dispatched when the user agrees to the terms-of-service.
-    AGREE: 'print_preview.FedexTos.AGREE'
-  };
-
-  FedexTos.prototype = {
-    __proto__: print_preview.Component.prototype,
-
-    /** @param {boolean} isVisible Whether the widget is visible. */
-    setIsVisible: function(isVisible) {
-      if (isVisible) {
-        var heightHelperEl = this.getElement().querySelector('.height-helper');
-        this.getElement().style.height = heightHelperEl.offsetHeight + 'px';
-      } else {
-        this.getElement().style.height = 0;
-      }
-    },
-
-    /** @override */
-    createDom: function() {
-      this.setElementInternal(this.cloneTemplateInternal('fedex-tos-template'));
-      var tosTextEl = this.getElement().querySelector('.tos-text');
-      tosTextEl.innerHTML = loadTimeData.getStringF(
-          'fedexTos',
-          '<a href="http://www.fedex.com/us/office/copyprint/online/' +
-              'googlecloudprint/termsandconditions">',
-          '</a>');
-    },
-
-    /** @override */
-    enterDocument: function() {
-      var agreeCheckbox = this.getElement().querySelector('.agree-checkbox');
-      this.tracker.add(
-          agreeCheckbox, 'click', this.onAgreeCheckboxClick_.bind(this));
-    },
-
-    /**
-     * Called when the agree checkbox is clicked. Dispatches a AGREE event.
-     * @private
-     */
-    onAgreeCheckboxClick_: function() {
-      cr.dispatchSimpleEvent(this, FedexTos.EventType.AGREE);
-    }
-  };
-
-  // Export
-  return {
-    FedexTos: FedexTos
-  };
-});
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 9a26583..10ad783 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -50,18 +50,19 @@
 </if>
 
 <if expr="not chromeos">
-    <div class="settings-box first two-line" on-tap="onMoreFeaturesTap_"
-        actionable>
+    <a class="settings-box first two-line inherit-color no-outline"
+        tabindex="-1" target="_blank"
+        href="https://chrome.google.com/webstore/category/collection/accessibility">
       <div class="start">
         $i18n{moreFeaturesLink}
         <div class="secondary" id="moreFeaturesSecondary">
           $i18n{a11yWebStore}
         </div>
       </div>
-      <button class="icon-external" is="paper-icon-button-light"
+      <button class="icon-external" is="paper-icon-button-light" actionable
           aria-label="$i18n{moreFeaturesLink}"
           aria-describedby="moreFeaturesSecondary"></button>
-    </div>
+    </a>
 </if>
 
   </template>
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.js b/chrome/browser/resources/settings/a11y_page/a11y_page.js
index 0bcbcbc..5f236ee 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.js
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.js
@@ -49,10 +49,4 @@
     settings.navigateTo(settings.Route.MANAGE_ACCESSIBILITY);
   },
 // </if>
-
-  /** @private */
-  onMoreFeaturesTap_: function() {
-    window.open(
-        'https://chrome.google.com/webstore/category/collection/accessibility');
-  },
 });
diff --git a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
index f00de249..31e0da37 100644
--- a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
@@ -205,17 +205,19 @@
     </div>
 
     <template is="dom-if" if="[[!isGuest_]]">
-      <div class="settings-box two-line" on-tap="onMoreFeaturesTap_" actionable>
+      <a class="settings-box two-line inherit-color no-outline" tabindex="-1"
+          target="_blank"
+          href="https://chrome.google.com/webstore/category/collection/accessibility">
         <div class="start">
           $i18n{additionalFeaturesTitle}
           <div class="secondary" id="moreFeaturesSecondary">
             $i18n{a11yWebStore}
           </div>
         </div>
-        <button class="icon-external" is="paper-icon-button-light"
+        <button class="icon-external" is="paper-icon-button-light" actionable
             aria-label="$i18n{additionalFeaturesTitle}"
             aria-describedby="moreFeaturesSecondary"></button>
-      </div>
+      </a>
     </template>
   </template>
   <script src="manage_a11y_page.js"></script>
diff --git a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
index 84d2a77..848bd57 100644
--- a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
+++ b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
@@ -101,10 +101,4 @@
         settings.Route.POINTERS,
         /* dynamicParams */ null, /* removeSearch */ true);
   },
-
-  /** @private */
-  onMoreFeaturesTap_: function() {
-    window.open(
-        'https://chrome.google.com/webstore/category/collection/accessibility');
-  },
 });
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chrome/browser/resources/settings/appearance_page/appearance_page.html
index 06b4e12e..fdf0277e 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.html
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -61,7 +61,8 @@
         <div class="settings-box two-line first"
             hidden="[[!pageVisibility.setTheme]]">
 </if>
-          <div class="start two-line" on-tap="onThemesTap_" actionable>
+          <a class="start two-line inherit-color no-outline" tabindex="-1"
+              target="_blank" href$="[[getThemeHref_(themeUrl_)]]">
             <div class="flex">
               $i18n{themes}
               <div class="secondary" id="themesSecondary">
@@ -69,9 +70,9 @@
               </div>
             </div>
             <button class="icon-external" is="paper-icon-button-light"
-                aria-label="$i18n{themes}"
+                actionable aria-label="$i18n{themes}"
                 aria-describedby="themesSecondary"></button>
-          </div>
+          </a>
 <if expr="not is_linux or chromeos">
           <template is="dom-if" if="[[prefs.extensions.theme.id.value]]">
             <div class="secondary-action">
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.js b/chrome/browser/resources/settings/appearance_page/appearance_page.js
index 5571b2d..37a6a96 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.js
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.js
@@ -94,6 +94,9 @@
     themeSublabel_: String,
 
     /** @private */
+    themeUrl_: String,
+
+    /** @private */
     useSystemTheme_: {
       type: Boolean,
       value: false,  // Can only be true on Linux, but value exists everywhere.
@@ -115,9 +118,6 @@
   /** @private {?settings.AppearanceBrowserProxy} */
   browserProxy_: null,
 
-  /** @private {string} */
-  themeUrl_: '',
-
   observers: [
     'defaultFontSizeChanged_(prefs.webkit.webprefs.default_font_size.value)',
     'themeChanged_(prefs.extensions.theme.id.value, useSystemTheme_)',
@@ -187,9 +187,13 @@
         value - SIZE_DIFFERENCE_FIXED_STANDARD_);
   },
 
-  /** @private */
-  onThemesTap_: function() {
-    window.open(this.themeUrl_ || loadTimeData.getString('themesGalleryUrl'));
+  /**
+   * URL for either current theme or the theme gallery.
+   * @return {string}
+   * @private
+   */
+  getThemeHref_: function() {
+    return this.themeUrl_ || loadTimeData.getString('themesGalleryUrl');
   },
 
   // <if expr="chromeos">
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index 760db46b..ef74fd5 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -267,12 +267,24 @@
    * @private
    */
   getPropertiesCallback_: function(properties) {
-    this.networkProperties = properties;
-    if (!properties) {
-      // If |properties| is null, the network is no longer visible, close this.
-      console.error('Network no longer exists: ' + this.guid);
+    if (chrome.runtime.lastError) {
+      var message = chrome.runtime.lastError.message;
+      if (message == 'Error.InvalidNetworkGuid') {
+        console.error('Details page: GUID no longer exists: ' + this.guid);
+      } else {
+        console.error(
+            'Unexpected networkingPrivate.getManagedProperties error: ' +
+            message + ' For: ' + this.guid);
+      }
       this.close_();
+      return;
     }
+    if (!properties) {
+      console.error('No properties for: ' + this.guid);
+      this.close_();
+      return;
+    }
+    this.networkProperties = properties;
   },
 
   /**
diff --git a/chrome/browser/resources/settings/internet_page/internet_subpage.js b/chrome/browser/resources/settings/internet_page/internet_subpage.js
index 09126808..5ff70c3 100644
--- a/chrome/browser/resources/settings/internet_page/internet_subpage.js
+++ b/chrome/browser/resources/settings/internet_page/internet_subpage.js
@@ -417,11 +417,13 @@
     this.networkingPrivate.startConnect(state.GUID, function() {
       if (chrome.runtime.lastError) {
         var message = chrome.runtime.lastError.message;
-        if (message != 'connecting') {
-          console.error(
-              'Unexpected networkingPrivate.startConnect error: ' + message +
-              'For: ' + state.GUID);
+        if (message == 'connecting' || message == 'connect-canceled' ||
+            message == 'connected' || message == 'Error.InvalidNetworkGuid') {
+          return;
         }
+        console.error(
+            'Unexpected networkingPrivate.startConnect error: ' + message +
+                ' For: ' + state.GUID);
       }
     });
   },
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html
index 9e488b3..8f7dbb5 100644
--- a/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -231,12 +231,14 @@
 </if>
 
         <template is="dom-if" if="[[profileManagesSupervisedUsers_]]">
-          <div id="manageSupervisedUsersContainer" class="settings-box"
-              on-tap="onManageSupervisedUsers_" actionable>
+          <a id="manageSupervisedUsersContainer"
+              class="settings-box inherit-color no-outline" tabindex="-1"
+              target="_blank" href="$i18n{supervisedUsersUrl}">
             <div class="start">$i18n{manageSupervisedUsers}</div>
             <button class="icon-external" is="paper-icon-button-light"
-                aria-label="$i18n{manageSupervisedUsers}"></button>
-          </div>
+                actionable aria-label="$i18n{manageSupervisedUsers}">
+            </button>
+          </a>
         </template>
       </neon-animatable>
       <template is="dom-if" route-path="/syncSetup"
diff --git a/chrome/browser/resources/settings/people_page/people_page.js b/chrome/browser/resources/settings/people_page/people_page.js
index f587973..26fd9dc5 100644
--- a/chrome/browser/resources/settings/people_page/people_page.js
+++ b/chrome/browser/resources/settings/people_page/people_page.js
@@ -343,11 +343,6 @@
 // </if>
   },
 
-  /** @private */
-  onManageSupervisedUsers_: function() {
-    window.open(loadTimeData.getString('supervisedUsersUrl'));
-  },
-
 // <if expr="not chromeos">
   /**
    * @private
diff --git a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
index 1d332be..572dcb4 100644
--- a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
@@ -232,7 +232,6 @@
     openActivityControlsUrl: function() {
       chrome.metricsPrivate.recordUserAction(
           'Signin_AccountSettings_GoogleActivityControlsClicked');
-      window.open(loadTimeData.getString('activityControlsUrl'));
     }
   };
 
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index 3761fa5..32bf38d 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -249,7 +249,8 @@
         </div>
       </div>
 
-      <div class="settings-box two-line" actionable
+      <a class="settings-box two-line inherit-color no-outline" tabindex="-1"
+          target="_blank" href="$i18n{activityControlsUrl}"
           on-tap="onActivityControlsTap_">
         <div class="start">
           $i18n{personalizeGoogleServicesTitle}
@@ -258,17 +259,18 @@
           </div>
         </div>
         <button class="icon-external" is="paper-icon-button-light"
-            aria-label="$i18n{personalizeGoogleServicesTitle}"
+            actionable aria-label="$i18n{personalizeGoogleServicesTitle}"
             aria-describedby="activityControlsSecondary"></button>
-      </div>
+      </a>
 
-      <div class="settings-box" actionable on-tap="onManageSyncedDataTap_">
+      <a class="settings-box inherit-color no-outline" tabindex="-1"
+          target="_blank" href="$i18n{syncDashboardUrl}">
         <div class="start">
           $i18n{manageSyncedDataTitle}
         </div>
-        <button class="icon-external" is="paper-icon-button-light"
+        <button class="icon-external" is="paper-icon-button-light" actionable
             aria-label="$i18n{manageSyncedDataTitle}"></button>
-      </div>
+      </a>
 
       <div id="encryptionDescription" hidden="[[syncPrefs.passphraseRequired]]"
           class="settings-box two-line single-column">
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js
index ae28f92..3c99f727e 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.js
+++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -269,11 +269,6 @@
     this.browserProxy_.openActivityControlsUrl();
   },
 
-  /** @private */
-  onManageSyncedDataTap_: function() {
-    window.open(loadTimeData.getString('syncDashboardUrl'));
-  },
-
   /**
    * Handler for when the autofill data type checkbox is changed.
    * @private
diff --git a/chrome/browser/resources/settings/printing_page/cloud_printers.html b/chrome/browser/resources/settings/printing_page/cloud_printers.html
index d47403f..4a74eae 100644
--- a/chrome/browser/resources/settings/printing_page/cloud_printers.html
+++ b/chrome/browser/resources/settings/printing_page/cloud_printers.html
@@ -20,13 +20,14 @@
           label="$i18n{printingNotificationsLabel}">
       </settings-toggle-button>
     </div>
-    <div class="settings-box" on-tap="onManageTap_" actionable>
+    <a class="settings-box inherit-color no-outline" tabindex="-1"
+        target="_blank" href="$i18n{devicesUrl}">
       <div class="start">
         $i18n{printingManageCloudPrintDevices}
       </div>
-      <button class="icon-external" is="paper-icon-button-light"
+      <button class="icon-external" is="paper-icon-button-light" actionable
           aria-label="$i18n{printingManageCloudPrintDevices}"></button>
-    </div>
+    </a>
   </template>
   <script src="cloud_printers.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/printing_page/cloud_printers.js b/chrome/browser/resources/settings/printing_page/cloud_printers.js
index 3537e5d..1de17f79 100644
--- a/chrome/browser/resources/settings/printing_page/cloud_printers.js
+++ b/chrome/browser/resources/settings/printing_page/cloud_printers.js
@@ -16,9 +16,4 @@
       notify: true,
     },
   },
-
-  /** @private */
-  onManageTap_: function() {
-    window.open(loadTimeData.getString('devicesUrl'));
-  },
 });
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index b6e0dce6..428ca86d 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -378,12 +378,13 @@
               sub-option-secondary="$i18n{siteSettingsFlashAskBeforeSubtitle}">
           </category-default-setting>
 <if expr="chromeos">
-          <div actionable class="settings-box"
-              on-tap="onAdobeFlashStorageClicked_">
+          <a class="settings-box inherit-color no-outline" tabindex="-1"
+              target="_blank"
+              href="https://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager07.html">
             <div class="start">$i18n{adobeFlashStorage}</div>
             <button class="icon-external" is="paper-icon-button-light"
-                aria-label="$i18n{adobeFlashStorage}"></button>
-          </div>
+                actionable aria-label="$i18n{adobeFlashStorage}"></button>
+          </a>
 </if>
           <category-setting-exceptions
               category="{{ContentSettingsTypes.PLUGINS}}">
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chrome/browser/resources/settings/privacy_page/privacy_page.js
index a8ac950..5912a1b 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.js
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.js
@@ -246,12 +246,6 @@
   },
 
   /** @private */
-  onHelpTap_: function() {
-    window.open(
-        'https://support.google.com/chrome/?p=settings_manage_exceptions');
-  },
-
-  /** @private */
   onSberChange_: function() {
     var enabled = this.$.safeBrowsingExtendedReportingControl.checked;
     this.browserProxy_.setSafeBrowsingExtendedReportingEnabled(enabled);
@@ -325,14 +319,6 @@
         loadTimeData.getString('contentSettings');
   },
 
-// <if expr="chromeos">
-  /** @private */
-  onAdobeFlashStorageClicked_: function() {
-    window.open('https://www.macromedia.com/support/' +
-        'documentation/en/flashplayer/help/settings_manager07.html');
-  },
-// </if>
-
   /** @private */
   getProtectedContentLabel_: function(value) {
     return value ? this.i18n('siteSettingsProtectedContentEnable')
diff --git a/chrome/browser/resources/settings/search_page/search_page.html b/chrome/browser/resources/settings/search_page/search_page.html
index f19514a3..e87bb580 100644
--- a/chrome/browser/resources/settings/search_page/search_page.html
+++ b/chrome/browser/resources/settings/search_page/search_page.html
@@ -106,8 +106,9 @@
           </template>
 
           <template is="dom-if" if="[[hotwordInfo_.historyEnabled]]">
-            <div class="settings-box two-line continuation indented"
-                on-tap="onManageAudioHistoryTap_" actionable>
+            <a class="settings-box two-line continuation indented inherit-color
+                no-outline" tabindex="-1" target="_blank"
+                href="$i18n{manageAudioHistoryUrl}">
               <div class="start">
                 [[i18n('searchOkGoogleAudioHistoryLabel',
                        hotwordInfo_.userName)]]
@@ -115,11 +116,12 @@
                   $i18n{searchOkGoogleAudioHistorySubtext}
                 </div>
               </div>
-              <button class="icon-external" is="paper-icon-button-light"
+              <button actionable class="icon-external"
+                  is="paper-icon-button-light"
                   aria-label$="[[i18n('searchOkGoogleAudioHistoryLabel',
                                       hotwordInfo_.userName)]]"
                   aria-describedby="audioHistorySecondary"></button>
-            </div>
+            </a>
           </template>
         </template>
 
diff --git a/chrome/browser/resources/settings/search_page/search_page.js b/chrome/browser/resources/settings/search_page/search_page.js
index a307f3d..41db7cb 100644
--- a/chrome/browser/resources/settings/search_page/search_page.js
+++ b/chrome/browser/resources/settings/search_page/search_page.js
@@ -166,11 +166,6 @@
     this.browserProxy_.setHotwordSearchEnabled(this.hotwordInfo_.enabled);
   },
 
-  /** @private */
-  onManageAudioHistoryTap_: function() {
-    window.open(loadTimeData.getString('manageAudioHistoryUrl'));
-  },
-
   /**
    * @param {Event} event
    * @private
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index 5204d006..1b42c64 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -5,12 +5,6 @@
 <dom-module id="settings-shared">
   <template>
     <style include="cr-shared-style">
-      /* Included here so we don't have to include "iron-positioning" in every
-       * stylesheet. See crbug.com/498405. */
-      [hidden] {
-        display: none !important;
-      }
-
       :host-context([dir=rtl]) button[is='paper-icon-button-light'] {
         transform: scaleX(-1);  /* Flip on the X axis (aka mirror). */
       }
@@ -75,8 +69,13 @@
         text-decoration: none;
       }
 
-      /* There are three main button styles, .primary-button and
-       * .secondary-button. The primary is the action button (e.g. "edit",
+      /* For elements that are simple outlinks but dont look like anchors. */
+      .inherit-color {
+        color: inherit !important;
+      }
+
+      /* There are three main button styles, .primary-button, .secondary-button,
+       * and .tertiary-button. The primary is the action button (e.g. "edit",
        * "delete") while the secondary is often a "Cancel" button. A tertiary
        * button may be used to get more information or similar, that we expect
        * most users will not need. */
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index ac2d1330..a4d4207 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -25,7 +25,7 @@
       :host {
         @apply(--layout-fit);
         -webkit-user-select: none;
-        color: var(--paper-grey-900);
+        color: var(--primary-text-color);
         display: flex;
         flex-direction: column;
         line-height: 154%; /* Apply 20px line-height to all texts by default. */
diff --git a/chrome/browser/resources/settings/settings_vars_css.html b/chrome/browser/resources/settings/settings_vars_css.html
index 69e23ac..4a01124 100644
--- a/chrome/browser/resources/settings/settings_vars_css.html
+++ b/chrome/browser/resources/settings/settings_vars_css.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/default-theme.html">
 
 <!-- Common css variables for Material Design settings. -->
 <style is="custom-style">
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
index d82621b..1ca9e18 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
@@ -64,9 +64,8 @@
   // the only thing this is used for is choosing the right scale factor in
   // AppListMainView::PreloadIcons(), so we take care of that - perhaps by
   // passing the display_id or the scale factor directly
-  view->Initialize(nullptr /* parent */, current_apps_page);
-
-  view->MaybeSetAnchorPoint(
+  view->InitAsBubble(nullptr /* parent */, current_apps_page);
+  view->SetAnchorPoint(
       GetCenterOfDisplay(display_id, GetMinimumBoundsHeightForAppList(view)));
 
   // TODO(mfomitchev): Setup updating bounds on keyboard bounds change.
diff --git a/chrome/browser/ui/ash/session_controller_client.cc b/chrome/browser/ui/ash/session_controller_client.cc
index 25379e4..905fb90 100644
--- a/chrome/browser/ui/ash/session_controller_client.cc
+++ b/chrome/browser/ui/ash/session_controller_client.cc
@@ -31,8 +31,10 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
+#include "mojo/public/cpp/bindings/equals_traits.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
 
 using session_manager::Session;
 using session_manager::SessionManager;
@@ -101,6 +103,20 @@
 
 }  // namespace
 
+namespace mojo {
+
+// When comparing two mojom::UserSession objects we need to decide if the avatar
+// images are changed. Consider them equal if they have the same storage rather
+// than comparing the backing pixels.
+template <>
+struct EqualsTraits<gfx::ImageSkia> {
+  static bool Equals(const gfx::ImageSkia& a, const gfx::ImageSkia& b) {
+    return a.BackedBySameObjectAs(b);
+  }
+};
+
+}  // namespace mojo
+
 SessionControllerClient::SessionControllerClient()
     : binding_(this), weak_ptr_factory_(this) {
   SessionManager::Get()->AddObserver(this);
@@ -392,10 +408,10 @@
   if (!user_session)
     return;
 
-  // TODO(jamescook): Only send if it changed. This will require an Equals()
-  // method for gfx::ImageSkia to allow mojom::UserSession comparison.
-  // http://crbug.com/714689
-  session_controller_->UpdateUserSession(std::move(user_session));
+  if (user_session != last_sent_user_session_) {
+    last_sent_user_session_ = user_session->Clone();
+    session_controller_->UpdateUserSession(std::move(user_session));
+  }
 }
 
 void SessionControllerClient::SendUserSessionOrder() {
diff --git a/chrome/browser/ui/ash/session_controller_client.h b/chrome/browser/ui/ash/session_controller_client.h
index 4241d84..9fffbe0 100644
--- a/chrome/browser/ui/ash/session_controller_client.h
+++ b/chrome/browser/ui/ash/session_controller_client.h
@@ -95,6 +95,7 @@
   static void FlushForTesting();
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(SessionControllerClientTest, SendUserSession);
   FRIEND_TEST_ALL_PREFIXES(SessionControllerClientTest, SupervisedUser);
 
   // Called when the login profile is ready.
@@ -130,7 +131,9 @@
 
   content::NotificationRegistrar registrar_;
 
+  // Used to suppress duplicate IPCs to ash.
   ash::mojom::SessionInfoPtr last_sent_session_info_;
+  ash::mojom::UserSessionPtr last_sent_user_session_;
 
   base::WeakPtrFactory<SessionControllerClient> weak_ptr_factory_;
 
diff --git a/chrome/browser/ui/ash/session_controller_client_unittest.cc b/chrome/browser/ui/ash/session_controller_client_unittest.cc
index 1e5d0a5..2d0db44 100644
--- a/chrome/browser/ui/ash/session_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/session_controller_client_unittest.cc
@@ -87,6 +87,8 @@
     return last_user_session_.get();
   }
 
+  int update_user_session_count() { return update_user_session_count_; }
+
   // ash::mojom::SessionController:
   void SetClient(ash::mojom::SessionControllerClientPtr client) override {}
   void SetSessionInfo(ash::mojom::SessionInfoPtr info) override {
@@ -94,6 +96,7 @@
   }
   void UpdateUserSession(ash::mojom::UserSessionPtr user_session) override {
     last_user_session_ = user_session->Clone();
+    update_user_session_count_++;
   }
   void SetUserSessionOrder(
       const std::vector<uint32_t>& user_session_order) override {}
@@ -108,6 +111,7 @@
 
   ash::mojom::SessionInfoPtr last_session_info_;
   ash::mojom::UserSessionPtr last_user_session_;
+  int update_user_session_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(TestSessionController);
 };
@@ -361,6 +365,36 @@
             SessionControllerClient::GetAddUserSessionPolicy());
 }
 
+TEST_F(SessionControllerClientTest, SendUserSession) {
+  // Create an object to test and connect it to our test interface.
+  SessionControllerClient client;
+  TestSessionController session_controller;
+  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
+  client.Init();
+  SessionControllerClient::FlushForTesting();
+
+  // No user session sent yet.
+  EXPECT_EQ(0, session_controller.update_user_session_count());
+
+  // Simulate login.
+  const AccountId account_id(AccountId::FromUserEmail("user@test.com"));
+  user_manager()->AddUser(account_id);
+  session_manager_.CreateSession(
+      account_id, chromeos::ProfileHelper::GetUserIdHashByUserIdForTesting(
+                      "user@test.com"));
+  SessionControllerClient::FlushForTesting();
+
+  // User session was sent.
+  EXPECT_EQ(1, session_controller.update_user_session_count());
+
+  // Simulate a request for an update where nothing changed.
+  client.SendUserSession(*user_manager()->GetLoggedInUsers()[0]);
+  SessionControllerClient::FlushForTesting();
+
+  // Session was not updated because nothing changed.
+  EXPECT_EQ(1, session_controller.update_user_session_count());
+}
+
 TEST_F(SessionControllerClientTest, SupervisedUser) {
   // Create an object to test and connect it to our test interface.
   SessionControllerClient client;
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h
index b9df782..d7a5dd2 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h
@@ -172,7 +172,7 @@
   void UpdateAlertState(TabAlertState alert_state);
 
   // Returns the cocoa-world BrowserWindowController
-  BrowserWindowController* cocoa_controller() { return controller_; }
+  BrowserWindowController* cocoa_controller() const { return controller_; }
 
   // Returns window title based on the active tab title and the window's alert
   // state.
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index 36f0ab7c..1e9353d9 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -333,10 +333,7 @@
   if (!IsFullscreen())
     return true;
 
-  // TODO(zijiehe): Retrieve the visibility of toolbar from
-  // FullscreenToolbarController. See http://crbug.com/702251 and
-  // http://crbug.com/680809.
-  return true;
+  return [cocoa_controller() isToolbarShowing] == YES;
 }
 
 void BrowserWindowCocoa::BookmarkBarStateChanged(
@@ -734,13 +731,14 @@
     return content::KeyboardEventProcessingResult::NOT_HANDLED;
 
   if (browser_->command_controller()->IsReservedCommandOrKey(id, event)) {
+    using Result = content::KeyboardEventProcessingResult;
     return [BrowserWindowUtils handleKeyboardEvent:event.os_event
                                           inWindow:window()]
-               ? content::KeyboardEventProcessingResult::HANDLED
-               : content::KeyboardEventProcessingResult::NOT_HANDLED;
+               ? Result::HANDLED
+               : Result::NOT_HANDLED_IS_SHORTCUT;
   }
 
-  return content::KeyboardEventProcessingResult::NOT_HANDLED_IS_SHORTCUT;
+  return content::KeyboardEventProcessingResult::NOT_HANDLED;
 }
 
 void BrowserWindowCocoa::HandleKeyboardEvent(
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.h b/chrome/browser/ui/cocoa/browser_window_controller.h
index d98a2b9..447add8 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.h
+++ b/chrome/browser/ui/cocoa/browser_window_controller.h
@@ -404,6 +404,10 @@
 // Invalidates the browser's touch bar.
 - (void)invalidateTouchBar;
 
+// Indicates whether the toolbar is visible to the user. Toolbar is usually
+// triggered by moving mouse cursor to the top of the monitor.
+- (BOOL)isToolbarShowing;
+
 @end  // @interface BrowserWindowController
 
 
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 214d8ce4..7ca369af 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -1919,6 +1919,10 @@
     [[self window] performSelector:@selector(setTouchBar:) withObject:nil];
 }
 
+- (BOOL)isToolbarShowing {
+  return [fullscreenToolbarController_ mustShowFullscreenToolbar];
+}
+
 @end  // @implementation BrowserWindowController
 
 @implementation BrowserWindowController(Fullscreen)
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 8da4f1c4..820b325 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -209,7 +209,7 @@
   // Forces the opening of a new tab. |args| should consist of one element: the
   // URL to set the new tab to.
   //
-  // NOTE: This is needed to open FedEx confirmation window as a new tab.
+  // NOTE: This is needed to open register promo for Cloud Print as a new tab.
   // Javascript's "window.open" opens a new window popup (since initiated from
   // async HTTP request) and worse yet, on Windows and Chrome OS, the opened
   // window opens behind the initiator window.
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index af6bab18..955e77b 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -338,9 +338,6 @@
   source->AddLocalizedString("offlineForWeek",
                              IDS_PRINT_PREVIEW_OFFLINE_FOR_WEEK);
   source->AddLocalizedString("offline", IDS_PRINT_PREVIEW_OFFLINE);
-  source->AddLocalizedString("fedexTos", IDS_PRINT_PREVIEW_FEDEX_TOS);
-  source->AddLocalizedString("tosCheckboxLabel",
-                             IDS_PRINT_PREVIEW_TOS_CHECKBOX_LABEL);
   source->AddLocalizedString("noDestsPromoTitle",
                              IDS_PRINT_PREVIEW_NO_DESTS_PROMO_TITLE);
   source->AddLocalizedString("noDestsPromoBody",
@@ -398,8 +395,6 @@
                           IDR_PRINT_PREVIEW_IMAGES_ENTERPRISE_PRINTER);
   source->AddResourcePath("images/third_party.png",
                           IDR_PRINT_PREVIEW_IMAGES_THIRD_PARTY);
-  source->AddResourcePath("images/third_party_fedex.png",
-                          IDR_PRINT_PREVIEW_IMAGES_THIRD_PARTY_FEDEX);
   source->AddResourcePath("images/google_doc.png",
                           IDR_PRINT_PREVIEW_IMAGES_GOOGLE_DOC);
   source->AddResourcePath("images/pdf.png", IDR_PRINT_PREVIEW_IMAGES_PDF);
diff --git a/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc b/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
index f8cb64a..03abb0f 100644
--- a/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
@@ -22,7 +22,10 @@
       g_browser_process->local_state()->FindPreference(
           prefs::kDefaultBrowserSettingEnabled);
   DCHECK(pref);
-  return pref->IsManaged() && !pref->GetValue();
+  bool may_set_default_browser;
+  bool success = pref->GetValue()->GetAsBoolean(&may_set_default_browser);
+  DCHECK(success);
+  return pref->IsManaged() && !may_set_default_browser;
 }
 
 }  // namespace
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index b0596a6d..bc3c673 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -28,7 +28,7 @@
   "+components/translate/core/common",
   "+components/url_formatter",
   "+components/version_info",
-  "+device/vr/features.h",
+  "+device/vr/features/features.h",
   "+extensions/common",
   "+extensions/features",
   "+gin/public",  # For profiling.cc
diff --git a/chrome/common/features.gni b/chrome/common/features.gni
index 9364bd9..b302e5d7 100644
--- a/chrome/common/features.gni
+++ b/chrome/common/features.gni
@@ -6,7 +6,7 @@
 import("//build/config/chromecast_build.gni")
 import("//build/config/compiler/compiler.gni")
 import("//build/config/features.gni")
-import("//device/vr/features.gni")
+import("//device/vr/features/features.gni")
 import("//extensions/features/features.gni")
 import("//media/media_options.gni")
 import("//net/features.gni")
diff --git a/chrome/installer/mac/sign_app.sh.in b/chrome/installer/mac/sign_app.sh.in
index d293a4e..5114af6 100644
--- a/chrome/installer/mac/sign_app.sh.in
+++ b/chrome/installer/mac/sign_app.sh.in
@@ -71,13 +71,18 @@
 # Verify everything. Check the framework and helper apps to make sure that the
 # signatures are present and weren't altered by the signing process. Use
 # --ignore-resources on the app mode loader because its signature only covers
-# the main executable, not its containing .app bundle. Use --no-strict on the
-# outermost browser .app because it uses custom resource rules.
+# the main executable, not its containing .app bundle. Use --no-strict to
+# verify items that use custom resource rules:
+#  - The outermost brrowser .app
+#  - The inner .framework, which has a nested component that uses them.
 codesign --verify --verbose=6 --deep --no-strict "${browser_app}"
 codesign --verify --verbose=6 --deep "${crashpad_handler}"
 codesign --verify --verbose=6 --ignore-resources "${app_mode_loader}"
 codesign --verify --verbose=6 --deep "${notification_service}"
-codesign --verify --verbose=6 --deep "${framework}"
+# Check the framework twice: once deep with no-strict, and once shallow with
+# strict verification.
+codesign --verify --verbose=6 --deep --no-strict "${framework}"
+codesign --verify --verbose=6 --strict "${framework}"
 codesign --verify --verbose=6 --deep "${helper_app}"
 
 # Verify with spctl, which uses the same rules that Gatekeeper does for
diff --git a/chrome/installer/mac/sign_versioned_dir.sh.in b/chrome/installer/mac/sign_versioned_dir.sh.in
index 2d03ee45..e4213f5 100644
--- a/chrome/installer/mac/sign_versioned_dir.sh.in
+++ b/chrome/installer/mac/sign_versioned_dir.sh.in
@@ -109,5 +109,8 @@
 codesign_display_and_verify "${crashpad_handler}" --deep
 codesign_display_and_verify "${app_mode_loader}" --ignore-resources
 codesign_display_and_verify "${notification_service}" --deep
-codesign_display_and_verify "${framework}" --deep
+# The framework contains KeystoneRegistration.framework, which uses
+# custom resource rules, so use --no-strict to verify.
+codesign_display_and_verify "${framework}" --deep --no-strict
+codesign_display_and_verify "${framework}" --strict
 codesign_display_and_verify "${helper_app}" --deep
diff --git a/chrome/tools/build/mac/copy_keystone_framework.py b/chrome/tools/build/mac/copy_keystone_framework.py
index 19b8f2f..513fdc91 100644
--- a/chrome/tools/build/mac/copy_keystone_framework.py
+++ b/chrome/tools/build/mac/copy_keystone_framework.py
@@ -31,10 +31,7 @@
   # dotfiles and the Headers directories.
   subprocess.check_call(
       ['rsync', '-acC', '--delete',
-       # TODO(rsesek): Exclude these directories again after they are marked as
-       # optional in the code signing resource rules, otherwise the code
-       # signature is invalidated. https://crbug.com/688076
-       #'--exclude', 'Headers', '--exclude', 'PrivateHeaders',
+       '--exclude', 'Headers', '--exclude', 'PrivateHeaders',
        '--include', '*.so',
        os.path.join(args[1], 'Versions/Current/'),
        output_path])
diff --git a/chromecast/media/cma/decoder/BUILD.gn b/chromecast/media/cma/decoder/BUILD.gn
index 5adf546..be40a3c 100644
--- a/chromecast/media/cma/decoder/BUILD.gn
+++ b/chromecast/media/cma/decoder/BUILD.gn
@@ -4,9 +4,8 @@
 
 source_set("decoder") {
   sources = [
+    "cast_audio_decoder.cc",
     "cast_audio_decoder.h",
-    "cast_audio_decoder_android.cc",
-    "cast_audio_decoder_linux.cc",
   ]
 
   deps = [
diff --git a/chromecast/media/cma/decoder/cast_audio_decoder_linux.cc b/chromecast/media/cma/decoder/cast_audio_decoder.cc
similarity index 100%
rename from chromecast/media/cma/decoder/cast_audio_decoder_linux.cc
rename to chromecast/media/cma/decoder/cast_audio_decoder.cc
diff --git a/chromecast/media/cma/decoder/cast_audio_decoder_android.cc b/chromecast/media/cma/decoder/cast_audio_decoder_android.cc
deleted file mode 100644
index d011280..0000000
--- a/chromecast/media/cma/decoder/cast_audio_decoder_android.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromecast/media/cma/decoder/cast_audio_decoder.h"
-
-#include "base/logging.h"
-
-namespace chromecast {
-namespace media {
-
-// static
-std::unique_ptr<CastAudioDecoder> CastAudioDecoder::Create(
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    const media::AudioConfig& config,
-    OutputFormat output_format,
-    const InitializedCallback& initialized_callback) {
-  return std::unique_ptr<CastAudioDecoder>();
-}
-
-// static
-int CastAudioDecoder::OutputFormatSizeInBytes(
-    CastAudioDecoder::OutputFormat format) {
-  switch (format) {
-    case CastAudioDecoder::OutputFormat::kOutputSigned16:
-      return 2;
-    case CastAudioDecoder::OutputFormat::kOutputPlanarFloat:
-      return 4;
-  }
-  NOTREACHED();
-  return 1;
-}
-
-}  // namespace media
-}  // namespace chromecast
diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc
index b2c4cd56..78abfb2 100644
--- a/chromeos/network/onc/onc_signature.cc
+++ b/chromeos/network/onc/onc_signature.cc
@@ -57,10 +57,11 @@
     {::onc::eap::kSaveCredentials, &kBoolSignature},
     // Used internally. Not officially supported.
     {::onc::eap::kServerCAPEMs, &kStringListSignature},
+    // Deprecated.
     {::onc::eap::kServerCARef, &kStringSignature},
     {::onc::eap::kServerCARefs, &kStringListSignature},
-    {::onc::eap::kUseSystemCAs, &kBoolSignature},
     {::onc::eap::kUseProactiveKeyCaching, &kBoolSignature},
+    {::onc::eap::kUseSystemCAs, &kBoolSignature},
     {NULL}};
 
 const OncFieldSignature ipsec_fields[] = {
diff --git a/components/feature_engagement_tracker/internal/feature_constants.cc b/components/feature_engagement_tracker/internal/feature_constants.cc
index 581600e..d3f22bc4 100644
--- a/components/feature_engagement_tracker/internal/feature_constants.cc
+++ b/components/feature_engagement_tracker/internal/feature_constants.cc
@@ -13,5 +13,7 @@
 
 const base::Feature kIPHDownloadPageFeature{"IPH_DownloadPage",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHDownloadHomeFeature{"IPH_DownloadHome",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace feature_engagement_tracker
diff --git a/components/feature_engagement_tracker/internal/feature_list.cc b/components/feature_engagement_tracker/internal/feature_list.cc
index 1cedb6b..39a92827 100644
--- a/components/feature_engagement_tracker/internal/feature_list.cc
+++ b/components/feature_engagement_tracker/internal/feature_list.cc
@@ -11,7 +11,8 @@
 
 namespace {
 
-const base::Feature* kAllFeatures[] = {&kIPHDownloadPageFeature};
+const base::Feature* kAllFeatures[] = {&kIPHDownloadPageFeature,
+                                       &kIPHDownloadHomeFeature};
 
 }  // namespace
 
diff --git a/components/feature_engagement_tracker/public/android/java/src/org/chromium/components/feature_engagement_tracker/EventConstants.java b/components/feature_engagement_tracker/public/android/java/src/org/chromium/components/feature_engagement_tracker/EventConstants.java
index 6c54c7b..0f94cd801 100644
--- a/components/feature_engagement_tracker/public/android/java/src/org/chromium/components/feature_engagement_tracker/EventConstants.java
+++ b/components/feature_engagement_tracker/public/android/java/src/org/chromium/components/feature_engagement_tracker/EventConstants.java
@@ -19,6 +19,16 @@
     public static final String DOWNLOAD_PAGE_STARTED = "download_page_started";
 
     /**
+     * The download has completed successfully.
+     */
+    public static final String DOWNLOAD_COMPLETED = "download_completed";
+
+    /**
+     * The download home was opened by the user (from toolbar menu or notifications).
+     */
+    public static final String DOWNLOAD_HOME_OPENED = "download_home_opened";
+
+    /**
      * Do not instantiate.
      */
     private EventConstants() {}
diff --git a/components/feature_engagement_tracker/public/android/java/src/org/chromium/components/feature_engagement_tracker/FeatureConstants.java b/components/feature_engagement_tracker/public/android/java/src/org/chromium/components/feature_engagement_tracker/FeatureConstants.java
index cf1ab1a8..5013839 100644
--- a/components/feature_engagement_tracker/public/android/java/src/org/chromium/components/feature_engagement_tracker/FeatureConstants.java
+++ b/components/feature_engagement_tracker/public/android/java/src/org/chromium/components/feature_engagement_tracker/FeatureConstants.java
@@ -10,6 +10,7 @@
  */
 public final class FeatureConstants {
     public static final String DOWNLOAD_PAGE_FEATURE = "IPH_DownloadPage";
+    public static final String DOWNLOAD_HOME_FEATURE = "IPH_DownloadHome";
 
     /**
      * Do not instantiate.
diff --git a/components/feature_engagement_tracker/public/feature_constants.h b/components/feature_engagement_tracker/public/feature_constants.h
index 2bc24c5..215488bc 100644
--- a/components/feature_engagement_tracker/public/feature_constants.h
+++ b/components/feature_engagement_tracker/public/feature_constants.h
@@ -16,6 +16,7 @@
 // version: org.chromium.components.feature_engagement_tracker.FeatureConstants.
 
 extern const base::Feature kIPHDownloadPageFeature;
+extern const base::Feature kIPHDownloadHomeFeature;
 
 }  // namespace feature_engagement_tracker
 
diff --git a/components/onc/docs/onc_spec.md b/components/onc/docs/onc_spec.md
index bafb102..c42e357c 100644
--- a/components/onc/docs/onc_spec.md
+++ b/components/onc/docs/onc_spec.md
@@ -1085,6 +1085,11 @@
       **SaveCredentials** is
       *false* is not allowed.
 
+* **ServerCAPEMs**
+    * (optional) - **array of string**
+    * Non-empty list of CA certificates in PEM format, If this field is set,
+      **ServerCARef** and **ServerCARefs** must be unset.
+
 * **ServerCARefs**
     * (optional) - **array of string**
     * Non-empty list of references to CA certificates in **Certificates** to be
diff --git a/components/printing/renderer/print_web_view_helper.cc b/components/printing/renderer/print_web_view_helper.cc
index 36d169b..9e0c9d3 100644
--- a/components/printing/renderer/print_web_view_helper.cc
+++ b/components/printing/renderer/print_web_view_helper.cc
@@ -1907,11 +1907,11 @@
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
   if (params.display_header_footer) {
-    // TODO(thestig): Figure out why Linux needs this. It is almost certainly
-    // |printingMinimumShrinkFactor| from Blink.
 #if defined(OS_WIN)
     const float fudge_factor = 1;
 #else
+    // TODO(thestig): Figure out why Linux needs this. It is almost certainly
+    // |kPrintingMinimumShrinkFactor| from Blink.
     const float fudge_factor = kPrintingMinimumShrinkFactor;
 #endif
     // |page_number| is 0-based, so 1 is added.
diff --git a/components/tracing/common/process_metrics_memory_dump_provider.cc b/components/tracing/common/process_metrics_memory_dump_provider.cc
index 2936256..3f2abde 100644
--- a/components/tracing/common/process_metrics_memory_dump_provider.cc
+++ b/components/tracing/common/process_metrics_memory_dump_provider.cc
@@ -614,6 +614,13 @@
   pmd->process_totals()->SetExtraFieldInBytes("private_bytes", private_bytes);
   pmd->process_totals()->SetExtraFieldInBytes("shared_bytes", shared_bytes);
   pmd->process_totals()->SetExtraFieldInBytes("locked_bytes", locked_bytes);
+
+  base::trace_event::ProcessMemoryTotals::PlatformPrivateFootprint& footprint =
+      pmd->process_totals()->GetPlatformPrivateFootprint();
+  base::ProcessMetrics::TaskVMInfo info = process_metrics_->GetTaskVMInfo();
+  footprint.phys_footprint_bytes = info.phys_footprint;
+  footprint.internal_bytes = info.internal;
+  footprint.compressed_bytes = info.compressed;
 #else
   uint64_t rss_bytes = process_metrics_->GetWorkingSetSize();
 #endif  // defined(OS_MACOSX)
diff --git a/components/viz/frame_sinks/mojo_frame_sink_manager.cc b/components/viz/frame_sinks/mojo_frame_sink_manager.cc
index 71b56c6..a41c1a4 100644
--- a/components/viz/frame_sinks/mojo_frame_sink_manager.cc
+++ b/components/viz/frame_sinks/mojo_frame_sink_manager.cc
@@ -18,17 +18,13 @@
 
 namespace viz {
 
-MojoFrameSinkManager::MojoFrameSinkManager(
-    bool use_surface_references,
-    DisplayProvider* display_provider,
-    cc::mojom::FrameSinkManagerRequest request,
-    cc::mojom::FrameSinkManagerClientPtr client)
+MojoFrameSinkManager::MojoFrameSinkManager(bool use_surface_references,
+                                           DisplayProvider* display_provider)
     : manager_(use_surface_references
                    ? cc::SurfaceManager::LifetimeType::REFERENCES
                    : cc::SurfaceManager::LifetimeType::SEQUENCES),
       display_provider_(display_provider),
-      client_(std::move(client)),
-      binding_(this, std::move(request)) {
+      binding_(this) {
   manager_.AddObserver(this);
 }
 
@@ -37,6 +33,14 @@
   manager_.RemoveObserver(this);
 }
 
+void MojoFrameSinkManager::Connect(
+    cc::mojom::FrameSinkManagerRequest request,
+    cc::mojom::FrameSinkManagerClientPtr client) {
+  DCHECK(!binding_.is_bound());
+  binding_.Bind(std::move(request));
+  client_ = std::move(client);
+}
+
 void MojoFrameSinkManager::CreateRootCompositorFrameSink(
     const cc::FrameSinkId& frame_sink_id,
     gpu::SurfaceHandle surface_handle,
diff --git a/components/viz/frame_sinks/mojo_frame_sink_manager.h b/components/viz/frame_sinks/mojo_frame_sink_manager.h
index 5fb8fa60..35db0ed4 100644
--- a/components/viz/frame_sinks/mojo_frame_sink_manager.h
+++ b/components/viz/frame_sinks/mojo_frame_sink_manager.h
@@ -39,13 +39,15 @@
       public NON_EXPORTED_BASE(cc::mojom::FrameSinkManager) {
  public:
   MojoFrameSinkManager(bool use_surface_references,
-                       DisplayProvider* display_provider,
-                       cc::mojom::FrameSinkManagerRequest request,
-                       cc::mojom::FrameSinkManagerClientPtr client);
+                       DisplayProvider* display_provider);
   ~MojoFrameSinkManager() override;
 
   cc::SurfaceManager* surface_manager() { return &manager_; }
 
+  // Binds to |request| and store connection back to |client|.
+  void Connect(cc::mojom::FrameSinkManagerRequest request,
+               cc::mojom::FrameSinkManagerClientPtr client);
+
   // cc::mojom::FrameSinkManager implementation:
   void CreateRootCompositorFrameSink(
       const cc::FrameSinkId& frame_sink_id,
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 7423578..eb0ac5e1 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -76,7 +76,7 @@
     "//device/power_save_blocker",
     "//device/screen_orientation/public/interfaces",
     "//device/vr",
-    "//device/vr:features",
+    "//device/vr/features",
     "//device/wake_lock/public/interfaces",
     "//google_apis",
     "//gpu",
diff --git a/content/browser/compositor/frame_sink_manager_host.cc b/content/browser/compositor/frame_sink_manager_host.cc
index 089abdb..ea57d32 100644
--- a/content/browser/compositor/frame_sink_manager_host.cc
+++ b/content/browser/compositor/frame_sink_manager_host.cc
@@ -14,9 +14,7 @@
 FrameSinkManagerHost::FrameSinkManagerHost()
     : binding_(this),
       frame_sink_manager_(false,  // Use surface sequences.
-                          nullptr,
-                          MakeRequest(&frame_sink_manager_ptr_),
-                          binding_.CreateInterfacePtrAndBind()) {}
+                          nullptr) {}
 
 FrameSinkManagerHost::~FrameSinkManagerHost() {}
 
@@ -24,11 +22,18 @@
   return frame_sink_manager_.surface_manager();
 }
 
+void FrameSinkManagerHost::ConnectToFrameSinkManager() {
+  DCHECK(!frame_sink_manager_ptr_.is_bound());
+  frame_sink_manager_.Connect(mojo::MakeRequest(&frame_sink_manager_ptr_),
+                              binding_.CreateInterfacePtrAndBind());
+}
+
 void FrameSinkManagerHost::CreateCompositorFrameSink(
     const cc::FrameSinkId& frame_sink_id,
     cc::mojom::MojoCompositorFrameSinkRequest request,
     cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
     cc::mojom::MojoCompositorFrameSinkClientPtr client) {
+  DCHECK(frame_sink_manager_ptr_.is_bound());
   frame_sink_manager_ptr_->CreateCompositorFrameSink(
       frame_sink_id, std::move(request), std::move(private_request),
       std::move(client));
@@ -37,6 +42,7 @@
 void FrameSinkManagerHost::RegisterFrameSinkHierarchy(
     const cc::FrameSinkId& parent_frame_sink_id,
     const cc::FrameSinkId& child_frame_sink_id) {
+  DCHECK(frame_sink_manager_ptr_.is_bound());
   frame_sink_manager_ptr_->RegisterFrameSinkHierarchy(parent_frame_sink_id,
                                                       child_frame_sink_id);
 }
@@ -44,6 +50,7 @@
 void FrameSinkManagerHost::UnregisterFrameSinkHierarchy(
     const cc::FrameSinkId& parent_frame_sink_id,
     const cc::FrameSinkId& child_frame_sink_id) {
+  DCHECK(frame_sink_manager_ptr_.is_bound());
   frame_sink_manager_ptr_->UnregisterFrameSinkHierarchy(parent_frame_sink_id,
                                                         child_frame_sink_id);
 }
diff --git a/content/browser/compositor/frame_sink_manager_host.h b/content/browser/compositor/frame_sink_manager_host.h
index a439730..8c7865d 100644
--- a/content/browser/compositor/frame_sink_manager_host.h
+++ b/content/browser/compositor/frame_sink_manager_host.h
@@ -5,10 +5,12 @@
 #ifndef CONTENT_BROWSER_COMPOSITOR_FRAME_SINK_MANAGER_HOST_H_
 #define CONTENT_BROWSER_COMPOSITOR_FRAME_SINK_MANAGER_HOST_H_
 
+#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "cc/ipc/frame_sink_manager.mojom.h"
 #include "cc/surfaces/frame_sink_id.h"
 #include "components/viz/frame_sinks/mojo_frame_sink_manager.h"
+#include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
 namespace cc {
@@ -19,14 +21,18 @@
 namespace content {
 
 // Browser side implementation of mojom::FrameSinkManager. Manages frame sinks
-// and is inteded to replace SurfaceManager.
-class FrameSinkManagerHost : cc::mojom::FrameSinkManagerClient {
+// and is intended to replace SurfaceManager.
+class CONTENT_EXPORT FrameSinkManagerHost
+    : NON_EXPORTED_BASE(cc::mojom::FrameSinkManagerClient) {
  public:
   FrameSinkManagerHost();
   ~FrameSinkManagerHost() override;
 
   cc::SurfaceManager* surface_manager();
 
+  // Start Mojo connection to FrameSinkManager. Most tests won't need this.
+  void ConnectToFrameSinkManager();
+
   // See frame_sink_manager.mojom for descriptions.
   void CreateCompositorFrameSink(
       const cc::FrameSinkId& frame_sink_id,
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 25b59f06..dbd83c60 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -197,6 +197,7 @@
   cc::SetClientNameForMetrics("Browser");
 
   frame_sink_manager_host_ = base::MakeUnique<FrameSinkManagerHost>();
+  frame_sink_manager_host_->ConnectToFrameSinkManager();
 
   task_graph_runner_->Start("CompositorTileWorker1",
                             base::SimpleThread::Options());
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.cc b/content/browser/compositor/test/no_transport_image_transport_factory.cc
index f326a4b2..51a04233 100644
--- a/content/browser/compositor/test/no_transport_image_transport_factory.cc
+++ b/content/browser/compositor/test/no_transport_image_transport_factory.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/memory/ptr_util.h"
 #include "build/build_config.h"
 #include "cc/output/context_provider.h"
 #include "cc/surfaces/surface_manager.h"
@@ -17,11 +18,12 @@
 namespace content {
 
 NoTransportImageTransportFactory::NoTransportImageTransportFactory()
-    : surface_manager_(new cc::SurfaceManager),
+    : frame_sink_manager_host_(base::MakeUnique<FrameSinkManagerHost>()),
       // The context factory created here is for unit tests, thus passing in
       // true in constructor.
-      context_factory_(
-          new ui::InProcessContextFactory(true, surface_manager_.get())) {}
+      context_factory_(base::MakeUnique<ui::InProcessContextFactory>(
+          true,
+          frame_sink_manager_host_->surface_manager())) {}
 
 NoTransportImageTransportFactory::~NoTransportImageTransportFactory() {
   std::unique_ptr<display_compositor::GLHelper> lost_gl_helper =
@@ -40,8 +42,7 @@
 
 FrameSinkManagerHost*
 NoTransportImageTransportFactory::GetFrameSinkManagerHost() {
-  NOTIMPLEMENTED();
-  return nullptr;
+  return frame_sink_manager_host_.get();
 }
 
 display_compositor::GLHelper* NoTransportImageTransportFactory::GetGLHelper() {
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.h b/content/browser/compositor/test/no_transport_image_transport_factory.h
index 0b3a357..87551fa 100644
--- a/content/browser/compositor/test/no_transport_image_transport_factory.h
+++ b/content/browser/compositor/test/no_transport_image_transport_factory.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "cc/surfaces/surface_manager.h"
+#include "content/browser/compositor/frame_sink_manager_host.h"
 #include "content/browser/compositor/image_transport_factory.h"
 
 namespace cc {
@@ -41,7 +42,7 @@
 #endif
 
  private:
-  std::unique_ptr<cc::SurfaceManager> surface_manager_;
+  std::unique_ptr<FrameSinkManagerHost> frame_sink_manager_host_;
   std::unique_ptr<ui::InProcessContextFactory> context_factory_;
   scoped_refptr<cc::ContextProvider> context_provider_;
   std::unique_ptr<display_compositor::GLHelper> gl_helper_;
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index c070f2df..b26cfd0 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -279,6 +279,7 @@
   DCHECK(GetEntryCount() == 0 && !GetPendingEntry());
   DCHECK(selected_navigation >= 0 &&
          selected_navigation < static_cast<int>(entries->size()));
+  DCHECK_EQ(-1, pending_entry_index_);
 
   needs_reload_ = true;
   entries_.reserve(entries->size());
@@ -447,10 +448,14 @@
     std::unique_ptr<NavigationEntryImpl> entry) {
   // Remember the last pending entry for which we haven't received a response
   // yet. This will be deleted in the NavigateToPendingEntry() function.
+  DCHECK_EQ(nullptr, last_pending_entry_);
+  DCHECK_EQ(-1, last_pending_entry_index_);
   last_pending_entry_ = pending_entry_;
   last_pending_entry_index_ = pending_entry_index_;
   last_transient_entry_index_ = transient_entry_index_;
+
   pending_entry_ = nullptr;
+  pending_entry_index_ = -1;
   // When navigating to a new page, we don't know for sure if we will actually
   // end up leaving the current page.  The new page load could for example
   // result in a download or a 'no content' response (e.g., a mailto: URL).
@@ -462,6 +467,7 @@
     std::unique_ptr<NavigationEntryImpl> entry) {
   DiscardNonCommittedEntriesInternal();
   pending_entry_ = entry.release();
+  DCHECK_EQ(-1, pending_entry_index_);
   NotificationService::current()->Notify(
       NOTIFICATION_NAV_ENTRY_PENDING,
       Source<NavigationController>(this),
@@ -534,11 +540,16 @@
 }
 
 int NavigationControllerImpl::GetLastCommittedEntryIndex() const {
+  // The last committed entry index must always be less than the number of
+  // entries.  If there are no entries, it must be -1. However, there may be a
+  // transient entry while the last committed entry index is still -1.
+  DCHECK_LT(last_committed_entry_index_, GetEntryCount());
+  DCHECK(GetEntryCount() || last_committed_entry_index_ == -1);
   return last_committed_entry_index_;
 }
 
 int NavigationControllerImpl::GetEntryCount() const {
-  DCHECK(entries_.size() <= max_entry_count());
+  DCHECK_LE(entries_.size(), max_entry_count());
   return static_cast<int>(entries_.size());
 }
 
@@ -615,11 +626,12 @@
 
   DiscardNonCommittedEntries();
 
+  DCHECK_EQ(nullptr, pending_entry_);
+  DCHECK_EQ(-1, pending_entry_index_);
+  pending_entry_ = entries_[index].get();
   pending_entry_index_ = index;
-  entries_[pending_entry_index_]->SetTransitionType(
-      ui::PageTransitionFromInt(
-          entries_[pending_entry_index_]->GetTransitionType() |
-          ui::PAGE_TRANSITION_FORWARD_BACK));
+  pending_entry_->SetTransitionType(ui::PageTransitionFromInt(
+      pending_entry_->GetTransitionType() | ui::PAGE_TRANSITION_FORWARD_BACK));
   NavigateToPendingEntry(ReloadType::NONE);
 }
 
@@ -1734,10 +1746,22 @@
 }
 
 NavigationEntryImpl* NavigationControllerImpl::GetPendingEntry() const {
+  // If there is no pending_entry_, there should be no pending_entry_index_.
+  DCHECK(pending_entry_ || pending_entry_index_ == -1);
+
+  // If there is a pending_entry_index_, then pending_entry_ must be the entry
+  // at that index.
+  DCHECK(pending_entry_index_ == -1 ||
+         pending_entry_ == GetEntryAtIndex(pending_entry_index_));
+
   return pending_entry_;
 }
 
 int NavigationControllerImpl::GetPendingEntryIndex() const {
+  // The pending entry index must always be less than the number of entries.
+  // If there are no entries, it must be exactly -1.
+  DCHECK_LT(pending_entry_index_, GetEntryCount());
+  DCHECK(GetEntryCount() != 0 || pending_entry_index_ == -1);
   return pending_entry_index_;
 }
 
@@ -1799,6 +1823,7 @@
 }
 
 void NavigationControllerImpl::NavigateToPendingEntry(ReloadType reload_type) {
+  DCHECK(pending_entry_);
   needs_reload_ = false;
 
   // If we were navigating to a slow-to-commit page, and the user performs
@@ -1809,9 +1834,8 @@
   // page from loading (which would normally happen during the navigation).
   if (pending_entry_index_ != -1 &&
       pending_entry_index_ == last_committed_entry_index_ &&
-      (entries_[pending_entry_index_]->restore_type() == RestoreType::NONE) &&
-      (entries_[pending_entry_index_]->GetTransitionType() &
-       ui::PAGE_TRANSITION_FORWARD_BACK)) {
+      pending_entry_->restore_type() == RestoreType::NONE &&
+      pending_entry_->GetTransitionType() & ui::PAGE_TRANSITION_FORWARD_BACK) {
     delegate_->Stop();
 
     // If an interstitial page is showing, we want to close it to get back
@@ -1840,10 +1864,12 @@
   // Convert Enter-in-omnibox to a reload. This is what Blink does in
   // FrameLoader, but we want to handle it here so that if the navigation is
   // redirected or handled purely on the browser side in PlzNavigate we have the
-  // same behaviour as Blink would. Note that we don't want to convert to a
-  // reload for history navigations, so this must be above the retrieval of the
-  // pending_entry_ below when pending_entry_index_ is used.
-  if (reload_type == ReloadType::NONE && last_navigation && pending_entry_ &&
+  // same behaviour as Blink would.
+  if (reload_type == ReloadType::NONE && last_navigation &&
+      // When |pending_entry_index_| is different from -1, it means this is an
+      // history navigation. History navigation mustn't be converted to a
+      // reload.
+      pending_entry_index_ == -1 &&
       // Please refer to the ShouldTreatNavigationAsReload() function for info
       // on which navigations are treated as reloads. In general navigating to
       // the last committed or pending entry via the address bar, clicking on
@@ -1878,12 +1904,6 @@
   last_pending_entry_ = nullptr;
   last_pending_entry_index_ = -1;
 
-  // For session history navigations only the pending_entry_index_ is set.
-  if (!pending_entry_) {
-    CHECK_NE(pending_entry_index_, -1);
-    pending_entry_ = entries_[pending_entry_index_].get();
-  }
-
   // Any renderer-side debug URLs or javascript: URLs should be ignored if the
   // renderer process is not live, unless it is the initial navigation of the
   // tab.
@@ -2052,6 +2072,7 @@
   if (pending_entry_) {
     NavigateToPendingEntry(ReloadType::NONE);
   } else if (last_committed_entry_index_ != -1) {
+    pending_entry_ = entries_[last_committed_entry_index_].get();
     pending_entry_index_ = last_committed_entry_index_;
     NavigateToPendingEntry(ReloadType::NONE);
   } else {
@@ -2099,10 +2120,12 @@
     failed_pending_entry_id_ = 0;
   }
 
-  if (pending_entry_index_ == -1)
-    delete pending_entry_;
-  pending_entry_ = NULL;
-  pending_entry_index_ = -1;
+  if (pending_entry_) {
+    if (pending_entry_index_ == -1)
+      delete pending_entry_;
+    pending_entry_index_ = -1;
+    pending_entry_ = nullptr;
+  }
 }
 
 void NavigationControllerImpl::SetPendingNavigationSSLError(bool error) {
@@ -2116,6 +2139,8 @@
   entries_.erase(entries_.begin() + transient_entry_index_);
   if (last_committed_entry_index_ > transient_entry_index_)
     last_committed_entry_index_--;
+  if (pending_entry_index_ > transient_entry_index_)
+    pending_entry_index_--;
   transient_entry_index_ = -1;
 }
 
@@ -2143,6 +2168,8 @@
   DiscardTransientEntry();
   entries_.insert(entries_.begin() + index,
                   NavigationEntryImpl::FromNavigationEntry(std::move(entry)));
+  if (pending_entry_index_ >= index)
+    pending_entry_index_++;
   transient_entry_index_ = index;
   delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL);
 }
@@ -2162,6 +2189,8 @@
                       source.entries_[i]->Clone());
     }
   }
+  DCHECK(pending_entry_index_ == -1 ||
+         pending_entry_ == GetEntryAtIndex(pending_entry_index_));
 }
 
 void NavigationControllerImpl::SetGetTimestampCallbackForTest(
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 6d478e4f..b23fb01 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -5288,4 +5288,82 @@
   main_test_rfh()->SimulateNavigationCommit(url_1);
 }
 
+// Test to ensure that the pending entry index is updated when a transient entry
+// is inserted or removed.
+TEST_F(NavigationControllerTest, PendingEntryIndexUpdatedWithTransient) {
+  NavigationControllerImpl& controller = controller_impl();
+  const GURL url_0("http://foo/0");
+  const GURL url_1("http://foo/1");
+  const GURL url_transient_1("http://foo/transient_1");
+  const GURL url_transient_2("http://foo/transient_2");
+
+  NavigateAndCommit(url_0);
+  NavigateAndCommit(url_1);
+  controller.GoBack();
+  contents()->CommitPendingNavigation();
+  controller.GoForward();
+
+  // Check the state before the insertion of the transient entry.
+  // entries[0] = url_0  <- last committed entry.
+  // entries[1] = url_1  <- pending entry.
+  ASSERT_EQ(2, controller.GetEntryCount());
+  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
+  EXPECT_EQ(1, controller.GetPendingEntryIndex());
+  EXPECT_EQ(controller.GetEntryAtIndex(1), controller.GetPendingEntry());
+  EXPECT_EQ(url_0, controller.GetEntryAtIndex(0)->GetURL());
+  EXPECT_EQ(url_1, controller.GetEntryAtIndex(1)->GetURL());
+
+  // Insert a transient entry before the pending one. It should increase the
+  // pending entry index by one (1 -> 2).
+  std::unique_ptr<NavigationEntry> transient_entry_1(new NavigationEntryImpl);
+  transient_entry_1->SetURL(url_transient_1);
+  controller.SetTransientEntry(std::move(transient_entry_1));
+
+  // Check the state after the insertion of the transient entry.
+  // entries[0] = url_0           <- last committed entry
+  // entries[1] = url_transient_1 <- transient entry
+  // entries[2] = url_1           <- pending entry
+  ASSERT_EQ(3, controller.GetEntryCount());
+  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
+  EXPECT_EQ(2, controller.GetPendingEntryIndex());
+  EXPECT_EQ(controller.GetEntryAtIndex(1), controller.GetTransientEntry());
+  EXPECT_EQ(controller.GetEntryAtIndex(2), controller.GetPendingEntry());
+  EXPECT_EQ(url_0, controller.GetEntryAtIndex(0)->GetURL());
+  EXPECT_EQ(url_transient_1, controller.GetEntryAtIndex(1)->GetURL());
+  EXPECT_EQ(url_1, controller.GetEntryAtIndex(2)->GetURL());
+
+  // Insert another transient entry. It should replace the previous one and this
+  // time the pending entry index should retain its value (i.e. 2).
+  std::unique_ptr<NavigationEntry> transient_entry_2(new NavigationEntryImpl);
+  transient_entry_2->SetURL(url_transient_2);
+  controller.SetTransientEntry(std::move(transient_entry_2));
+
+  // Check the state after the second insertion of a transient entry.
+  // entries[0] = url_0           <- last committed entry
+  // entries[1] = url_transient_2 <- transient entry
+  // entries[2] = url_1           <- pending entry
+  ASSERT_EQ(3, controller.GetEntryCount());
+  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
+  EXPECT_EQ(2, controller.GetPendingEntryIndex());
+  EXPECT_EQ(controller.GetEntryAtIndex(1), controller.GetTransientEntry());
+  EXPECT_EQ(controller.GetEntryAtIndex(2), controller.GetPendingEntry());
+  EXPECT_EQ(url_0, controller.GetEntryAtIndex(0)->GetURL());
+  EXPECT_EQ(url_transient_2, controller.GetEntryAtIndex(1)->GetURL());
+  EXPECT_EQ(url_1, controller.GetEntryAtIndex(2)->GetURL());
+
+  // Commit the pending entry.
+  contents()->CommitPendingNavigation();
+
+  // Check the final state.
+  // entries[0] = url_0
+  // entries[1] = url_1  <- last committed entry
+  ASSERT_EQ(2, controller.GetEntryCount());
+  EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
+  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
+  EXPECT_EQ(nullptr, controller.GetPendingEntry());
+  EXPECT_EQ(nullptr, controller.GetTransientEntry());
+  EXPECT_EQ(url_0, controller.GetEntryAtIndex(0)->GetURL());
+  EXPECT_EQ(url_1, controller.GetEntryAtIndex(1)->GetURL());
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 45cf9cf..40b9c06 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -97,7 +97,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "device/geolocation/geolocation_service_context.h"
-#include "device/vr/features.h"
+#include "device/vr/features/features.h"
 #include "device/wake_lock/public/interfaces/wake_lock_context.mojom.h"
 #include "device/wake_lock/public/interfaces/wake_lock_service.mojom.h"
 #include "media/base/media_switches.h"
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 d32665c..263113a 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
@@ -560,6 +560,10 @@
 }
 
 void RenderWidgetHostViewChildFrame::ShowDefinitionForSelection() {
+  if (frame_connector_) {
+    frame_connector_->GetRootRenderWidgetHostView()
+        ->ShowDefinitionForSelection();
+  }
 }
 
 bool RenderWidgetHostViewChildFrame::SupportsSpeech() const {
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index efbd3eb8..c3e20e2d 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -95,7 +95,9 @@
 };
 
 struct CompositorDependencies {
-  CompositorDependencies() : frame_sink_id_allocator(kDefaultClientId) {}
+  CompositorDependencies() : frame_sink_id_allocator(kDefaultClientId) {
+    frame_sink_manager_host.ConnectToFrameSinkManager();
+  }
 
   SingleThreadTaskGraphRunner task_graph_runner;
   FrameSinkManagerHost frame_sink_manager_host;
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 1884d92a..ed09e523 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1685,6 +1685,7 @@
     switches::kAllowLoopbackInPeerConnection,
     switches::kAndroidFontsPath,
     switches::kAudioBufferSize,
+    switches::kAutoplayPolicy,
     switches::kBlinkSettings,
     switches::kDefaultTileWidth,
     switches::kDefaultTileHeight,
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index ba2cd466..db981b6 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -439,16 +439,19 @@
   // On Android, user gestures are normally required, unless that requirement
   // is disabled with a command-line switch or the equivalent field trial is
   // is set to "Enabled".
-  prefs.user_gesture_required_for_media_playback = !command_line.HasSwitch(
-      switches::kDisableGestureRequirementForMediaPlayback);
+  prefs.user_gesture_required_for_media_playback =
+      !command_line.HasSwitch(
+          switches::kDisableGestureRequirementForMediaPlayback) &&
+      command_line.GetSwitchValueASCII(switches::kAutoplayPolicy) !=
+          switches::autoplay::kNoUserGestureRequiredPolicy;
 
   prefs.progress_bar_completion = GetProgressBarCompletionPolicy();
 
   prefs.use_solid_color_scrollbars = true;
 #else  // defined(OS_ANDROID)
   prefs.cross_origin_media_playback_requires_user_gesture =
-      base::FeatureList::GetInstance()->IsEnabled(
-          features::kCrossOriginMediaPlaybackRequiresUserGesture);
+      command_line.GetSwitchValueASCII(switches::kAutoplayPolicy) ==
+      switches::autoplay::kCrossOriginUserGestureRequiredPolicy;
 #endif  // defined(OS_ANDROID)
 
   const std::string touch_enabled_switch =
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 4c7c8f1..3c7f850 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -484,6 +484,9 @@
   // platform view for a guest).
   const TextInputManager::TextSelection* GetTextSelection();
 
+  // Get the focused view that should be used for retrieving the text selection.
+  RenderWidgetHostViewBase* GetFocusedViewForTextSelection();
+
  private:
   friend class RenderWidgetHostViewMacTest;
 
@@ -503,9 +506,6 @@
   // Dispatches a TTS session.
   void SpeakText(const std::string& text);
 
-  // Get the focused view that should be used for retrieving the text selection.
-  RenderWidgetHostViewBase* GetFocusedViewForTextSelection();
-
   // Adds/Removes frame observer based on state.
   void UpdateNeedsBeginFramesInternal();
 
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 41d21d0..1e60855 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -542,24 +542,24 @@
 ui::TextInputType RenderWidgetHostViewMac::GetTextInputType() {
   if (!GetActiveWidget())
     return ui::TEXT_INPUT_TYPE_NONE;
-  return GetTextInputManager()->GetTextInputState()->type;
+  return text_input_manager_->GetTextInputState()->type;
 }
 
 RenderWidgetHostImpl* RenderWidgetHostViewMac::GetActiveWidget() {
-  return GetTextInputManager() ? GetTextInputManager()->GetActiveWidget()
-                               : nullptr;
+  return text_input_manager_ ? text_input_manager_->GetActiveWidget() : nullptr;
 }
 
 const TextInputManager::CompositionRangeInfo*
 RenderWidgetHostViewMac::GetCompositionRangeInfo() {
-  return GetTextInputManager() ? text_input_manager_->GetCompositionRangeInfo()
-                               : nullptr;
+  return text_input_manager_ ? text_input_manager_->GetCompositionRangeInfo()
+                             : nullptr;
 }
 
 const TextInputManager::TextSelection*
 RenderWidgetHostViewMac::GetTextSelection() {
-  return text_input_manager_->GetTextSelection(
-      GetFocusedViewForTextSelection());
+  return text_input_manager_ ? text_input_manager_->GetTextSelection(
+                                   GetFocusedViewForTextSelection())
+                             : nullptr;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2421,41 +2421,32 @@
       NSPerformService(@"Look Up in Dictionary", pasteboard->get());
     return;
   }
-  dispatch_async(dispatch_get_main_queue(), ^{
-    NSPoint flippedBaselinePoint = {
-        baselinePoint.x, [view frame].size.height - baselinePoint.y,
-    };
-    [view showDefinitionForAttributedString:string
-                                    atPoint:flippedBaselinePoint];
-  });
+  NSPoint flippedBaselinePoint = {
+      baselinePoint.x, [view frame].size.height - baselinePoint.y,
+  };
+  [view showDefinitionForAttributedString:string atPoint:flippedBaselinePoint];
 }
 
 - (void)showLookUpDictionaryOverlayFromRange:(NSRange)range
                                   targetView:(NSView*)targetView {
-  RenderWidgetHostImpl* widgetHost = renderWidgetHostView_->render_widget_host_;
-  if (!widgetHost || !widgetHost->delegate())
+  content::RenderWidgetHostViewBase* focusedView =
+      renderWidgetHostView_->GetFocusedViewForTextSelection();
+  if (!focusedView)
     return;
-  widgetHost = widgetHost->delegate()->GetFocusedRenderWidgetHost(widgetHost);
 
+  RenderWidgetHostImpl* widgetHost =
+      RenderWidgetHostImpl::From(focusedView->GetRenderWidgetHost());
   if (!widgetHost)
     return;
 
-  // TODO(ekaramad): The position reported by the renderer is with respect to
-  // |widgetHost|'s coordinate space with y-axis inverted to conform to AppKit
-  // coordinate system. The point will need to be transformed into root view's
-  // coordinate system (RenderWidgetHostViewMac in this case). However, since
-  // the callback is invoked on IO thread it will require some thread hopping to
-  // do so. For this reason, for now, we accept this non-ideal way of fixing the
-  // point offset manually from the view bounds. This should be revisited when
-  // fixing issues in TextInputClientMac (https://crbug.com/643233).
-  gfx::Rect root_box = renderWidgetHostView_->GetViewBounds();
-  gfx::Rect view_box = widgetHost->GetView()->GetViewBounds();
-
   TextInputClientMac::GetInstance()->GetStringFromRange(
       widgetHost, range, ^(NSAttributedString* string, NSPoint baselinePoint) {
-        baselinePoint.x += view_box.origin().x() - root_box.origin().x();
-        baselinePoint.y +=
-            root_box.bottom_left().y() - view_box.bottom_left().y();
+        if (auto* rwhv = widgetHost->GetView()) {
+          gfx::Point pointInRootView = rwhv->TransformPointToRootCoordSpace(
+              gfx::Point(baselinePoint.x, baselinePoint.y));
+          baselinePoint.x = pointInRootView.x();
+          baselinePoint.y = pointInRootView.y();
+        }
         [self showLookUpDictionaryOverlayInternal:string
                                     baselinePoint:baselinePoint
                                        targetView:targetView];
@@ -2479,23 +2470,15 @@
   if (!widgetHost)
     return;
 
-  // TODO(ekaramad): The position reported by the renderer is with respect to
-  // |widgetHost|'s coordinate space with y-axis inverted to conform to AppKit
-  // coordinate system. The point will need to be transformed into root view's
-  // coordinate system (RenderWidgetHostViewMac in this case). However, since
-  // the callback is invoked on IO thread it will require some thread hopping to
-  // do so. For this reason, for now, we accept this non-ideal way of fixing the
-  // point offset manually from the view bounds. This should be revisited when
-  // fixing issues in TextInputClientMac (https://crbug.com/643233).
-  gfx::Rect root_box = renderWidgetHostView_->GetViewBounds();
-  gfx::Rect view_box = widgetHost->GetView()->GetViewBounds();
-
   TextInputClientMac::GetInstance()->GetStringAtPoint(
       widgetHost, transformedPoint,
       ^(NSAttributedString* string, NSPoint baselinePoint) {
-        baselinePoint.x += view_box.origin().x() - root_box.origin().x();
-        baselinePoint.y +=
-            root_box.bottom_left().y() - view_box.bottom_left().y();
+        if (auto* rwhv = widgetHost->GetView()) {
+          gfx::Point pointInRootView = rwhv->TransformPointToRootCoordSpace(
+              gfx::Point(baselinePoint.x, baselinePoint.y));
+          baselinePoint.x = pointInRootView.x();
+          baselinePoint.y = pointInRootView.y();
+        }
         [self showLookUpDictionaryOverlayInternal:string
                                     baselinePoint:baselinePoint
                                        targetView:self];
diff --git a/content/browser/renderer_host/text_input_client_message_filter.h b/content/browser/renderer_host/text_input_client_message_filter.h
index 6968cd79..4ded7f33 100644
--- a/content/browser/renderer_host/text_input_client_message_filter.h
+++ b/content/browser/renderer_host/text_input_client_message_filter.h
@@ -28,6 +28,8 @@
 
   // BrowserMessageFilter override:
   bool OnMessageReceived(const IPC::Message& message) override;
+  void OverrideThreadForMessage(const IPC::Message& message,
+                                BrowserThread::ID* thread) override;
 
  protected:
   ~TextInputClientMessageFilter() override;
diff --git a/content/browser/renderer_host/text_input_client_message_filter.mm b/content/browser/renderer_host/text_input_client_message_filter.mm
index 7ab20e40..35d43000 100644
--- a/content/browser/renderer_host/text_input_client_message_filter.mm
+++ b/content/browser/renderer_host/text_input_client_message_filter.mm
@@ -35,6 +35,17 @@
   return handled;
 }
 
+void TextInputClientMessageFilter::OverrideThreadForMessage(
+    const IPC::Message& message,
+    BrowserThread::ID* thread) {
+  switch (message.type()) {
+    case TextInputClientReplyMsg_GotStringAtPoint::ID:
+    case TextInputClientReplyMsg_GotStringForRange::ID:
+      *thread = BrowserThread::UI;
+      break;
+  }
+}
+
 TextInputClientMessageFilter::~TextInputClientMessageFilter() {}
 
 void TextInputClientMessageFilter::OnGotStringAtPoint(
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index ab89709..f22e0211 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -15,6 +15,7 @@
 #include "content/common/content_switches_internal.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
+#include "media/base/media_switches.h"
 #include "services/device/public/cpp/device_features.h"
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
 #include "ui/gl/gl_switches.h"
@@ -49,10 +50,6 @@
   WebRuntimeFeatures::EnableMediaControlsOverlayPlayButton(true);
 #else  // defined(OS_ANDROID)
   WebRuntimeFeatures::EnableNavigatorContentUtils(true);
-  if (base::FeatureList::IsEnabled(
-          features::kCrossOriginMediaPlaybackRequiresUserGesture)) {
-    WebRuntimeFeatures::EnableAutoplayMutedVideos(true);
-  }
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_ANDROID) || defined(USE_AURA)
@@ -383,6 +380,13 @@
   if (base::FeatureList::IsEnabled(features::kIdleTimeSpellChecking))
     WebRuntimeFeatures::EnableFeatureFromString("IdleTimeSpellChecking", true);
 
+#if !defined(OS_ANDROID)
+  if (command_line.GetSwitchValueASCII(switches::kAutoplayPolicy) ==
+      switches::autoplay::kCrossOriginUserGestureRequiredPolicy) {
+    WebRuntimeFeatures::EnableAutoplayMutedVideos(true);
+  }
+#endif
+
   // Enable explicitly enabled features, and then disable explicitly disabled
   // ones.
   if (command_line.HasSwitch(switches::kEnableBlinkFeatures)) {
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 1d04f8c..d7b13a1 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -110,12 +110,11 @@
     "java/src/org/chromium/content/browser/ActivityContentVideoViewEmbedder.java",
     "java/src/org/chromium/content/browser/AudioFocusDelegate.java",
     "java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java",
+    "java/src/org/chromium/content/browser/BaseChildProcessConnection.java",
     "java/src/org/chromium/content/browser/BindingManager.java",
     "java/src/org/chromium/content/browser/BindingManagerImpl.java",
     "java/src/org/chromium/content/browser/BrowserStartupController.java",
     "java/src/org/chromium/content/browser/ChildConnectionAllocator.java",
-    "java/src/org/chromium/content/browser/ChildProcessConnection.java",
-    "java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java",
     "java/src/org/chromium/content/browser/ChildProcessConstants.java",
     "java/src/org/chromium/content/browser/ChildProcessLauncher.java",
     "java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java",
@@ -133,10 +132,12 @@
     "java/src/org/chromium/content/browser/DeviceUtils.java",
     "java/src/org/chromium/content/browser/FloatingActionModeCallback.java",
     "java/src/org/chromium/content/browser/GpuProcessCallback.java",
+    "java/src/org/chromium/content/browser/ImportantChildProcessConnection.java",
     "java/src/org/chromium/content/browser/InterfaceRegistrarImpl.java",
     "java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java",
     "java/src/org/chromium/content/browser/JavascriptInterface.java",
     "java/src/org/chromium/content/browser/LauncherThread.java",
+    "java/src/org/chromium/content/browser/ManagedChildProcessConnection.java",
     "java/src/org/chromium/content/browser/MediaResourceGetter.java",
     "java/src/org/chromium/content/browser/MediaSessionImpl.java",
     "java/src/org/chromium/content/browser/MemoryMonitorAndroid.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java b/content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
similarity index 61%
rename from content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
rename to content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
index 710741b..52bfe5e5 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
@@ -26,67 +26,155 @@
 import java.io.IOException;
 
 import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
 
 /**
  * Manages a connection between the browser activity and a child service.
  */
-public class ChildProcessConnectionImpl implements ChildProcessConnection {
+public abstract class BaseChildProcessConnection {
+    private static final String TAG = "BaseChildProcessConn";
+
+    /**
+     * Used to notify the consumer about disconnection of the service. This callback is provided
+     * earlier than ConnectionCallbacks below, as a child process might die before the connection is
+     * fully set up.
+     */
+    interface DeathCallback {
+        // Called on Launcher thread.
+        void onChildProcessDied(BaseChildProcessConnection connection);
+    }
+
+    /**
+     * Used to notify the consumer about the process start. These callbacks will be invoked before
+     * the ConnectionCallbacks.
+     */
+    interface StartCallback {
+        /**
+         * Called when the child process has successfully started and is ready for connection
+         * setup.
+         */
+        void onChildStarted();
+
+        /**
+         * Called when the child process failed to start. This can happen if the process is already
+         * in use by another client.
+         */
+        void onChildStartFailed();
+    }
+
+    /**
+     * Used to notify the consumer about the connection being established.
+     */
+    interface ConnectionCallback {
+        /**
+         * Called when the connection to the service is established.
+         * @param connecion the connection object to the child process
+         */
+        void onConnected(BaseChildProcessConnection connection);
+    }
+
+    /** Used to create specialization connection instances. */
+    interface Factory {
+        BaseChildProcessConnection create(Context context, int number, boolean sandboxed,
+                DeathCallback deathCallback, String serviceClassName,
+                Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams);
+    }
+
+    /** Interface representing a connection to the Android service. Can be mocked in unit-tests. */
+    protected interface ChildServiceConnection {
+        boolean bind();
+        void unbind();
+        boolean isBound();
+    }
+
+    /** Implementation of ChildServiceConnection that does connect to a service. */
+    protected class ChildServiceConnectionImpl
+            implements ChildServiceConnection, ServiceConnection {
+        private final int mBindFlags;
+        private boolean mBound;
+
+        private Intent createServiceBindIntent() {
+            Intent intent = new Intent();
+            if (mCreationParams != null) {
+                mCreationParams.addIntentExtras(intent);
+            }
+            intent.setComponent(mServiceName);
+            return intent;
+        }
+
+        private ChildServiceConnectionImpl(int bindFlags) {
+            mBindFlags = bindFlags;
+        }
+
+        @Override
+        public boolean bind() {
+            if (!mBound) {
+                try {
+                    TraceEvent.begin("BaseChildProcessConnection.ChildServiceConnection.bind");
+                    Intent intent = createServiceBindIntent();
+                    if (mChildProcessCommonParameters != null) {
+                        intent.putExtras(mChildProcessCommonParameters);
+                    }
+                    mBound = mContext.bindService(intent, this, mBindFlags);
+                } finally {
+                    TraceEvent.end("BaseChildProcessConnection.ChildServiceConnection.bind");
+                }
+            }
+            return mBound;
+        }
+
+        @Override
+        public void unbind() {
+            if (mBound) {
+                mContext.unbindService(this);
+                mBound = false;
+            }
+        }
+
+        @Override
+        public boolean isBound() {
+            return mBound;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName className, final IBinder service) {
+            LauncherThread.post(new Runnable() {
+                @Override
+                public void run() {
+                    BaseChildProcessConnection.this.onServiceConnectedOnLauncherThread(service);
+                }
+            });
+        }
+
+        // Called on the main thread to notify that the child service did not disconnect gracefully.
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            LauncherThread.post(new Runnable() {
+                @Override
+                public void run() {
+                    BaseChildProcessConnection.this.onServiceDisconnectedOnLauncherThread();
+                }
+            });
+        }
+    }
+
+    // Caches whether non-sandboxed and sandboxed services require an extra
+    // binding flag provided via ChildProcessCreationParams.
+    // TODO(mnaganov): Get rid of it after the release of the next Android SDK.
+    private static Boolean sNeedsExtrabindFlags[] = new Boolean[2];
     private final Context mContext;
     private final int mServiceNumber;
-    private final boolean mInSandbox;
-    private final ChildProcessConnection.DeathCallback mDeathCallback;
+    private final boolean mSandboxed;
+    private final BaseChildProcessConnection.DeathCallback mDeathCallback;
     private final ComponentName mServiceName;
 
-    // Synchronization: While most internal flow occurs on the UI thread, the public API
-    // (specifically start and stop) may be called from any thread, hence all entry point methods
-    // into the class are synchronized on the lock to protect access to these members.
-    private final Object mLock = new Object();
-    private IChildProcessService mService;
-    // Set to true when the service connection callback runs. This differs from
-    // mServiceConnectComplete, which tracks that the connection completed successfully.
-    private boolean mDidOnServiceConnected;
-    // Set to true when the service connected successfully.
-    private boolean mServiceConnectComplete;
-    // Set to true when the service disconnects, as opposed to being properly closed. This happens
-    // when the process crashes or gets killed by the system out-of-memory killer.
-    private boolean mServiceDisconnected;
-    // When the service disconnects (i.e. mServiceDisconnected is set to true), the status of the
-    // oom bindings is stashed here for future inspection.
-    private boolean mWasOomProtected;
-    private int mPid;  // Process ID of the corresponding child process.
-    // Initial binding protects the newly spawned process from being killed before it is put to use,
-    // it is maintained between calls to start() and removeInitialBinding().
-    private ChildServiceConnection mInitialBinding;
-    // Strong binding will make the service priority equal to the priority of the activity. We want
-    // the OS to be able to kill background renderers as it kills other background apps, so strong
-    // bindings are maintained only for services that are active at the moment (between
-    // addStrongBinding() and removeStrongBinding()).
-    private ChildServiceConnection mStrongBinding;
-    // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls
-    // to start() and stop().
-    private ChildServiceConnection mWaivedBinding;
-    // Incremented on addStrongBinding(), decremented on removeStrongBinding().
-    private int mStrongBindingCount;
-    // Moderate binding will make the service priority equal to the priority of a visible process
-    // while the app is in the foreground. It will stay bound only while the app is in the
-    // foreground to protect a background process from the system out-of-memory killer.
-    private ChildServiceConnection mModerateBinding;
-
     // Parameters passed to the child process through the service binding intent.
     // If the service gets recreated by the framework the intent will be reused, so these parameters
     // should be common to all processes of that type.
     private final Bundle mChildProcessCommonParameters;
 
-    private final boolean mAlwaysInForeground;
     private final ChildProcessCreationParams mCreationParams;
 
-    // Caches whether non-sandboxed and sandboxed services require an extra
-    // binding flag provided via ChildProcessCreationParams.
-    // TODO(mnaganov): Get rid of it after the release of the next Android SDK.
-    private static Boolean sNeedsExtrabindFlags[] = new Boolean[2];
-
-    private static final String TAG = "ChildProcessConnect";
-
     private static class ConnectionParams {
         final String[] mCommandLine;
         final FileDescriptorInfo[] mFilesToBeMapped;
@@ -100,197 +188,142 @@
         }
     }
 
+    // Synchronization: While most internal flow occurs on the UI thread, the public API
+    // (specifically start and stop) may be called from any thread, hence all entry point methods
+    // into the class are synchronized on the lock to protect access to these members.
+    // TODO(jcivelli): crbug.com/714657 remove this lock.
+    private final Object mLock = new Object();
+
     // This is set in start() and is used in onServiceConnected().
-    private ChildProcessConnection.StartCallback mStartCallback;
+    @GuardedBy("mLock")
+    private StartCallback mStartCallback;
 
     // This is set in setupConnection() and is later used in doConnectionSetupLocked(), after which
     // the variable is cleared. Therefore this is only valid while the connection is being set up.
+    @GuardedBy("mLock")
     private ConnectionParams mConnectionParams;
 
     // Callback provided in setupConnection() that will communicate the result to the caller. This
     // has to be called exactly once after setupConnection(), even if setup fails, so that the
     // caller can free up resources associated with the setup attempt. This is set to null after the
     // call.
-    private ChildProcessConnection.ConnectionCallback mConnectionCallback;
+    @GuardedBy("mLock")
+    private ConnectionCallback mConnectionCallback;
 
-    private class ChildServiceConnection implements ServiceConnection {
-        private boolean mBound;
+    @GuardedBy("mLock")
+    private IChildProcessService mService;
 
-        private final int mBindFlags;
+    // Set to true when the service connection callback runs. This differs from
+    // mServiceConnectComplete, which tracks that the connection completed successfully.
+    @GuardedBy("mLock")
+    private boolean mDidOnServiceConnected;
 
-        private Intent createServiceBindIntent() {
-            Intent intent = new Intent();
-            if (mCreationParams != null) {
-                mCreationParams.addIntentExtras(intent);
-            }
-            intent.setComponent(mServiceName);
-            return intent;
-        }
+    // Set to true when the service connected successfully.
+    @GuardedBy("mLock")
+    private boolean mServiceConnectComplete;
 
-        public ChildServiceConnection(int bindFlags) {
-            mBindFlags = bindFlags;
-        }
+    // Set to true when the service disconnects, as opposed to being properly closed. This happens
+    // when the process crashes or gets killed by the system out-of-memory killer.
+    @GuardedBy("mLock")
+    private boolean mServiceDisconnected;
 
-        boolean bind() {
-            if (!mBound) {
-                try {
-                    TraceEvent.begin("ChildProcessConnectionImpl.ChildServiceConnection.bind");
-                    Intent intent = createServiceBindIntent();
-                    if (mChildProcessCommonParameters != null) {
-                        intent.putExtras(mChildProcessCommonParameters);
-                    }
-                    mBound = mContext.bindService(intent, this, mBindFlags);
-                } finally {
-                    TraceEvent.end("ChildProcessConnectionImpl.ChildServiceConnection.bind");
-                }
-            }
-            return mBound;
-        }
+    // Process ID of the corresponding child process.
+    @GuardedBy("mLock")
+    private int mPid;
 
-        void unbind() {
-            if (mBound) {
-                mContext.unbindService(this);
-                mBound = false;
-            }
-        }
-
-        boolean isBound() {
-            return mBound;
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName className, final IBinder service) {
-            LauncherThread.post(new Runnable() {
-                @Override
-                public void run() {
-                    ChildProcessConnectionImpl.this.onServiceConnectedOnLauncherThread(service);
-                }
-            });
-        }
-
-        // Called on the main thread to notify that the child service did not disconnect gracefully.
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            LauncherThread.post(new Runnable() {
-                @Override
-                public void run() {
-                    ChildProcessConnectionImpl.this.onServiceDisconnectedOnLauncherThread();
-                }
-            });
-        }
-    }
-
-    ChildProcessConnectionImpl(Context context, int number, boolean inSandbox,
-            ChildProcessConnection.DeathCallback deathCallback, String serviceClassName,
-            Bundle childProcessCommonParameters, boolean alwaysInForeground,
-            ChildProcessCreationParams creationParams) {
+    protected BaseChildProcessConnection(Context context, int number, boolean sandboxed,
+            DeathCallback deathCallback, String serviceClassName,
+            Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) {
         mContext = context;
         mServiceNumber = number;
-        mInSandbox = inSandbox;
+        mSandboxed = sandboxed;
         mDeathCallback = deathCallback;
         String packageName =
                 creationParams != null ? creationParams.getPackageName() : context.getPackageName();
         mServiceName = new ComponentName(packageName, serviceClassName + mServiceNumber);
         mChildProcessCommonParameters = childProcessCommonParameters;
-        mAlwaysInForeground = alwaysInForeground;
         mCreationParams = creationParams;
-        int initialFlags = Context.BIND_AUTO_CREATE;
-        if (mAlwaysInForeground) initialFlags |= Context.BIND_IMPORTANT;
-        int extraBindFlags = 0;
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && mCreationParams != null
-                && mCreationParams.getIsExternalService()
-                && isExportedService(inSandbox, mContext, mServiceName)) {
-            extraBindFlags = Context.BIND_EXTERNAL_SERVICE;
-        }
-        mInitialBinding = new ChildServiceConnection(initialFlags | extraBindFlags);
-        mStrongBinding = new ChildServiceConnection(
-                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFlags);
-        mWaivedBinding = new ChildServiceConnection(
-                Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBindFlags);
-        mModerateBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE | extraBindFlags);
     }
 
-    private static boolean isExportedService(boolean inSandbox, Context context,
-            ComponentName serviceName) {
-        // Check for the cached value first. It is assumed that all pooled child services
-        // have identical attributes in the manifest.
-        final int arrayIndex = inSandbox ? 1 : 0;
-        if (sNeedsExtrabindFlags[arrayIndex] != null) {
-            return sNeedsExtrabindFlags[arrayIndex].booleanValue();
-        }
-        boolean result = false;
-        try {
-            PackageManager packageManager = context.getPackageManager();
-            ServiceInfo serviceInfo = packageManager.getServiceInfo(serviceName, 0);
-            result = serviceInfo.exported;
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Could not retrieve info about service %s", serviceName, e);
-        }
-        sNeedsExtrabindFlags[arrayIndex] = Boolean.valueOf(result);
-        return result;
+    public final Context getContext() {
+        return mContext;
     }
 
-    @Override
-    public int getServiceNumber() {
+    public final int getServiceNumber() {
         return mServiceNumber;
     }
 
-    @Override
-    public boolean isInSandbox() {
-        return mInSandbox;
+    public final boolean isSandboxed() {
+        return mSandboxed;
     }
 
-    @Override
-    public String getPackageName() {
+    public final String getPackageName() {
         return mCreationParams != null ? mCreationParams.getPackageName()
-                : mContext.getPackageName();
+                                       : mContext.getPackageName();
     }
 
-    @Override
-    public ChildProcessCreationParams getCreationParams() {
+    public final ChildProcessCreationParams getCreationParams() {
         return mCreationParams;
     }
 
-    @Override
-    public IChildProcessService getService() {
+    public final IChildProcessService getService() {
         synchronized (mLock) {
             return mService;
         }
     }
 
-    @Override
+    public final ComponentName getServiceName() {
+        return mServiceName;
+    }
+
+    /**
+     * @return the connection pid, or 0 if not yet connected
+     */
     public int getPid() {
         synchronized (mLock) {
             return mPid;
         }
     }
 
-    @Override
-    public void start(ChildProcessConnection.StartCallback startCallback) {
+    /**
+     * Starts a connection to an IChildProcessService. This must be followed by a call to
+     * setupConnection() to setup the connection parameters. start() and setupConnection() are
+     * separate to allow to pass whatever parameters are available in start(), and complete the
+     * remainder later while reducing the connection setup latency.
+     * @param startCallback (optional) callback when the child process starts or fails to start.
+     */
+    public void start(StartCallback startCallback) {
         try {
-            TraceEvent.begin("ChildProcessConnectionImpl.start");
+            TraceEvent.begin("BaseChildProcessConnection.start");
             assert LauncherThread.runningOnLauncherThread();
             synchronized (mLock) {
-                assert mConnectionParams == null :
-                        "setupConnection() called before start() in ChildProcessConnectionImpl.";
+                assert mConnectionParams
+                        == null
+                        : "setupConnection() called before start() in BaseChildProcessConnection.";
 
                 mStartCallback = startCallback;
 
-                if (!mInitialBinding.bind()) {
+                if (!bind()) {
                     Log.e(TAG, "Failed to establish the service connection.");
                     // We have to notify the caller so that they can free-up associated resources.
                     // TODO(ppi): Can we hard-fail here?
-                    mDeathCallback.onChildProcessDied(ChildProcessConnectionImpl.this);
-                } else {
-                    mWaivedBinding.bind();
+                    mDeathCallback.onChildProcessDied(BaseChildProcessConnection.this);
                 }
             }
         } finally {
-            TraceEvent.end("ChildProcessConnectionImpl.start");
+            TraceEvent.end("BaseChildProcessConnection.start");
         }
     }
 
-    @Override
+    /**
+     * Setups the connection after it was started with start().
+     * @param commandLine (optional) will be ignored if the command line was already sent in start()
+     * @param filesToBeMapped a list of file descriptors that should be registered
+     * @param callback optional client specified callbacks that the child can use to communicate
+     *                 with the parent process
+     * @param connectionCallback will be called exactly once after the connection is set up or the
+     *                           setup fails
+     */
     public void setupConnection(String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
             @Nullable IBinder callback, ConnectionCallback connectionCallback) {
         assert LauncherThread.runningOnLauncherThread();
@@ -298,11 +331,11 @@
             assert mConnectionParams == null;
             if (mServiceDisconnected) {
                 Log.w(TAG, "Tried to setup a connection that already disconnected.");
-                connectionCallback.onConnected(0);
+                connectionCallback.onConnected(null);
                 return;
             }
             try {
-                TraceEvent.begin("ChildProcessConnectionImpl.setupConnection");
+                TraceEvent.begin("BaseChildProcessConnection.setupConnection");
                 mConnectionCallback = connectionCallback;
                 mConnectionParams = new ConnectionParams(commandLine, filesToBeMapped, callback);
                 // Run the setup if the service is already connected. If not,
@@ -311,22 +344,19 @@
                     doConnectionSetupLocked();
                 }
             } finally {
-                TraceEvent.end("ChildProcessConnectionImpl.setupConnection");
+                TraceEvent.end("BaseChildProcessConnection.setupConnection");
             }
         }
     }
 
-    @Override
+    /**
+     * Terminates the connection to IChildProcessService, closing all bindings. It is safe to call
+     * this multiple times.
+     */
     public void stop() {
         synchronized (mLock) {
-            mInitialBinding.unbind();
-            mStrongBinding.unbind();
-            mWaivedBinding.unbind();
-            mModerateBinding.unbind();
-            mStrongBindingCount = 0;
-            if (mService != null) {
-                mService = null;
-            }
+            unbind();
+            mService = null;
             mConnectionParams = null;
         }
     }
@@ -341,7 +371,7 @@
             }
             try {
                 TraceEvent.begin(
-                        "ChildProcessConnectionImpl.ChildServiceConnection.onServiceConnected");
+                        "BaseChildProcessConnection.ChildServiceConnection.onServiceConnected");
                 mDidOnServiceConnected = true;
                 mService = IChildProcessService.Stub.asInterface(service);
 
@@ -381,7 +411,7 @@
                 }
             } finally {
                 TraceEvent.end(
-                        "ChildProcessConnectionImpl.ChildServiceConnection.onServiceConnected");
+                        "BaseChildProcessConnection.ChildServiceConnection.onServiceConnected");
             }
         }
     }
@@ -394,16 +424,14 @@
             if (mServiceDisconnected) {
                 return;
             }
-            // Stash the status of the oom bindings, since stop() will release all bindings.
-            mWasOomProtected = isCurrentlyOomProtected();
             mServiceDisconnected = true;
             Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d", mPid);
             stop(); // We don't want to auto-restart on crash. Let the browser do that.
-            mDeathCallback.onChildProcessDied(ChildProcessConnectionImpl.this);
+            mDeathCallback.onChildProcessDied(BaseChildProcessConnection.this);
             // If we have a pending connection callback, we need to communicate the failure to
             // the caller.
             if (mConnectionCallback != null) {
-                mConnectionCallback.onConnected(0);
+                mConnectionCallback.onConnected(null);
             }
             mConnectionCallback = null;
         }
@@ -415,7 +443,7 @@
             assert mPid != 0 : "Child service claims to be run by a process of pid=0.";
 
             if (mConnectionCallback != null) {
-                mConnectionCallback.onConnected(mPid);
+                mConnectionCallback.onConnected(this);
             }
             mConnectionCallback = null;
         }
@@ -426,9 +454,10 @@
      * connection has been established (as signaled by onServiceConnected()). These two events can
      * happen in any order. Has to be called with mLock.
      */
+    @GuardedBy("mLock")
     private void doConnectionSetupLocked() {
         try {
-            TraceEvent.begin("ChildProcessConnectionImpl.doConnectionSetupLocked");
+            TraceEvent.begin("BaseChildProcessConnection.doConnectionSetupLocked");
             assert mServiceConnectComplete && mService != null;
             assert mConnectionParams != null;
 
@@ -460,129 +489,55 @@
             }
             mConnectionParams = null;
         } finally {
-            TraceEvent.end("ChildProcessConnectionImpl.doConnectionSetupLocked");
+            TraceEvent.end("BaseChildProcessConnection.doConnectionSetupLocked");
         }
     }
 
-    @Override
-    public boolean isInitialBindingBound() {
-        synchronized (mLock) {
-            return mInitialBinding.isBound();
-        }
+    /** Subclasses should implement this method to bind/unbind to the actual service. */
+    protected abstract boolean bind();
+    protected abstract void unbind();
+
+    protected ChildServiceConnection createServiceConnection(int bindFlags) {
+        return new ChildServiceConnectionImpl(bindFlags);
     }
 
-    @Override
-    public boolean isStrongBindingBound() {
-        synchronized (mLock) {
-            return mStrongBinding.isBound();
-        }
+    protected boolean shouldBindAsExportedService() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && getCreationParams() != null
+                && getCreationParams().getIsExternalService()
+                && isExportedService(isSandboxed(), getContext(), getServiceName());
     }
 
-    @Override
-    public void removeInitialBinding() {
-        synchronized (mLock) {
-            assert !mAlwaysInForeground;
-            mInitialBinding.unbind();
+    private static boolean isExportedService(
+            boolean inSandbox, Context context, ComponentName serviceName) {
+        // Check for the cached value first. It is assumed that all pooled child services
+        // have identical attributes in the manifest.
+        final int arrayIndex = inSandbox ? 1 : 0;
+        if (sNeedsExtrabindFlags[arrayIndex] != null) {
+            return sNeedsExtrabindFlags[arrayIndex].booleanValue();
         }
-    }
-
-    @Override
-    public boolean isOomProtectedOrWasWhenDied() {
-        synchronized (mLock) {
-            if (mServiceDisconnected) {
-                return mWasOomProtected;
-            } else {
-                return isCurrentlyOomProtected();
-            }
+        boolean result = false;
+        try {
+            PackageManager packageManager = context.getPackageManager();
+            ServiceInfo serviceInfo = packageManager.getServiceInfo(serviceName, 0);
+            result = serviceInfo.exported;
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Could not retrieve info about service %s", serviceName, e);
         }
-    }
-
-    private boolean isCurrentlyOomProtected() {
-        synchronized (mLock) {
-            assert !mServiceDisconnected;
-            if (mAlwaysInForeground) return ChildProcessLauncher.isApplicationInForeground();
-            return mInitialBinding.isBound() || mStrongBinding.isBound();
-        }
-    }
-
-    @Override
-    public void dropOomBindings() {
-        synchronized (mLock) {
-            assert !mAlwaysInForeground;
-            mInitialBinding.unbind();
-
-            mStrongBindingCount = 0;
-            mStrongBinding.unbind();
-
-            mModerateBinding.unbind();
-        }
-    }
-
-    @Override
-    public void addStrongBinding() {
-        synchronized (mLock) {
-            if (mService == null) {
-                Log.w(TAG, "The connection is not bound for %d", mPid);
-                return;
-            }
-            if (mStrongBindingCount == 0) {
-                mStrongBinding.bind();
-            }
-            mStrongBindingCount++;
-        }
-    }
-
-    @Override
-    public void removeStrongBinding() {
-        synchronized (mLock) {
-            if (mService == null) {
-                Log.w(TAG, "The connection is not bound for %d", mPid);
-                return;
-            }
-            assert mStrongBindingCount > 0;
-            mStrongBindingCount--;
-            if (mStrongBindingCount == 0) {
-                mStrongBinding.unbind();
-            }
-        }
-    }
-
-    @Override
-    public boolean isModerateBindingBound() {
-        synchronized (mLock) {
-            return mModerateBinding.isBound();
-        }
-    }
-
-    @Override
-    public void addModerateBinding() {
-        synchronized (mLock) {
-            if (mService == null) {
-                Log.w(TAG, "The connection is not bound for %d", mPid);
-                return;
-            }
-            mModerateBinding.bind();
-        }
-    }
-
-    @Override
-    public void removeModerateBinding() {
-        synchronized (mLock) {
-            if (mService == null) {
-                Log.w(TAG, "The connection is not bound for %d", mPid);
-                return;
-            }
-            mModerateBinding.unbind();
-        }
+        sNeedsExtrabindFlags[arrayIndex] = Boolean.valueOf(result);
+        return result;
     }
 
     @VisibleForTesting
     public void crashServiceForTesting() throws RemoteException {
-        mService.crashIntentionallyForTesting();
+        synchronized (mLock) {
+            mService.crashIntentionallyForTesting();
+        }
     }
 
     @VisibleForTesting
     public boolean isConnected() {
-        return mService != null;
+        synchronized (mLock) {
+            return mService != null;
+        }
     }
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/BindingManager.java b/content/public/android/java/src/org/chromium/content/browser/BindingManager.java
index 75fda5a8..dfa3581 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BindingManager.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BindingManager.java
@@ -32,7 +32,7 @@
      * Registers a freshly started child process. This can be called on any thread.
      * @param pid handle of the service process
      */
-    void addNewConnection(int pid, ChildProcessConnection connection);
+    void addNewConnection(int pid, ManagedChildProcessConnection connection);
 
     /**
      * Called when the service visibility changes or is determined for the first time. On low-memory
@@ -70,18 +70,11 @@
     void onBroughtToForeground();
 
     /**
-     * @return True iff the given service process is protected from the out-of-memory killing, or it
-     * was protected when it died unexpectedly. This can be used to decide if a disconnection of a
-     * renderer was a crash or a probable out-of-memory kill. This can be called on any thread.
-     */
-    boolean isOomProtected(int pid);
-
-    /**
      * Should be called when the connection to the child process goes away (either after a clean
      * exit or an unexpected crash). At this point we let go of the reference to the
      * ChildProcessConnection. This can be called on any thread.
      */
-    void clearConnection(int pid);
+    void removeConnection(int pid);
 
     /**
      * Starts moderate binding management.
diff --git a/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java b/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
index beb3074..81ee555 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
@@ -22,6 +22,8 @@
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
+import javax.annotation.concurrent.GuardedBy;
+
 /**
  * Manages oom bindings used to bound child services.
  */
@@ -47,7 +49,10 @@
     private static class ModerateBindingPool
             extends LruCache<Integer, ManagedConnection> implements ComponentCallbacks2 {
         private final Object mDelayedClearerLock = new Object();
+
+        @GuardedBy("mDelayedClearerLock")
         private Runnable mDelayedClearer;
+
         private final Handler mHandler = new Handler(ThreadUtils.getUiThreadLooper());
 
         public ModerateBindingPool(int maxSize) {
@@ -102,8 +107,8 @@
         }
 
         void addConnection(ManagedConnection managedConnection) {
-            ChildProcessConnection connection = managedConnection.mConnection;
-            if (connection != null && connection.isInSandbox()) {
+            ManagedChildProcessConnection connection = managedConnection.mConnection;
+            if (connection != null && connection.isSandboxed()) {
                 managedConnection.addModerateBinding();
                 if (connection.isModerateBindingBound()) {
                     put(connection.getServiceNumber(), managedConnection);
@@ -114,8 +119,8 @@
         }
 
         void removeConnection(ManagedConnection managedConnection) {
-            ChildProcessConnection connection = managedConnection.mConnection;
-            if (connection != null && connection.isInSandbox()) {
+            ManagedChildProcessConnection connection = managedConnection.mConnection;
+            if (connection != null && connection.isSandboxed()) {
                 remove(connection.getServiceNumber());
             }
         }
@@ -163,15 +168,15 @@
             new AtomicReference<>();
 
     /**
-     * Wraps ChildProcessConnection keeping track of additional information needed to manage the
-     * bindings of the connection. The reference to ChildProcessConnection is cleared when the
-     * connection goes away, but ManagedConnection itself is kept (until overwritten by a new entry
-     * for the same pid).
+     * Wraps ManagedChildProcessConnection keeping track of additional information needed to manage
+     * the bindings of the connection. The reference to ManagedChildProcessConnection is cleared
+     * when the connection goes away, but ManagedConnection itself is kept (until overwritten by a
+     * new entry for the same pid).
      */
     private class ManagedConnection {
         // Set in constructor, cleared in clearConnection() (on a separate thread).
         // Need to keep a local reference to avoid it being cleared while using it.
-        private ChildProcessConnection mConnection;
+        private ManagedChildProcessConnection mConnection;
 
         // True iff there is a strong binding kept on the service because it is working in
         // foreground.
@@ -181,15 +186,12 @@
         // application background period.
         private boolean mBoundForBackgroundPeriod;
 
-        // When mConnection is cleared, oom binding status is stashed here.
-        private boolean mWasOomProtected;
-
         /**
          * Removes the initial service binding.
          * @return true if the binding was removed.
          */
         private boolean removeInitialBinding() {
-            ChildProcessConnection connection = mConnection;
+            ManagedChildProcessConnection connection = mConnection;
             if (connection == null || !connection.isInitialBindingBound()) return false;
 
             connection.removeInitialBinding();
@@ -198,7 +200,7 @@
 
         /** Adds a strong service binding. */
         private void addStrongBinding() {
-            ChildProcessConnection connection = mConnection;
+            ManagedChildProcessConnection connection = mConnection;
             if (connection == null) return;
 
             connection.addStrongBinding();
@@ -208,7 +210,7 @@
 
         /** Removes a strong service binding. */
         private void removeStrongBinding(final boolean keepAsModerate) {
-            final ChildProcessConnection connection = mConnection;
+            final ManagedChildProcessConnection connection = mConnection;
             // We have to fail gracefully if the strong binding is not present, as on low-end the
             // binding could have been removed by dropOomBindings() when a new service was started.
             if (connection == null || !connection.isStrongBindingBound()) return;
@@ -239,7 +241,7 @@
          * binding.
          * @param connection The ChildProcessConnection to add to the moderate binding pool.
          */
-        private void addConnectionToModerateBindingPool(ChildProcessConnection connection) {
+        private void addConnectionToModerateBindingPool(ManagedChildProcessConnection connection) {
             ModerateBindingPool moderateBindingPool = mModerateBindingPool.get();
             if (moderateBindingPool != null && !connection.isStrongBindingBound()) {
                 moderateBindingPool.addConnection(ManagedConnection.this);
@@ -248,15 +250,14 @@
 
         /** Removes the moderate service binding. */
         private void removeModerateBinding() {
-            ChildProcessConnection connection = mConnection;
+            ManagedChildProcessConnection connection = mConnection;
             if (connection == null || !connection.isModerateBindingBound()) return;
-
             connection.removeModerateBinding();
         }
 
         /** Adds the moderate service binding. */
         private void addModerateBinding() {
-            ChildProcessConnection connection = mConnection;
+            ManagedChildProcessConnection connection = mConnection;
             if (connection == null) return;
 
             connection.addModerateBinding();
@@ -268,13 +269,13 @@
          */
         private void dropBindings() {
             assert mIsLowMemoryDevice;
-            ChildProcessConnection connection = mConnection;
+            ManagedChildProcessConnection connection = mConnection;
             if (connection == null) return;
 
             connection.dropOomBindings();
         }
 
-        ManagedConnection(ChildProcessConnection connection) {
+        ManagedConnection(ManagedChildProcessConnection connection) {
             mConnection = connection;
         }
 
@@ -315,27 +316,11 @@
             mBoundForBackgroundPeriod = nextBound;
         }
 
-        boolean isOomProtected() {
-            // When a process crashes, we can be queried about its oom status before or after the
-            // connection is cleared. For the latter case, the oom status is stashed in
-            // mWasOomProtected.
-            ChildProcessConnection connection = mConnection;
-            return connection != null
-                    ? connection.isOomProtectedOrWasWhenDied() : mWasOomProtected;
-        }
-
         void clearConnection() {
-            mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied();
             ModerateBindingPool moderateBindingPool = mModerateBindingPool.get();
             if (moderateBindingPool != null) moderateBindingPool.removeConnection(this);
             mConnection = null;
         }
-
-        /** @return true iff the reference to the connection is no longer held */
-        @VisibleForTesting
-        boolean isConnectionCleared() {
-            return mConnection == null;
-        }
     }
 
     // This can be manipulated on different threads, synchronize access on mManagedConnections.
@@ -381,7 +366,7 @@
     }
 
     @Override
-    public void addNewConnection(int pid, ChildProcessConnection connection) {
+    public void addNewConnection(int pid, ManagedChildProcessConnection connection) {
         // This will reset the previous entry for the pid in the unlikely event of the OS
         // reusing renderer pids.
         synchronized (mManagedConnections) {
@@ -454,31 +439,24 @@
     }
 
     @Override
-    public boolean isOomProtected(int pid) {
-        // In the unlikely event of the OS reusing renderer pid, the call will refer to the most
-        // recent renderer of the given pid. The binding state for a pid is being reset in
-        // addNewConnection().
+    public void removeConnection(int pid) {
         ManagedConnection managedConnection;
         synchronized (mManagedConnections) {
             managedConnection = mManagedConnections.get(pid);
+            if (managedConnection != null) {
+                mManagedConnections.remove(pid);
+            }
         }
-        return managedConnection != null ? managedConnection.isOomProtected() : false;
-    }
-
-    @Override
-    public void clearConnection(int pid) {
-        ManagedConnection managedConnection;
-        synchronized (mManagedConnections) {
-            managedConnection = mManagedConnections.get(pid);
+        if (managedConnection != null) {
+            managedConnection.clearConnection();
         }
-        if (managedConnection != null) managedConnection.clearConnection();
     }
 
     /** @return true iff the connection reference is no longer held */
     @VisibleForTesting
     public boolean isConnectionCleared(int pid) {
         synchronized (mManagedConnections) {
-            return mManagedConnections.get(pid).isConnectionCleared();
+            return mManagedConnections.get(pid) == null;
         }
     }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java b/content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java
index 43c0367..7e3e9db 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java
@@ -48,8 +48,11 @@
     private static int sSandboxedServicesCountForTesting = -1;
     private static String sSandboxedServicesNameForTesting;
 
+    // The factory used to create BaseChildProcessConnection instances.
+    private final BaseChildProcessConnection.Factory mConnectionFactory;
+
     // Connections to services. Indices of the array correspond to the service numbers.
-    private final ChildProcessConnection[] mChildProcessConnections;
+    private final BaseChildProcessConnection[] mChildProcessConnections;
 
     private final String mChildClassName;
     private final boolean mInSandbox;
@@ -63,11 +66,12 @@
 
     @SuppressFBWarnings("LI_LAZY_INIT_STATIC") // Method is single thread.
     public static ChildConnectionAllocator getAllocator(
-            Context context, String packageName, boolean inSandbox) {
+            Context context, String packageName, boolean sandboxed) {
         assert LauncherThread.runningOnLauncherThread();
-        if (!inSandbox) {
+        if (!sandboxed) {
             if (sPrivilegedChildConnectionAllocator == null) {
-                sPrivilegedChildConnectionAllocator = new ChildConnectionAllocator(false,
+                sPrivilegedChildConnectionAllocator = new ChildConnectionAllocator(
+                        ImportantChildProcessConnection.FACTORY, false /* sandboxed */,
                         getNumberOfServices(context, false, packageName),
                         getClassNameOfService(context, false, packageName));
             }
@@ -83,8 +87,8 @@
                             + " inSandbox = true",
                     packageName);
             sSandboxedChildConnectionAllocatorMap.put(packageName,
-                    new ChildConnectionAllocator(true,
-                            getNumberOfServices(context, true, packageName),
+                    new ChildConnectionAllocator(ManagedChildProcessConnection.FACTORY,
+                            true /* sandboxed */, getNumberOfServices(context, true, packageName),
                             getClassNameOfService(context, true, packageName)));
         }
         return sSandboxedChildConnectionAllocatorMap.get(packageName);
@@ -160,9 +164,10 @@
         sSandboxedServicesNameForTesting = serviceName;
     }
 
-    private ChildConnectionAllocator(
+    private ChildConnectionAllocator(BaseChildProcessConnection.Factory connectionFactory,
             boolean inSandbox, int numChildServices, String serviceClassName) {
-        mChildProcessConnections = new ChildProcessConnectionImpl[numChildServices];
+        mConnectionFactory = connectionFactory;
+        mChildProcessConnections = new BaseChildProcessConnection[numChildServices];
         mFreeConnectionIndices = new ArrayList<Integer>(numChildServices);
         for (int i = 0; i < numChildServices; i++) {
             mFreeConnectionIndices.add(i);
@@ -172,9 +177,9 @@
     }
 
     // Allocates or enqueues. If there are no free slots, returns null and enqueues the spawn data.
-    public ChildProcessConnection allocate(ChildSpawnData spawnData,
-            ChildProcessConnection.DeathCallback deathCallback, Bundle childProcessCommonParameters,
-            boolean queueIfNoSlotAvailable) {
+    public BaseChildProcessConnection allocate(ChildSpawnData spawnData,
+            BaseChildProcessConnection.DeathCallback deathCallback,
+            Bundle childProcessCommonParameters, boolean queueIfNoSlotAvailable) {
         assert LauncherThread.runningOnLauncherThread();
         assert spawnData.isInSandbox() == mInSandbox;
         if (mFreeConnectionIndices.isEmpty()) {
@@ -186,15 +191,15 @@
         }
         int slot = mFreeConnectionIndices.remove(0);
         assert mChildProcessConnections[slot] == null;
-        mChildProcessConnections[slot] = new ChildProcessConnectionImpl(spawnData.getContext(),
-                slot, mInSandbox, deathCallback, mChildClassName, childProcessCommonParameters,
-                spawnData.isAlwaysInForeground(), spawnData.getCreationParams());
+        mChildProcessConnections[slot] = mConnectionFactory.create(spawnData.getContext(), slot,
+                mInSandbox, deathCallback, mChildClassName, childProcessCommonParameters,
+                spawnData.getCreationParams());
         Log.d(TAG, "Allocator allocated a connection, sandbox: %b, slot: %d", mInSandbox, slot);
         return mChildProcessConnections[slot];
     }
 
     // Also return the first ChildSpawnData in the pending queue, if any.
-    public ChildSpawnData free(ChildProcessConnection connection) {
+    public ChildSpawnData free(BaseChildProcessConnection connection) {
         assert LauncherThread.runningOnLauncherThread();
         int slot = connection.getServiceNumber();
         if (mChildProcessConnections[slot] != connection) {
@@ -228,7 +233,7 @@
     }
 
     @VisibleForTesting
-    ChildProcessConnection[] connectionArrayForTesting() {
+    BaseChildProcessConnection[] connectionArrayForTesting() {
         return mChildProcessConnections;
     }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
deleted file mode 100644
index 79bb922..0000000
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.content.browser;
-
-import android.os.IBinder;
-
-import org.chromium.base.process_launcher.ChildProcessCreationParams;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
-import org.chromium.base.process_launcher.IChildProcessService;
-
-import javax.annotation.Nullable;
-
-/**
- * Manages a connection between the browser activity and a child service. ChildProcessConnection is
- * responsible for estabilishing the connection (start()), closing it (stop()) and manipulating the
- * bindings held onto the service (addStrongBinding(), removeStrongBinding(),
- * removeInitialBinding()).
- */
-public interface ChildProcessConnection {
-    /**
-     * Used to notify the consumer about disconnection of the service. This callback is provided
-     * earlier than ConnectionCallbacks below, as a child process might die before the connection is
-     * fully set up.
-     */
-    interface DeathCallback {
-        // Called on Launcher thread.
-        void onChildProcessDied(ChildProcessConnection connection);
-    }
-
-    /**
-     * Used to notify the consumer about the process start. These callbacks will be invoked before
-     * the ConnectionCallbacks.
-     */
-    interface StartCallback {
-        /**
-         * Called when the child process has successfully started and is ready for connection
-         * setup.
-         */
-        void onChildStarted();
-
-        /**
-         * Called when the child process failed to start. This can happen if the process is already
-         * in use by another client.
-         */
-        void onChildStartFailed();
-    }
-
-    /**
-     * Used to notify the consumer about the connection being established.
-     */
-    interface ConnectionCallback {
-        /**
-         * Called when the connection to the service is established.
-         * @param pid the pid of the child process
-         */
-        void onConnected(int pid);
-    }
-
-    int getServiceNumber();
-
-    boolean isInSandbox();
-
-    String getPackageName();
-
-    ChildProcessCreationParams getCreationParams();
-
-    IChildProcessService getService();
-
-    /**
-     * @return the connection pid, or 0 if not yet connected
-     */
-    int getPid();
-
-    /**
-     * Starts a connection to an IChildProcessService. This must be followed by a call to
-     * setupConnection() to setup the connection parameters. start() and setupConnection() are
-     * separate to allow to pass whatever parameters are available in start(), and complete the
-     * remainder later while reducing the connection setup latency.
-     * @param startCallback (optional) callback when the child process starts or fails to start.
-     */
-    void start(StartCallback startCallback);
-
-    /**
-     * Setups the connection after it was started with start().
-     * @param commandLine (optional) will be ignored if the command line was already sent in start()
-     * @param filesToBeMapped a list of file descriptors that should be registered
-     * @param callback optional client specified callbacks that the child can use to communicate
-     *                 with the parent process
-     * @param connectionCallback will be called exactly once after the connection is set up or the
-     *                           setup fails
-     */
-    void setupConnection(String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
-            @Nullable IBinder callback, ConnectionCallback connectionCallback);
-
-    /**
-     * Terminates the connection to IChildProcessService, closing all bindings. It is safe to call
-     * this multiple times.
-     */
-    void stop();
-
-    /** @return true iff the initial oom binding is currently bound. */
-    boolean isInitialBindingBound();
-
-    /** @return true iff the strong oom binding is currently bound. */
-    boolean isStrongBindingBound();
-
-    /**
-     * Called to remove the strong binding established when the connection was started. It is safe
-     * to call this multiple times.
-     */
-    void removeInitialBinding();
-
-    /**
-     * For live connections, this returns true iff either the initial or the strong binding is
-     * bound, i.e. the connection has at least one oom binding. For connections that disconnected
-     * (did not exit properly), this returns true iff the connection had at least one oom binding
-     * when it disconnected.
-     */
-    boolean isOomProtectedOrWasWhenDied();
-
-    /**
-     * Unbinds the bindings that protect the process from oom killing. It is safe to call this
-     * multiple times, before as well as after stop().
-     */
-    void dropOomBindings();
-
-    /**
-     * Attaches a strong binding that will make the service as important as the main process. Each
-     * call should be succeeded by removeStrongBinding(), but multiple strong bindings can be
-     * requested and released independently.
-     */
-    void addStrongBinding();
-
-    /**
-     * Called when the service is no longer in active use of the consumer.
-     */
-    void removeStrongBinding();
-
-    /**
-     * Attaches a moderate binding that will give the service the priority of a visible process, but
-     * keep the priority below a strongly bound process.
-     */
-    void addModerateBinding();
-
-    /**
-     * Called when the service is no longer in moderate use of the consumer.
-     */
-    void removeModerateBinding();
-
-    /** @return true iff the moderate oom binding is currently bound. */
-    boolean isModerateBindingBound();
-}
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
index c6e5262..611e2a1b 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
@@ -36,18 +36,20 @@
     /**
      * Implemented by ChildProcessLauncherHelper.
      */
-    public interface LaunchCallback { void onChildProcessStarted(int pid); }
+    public interface LaunchCallback {
+        void onChildProcessStarted(BaseChildProcessConnection connection);
+    }
 
     private static final boolean SPARE_CONNECTION_ALWAYS_IN_FOREGROUND = false;
 
     @VisibleForTesting
-    static ChildProcessConnection allocateConnection(
+    static BaseChildProcessConnection allocateConnection(
             ChildSpawnData spawnData, Bundle childProcessCommonParams, boolean forWarmUp) {
         assert LauncherThread.runningOnLauncherThread();
-        ChildProcessConnection.DeathCallback deathCallback =
-                new ChildProcessConnection.DeathCallback() {
+        BaseChildProcessConnection.DeathCallback deathCallback =
+                new BaseChildProcessConnection.DeathCallback() {
                     @Override
-                    public void onChildProcessDied(ChildProcessConnection connection) {
+                    public void onChildProcessDied(BaseChildProcessConnection connection) {
                         assert LauncherThread.runningOnLauncherThread();
                         if (connection.getPid() != 0) {
                             stop(connection.getPid());
@@ -106,14 +108,14 @@
     }
 
     @VisibleForTesting
-    static ChildProcessConnection allocateBoundConnection(ChildSpawnData spawnData,
-            ChildProcessConnection.StartCallback startCallback, boolean forWarmUp) {
+    static BaseChildProcessConnection allocateBoundConnection(ChildSpawnData spawnData,
+            BaseChildProcessConnection.StartCallback startCallback, boolean forWarmUp) {
         assert LauncherThread.runningOnLauncherThread();
         final Context context = spawnData.getContext();
         final boolean inSandbox = spawnData.isInSandbox();
         final ChildProcessCreationParams creationParams = spawnData.getCreationParams();
 
-        ChildProcessConnection connection = allocateConnection(
+        BaseChildProcessConnection connection = allocateConnection(
                 spawnData, createCommonParamsBundle(spawnData.getCreationParams()), forWarmUp);
         if (connection != null) {
             connection.start(startCallback);
@@ -121,7 +123,8 @@
             String packageName = creationParams != null ? creationParams.getPackageName()
                                                         : context.getPackageName();
             if (inSandbox
-                    && !ChildConnectionAllocator.getAllocator(context, packageName, inSandbox)
+                    && !ChildConnectionAllocator
+                                .getAllocator(context, packageName, true /* sandboxed */)
                                 .isFreeConnectionAvailable()) {
                 // Proactively releases all the moderate bindings once all the sandboxed services
                 // are allocated, which will be very likely to have some of them killed by OOM
@@ -134,7 +137,7 @@
 
     private static final long FREE_CONNECTION_DELAY_MILLIS = 1;
 
-    private static void freeConnection(ChildProcessConnection connection) {
+    private static void freeConnection(BaseChildProcessConnection connection) {
         assert LauncherThread.runningOnLauncherThread();
         if (connection == sSpareSandboxedConnection) clearSpareConnection();
 
@@ -143,7 +146,7 @@
         // alive when it's been unbound for a short time. If a new connection to the same service
         // is bound at that point, the process is reused and bad things happen (mostly static
         // variables are set when we don't expect them to).
-        final ChildProcessConnection conn = connection;
+        final BaseChildProcessConnection conn = connection;
         LauncherThread.postDelayed(new Runnable() {
             @Override
             public void run() {
@@ -154,7 +157,7 @@
                 // ChildProcessLauncherHelper, we'll have a context around that we can pass in
                 // there.
                 ChildConnectionAllocator allocator = ChildConnectionAllocator.getAllocator(
-                        null /* context */, conn.getPackageName(), conn.isInSandbox());
+                        null /* context */, conn.getPackageName(), conn.isSandboxed());
                 assert allocator != null;
                 final ChildSpawnData pendingSpawn = allocator.free(conn);
                 if (pendingSpawn != null) {
@@ -175,12 +178,9 @@
         }, FREE_CONNECTION_DELAY_MILLIS);
     }
 
-    // Represents an invalid process handle; same as base/process/process.h kNullProcessHandle.
-    private static final int NULL_PROCESS_HANDLE = 0;
-
     // Map from pid to ChildService connection.
-    private static Map<Integer, ChildProcessConnection> sServiceMap =
-            new ConcurrentHashMap<Integer, ChildProcessConnection>();
+    private static Map<Integer, BaseChildProcessConnection> sServiceMap =
+            new ConcurrentHashMap<Integer, BaseChildProcessConnection>();
 
     // Lock for getBindingManager()
     private static final Object sBindingManagerLock = new Object();
@@ -192,9 +192,9 @@
     // This is used for a child process allocation to determine if StartCallback should be chained.
     // |sSpareConnectionStartCallback| is the chained StartCallback. This is also used to determine
     // if there is already a child process launch that's used this this connection.
-    private static ChildProcessConnection sSpareSandboxedConnection;
+    private static BaseChildProcessConnection sSpareSandboxedConnection;
     private static boolean sSpareConnectionStarting;
-    private static ChildProcessConnection.StartCallback sSpareConnectionStartCallback;
+    private static BaseChildProcessConnection.StartCallback sSpareConnectionStartCallback;
 
     // Manages oom bindings used to bind chind services. Lazily initialized by getBindingManager()
     private static BindingManager sBindingManager;
@@ -278,8 +278,8 @@
                 if (sSpareSandboxedConnection != null) return;
                 ChildProcessCreationParams params = ChildProcessCreationParams.getDefault();
 
-                ChildProcessConnection.StartCallback startCallback =
-                        new ChildProcessConnection.StartCallback() {
+                BaseChildProcessConnection.StartCallback startCallback =
+                        new BaseChildProcessConnection.StartCallback() {
                             @Override
                             public void onChildStarted() {
                                 assert LauncherThread.runningOnLauncherThread();
@@ -347,7 +347,7 @@
         if (!ContentSwitches.SWITCH_RENDERER_PROCESS.equals(processType)) {
             if (params != null && !params.getPackageName().equals(context.getPackageName())) {
                 // WebViews and WebAPKs have renderer processes running in their applications.
-                // When launching these renderer processes, {@link ChildProcessConnectionImpl}
+                // When launching these renderer processes, {@link ManagedChildProcessConnection}
                 // requires the package name of the application which holds the renderer process.
                 // Therefore, the package name in ChildProcessCreationParams could be the package
                 // name of WebViews, WebAPKs, or Chrome, depending on the host application.
@@ -375,7 +375,7 @@
     }
 
     @VisibleForTesting
-    public static ChildProcessConnection startInternal(final Context context,
+    public static BaseChildProcessConnection startInternal(final Context context,
             final String[] commandLine, final int childProcessId,
             final FileDescriptorInfo[] filesToBeMapped, final LaunchCallback launchCallback,
             final IBinder childProcessCallback, final boolean inSandbox,
@@ -384,18 +384,18 @@
         try {
             TraceEvent.begin("ChildProcessLauncher.startInternal");
 
-            ChildProcessConnection allocatedConnection = null;
+            BaseChildProcessConnection allocatedConnection = null;
             String packageName = creationParams != null ? creationParams.getPackageName()
                     : context.getPackageName();
-            ChildProcessConnection.StartCallback startCallback =
-                    new ChildProcessConnection.StartCallback() {
+            BaseChildProcessConnection.StartCallback startCallback =
+                    new BaseChildProcessConnection.StartCallback() {
                         @Override
                         public void onChildStarted() {}
 
                         @Override
                         public void onChildStartFailed() {
                             assert LauncherThread.runningOnLauncherThread();
-                            Log.e(TAG, "ChildProcessConnection.start failed, trying again");
+                            Log.e(TAG, "BaseChildProcessConnection.start failed, trying again");
                             LauncherThread.post(new Runnable() {
                                 @Override
                                 public void run() {
@@ -464,25 +464,29 @@
     }
 
     @VisibleForTesting
-    static void triggerConnectionSetup(final ChildProcessConnection connection,
+    static void triggerConnectionSetup(final BaseChildProcessConnection connection,
             String[] commandLine, int childProcessId, FileDescriptorInfo[] filesToBeMapped,
             final IBinder childProcessCallback, final LaunchCallback launchCallback) {
         assert LauncherThread.runningOnLauncherThread();
         Log.d(TAG, "Setting up connection to process: slot=%d", connection.getServiceNumber());
-        ChildProcessConnection.ConnectionCallback connectionCallback =
-                new ChildProcessConnection.ConnectionCallback() {
+        BaseChildProcessConnection.ConnectionCallback connectionCallback =
+                new BaseChildProcessConnection.ConnectionCallback() {
                     @Override
-                    public void onConnected(int pid) {
-                        Log.d(TAG, "on connect callback, pid=%d", pid);
-                        if (pid != NULL_PROCESS_HANDLE) {
-                            getBindingManager().addNewConnection(pid, connection);
+                    public void onConnected(BaseChildProcessConnection connection) {
+                        if (connection != null) {
+                            int pid = connection.getPid();
+                            Log.d(TAG, "on connect callback, pid=%d", pid);
+                            if (connection instanceof ManagedChildProcessConnection) {
+                                getBindingManager().addNewConnection(
+                                        pid, (ManagedChildProcessConnection) connection);
+                            }
                             sServiceMap.put(pid, connection);
                         }
                         // If the connection fails and pid == 0, the Java-side cleanup was already
                         // handled by DeathCallback. We still have to call back to native for
                         // cleanup there.
                         if (launchCallback != null) { // Will be null in Java instrumentation tests.
-                            launchCallback.onChildProcessStarted(pid);
+                            launchCallback.onChildProcessStarted(connection);
                         }
                     }
                 };
@@ -499,12 +503,12 @@
     static void stop(int pid) {
         assert LauncherThread.runningOnLauncherThread();
         Log.d(TAG, "stopping child connection: pid=%d", pid);
-        ChildProcessConnection connection = sServiceMap.remove(pid);
+        BaseChildProcessConnection connection = sServiceMap.remove(pid);
         if (connection == null) {
             // Can happen for single process.
             return;
         }
-        getBindingManager().clearConnection(pid);
+        getBindingManager().removeConnection(pid);
         connection.stop();
         freeConnection(connection);
     }
@@ -524,7 +528,7 @@
         if (sServiceMap.get(pid) == null) return false;
 
         try {
-            ((ChildProcessConnectionImpl) sServiceMap.get(pid)).crashServiceForTesting();
+            ((ManagedChildProcessConnection) sServiceMap.get(pid)).crashServiceForTesting();
         } catch (RemoteException ex) {
             return false;
         }
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
index cd0f0703..ffe74a8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
@@ -25,9 +25,12 @@
 class ChildProcessLauncherHelper {
     private static final String TAG = "ChildProcLH";
 
+    // Represents an invalid process handle; same as base/process/process.h kNullProcessHandle.
+    private static final int NULL_PROCESS_HANDLE = 0;
+
     // Note native pointer is only guaranteed live until nativeOnChildProcessStarted.
     private long mNativeChildProcessLauncherHelper;
-    private int mPid;
+    private BaseChildProcessConnection mChildProcessConnection;
 
     @CalledByNative
     private static FileDescriptorInfo makeFdInfo(
@@ -64,27 +67,47 @@
         ChildProcessLauncher.start(ContextUtils.getApplicationContext(), paramId, commandLine,
                 childProcessId, filesToBeMapped, new ChildProcessLauncher.LaunchCallback() {
                     @Override
-                    public void onChildProcessStarted(int pid) {
-                        mPid = pid;
+                    public void onChildProcessStarted(BaseChildProcessConnection connection) {
+                        mChildProcessConnection = connection;
                         if (mNativeChildProcessLauncherHelper != 0) {
-                            nativeOnChildProcessStarted(mNativeChildProcessLauncherHelper, pid);
+                            nativeOnChildProcessStarted(
+                                    mNativeChildProcessLauncherHelper, getPid());
                         }
                         mNativeChildProcessLauncherHelper = 0;
                     }
                 });
     }
 
+    private int getPid() {
+        return mChildProcessConnection == null ? NULL_PROCESS_HANDLE
+                                               : mChildProcessConnection.getPid();
+    }
+
     // Called on client (UI or IO) thread.
     @CalledByNative
     private boolean isOomProtected() {
-        return ChildProcessLauncher.getBindingManager().isOomProtected(mPid);
+        // mChildProcessConnection is set on a different thread but does not change once it's been
+        // set. So it is safe to test whether it's null from a different thread.
+        if (mChildProcessConnection == null) {
+            return false;
+        }
+
+        if (mChildProcessConnection instanceof ImportantChildProcessConnection) {
+            // The connection was bound as BIND_IMPORTANT. This should prevent it from being killed
+            // when the app is on the foreground (that's our best guess, but there is no absolute
+            // guarantee).
+            return ChildProcessLauncher.isApplicationInForeground();
+        }
+
+        return ((ManagedChildProcessConnection) mChildProcessConnection)
+                .isOomProtectedOrWasWhenDied();
     }
 
     @CalledByNative
     private void setInForeground(int pid, boolean inForeground) {
         assert LauncherThread.runningOnLauncherThread();
-        assert mPid == pid;
-        ChildProcessLauncher.getBindingManager().setInForeground(mPid, inForeground);
+        assert getPid() == pid;
+        ChildProcessLauncher.getBindingManager().setInForeground(pid, inForeground);
     }
 
     @CalledByNative
diff --git a/content/public/android/java/src/org/chromium/content/browser/ImportantChildProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/ImportantChildProcessConnection.java
new file mode 100644
index 0000000..ec5b738e
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/ImportantChildProcessConnection.java
@@ -0,0 +1,50 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import org.chromium.base.process_launcher.ChildProcessCreationParams;
+
+/**
+ * A connection that is bound as important, meaning the framework brings it to the foreground
+ * process level when the app is.
+ */
+public class ImportantChildProcessConnection extends BaseChildProcessConnection {
+    public static final Factory FACTORY = new BaseChildProcessConnection.Factory() {
+        @Override
+        public BaseChildProcessConnection create(Context context, int number, boolean sandboxed,
+                DeathCallback deathCallback, String serviceClassName,
+                Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) {
+            return new ImportantChildProcessConnection(context, number, sandboxed, deathCallback,
+                    serviceClassName, childProcessCommonParameters, creationParams);
+        }
+    };
+
+    private final ChildServiceConnection mBinding;
+
+    private ImportantChildProcessConnection(Context context, int number, boolean sandboxed,
+            DeathCallback deathCallback, String serviceClassName,
+            Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) {
+        super(context, number, sandboxed, deathCallback, serviceClassName,
+                childProcessCommonParameters, creationParams);
+        int flags = Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT;
+        if (shouldBindAsExportedService()) {
+            flags |= Context.BIND_EXTERNAL_SERVICE;
+        }
+        mBinding = createServiceConnection(flags);
+    }
+
+    @Override
+    public boolean bind() {
+        return mBinding.bind();
+    }
+
+    @Override
+    public void unbind() {
+        mBinding.unbind();
+    }
+}
diff --git a/content/public/android/java/src/org/chromium/content/browser/LauncherThread.java b/content/public/android/java/src/org/chromium/content/browser/LauncherThread.java
index 622b92b..f9732a60 100644
--- a/content/public/android/java/src/org/chromium/content/browser/LauncherThread.java
+++ b/content/public/android/java/src/org/chromium/content/browser/LauncherThread.java
@@ -8,6 +8,7 @@
 import android.os.Looper;
 
 import org.chromium.base.JavaHandlerThread;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 
@@ -16,10 +17,13 @@
 public final class LauncherThread {
     private static final JavaHandlerThread sThread =
             new JavaHandlerThread("Chrome_ProcessLauncherThread");
-    private static final Handler sHandler;
+    private static final Handler sThreadHandler;
+    // Can be overritten in tests.
+    private static Handler sHandler;
     static {
         sThread.maybeStart();
-        sHandler = new Handler(sThread.getLooper());
+        sThreadHandler = new Handler(sThread.getLooper());
+        sHandler = sThreadHandler;
     }
 
     public static void post(Runnable r) {
@@ -34,6 +38,16 @@
         return sHandler.getLooper() == Looper.myLooper();
     }
 
+    @VisibleForTesting
+    public static void setCurrentThreadAsLauncherThread() {
+        sHandler = new Handler();
+    }
+
+    @VisibleForTesting
+    public static void setLauncherThreadAsLauncherThread() {
+        sHandler = sThreadHandler;
+    }
+
     @CalledByNative
     private static JavaHandlerThread getHandlerThread() {
         return sThread;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java
new file mode 100644
index 0000000..99f4122
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java
@@ -0,0 +1,225 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import org.chromium.base.Log;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.process_launcher.ChildProcessCreationParams;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * ManagedChildProcessConnection is a connection to a child service that can hold several bindings
+ * to the service so it can be more or less agressively protected against OOM.
+ */
+public class ManagedChildProcessConnection extends BaseChildProcessConnection {
+    private static final String TAG = "ManChildProcessConn";
+
+    public static final Factory FACTORY = new BaseChildProcessConnection.Factory() {
+        @Override
+        public BaseChildProcessConnection create(Context context, int number, boolean sandboxed,
+                DeathCallback deathCallback, String serviceClassName,
+                Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) {
+            return new ManagedChildProcessConnection(context, number, sandboxed, deathCallback,
+                    serviceClassName, childProcessCommonParameters, creationParams);
+        }
+    };
+
+    // Synchronization: While most internal flow occurs on the UI thread, the public API
+    // (specifically start and stop) may be called from any thread, hence all entry point methods
+    // into the class are synchronized on the lock to protect access to these members.
+    private final Object mBindingLock = new Object();
+
+    // Initial binding protects the newly spawned process from being killed before it is put to use,
+    // it is maintained between calls to start() and removeInitialBinding().
+    @GuardedBy("mBindingLock")
+    private final ChildServiceConnection mInitialBinding;
+
+    // Strong binding will make the service priority equal to the priority of the activity. We want
+    // the OS to be able to kill background renderers as it kills other background apps, so strong
+    // bindings are maintained only for services that are active at the moment (between
+    // addStrongBinding() and removeStrongBinding()).
+    @GuardedBy("mBindingLock")
+    private final ChildServiceConnection mStrongBinding;
+
+    // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls
+    // to start() and stop().
+    @GuardedBy("mBindingLock")
+    private final ChildServiceConnection mWaivedBinding;
+
+    // Incremented on addStrongBinding(), decremented on removeStrongBinding().
+    @GuardedBy("mBindingLock")
+    private int mStrongBindingCount;
+
+    // Moderate binding will make the service priority equal to the priority of a visible process
+    // while the app is in the foreground. It will stay bound only while the app is in the
+    // foreground to protect a background process from the system out-of-memory killer.
+    @GuardedBy("mBindingLock")
+    private final ChildServiceConnection mModerateBinding;
+
+    @GuardedBy("mBindingLock")
+    private boolean mWasOomProtectedOnUnbind;
+
+    @VisibleForTesting
+    ManagedChildProcessConnection(Context context, int number, boolean sandboxed,
+            DeathCallback deathCallback, String serviceClassName,
+            Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) {
+        super(context, number, sandboxed, deathCallback, serviceClassName,
+                childProcessCommonParameters, creationParams);
+
+        int initialFlags = Context.BIND_AUTO_CREATE;
+        int extraBindFlags = shouldBindAsExportedService() ? Context.BIND_EXTERNAL_SERVICE : 0;
+
+        synchronized (mBindingLock) {
+            mInitialBinding = createServiceConnection(initialFlags | extraBindFlags);
+            mStrongBinding = createServiceConnection(
+                    Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFlags);
+            mWaivedBinding = createServiceConnection(
+                    Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBindFlags);
+            mModerateBinding = createServiceConnection(Context.BIND_AUTO_CREATE | extraBindFlags);
+        }
+    }
+
+    @Override
+    protected boolean bind() {
+        synchronized (mBindingLock) {
+            if (!mInitialBinding.bind()) {
+                return false;
+            }
+            mWaivedBinding.bind();
+        }
+        return true;
+    }
+
+    @Override
+    public void unbind() {
+        synchronized (mBindingLock) {
+            if (!isBound()) {
+                return;
+            }
+            mWasOomProtectedOnUnbind = isCurrentlyOomProtected();
+            mInitialBinding.unbind();
+            mStrongBinding.unbind();
+            mWaivedBinding.unbind();
+            mModerateBinding.unbind();
+            mStrongBindingCount = 0;
+        }
+    }
+
+    @GuardedBy("mBindingLock")
+    private boolean isBound() {
+        return mInitialBinding.isBound() || mStrongBinding.isBound() || mWaivedBinding.isBound()
+                || mModerateBinding.isBound();
+    }
+
+    public boolean isInitialBindingBound() {
+        synchronized (mBindingLock) {
+            return mInitialBinding.isBound();
+        }
+    }
+
+    public boolean isStrongBindingBound() {
+        synchronized (mBindingLock) {
+            return mStrongBinding.isBound();
+        }
+    }
+
+    public void removeInitialBinding() {
+        synchronized (mBindingLock) {
+            mInitialBinding.unbind();
+        }
+    }
+
+    public boolean isOomProtectedOrWasWhenDied() {
+        // Call isConnected() outside of the synchronized block or we could deadlock.
+        final boolean isConnected = isConnected();
+        synchronized (mBindingLock) {
+            if (isConnected) {
+                return isCurrentlyOomProtected();
+            }
+            return mWasOomProtectedOnUnbind;
+        }
+    }
+
+    @GuardedBy("mBindingLock")
+    private boolean isCurrentlyOomProtected() {
+        return mInitialBinding.isBound() || mStrongBinding.isBound();
+    }
+
+    public void dropOomBindings() {
+        synchronized (mBindingLock) {
+            mInitialBinding.unbind();
+
+            mStrongBindingCount = 0;
+            mStrongBinding.unbind();
+
+            mModerateBinding.unbind();
+        }
+    }
+
+    public void addStrongBinding() {
+        // Call isConnected() outside of the synchronized block or we could deadlock.
+        final boolean isConnected = isConnected();
+        synchronized (mBindingLock) {
+            if (!isConnected) {
+                Log.w(TAG, "The connection is not bound for %d", getPid());
+                return;
+            }
+            if (mStrongBindingCount == 0) {
+                mStrongBinding.bind();
+            }
+            mStrongBindingCount++;
+        }
+    }
+
+    public void removeStrongBinding() {
+        // Call isConnected() outside of the synchronized block or we could deadlock.
+        final boolean isConnected = isConnected();
+        synchronized (mBindingLock) {
+            if (!isConnected) {
+                Log.w(TAG, "The connection is not bound for %d", getPid());
+                return;
+            }
+            assert mStrongBindingCount > 0;
+            mStrongBindingCount--;
+            if (mStrongBindingCount == 0) {
+                mStrongBinding.unbind();
+            }
+        }
+    }
+
+    public boolean isModerateBindingBound() {
+        synchronized (mBindingLock) {
+            return mModerateBinding.isBound();
+        }
+    }
+
+    public void addModerateBinding() {
+        // Call isConnected() outside of the synchronized block or we could deadlock.
+        final boolean isConnected = isConnected();
+        synchronized (mBindingLock) {
+            if (!isConnected) {
+                Log.w(TAG, "The connection is not bound for %d", getPid());
+                return;
+            }
+            mModerateBinding.bind();
+        }
+    }
+
+    public void removeModerateBinding() {
+        // Call isConnected() outside of the synchronized block or we could deadlock.
+        final boolean isConnected = isConnected();
+        synchronized (mBindingLock) {
+            if (!isConnected) {
+                Log.w(TAG, "The connection is not bound for %d", getPid());
+                return;
+            }
+            mModerateBinding.unbind();
+        }
+    }
+}
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
index c4f1d473..7ed9f11 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
@@ -102,7 +102,7 @@
         Assert.assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting());
 
         // Start and connect to a new service.
-        final ChildProcessConnectionImpl connection = startConnection();
+        final BaseChildProcessConnection connection = startConnection();
         Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount());
 
         // Verify that the service is not yet set up.
@@ -138,7 +138,7 @@
         Assert.assertEquals(0, allocatedChromeSandboxedConnectionsCount());
 
         // Start and connect to a new service.
-        final ChildProcessConnectionImpl connection = startConnection();
+        final BaseChildProcessConnection connection = startConnection();
         Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount());
 
         // Initiate the connection setup.
@@ -193,7 +193,7 @@
         Assert.assertEquals(0, allocatedChromeSandboxedConnectionsCount());
 
         // Start and connect to a new service.
-        final ChildProcessConnectionImpl connection = startConnection();
+        final BaseChildProcessConnection connection = startConnection();
         Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount());
 
         // Queue up a new spawn request. There is no way to kill the pending connection, leak it
@@ -269,10 +269,10 @@
         Assert.assertEquals(0, allocatedChromeSandboxedConnectionsCount());
 
         // Start and connect to a new service of an external APK.
-        ChildProcessConnectionImpl externalApkConnection =
+        BaseChildProcessConnection externalApkConnection =
                 allocateConnection(EXTERNAL_APK_PACKAGE_NAME);
         // Start and connect to a new service for a regular tab.
-        ChildProcessConnectionImpl tabConnection = allocateConnection(appContext.getPackageName());
+        BaseChildProcessConnection tabConnection = allocateConnection(appContext.getPackageName());
 
         // Verify that one connection is allocated for an external APK and a regular tab
         // respectively.
@@ -305,17 +305,17 @@
                         appContext, EXTERNAL_APK_PACKAGE_NAME));
 
         // Setup a connection for an external APK to reach the maximum allowed connection number.
-        ChildProcessConnectionImpl externalApkConnection =
+        BaseChildProcessConnection externalApkConnection =
                 allocateConnection(EXTERNAL_APK_PACKAGE_NAME);
         Assert.assertNotNull(externalApkConnection);
 
         // Verify that there isn't any connection available for the external APK.
-        ChildProcessConnectionImpl exceedNumberExternalApkConnection =
+        BaseChildProcessConnection exceedNumberExternalApkConnection =
                 allocateConnection(EXTERNAL_APK_PACKAGE_NAME);
         Assert.assertNull(exceedNumberExternalApkConnection);
 
         // Verify that we can still allocate connection for a regular tab.
-        ChildProcessConnectionImpl tabConnection = allocateConnection(appContext.getPackageName());
+        BaseChildProcessConnection tabConnection = allocateConnection(appContext.getPackageName());
         Assert.assertNotNull(tabConnection);
     }
 
@@ -405,7 +405,7 @@
         final ChildProcessCreationParams creationParams = new ChildProcessCreationParams(
                 context.getPackageName(), false /* isExternalService */,
                 LibraryProcessType.PROCESS_CHILD, true /* bindToCallerCheck */);
-        final ChildProcessConnection conn =
+        final BaseChildProcessConnection conn =
                 ChildProcessLauncherTestHelperService.startInternalForTesting(
                         context, sProcessWaitArguments, new FileDescriptorInfo[0], creationParams);
 
@@ -419,7 +419,7 @@
 
         Assert.assertEquals(0, conn.getServiceNumber());
 
-        final ChildProcessConnection[] sandboxedConnections =
+        final BaseChildProcessConnection[] sandboxedConnections =
                 getSandboxedConnectionArrayForTesting(context, context.getPackageName());
 
         // Wait for the retry to succeed.
@@ -429,7 +429,7 @@
                     public boolean isSatisfied() {
                         boolean allChildrenConnected = true;
                         for (int i = 0; i <= 1; ++i) {
-                            ChildProcessConnection conn = sandboxedConnections[i];
+                            BaseChildProcessConnection conn = sandboxedConnections[i];
                             allChildrenConnected &= conn != null && conn.getService() != null;
                         }
                         return allChildrenConnected;
@@ -438,7 +438,7 @@
 
         // Check that only two connections are created.
         for (int i = 0; i < sandboxedConnections.length; ++i) {
-            ChildProcessConnection sandboxedConn = sandboxedConnections[i];
+            BaseChildProcessConnection sandboxedConn = sandboxedConnections[i];
             if (i <= 1) {
                 Assert.assertNotNull(sandboxedConn);
                 Assert.assertNotNull(sandboxedConn.getService());
@@ -448,7 +448,7 @@
         }
 
         Assert.assertTrue(conn == sandboxedConnections[0]);
-        final ChildProcessConnection retryConn = sandboxedConnections[1];
+        final BaseChildProcessConnection retryConn = sandboxedConnections[1];
 
         Assert.assertFalse(conn == retryConn);
 
@@ -488,7 +488,7 @@
             public void run() {
                 Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount());
 
-                final ChildProcessConnection conn =
+                final BaseChildProcessConnection conn =
                         ChildProcessLauncherTestHelperService.startInternalForTesting(
                                 context, new String[0], new FileDescriptorInfo[0], null);
                 Assert.assertEquals(
@@ -530,12 +530,11 @@
         });
     }
 
-    private ChildProcessConnectionImpl startConnection() {
+    private BaseChildProcessConnection startConnection() {
         // Allocate a new connection.
         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
-        final ChildProcessConnectionImpl connection =
-                (ChildProcessConnectionImpl) allocateBoundConnectionForTesting(
-                        context, getDefaultChildProcessCreationParams(context.getPackageName()));
+        final BaseChildProcessConnection connection = allocateBoundConnectionForTesting(
+                context, getDefaultChildProcessCreationParams(context.getPackageName()));
 
         // Wait for the service to connect.
         CriteriaHelper.pollInstrumentationThread(
@@ -557,12 +556,12 @@
                 0 /* childProcessId */, filesToMap, null /* launchCallback */);
     }
 
-    private static ChildProcessConnection allocateBoundConnectionForTesting(
+    private static BaseChildProcessConnection allocateBoundConnectionForTesting(
             final Context context, final ChildProcessCreationParams creationParams) {
         return ChildProcessLauncherTestHelperService.runOnLauncherAndGetResult(
-                new Callable<ChildProcessConnection>() {
+                new Callable<BaseChildProcessConnection>() {
                     @Override
-                    public ChildProcessConnection call() {
+                    public BaseChildProcessConnection call() {
                         return ChildProcessLauncher.allocateBoundConnection(
                                 new ChildSpawnData(context, null /* commandLine */,
                                         0 /* childProcessId */, null /* filesToBeMapped */,
@@ -579,16 +578,16 @@
      * but doesn't really start the connection to bind a service. It is for testing whether the
      * connection is allocated properly for different application packages.
      */
-    private ChildProcessConnectionImpl allocateConnection(final String packageName) {
+    private BaseChildProcessConnection allocateConnection(final String packageName) {
         return ChildProcessLauncherTestHelperService.runOnLauncherAndGetResult(
-                new Callable<ChildProcessConnectionImpl>() {
+                new Callable<BaseChildProcessConnection>() {
                     @Override
-                    public ChildProcessConnectionImpl call() {
+                    public BaseChildProcessConnection call() {
                         // Allocate a new connection.
                         Context context = InstrumentationRegistry.getTargetContext();
                         ChildProcessCreationParams creationParams =
                                 getDefaultChildProcessCreationParams(packageName);
-                        return (ChildProcessConnectionImpl) ChildProcessLauncher.allocateConnection(
+                        return ChildProcessLauncher.allocateConnection(
                                 new ChildSpawnData(context, null /* commandLine */,
                                         0 /* childProcessId */, null /* filesToBeMapped */,
                                         null /* launchCallback */, null /* childProcessCallback */,
@@ -631,12 +630,12 @@
                 });
     }
 
-    private static ChildProcessConnection[] getSandboxedConnectionArrayForTesting(
+    private static BaseChildProcessConnection[] getSandboxedConnectionArrayForTesting(
             final Context context, final String packageName) {
         return ChildProcessLauncherTestHelperService.runOnLauncherAndGetResult(
-                new Callable<ChildProcessConnection[]>() {
+                new Callable<BaseChildProcessConnection[]>() {
                     @Override
-                    public ChildProcessConnection[] call() {
+                    public BaseChildProcessConnection[] call() {
                         return ChildConnectionAllocator
                                 .getAllocator(context, packageName, true /*isSandboxed */)
                                 .connectionArrayForTesting();
@@ -670,7 +669,7 @@
                 LibraryProcessType.PROCESS_CHILD, false /* bindToCallerCheck */);
     }
 
-    private void triggerConnectionSetup(final ChildProcessConnectionImpl connection) {
+    private void triggerConnectionSetup(final BaseChildProcessConnection connection) {
         ChildProcessLauncherTestHelperService.runOnLauncherThreadBlocking(new Runnable() {
             @Override
             public void run() {
diff --git a/content/public/android/junit/src/org/chromium/content/browser/BindingManagerImplTest.java b/content/public/android/junit/src/org/chromium/content/browser/BindingManagerImplTest.java
index 09268ad..f10218b 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/BindingManagerImplTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/BindingManagerImplTest.java
@@ -10,21 +10,18 @@
 
 import android.app.Activity;
 import android.app.Application;
-import android.os.IBinder;
 import android.util.Pair;
 
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-
 import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.process_launcher.ChildProcessCreationParams;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
-import org.chromium.base.process_launcher.IChildProcessService;
 import org.chromium.base.test.util.Feature;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 
@@ -40,19 +37,42 @@
 @RunWith(LocalRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BindingManagerImplTest {
-    private static class MockChildProcessConnection implements ChildProcessConnection {
-        boolean mInitialBindingBound;
-        boolean mModerateBindingBound;
-        int mStrongBindingCount;
-        final int mPid;
+    private static class MockChildServiceConnection
+            implements BaseChildProcessConnection.ChildServiceConnection {
+        private boolean mBound;
+
+        @Override
+        public boolean bind() {
+            mBound = true;
+            return true;
+        }
+
+        @Override
+        public void unbind() {
+            mBound = false;
+        }
+
+        @Override
+        public boolean isBound() {
+            return mBound;
+        }
+    }
+
+    private static class TestChildProcessConnection extends ManagedChildProcessConnection {
+        private final int mPid;
+        private boolean mConnected;
 
         /**
-         * Creates a mock binding corresponding to real ChildProcessConnectionImpl after the
+         * Creates a mock binding corresponding to real ManagedChildProcessConnection after the
          * connection is established: with initial binding bound and no strong binding.
          */
-        MockChildProcessConnection(int pid) {
-            mInitialBindingBound = true;
-            mStrongBindingCount = 0;
+        private TestChildProcessConnection(int pid) {
+            super(null /* context */, pid /* number */, true /* sandboxed */,
+                    null /* deathCallback */, null /* serviceClassName */,
+                    null /* childProcessCommonParameters */,
+                    new ChildProcessCreationParams("org.chromium.test",
+                            false /* isExternalService */, 0 /* libraryProcessType */,
+                            false /* bindToCallerCheck */));
             mPid = pid;
         }
 
@@ -62,97 +82,26 @@
         }
 
         @Override
-        public boolean isInitialBindingBound() {
-            return mInitialBindingBound;
+        protected ChildServiceConnection createServiceConnection(int bindFlags) {
+            return new MockChildServiceConnection();
         }
 
+        // We don't have a real service so we have to mock the connection status.
         @Override
-        public boolean isStrongBindingBound() {
-            return mStrongBindingCount > 0;
-        }
-
-        @Override
-        public void removeInitialBinding() {
-            mInitialBindingBound = false;
-        }
-
-        @Override
-        public boolean isOomProtectedOrWasWhenDied() {
-            return mInitialBindingBound || mStrongBindingCount > 0;
-        }
-
-        @Override
-        public void dropOomBindings() {
-            mInitialBindingBound = false;
-            mStrongBindingCount = 0;
-        }
-
-        @Override
-        public void addStrongBinding() {
-            mStrongBindingCount++;
-        }
-
-        @Override
-        public void removeStrongBinding() {
-            assert mStrongBindingCount > 0;
-            mStrongBindingCount--;
+        public void start(StartCallback startCallback) {
+            super.start(startCallback);
+            mConnected = true;
         }
 
         @Override
         public void stop() {
-            mInitialBindingBound = false;
-            mStrongBindingCount = 0;
+            super.stop();
+            mConnected = false;
         }
 
         @Override
-        public int getServiceNumber() {
-            return mPid;
-        }
-
-        @Override
-        public boolean isInSandbox() {
-            return true;
-        }
-
-        @Override
-        public IChildProcessService getService() {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void start(StartCallback startCallback) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void setupConnection(String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
-                IBinder callback, ConnectionCallback connectionCallbacks) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void addModerateBinding() {
-            mModerateBindingBound = true;
-        }
-
-        @Override
-        public void removeModerateBinding() {
-            mModerateBindingBound = false;
-        }
-
-        @Override
-        public boolean isModerateBindingBound() {
-            return mModerateBindingBound;
-        }
-
-        @Override
-        public String getPackageName() {
-            return null;
-        }
-
-        @Override
-        public ChildProcessCreationParams getCreationParams() {
-            return null;
+        public boolean isConnected() {
+            return mConnected;
         }
     }
 
@@ -185,18 +134,30 @@
 
     @Before
     public void setUp() {
+        // The tests run on only one thread. Pretend that is the launcher thread so LauncherThread
+        // asserts are not triggered.
+        LauncherThread.setCurrentThreadAsLauncherThread();
+
         mActivity = Robolectric.buildActivity(Activity.class).setup().get();
 
-        mLowEndManager = BindingManagerImpl.createBindingManagerForTesting(true);
-        mHighEndManager = BindingManagerImpl.createBindingManagerForTesting(false);
-        mModerateBindingManager = BindingManagerImpl.createBindingManagerForTesting(false);
-        mModerateBindingManager.startModerateBindingManagement(mActivity, 4);
+        mLowEndManager =
+                BindingManagerImpl.createBindingManagerForTesting(true /* isLowEndDevice */);
+        mHighEndManager =
+                BindingManagerImpl.createBindingManagerForTesting(false /* isLowEndDevice */);
+        mModerateBindingManager =
+                BindingManagerImpl.createBindingManagerForTesting(false /* isLowEndDevice */);
+        mModerateBindingManager.startModerateBindingManagement(mActivity, 4 /* maxSize */);
         mAllManagers = new ManagerEntry[] {
                 new ManagerEntry(mLowEndManager, "low-end"),
                 new ManagerEntry(mHighEndManager, "high-end"),
                 new ManagerEntry(mModerateBindingManager, "moderate-binding")};
     }
 
+    @After
+    public void tearDown() {
+        LauncherThread.setLauncherThreadAsLauncherThread();
+    }
+
     /**
      * Verifies that when running on low-end, the binding manager drops the oom bindings for the
      * previously bound connection when a new connection is used in foreground.
@@ -208,7 +169,8 @@
         BindingManagerImpl manager = mLowEndManager;
 
         // Add a connection to the manager.
-        MockChildProcessConnection firstConnection = new MockChildProcessConnection(1);
+        TestChildProcessConnection firstConnection = new TestChildProcessConnection(1);
+        firstConnection.start(null /* startCallback */);
         manager.addNewConnection(firstConnection.getPid(), firstConnection);
 
         // Bind a strong binding on the connection.
@@ -216,7 +178,8 @@
         Assert.assertTrue(firstConnection.isStrongBindingBound());
 
         // Add a new connection.
-        MockChildProcessConnection secondConnection = new MockChildProcessConnection(2);
+        TestChildProcessConnection secondConnection = new TestChildProcessConnection(2);
+        secondConnection.start(null /* startCallback */);
         manager.addNewConnection(secondConnection.getPid(), secondConnection);
 
         // Verify that the strong binding for the first connection wasn't dropped.
@@ -240,7 +203,8 @@
         final BindingManagerImpl manager = mLowEndManager;
 
         // Add a connection to the manager.
-        final MockChildProcessConnection connection = new MockChildProcessConnection(1);
+        final TestChildProcessConnection connection = new TestChildProcessConnection(1);
+        connection.start(null /* startCallback */);
         manager.addNewConnection(connection.getPid(), connection);
         Assert.assertTrue(connection.isInitialBindingBound());
         Assert.assertFalse(connection.isStrongBindingBound());
@@ -268,7 +232,8 @@
         final BindingManagerImpl manager = mHighEndManager;
 
         // Add a connection to the manager.
-        final MockChildProcessConnection connection = new MockChildProcessConnection(1);
+        final TestChildProcessConnection connection = new TestChildProcessConnection(1);
+        connection.start(null /* startCallback */);
         manager.addNewConnection(connection.getPid(), connection);
         Assert.assertTrue(connection.isInitialBindingBound());
         Assert.assertFalse(connection.isStrongBindingBound());
@@ -301,9 +266,11 @@
         // This test applies only to the moderate-binding manager.
         final BindingManagerImpl manager = mModerateBindingManager;
 
-        // Add a connection to the manager.
-        final MockChildProcessConnection connection = new MockChildProcessConnection(1);
+        // Add a connection to the manager and start it.
+        final TestChildProcessConnection connection = new TestChildProcessConnection(1);
+        connection.start(null /* startCallback */);
         manager.addNewConnection(connection.getPid(), connection);
+
         Assert.assertTrue(connection.isInitialBindingBound());
         Assert.assertFalse(connection.isStrongBindingBound());
         Assert.assertFalse(connection.isModerateBindingBound());
@@ -341,7 +308,8 @@
             String message = managerEntry.getErrorMessage();
 
             // Add a connection to the manager.
-            MockChildProcessConnection connection = new MockChildProcessConnection(1);
+            TestChildProcessConnection connection = new TestChildProcessConnection(1);
+            connection.start(null /* startCallback */);
             manager.addNewConnection(connection.getPid(), connection);
 
             // Verify that the initial binding is held.
@@ -354,13 +322,9 @@
     }
 
     /**
-     * Verifies that BindingManagerImpl correctly stashes the status of the connection oom bindings
-     * when the connection is cleared. BindingManagerImpl should reply to isOomProtected() queries
-     * with live status of the connection while it's still around and reply with stashed status
-     * after clearConnection() is called.
-     *
      * This test corresponds to a process crash scenario: after a process dies and its connection is
-     * cleared, isOomProtected() may be called to decide if it was a crash or out-of-memory kill.
+     * cleared, isOomProtectedOrWasWhenDied() may be called on the connection to decide if it was a
+     * crash or out-of-memory kill.
      */
     @Test
     @Feature({"ProcessManagement"})
@@ -371,26 +335,27 @@
             String message = managerEntry.getErrorMessage();
 
             // Add a connection to the manager.
-            MockChildProcessConnection connection = new MockChildProcessConnection(1);
+            TestChildProcessConnection connection = new TestChildProcessConnection(1);
+            connection.start(null /* startCallback */);
             manager.addNewConnection(connection.getPid(), connection);
 
             // Initial binding is an oom binding.
-            Assert.assertTrue(message, manager.isOomProtected(connection.getPid()));
+            Assert.assertTrue(message, connection.isOomProtectedOrWasWhenDied());
 
             // After initial binding is removed, the connection is no longer oom protected.
             manager.setInForeground(connection.getPid(), false);
             manager.determinedVisibility(connection.getPid());
             ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
-            Assert.assertFalse(message, manager.isOomProtected(connection.getPid()));
+            Assert.assertFalse(message, connection.isOomProtectedOrWasWhenDied());
 
             // Add a strong binding, restoring the oom protection.
             manager.setInForeground(connection.getPid(), true);
-            Assert.assertTrue(message, manager.isOomProtected(connection.getPid()));
+            Assert.assertTrue(message, connection.isOomProtectedOrWasWhenDied());
 
             // Simulate a process crash - clear a connection in binding manager and remove the
             // bindings.
             Assert.assertFalse(manager.isConnectionCleared(connection.getPid()));
-            manager.clearConnection(connection.getPid());
+            manager.removeConnection(connection.getPid());
             Assert.assertTrue(manager.isConnectionCleared(connection.getPid()));
             connection.stop();
 
@@ -398,7 +363,7 @@
             // oom status as protected.
             Assert.assertFalse(message, connection.isInitialBindingBound());
             Assert.assertFalse(message, connection.isStrongBindingBound());
-            Assert.assertTrue(message, manager.isOomProtected(connection.getPid()));
+            Assert.assertTrue(message, connection.isOomProtectedOrWasWhenDied());
         }
     }
 
@@ -421,18 +386,21 @@
             String message = managerEntry.getErrorMessage();
 
             // Add two connections, bind and release each.
-            MockChildProcessConnection firstConnection = new MockChildProcessConnection(1);
+            TestChildProcessConnection firstConnection = new TestChildProcessConnection(1);
+            firstConnection.start(null /* startCallback */);
             manager.addNewConnection(firstConnection.getPid(), firstConnection);
             manager.setInForeground(firstConnection.getPid(), true);
             manager.setInForeground(firstConnection.getPid(), false);
 
-            MockChildProcessConnection secondConnection = new MockChildProcessConnection(2);
+            TestChildProcessConnection secondConnection = new TestChildProcessConnection(2);
+            secondConnection.start(null /* startCallback */);
             manager.addNewConnection(secondConnection.getPid(), secondConnection);
             manager.setInForeground(secondConnection.getPid(), true);
             manager.setInForeground(secondConnection.getPid(), false);
 
             // Add third connection, do not bind it.
-            MockChildProcessConnection thirdConnection = new MockChildProcessConnection(3);
+            TestChildProcessConnection thirdConnection = new TestChildProcessConnection(3);
+            thirdConnection.start(null /* startCallback */);
             manager.addNewConnection(thirdConnection.getPid(), thirdConnection);
             manager.setInForeground(thirdConnection.getPid(), false);
 
@@ -470,15 +438,16 @@
         // This test applies only to the moderate-binding manager.
         final BindingManagerImpl manager = mModerateBindingManager;
 
-        MockChildProcessConnection[] connections = new MockChildProcessConnection[3];
+        TestChildProcessConnection[] connections = new TestChildProcessConnection[3];
         for (int i = 0; i < connections.length; i++) {
-            connections[i] = new MockChildProcessConnection(i + 1);
+            connections[i] = new TestChildProcessConnection(i + 1);
+            connections[i].start(null /* startCallback */);
             manager.addNewConnection(connections[i].getPid(), connections[i]);
         }
 
         // Verify that each connection has a moderate binding after binding and releasing a strong
         // binding.
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             manager.setInForeground(connection.getPid(), true);
             manager.setInForeground(connection.getPid(), false);
             ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
@@ -487,42 +456,44 @@
 
         // Exclude lastInForeground because it will be kept in foreground when onSentToBackground()
         // is called as |mLastInForeground|.
-        MockChildProcessConnection lastInForeground = new MockChildProcessConnection(0);
+        TestChildProcessConnection lastInForeground = new TestChildProcessConnection(0);
         manager.addNewConnection(lastInForeground.getPid(), lastInForeground);
+        lastInForeground.start(null /* startCallback */);
         manager.setInForeground(lastInForeground.getPid(), true);
         manager.setInForeground(lastInForeground.getPid(), false);
+
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
 
         // Verify that leaving the application for a short time doesn't clear the moderate bindings.
         manager.onSentToBackground();
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             Assert.assertTrue(connection.isModerateBindingBound());
         }
         Assert.assertTrue(lastInForeground.isStrongBindingBound());
         Assert.assertFalse(lastInForeground.isModerateBindingBound());
         manager.onBroughtToForeground();
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             Assert.assertTrue(connection.isModerateBindingBound());
         }
 
         // Call onSentToBackground() and verify that all the moderate bindings drop after some
         // delay.
         manager.onSentToBackground();
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             Assert.assertTrue(connection.isModerateBindingBound());
         }
         Assert.assertTrue(lastInForeground.isStrongBindingBound());
         Assert.assertFalse(lastInForeground.isModerateBindingBound());
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             Assert.assertFalse(connection.isModerateBindingBound());
         }
 
         // Call onBroughtToForeground() and verify that the previous moderate bindings aren't
         // recovered.
         manager.onBroughtToForeground();
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             Assert.assertFalse(connection.isModerateBindingBound());
         }
     }
@@ -536,15 +507,16 @@
         final Application app = mActivity.getApplication();
         final BindingManagerImpl manager = mModerateBindingManager;
 
-        MockChildProcessConnection[] connections = new MockChildProcessConnection[4];
+        TestChildProcessConnection[] connections = new TestChildProcessConnection[4];
         for (int i = 0; i < connections.length; i++) {
-            connections[i] = new MockChildProcessConnection(i + 1);
+            connections[i] = new TestChildProcessConnection(i + 1);
+            connections[i].start(null /* startCallback */);
             manager.addNewConnection(connections[i].getPid(), connections[i]);
         }
 
         // Verify that each connection has a moderate binding after binding and releasing a strong
         // binding.
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             manager.setInForeground(connection.getPid(), true);
             manager.setInForeground(connection.getPid(), false);
             ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
@@ -553,7 +525,7 @@
 
         // Call onLowMemory() and verify that all the moderate bindings drop.
         app.onLowMemory();
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             Assert.assertFalse(connection.isModerateBindingBound());
         }
     }
@@ -568,17 +540,17 @@
         // This test applies only to the moderate-binding manager.
         final BindingManagerImpl manager = mModerateBindingManager;
 
-        ArrayList<Pair<Integer, Integer>> levelAndExpectedVictimCountList =
-                new ArrayList<Pair<Integer, Integer>>();
+        ArrayList<Pair<Integer, Integer>> levelAndExpectedVictimCountList = new ArrayList<>();
         levelAndExpectedVictimCountList.add(
                 new Pair<Integer, Integer>(TRIM_MEMORY_RUNNING_MODERATE, 1));
         levelAndExpectedVictimCountList.add(new Pair<Integer, Integer>(TRIM_MEMORY_RUNNING_LOW, 2));
         levelAndExpectedVictimCountList.add(
                 new Pair<Integer, Integer>(TRIM_MEMORY_RUNNING_CRITICAL, 4));
 
-        MockChildProcessConnection[] connections = new MockChildProcessConnection[4];
+        TestChildProcessConnection[] connections = new TestChildProcessConnection[4];
         for (int i = 0; i < connections.length; i++) {
-            connections[i] = new MockChildProcessConnection(i + 1);
+            connections[i] = new TestChildProcessConnection(i + 1);
+            connections[i].start(null /* startCallback */);
             manager.addNewConnection(connections[i].getPid(), connections[i]);
         }
 
@@ -586,7 +558,7 @@
             String message = "Failed for the level=" + pair.first;
             // Verify that each connection has a moderate binding after binding and releasing a
             // strong binding.
-            for (MockChildProcessConnection connection : connections) {
+            for (TestChildProcessConnection connection : connections) {
                 manager.setInForeground(connection.getPid(), true);
                 manager.setInForeground(connection.getPid(), false);
                 ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
@@ -611,15 +583,16 @@
         // This test applies only to the moderate-binding manager.
         final BindingManagerImpl manager = mModerateBindingManager;
 
-        MockChildProcessConnection[] connections = new MockChildProcessConnection[4];
+        TestChildProcessConnection[] connections = new TestChildProcessConnection[4];
         for (int i = 0; i < connections.length; i++) {
-            connections[i] = new MockChildProcessConnection(i + 1);
+            connections[i] = new TestChildProcessConnection(i + 1);
+            connections[i].start(null /* startCallback */);
             manager.addNewConnection(connections[i].getPid(), connections[i]);
         }
 
         // Verify that each connection has a moderate binding after binding and releasing a strong
         // binding.
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             manager.setInForeground(connection.getPid(), true);
             manager.setInForeground(connection.getPid(), false);
             ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
@@ -629,7 +602,7 @@
         // Call BindingManager.releaseAllModerateBindings() and verify that all the moderate
         // bindings drop.
         manager.releaseAllModerateBindings();
-        for (MockChildProcessConnection connection : connections) {
+        for (TestChildProcessConnection connection : connections) {
             Assert.assertFalse(connection.isModerateBindingBound());
         }
     }
@@ -644,7 +617,8 @@
         BindingManagerImpl manager = BindingManagerImpl.createBindingManagerForTesting(false);
         manager.startModerateBindingManagement(mActivity, 4);
 
-        MockChildProcessConnection connection = new MockChildProcessConnection(0);
+        TestChildProcessConnection connection = new TestChildProcessConnection(0);
+        connection.start(null /* startCallback */);
         manager.addNewConnection(connection.getPid(), connection);
         Assert.assertTrue(connection.isInitialBindingBound());
         Assert.assertFalse(connection.isModerateBindingBound());
@@ -665,7 +639,8 @@
         BindingManagerImpl manager = BindingManagerImpl.createBindingManagerForTesting(false);
         manager.startModerateBindingManagement(mActivity, 4);
 
-        MockChildProcessConnection connection = new MockChildProcessConnection(0);
+        TestChildProcessConnection connection = new TestChildProcessConnection(0);
+        connection.start(null /* startCallback */);
         manager.addNewConnection(connection.getPid(), connection);
         Assert.assertTrue(connection.isInitialBindingBound());
         Assert.assertFalse(connection.isStrongBindingBound());
@@ -688,7 +663,8 @@
         BindingManagerImpl manager = BindingManagerImpl.createBindingManagerForTesting(false);
         manager.startModerateBindingManagement(mActivity, 4);
 
-        MockChildProcessConnection connection = new MockChildProcessConnection(0);
+        TestChildProcessConnection connection = new TestChildProcessConnection(0);
+        connection.start(null /* startCallback */);
         manager.addNewConnection(connection.getPid(), connection);
         manager.setInForeground(connection.getPid(), false);
         manager.determinedVisibility(connection.getPid());
diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h
index 4ece52b3..ffe2eaed 100644
--- a/content/public/browser/navigation_controller.h
+++ b/content/public/browser/navigation_controller.h
@@ -268,7 +268,9 @@
   // committed entries.
   virtual NavigationEntry* GetLastCommittedEntry() const = 0;
 
-  // Returns the index of the last committed entry.
+  // Returns the index of the last committed entry.  It will be -1 if there are
+  // no entries, or if there is a transient entry before the first entry
+  // commits.
   virtual int GetLastCommittedEntryIndex() const = 0;
 
   // Returns true if the source for the current entry can be viewed.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 850838a..a35805d 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -339,14 +339,6 @@
                                  base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
-#if !defined(OS_ANDROID)
-// Controls whether media playback in cross-origin iframes is enabled. The
-// feature overrides |kDisableGestureRequirementForMediaPlayback|.
-const base::Feature kCrossOriginMediaPlaybackRequiresUserGesture{
-    "CrossOriginMediaPlaybackRequiresUserGesture",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-#endif  // !defined(OS_ANDROID)
-
 #if defined(OS_WIN)
 // Emergency "off switch" for new Windows sandbox security mitigation,
 // sandbox::MITIGATION_EXTENSION_POINT_DISABLE.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index a14b144..df86cb3 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -88,11 +88,6 @@
 CONTENT_EXPORT extern const base::Feature kWebNfc;
 #endif  // defined(OS_ANDROID)
 
-#if !defined(OS_ANDROID)
-CONTENT_EXPORT extern const base::Feature
-    kCrossOriginMediaPlaybackRequiresUserGesture;
-#endif  // !defined(OS_ANDROID)
-
 #if defined(OS_WIN)
 CONTENT_EXPORT extern const base::Feature kWinSboxDisableExtensionPoints;
 #endif  // defined(OS_WIN)
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
index 51c4d26e..2907d16a 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
@@ -21,7 +21,7 @@
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.base.process_launcher.ChildProcessCreationParams;
 import org.chromium.base.process_launcher.FileDescriptorInfo;
-import org.chromium.content.browser.ChildProcessConnection;
+import org.chromium.content.browser.BaseChildProcessConnection;
 import org.chromium.content.browser.ChildProcessLauncher;
 import org.chromium.content.browser.LauncherThread;
 
@@ -86,12 +86,12 @@
         }
     }
 
-    public static ChildProcessConnection startInternalForTesting(final Context context,
+    public static BaseChildProcessConnection startInternalForTesting(final Context context,
             final String[] commandLine, final FileDescriptorInfo[] filesToMap,
             final ChildProcessCreationParams params) {
-        return runOnLauncherAndGetResult(new Callable<ChildProcessConnection>() {
+        return runOnLauncherAndGetResult(new Callable<BaseChildProcessConnection>() {
             @Override
-            public ChildProcessConnection call() {
+            public BaseChildProcessConnection call() {
                 return ChildProcessLauncher.startInternal(context, commandLine,
                         0 /* childProcessId */, filesToMap, null /* launchCallback */,
                         null /* childProcessCallback */, true /* inSandbox */,
@@ -124,7 +124,7 @@
         final boolean bindToCaller = true;
         ChildProcessCreationParams params = new ChildProcessCreationParams(
                 getPackageName(), false, LibraryProcessType.PROCESS_CHILD, bindToCaller);
-        final ChildProcessConnection conn =
+        final BaseChildProcessConnection conn =
                 startInternalForTesting(this, commandLine, new FileDescriptorInfo[0], params);
 
         // Poll the connection until it is set up. The main test in ChildProcessLauncherTest, which
diff --git a/content/utility/utility_thread_impl.cc b/content/utility/utility_thread_impl.cc
index 4f61f46..681bffd0 100644
--- a/content/utility/utility_thread_impl.cc
+++ b/content/utility/utility_thread_impl.cc
@@ -90,8 +90,12 @@
       base::Bind(&UtilityThreadImpl::BindServiceFactoryRequest,
                  base::Unretained(this)),
       base::ThreadTaskRunnerHandle::Get());
-  ChildThread::Get()->GetServiceManagerConnection()->AddConnectionFilter(
-      base::MakeUnique<SimpleConnectionFilter>(std::move(registry)));
+
+  content::ServiceManagerConnection* connection = GetServiceManagerConnection();
+  if (connection) {
+    connection->AddConnectionFilter(
+        base::MakeUnique<SimpleConnectionFilter>(std::move(registry)));
+  }
 
   GetContentClient()->utility()->UtilityThreadStarted();
 
diff --git a/courgette/BUILD.gn b/courgette/BUILD.gn
index 8e3d30f..8c96e43 100644
--- a/courgette/BUILD.gn
+++ b/courgette/BUILD.gn
@@ -13,6 +13,8 @@
     "assembly_program.h",
     "consecutive_range_visitor.h",
     "courgette.h",
+    "courgette_flow.cc",
+    "courgette_flow.h",
     "crc.cc",
     "crc.h",
     "difference_estimator.cc",
diff --git a/courgette/courgette_flow.cc b/courgette/courgette_flow.cc
new file mode 100644
index 0000000..2fa9810
--- /dev/null
+++ b/courgette/courgette_flow.cc
@@ -0,0 +1,189 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "courgette/courgette_flow.h"
+
+#include <stdarg.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "courgette/assembly_program.h"
+#include "courgette/encoded_program.h"
+#include "courgette/program_detector.h"
+
+namespace courgette {
+
+/******** CourgetteFlow::Data ********/
+
+CourgetteFlow::Data::Data() = default;
+
+CourgetteFlow::Data::~Data() = default;
+
+/******** CourgetteFlow ********/
+
+CourgetteFlow::CourgetteFlow() = default;
+
+CourgetteFlow::~CourgetteFlow() = default;
+
+// static
+const char* CourgetteFlow::name(Group group) {
+  switch (group) {
+    case ONLY:
+      return "input";
+    case OLD:
+      return "'old' input";
+    case NEW:
+      return "'new' input";
+    default:
+      NOTREACHED();
+      break;
+  }
+  return nullptr;
+}
+
+CourgetteFlow::Data* CourgetteFlow::data(Group group) {
+  switch (group) {
+    case ONLY:
+      return &data_only_;
+    case OLD:
+      return &data_old_;
+    case NEW:
+      return &data_new_;
+    default:
+      NOTREACHED();
+      break;
+  }
+  return nullptr;
+}
+
+bool CourgetteFlow::ok() {
+  return status_ == C_OK;
+}
+
+bool CourgetteFlow::failed() {
+  return status_ != C_OK;
+}
+
+Status CourgetteFlow::status() {
+  return status_;
+}
+
+const std::string& CourgetteFlow::message() {
+  return message_;
+}
+
+void CourgetteFlow::ReadSourceStreamSetFromBuffer(Group group,
+                                                  const BasicBuffer& buffer) {
+  if (failed())
+    return;
+  Data* d = data(group);
+  if (!check(d->sources.Init(buffer.data(), buffer.length()),
+             C_GENERAL_ERROR)) {
+    setMessage("Cannot read %s as SourceStreamSet.", name(group));
+  }
+}
+
+void CourgetteFlow::ReadAssemblyProgramFromBuffer(Group group,
+                                                  const BasicBuffer& buffer,
+                                                  bool annotate) {
+  if (failed())
+    return;
+  Data* d = data(group);
+  auto parser = annotate ? ParseDetectedExecutableWithAnnotation
+                         : ParseDetectedExecutable;
+  if (!check(parser(buffer.data(), buffer.length(), &d->program)))
+    setMessage("Cannot parse %s (code = %d).", name(group), status_);
+}
+
+void CourgetteFlow::ReadEncodedProgramFromSourceStreamSet(
+    Group group,
+    SourceStreamSet* opt_sources /* nullptr */) {
+  if (failed())
+    return;
+  Data* d = data(group);
+  SourceStreamSet* sources = opt_sources ? opt_sources : &d->sources;
+  if (!check(ReadEncodedProgram(sources, &d->encoded)))
+    setMessage("Cannot read %s as encoded program.", name(group));
+}
+
+void CourgetteFlow::CreateEncodedProgramFromAssemblyProgram(Group group) {
+  if (failed())
+    return;
+  Data* d = data(group);
+  if (!check(Encode(*d->program, &d->encoded)))
+    setMessage("Cannot encode %s (code = %d).", name(group), status_);
+}
+
+void CourgetteFlow::WriteSinkStreamFromSinkStreamSet(Group group,
+                                                     SinkStream* sink) {
+  DCHECK(sink);
+  if (failed())
+    return;
+  if (!check(data(group)->sinks.CopyTo(sink), C_GENERAL_ERROR))
+    setMessage("Cannnot combine serialized streams for %s.", name(group));
+}
+
+void CourgetteFlow::WriteSinkStreamSetFromEncodedProgram(
+    Group group,
+    SinkStreamSet* opt_sinks /* nullptr */) {
+  if (failed())
+    return;
+  Data* d = data(group);
+  SinkStreamSet* sinks = opt_sinks ? opt_sinks : &d->sinks;
+  if (!check(WriteEncodedProgram(d->encoded.get(), sinks)))
+    setMessage("Cannot serialize encoded %s.", name(group));
+}
+
+void CourgetteFlow::WriteExecutableFromEncodedProgram(Group group,
+                                                      SinkStream* sink) {
+  DCHECK(sink);
+  if (failed())
+    return;
+  if (!check(Assemble(data(group)->encoded.get(), sink)))
+    setMessage("Cannot assemble %s.", name(group));
+}
+
+void CourgetteFlow::AdjustNewAssemblyProgramToMatchOld() {
+  if (failed())
+    return;
+  if (!check(Adjust(*data_old_.program, data_new_.program.get())))
+    setMessage("Cannot adjust %s to match %s.", name(OLD), name(NEW));
+}
+
+void CourgetteFlow::DestroyAssemblyProgram(Group group) {
+  if (failed())
+    return;
+  data(group)->program.reset();
+}
+
+void CourgetteFlow::DestroyEncodedProgram(Group group) {
+  if (failed())
+    return;
+  data(group)->encoded.reset();
+}
+
+bool CourgetteFlow::check(Status new_status) {
+  if (new_status == C_OK)
+    return true;
+  status_ = new_status;
+  return false;
+}
+
+bool CourgetteFlow::check(bool success, Status failure_mode) {
+  if (success)
+    return true;
+  status_ = failure_mode;
+  return false;
+}
+
+void CourgetteFlow::setMessage(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  message_ = base::StringPrintV(format, args);
+  va_end(args);
+}
+
+}  // namespace courgette
diff --git a/courgette/courgette_flow.h b/courgette/courgette_flow.h
new file mode 100644
index 0000000..9b8cb889
--- /dev/null
+++ b/courgette/courgette_flow.h
@@ -0,0 +1,144 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COURGETTE_COURGETTE_FLOW_H_
+#define COURGETTE_COURGETTE_FLOW_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "courgette/courgette.h"
+#include "courgette/region.h"
+#include "courgette/streams.h"
+
+namespace courgette {
+
+class AssemblyProgram;
+class EncodedProgram;
+
+// An adaptor for Region as BasicBuffer.
+class RegionBuffer : public BasicBuffer {
+ public:
+  explicit RegionBuffer(const Region& region) : region_(region) {}
+  ~RegionBuffer() override {}
+
+  // BasicBuffer:
+  const uint8_t* data() const override { return region_.start(); }
+  size_t length() const override { return region_.length(); }
+
+ private:
+  Region region_;
+
+  DISALLOW_COPY_AND_ASSIGN(RegionBuffer);
+};
+
+// CourgetteFlow stores Courgette data arranged into groups, and exposes
+// "commands" that operate on them. On the first occurrence of an error, the
+// Courgette error code is recorded, error messages are generated and stored,
+// and all subsequent commands become no-op. This allows callers to concisely
+// specify high-level logic with minimal code for error handling.
+class CourgetteFlow {
+ public:
+  // A group of Courgette data, for a single executable. Takes negligible space
+  // when unused.
+  struct Data {
+    Data();
+    ~Data();
+
+    std::unique_ptr<AssemblyProgram> program;
+    std::unique_ptr<EncodedProgram> encoded;
+    SinkStreamSet sinks;
+    SourceStreamSet sources;
+  };
+
+  // Group enumeration into |data_*_| fields.
+  enum Group {
+    ONLY,  // The only file processed.
+    OLD,   // The "old" file during patching.
+    NEW,   // The "new" file during patching.
+  };
+
+  CourgetteFlow();
+  ~CourgetteFlow();
+
+  static const char* name(Group group);
+  Data* data(Group group);  // Allows caller to modify.
+  bool ok();
+  bool failed();
+  Status status();
+  const std::string& message();
+
+  // Commands that perform no-op on error. This allows caller to concisely
+  // specify high-level logic, and perform a single error check at the end. Care
+  // must be taken w.r.t. error handling if |data()| is harvested between
+  // commands.
+
+  // Reads |buffer| to initialize |data(group)->sources|.
+  void ReadSourceStreamSetFromBuffer(Group group, const BasicBuffer& buffer);
+
+  // Reads |buffer| to initialize |data(group)->program|, passing |annotate| as
+  // initialization parameter (true if AdjustNewAssemblyProgramToMatchOld() gets
+  // called later).
+  void ReadAssemblyProgramFromBuffer(Group group,
+                                     const BasicBuffer& buffer,
+                                     bool annotate);
+
+  // Reads |opt_sources| if given, or else |data(group)->sources| to initialize
+  // |data(group).encoded|.
+  void ReadEncodedProgramFromSourceStreamSet(
+      Group group,
+      SourceStreamSet* opt_sources = nullptr);
+
+  // Uses |data(group)->program| to initialize |data(group)->encoded|.
+  void CreateEncodedProgramFromAssemblyProgram(Group group);
+
+  // Serializese |data(group)->sinks| to |sink|.
+  void WriteSinkStreamFromSinkStreamSet(Group group, SinkStream* sink);
+
+  // Serializes |data(group)->encoded| to |opt_sinks| if given, or else to
+  // |data(group)->sinks|.
+  void WriteSinkStreamSetFromEncodedProgram(Group group,
+                                            SinkStreamSet* opt_sinks = nullptr);
+
+  // Converts |data(group)->encoded| to an exectuable and writes the result to
+  // |sink|.
+  void WriteExecutableFromEncodedProgram(Group group, SinkStream* sink);
+
+  // Adjusts |data(NEW)->program| Labels to match |data(OLD)->program| Labels.
+  void AdjustNewAssemblyProgramToMatchOld();
+
+  // Destructor commands to reduce memory usage.
+
+  void DestroyAssemblyProgram(Group group);
+
+  void DestroyEncodedProgram(Group group);
+
+ private:
+  // Utilities to process return values from Courgette functions, and assign
+  // |status_| and |message_|. Usage:
+  //   if (!check(some_courgette_function(param1, ...)))
+  //     setMessage("format string %s...", value1, ...);
+
+  // Reassigns |status_|, and returns true if |C_OK|.
+  bool check(Status new_status);
+
+  // check() alternative for functions that return true on succes. On failure
+  // assigns |status_| to |failure_mode|.
+  bool check(bool success, Status failure_mode);
+
+  void setMessage(const char* format, ...);
+
+  Status status_ = C_OK;
+  std::string message_;
+  Data data_only_;
+  Data data_old_;
+  Data data_new_;
+
+  DISALLOW_COPY_AND_ASSIGN(CourgetteFlow);
+};
+
+}  // namespace courgette
+
+#endif  // COURGETTE_COURGETTE_FLOW_H_
diff --git a/courgette/courgette_tool.cc b/courgette/courgette_tool.cc
index 5ca91d1aa..46bd578 100644
--- a/courgette/courgette_tool.cc
+++ b/courgette/courgette_tool.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
 
+#include <initializer_list>
 #include <memory>
 #include <string>
 #include <vector>
@@ -15,26 +17,45 @@
 #include "base/files/file_util.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/logging.h"
+#include "base/macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "courgette/assembly_program.h"
 #include "courgette/courgette.h"
+#include "courgette/courgette_flow.h"
 #include "courgette/encoded_program.h"
 #include "courgette/program_detector.h"
 #include "courgette/streams.h"
 #include "courgette/third_party/bsdiff/bsdiff.h"
 
+namespace {
+
+using courgette::CourgetteFlow;
+
+const char kUsageGen[] = "-gen <old_in> <new_in> <patch_out>";
+const char kUsageApply[] = "-apply <old_in> <patch_in> <new_out>";
+const char kUsageGenbsdiff[] = "-genbsdiff <old_in> <new_in> <patch_out>";
+const char kUsageApplybsdiff[] = "-applybsdiff <old_in> <patch_in> <new_out>";
+const char kUsageSupported[] = "-supported <exec_file_in>";
+const char kUsageDis[] = "-dis <exec_file_in> <assembly_file_out>";
+const char kUsageAsm[] = "-asm <assembly_file_in> <exec_file_out>";
+const char kUsageDisadj[] = "-disadj <old_in> <new_in> <new_assembly_file_out>";
+const char kUsageGen1[] = "-gen1[au] <old_in> <new_in> <patch_base_out>";
+
+/******** Utilities to print help and exit ********/
+
 void PrintHelp() {
-  fprintf(stderr,
-    "Usage:\n"
-    "  courgette -supported <executable_file>\n"
-    "  courgette -dis <executable_file> <binary_assembly_file>\n"
-    "  courgette -asm <binary_assembly_file> <executable_file>\n"
-    "  courgette -disadj <executable_file> <reference> <binary_assembly_file>\n"
-    "  courgette -gen <v1> <v2> <patch>\n"
-    "  courgette -apply <v1> <patch> <v2>\n"
-    "\n");
+  fprintf(stderr, "Main Usage:\n");
+  for (auto usage :
+       {kUsageGen, kUsageApply, kUsageGenbsdiff, kUsageApplybsdiff}) {
+    fprintf(stderr, "  courgette %s\n", usage);
+  }
+  fprintf(stderr, "Diagnosis Usage:\n");
+  for (auto usage :
+       {kUsageSupported, kUsageDis, kUsageAsm, kUsageDisadj, kUsageGen1}) {
+    fprintf(stderr, "  courgette %s\n", usage);
+  }
 }
 
 void UsageProblem(const char* message) {
@@ -53,20 +74,29 @@
   exit(1);
 }
 
+/******** BufferedFileReader ********/
+
 // A file reader that calls Problem() on failure.
-class BufferedFileReader {
+class BufferedFileReader : public courgette::BasicBuffer {
  public:
   BufferedFileReader(const base::FilePath& file_name, const char* kind) {
     if (!buffer_.Initialize(file_name))
       Problem("Can't read %s file.", kind);
   }
-  const uint8_t* data() const { return buffer_.data(); }
-  size_t length() const { return buffer_.length(); }
+  ~BufferedFileReader() override {}
+
+  // courgette::BasicBuffer:
+  const uint8_t* data() const override { return buffer_.data(); }
+  size_t length() const override { return buffer_.length(); }
 
  private:
   base::MemoryMappedFile buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(BufferedFileReader);
 };
 
+/******** Various helpers ********/
+
 void WriteSinkToFile(const courgette::SinkStream *sink,
                      const base::FilePath& output_file) {
   int count =
@@ -79,38 +109,6 @@
     Problem("Incomplete write.");
 }
 
-void Disassemble(const base::FilePath& input_file,
-                 const base::FilePath& output_file) {
-  BufferedFileReader buffer(input_file, "input");
-
-  std::unique_ptr<courgette::AssemblyProgram> program;
-  const courgette::Status parse_status = courgette::ParseDetectedExecutable(
-      buffer.data(), buffer.length(), &program);
-  if (parse_status != courgette::C_OK)
-    Problem("Can't parse input (code = %d).", parse_status);
-
-  std::unique_ptr<courgette::EncodedProgram> encoded;
-  const courgette::Status encode_status = Encode(*program, &encoded);
-  if (encode_status != courgette::C_OK)
-    Problem("Can't encode program.");
-
-  program.reset();
-
-  courgette::SinkStreamSet sinks;
-  const courgette::Status write_status =
-    courgette::WriteEncodedProgram(encoded.get(), &sinks);
-  if (write_status != courgette::C_OK)
-    Problem("Can't serialize encoded program.");
-
-  encoded.reset();
-
-  courgette::SinkStream sink;
-  if (!sinks.CopyTo(&sink))
-    Problem("Can't combine serialized encoded program streams.");
-
-  WriteSinkToFile(&sink, output_file);
-}
-
 bool Supported(const base::FilePath& input_file) {
   bool result = false;
 
@@ -153,50 +151,41 @@
   return result;
 }
 
-void DisassembleAndAdjust(const base::FilePath& program_file,
-                          const base::FilePath& model_file,
-                          const base::FilePath& output_file) {
-  BufferedFileReader program_buffer(program_file, "program");
-  BufferedFileReader model_buffer(model_file, "model");
-
-  std::unique_ptr<courgette::AssemblyProgram> program;
-  const courgette::Status parse_program_status =
-      courgette::ParseDetectedExecutable(program_buffer.data(),
-                                         program_buffer.length(), &program);
-  if (parse_program_status != courgette::C_OK)
-    Problem("Can't parse program input (code = %d).", parse_program_status);
-
-  std::unique_ptr<courgette::AssemblyProgram> model;
-  const courgette::Status parse_model_status =
-      courgette::ParseDetectedExecutable(model_buffer.data(),
-                                         model_buffer.length(), &model);
-  if (parse_model_status != courgette::C_OK)
-    Problem("Can't parse model input (code = %d).", parse_model_status);
-
-  const courgette::Status adjust_status = Adjust(*model, program.get());
-  if (adjust_status != courgette::C_OK)
-    Problem("Can't adjust program.");
-
-  model.reset();
-
-  std::unique_ptr<courgette::EncodedProgram> encoded;
-  const courgette::Status encode_status = Encode(*program, &encoded);
-  if (encode_status != courgette::C_OK)
-    Problem("Can't encode program.");
-
-  program.reset();
-
-  courgette::SinkStreamSet sinks;
-  const courgette::Status write_status =
-    courgette::WriteEncodedProgram(encoded.get(), &sinks);
-  if (write_status != courgette::C_OK)
-    Problem("Can't serialize encoded program.");
-
-  encoded.reset();
-
+void Disassemble(const base::FilePath& input_file,
+                 const base::FilePath& output_file) {
+  CourgetteFlow flow;
+  BufferedFileReader input_buffer(input_file, flow.name(flow.ONLY));
+  flow.ReadAssemblyProgramFromBuffer(flow.ONLY, input_buffer, false);
+  flow.CreateEncodedProgramFromAssemblyProgram(flow.ONLY);
+  flow.DestroyAssemblyProgram(flow.ONLY);
+  flow.WriteSinkStreamSetFromEncodedProgram(flow.ONLY);
+  flow.DestroyEncodedProgram(flow.ONLY);
   courgette::SinkStream sink;
-  if (!sinks.CopyTo(&sink))
-    Problem("Can't combine serialized encoded program streams.");
+  flow.WriteSinkStreamFromSinkStreamSet(flow.ONLY, &sink);
+  if (flow.failed())
+    Problem(flow.message().c_str());
+
+  WriteSinkToFile(&sink, output_file);
+}
+
+void DisassembleAndAdjust(const base::FilePath& old_file,
+                          const base::FilePath& new_file,
+                          const base::FilePath& output_file) {
+  CourgetteFlow flow;
+  BufferedFileReader old_buffer(old_file, flow.name(flow.OLD));
+  BufferedFileReader new_buffer(new_file, flow.name(flow.NEW));
+  flow.ReadAssemblyProgramFromBuffer(flow.OLD, old_buffer, true);
+  flow.ReadAssemblyProgramFromBuffer(flow.NEW, new_buffer, true);
+  flow.AdjustNewAssemblyProgramToMatchOld();
+  flow.DestroyAssemblyProgram(flow.OLD);
+  flow.CreateEncodedProgramFromAssemblyProgram(flow.NEW);
+  flow.DestroyAssemblyProgram(flow.NEW);
+  flow.WriteSinkStreamSetFromEncodedProgram(flow.NEW);
+  flow.DestroyEncodedProgram(flow.NEW);
+  courgette::SinkStream sink;
+  flow.WriteSinkStreamFromSinkStreamSet(flow.NEW, &sink);
+  if (flow.failed())
+    Problem(flow.message().c_str());
 
   WriteSinkToFile(&sink, output_file);
 }
@@ -206,69 +195,32 @@
 // original file's stream and the new file's stream.  This is completely
 // uninteresting to users, but it is handy for seeing how much each which
 // streams are contributing to the final file size.  Adjustment is optional.
-void DisassembleAdjustDiff(const base::FilePath& model_file,
-                           const base::FilePath& program_file,
+void DisassembleAdjustDiff(const base::FilePath& old_file,
+                           const base::FilePath& new_file,
                            const base::FilePath& output_file_root,
                            bool adjust) {
-  BufferedFileReader model_buffer(model_file, "old");
-  BufferedFileReader program_buffer(program_file, "new");
-
-  auto parser = adjust ? courgette::ParseDetectedExecutableWithAnnotation
-                       : courgette::ParseDetectedExecutable;
-
-  std::unique_ptr<courgette::AssemblyProgram> model;
-  const courgette::Status parse_model_status =
-      parser(model_buffer.data(), model_buffer.length(), &model);
-  if (parse_model_status != courgette::C_OK)
-    Problem("Can't parse model input (code = %d).", parse_model_status);
-
-  std::unique_ptr<courgette::AssemblyProgram> program;
-  const courgette::Status parse_program_status =
-      parser(program_buffer.data(), program_buffer.length(), &program);
-  if (parse_program_status != courgette::C_OK)
-    Problem("Can't parse program input (code = %d).", parse_program_status);
-
-  if (adjust) {
-    const courgette::Status adjust_status = Adjust(*model, program.get());
-    if (adjust_status != courgette::C_OK)
-      Problem("Can't adjust program.");
-  }
-
-  std::unique_ptr<courgette::EncodedProgram> encoded_program;
-  const courgette::Status encode_program_status =
-      Encode(*program, &encoded_program);
-  if (encode_program_status != courgette::C_OK)
-    Problem("Can't encode program.");
-
-  program.reset();
-
-  std::unique_ptr<courgette::EncodedProgram> encoded_model;
-  const courgette::Status encode_model_status = Encode(*model, &encoded_model);
-  if (encode_model_status != courgette::C_OK)
-    Problem("Can't encode model.");
-
-  model.reset();
-
-  courgette::SinkStreamSet program_sinks;
-  const courgette::Status write_program_status =
-    courgette::WriteEncodedProgram(encoded_program.get(), &program_sinks);
-  if (write_program_status != courgette::C_OK)
-    Problem("Can't serialize encoded program.");
-
-  encoded_program.reset();
-
-  courgette::SinkStreamSet model_sinks;
-  const courgette::Status write_model_status =
-    courgette::WriteEncodedProgram(encoded_model.get(), &model_sinks);
-  if (write_model_status != courgette::C_OK)
-    Problem("Can't serialize encoded model.");
-
-  encoded_model.reset();
+  CourgetteFlow flow;
+  BufferedFileReader old_buffer(old_file, flow.name(flow.OLD));
+  BufferedFileReader new_buffer(new_file, flow.name(flow.NEW));
+  flow.ReadAssemblyProgramFromBuffer(flow.OLD, old_buffer, adjust);
+  flow.ReadAssemblyProgramFromBuffer(flow.NEW, new_buffer, adjust);
+  if (adjust)
+    flow.AdjustNewAssemblyProgramToMatchOld();
+  flow.CreateEncodedProgramFromAssemblyProgram(flow.OLD);
+  flow.DestroyAssemblyProgram(flow.OLD);
+  flow.CreateEncodedProgramFromAssemblyProgram(flow.NEW);
+  flow.DestroyAssemblyProgram(flow.NEW);
+  flow.WriteSinkStreamSetFromEncodedProgram(flow.OLD);
+  flow.DestroyEncodedProgram(flow.OLD);
+  flow.WriteSinkStreamSetFromEncodedProgram(flow.NEW);
+  flow.DestroyEncodedProgram(flow.NEW);
+  if (flow.failed())
+    Problem(flow.message().c_str());
 
   courgette::SinkStream empty_sink;
   for (int i = 0;  ; ++i) {
-    courgette::SinkStream* old_stream = model_sinks.stream(i);
-    courgette::SinkStream* new_stream = program_sinks.stream(i);
+    courgette::SinkStream* old_stream = flow.data(flow.OLD)->sinks.stream(i);
+    courgette::SinkStream* new_stream = flow.data(flow.NEW)->sinks.stream(i);
     if (old_stream == NULL && new_stream == NULL)
       break;
 
@@ -291,24 +243,14 @@
 
 void Assemble(const base::FilePath& input_file,
               const base::FilePath& output_file) {
-  BufferedFileReader buffer(input_file, "input");
-
-  courgette::SourceStreamSet sources;
-  if (!sources.Init(buffer.data(), buffer.length()))
-    Problem("Bad input file.");
-
-  std::unique_ptr<courgette::EncodedProgram> encoded;
-  const courgette::Status read_status =
-      courgette::ReadEncodedProgram(&sources, &encoded);
-  if (read_status != courgette::C_OK)
-    Problem("Bad encoded program.");
-
+  CourgetteFlow flow;
+  BufferedFileReader input_buffer(input_file, flow.name(flow.ONLY));
+  flow.ReadSourceStreamSetFromBuffer(flow.ONLY, input_buffer);
+  flow.ReadEncodedProgramFromSourceStreamSet(flow.ONLY);
   courgette::SinkStream sink;
-
-  const courgette::Status assemble_status =
-      courgette::Assemble(encoded.get(), &sink);
-  if (assemble_status != courgette::C_OK)
-    Problem("Can't assemble.");
+  flow.WriteExecutableFromEncodedProgram(flow.ONLY, &sink);
+  if (flow.failed())
+    Problem(flow.message().c_str());
 
   WriteSinkToFile(&sink, output_file);
 }
@@ -430,6 +372,8 @@
   WriteSinkToFile(&new_stream, new_file);
 }
 
+}  // namespace
+
 int main(int argc, const char* argv[]) {
   base::AtExitManager at_exit_manager;
   base::CommandLine::Init(argc, argv);
@@ -468,50 +412,51 @@
       repeat_count = 1;
 
   if (cmd_sup + cmd_dis + cmd_asm + cmd_disadj + cmd_make_patch +
-      cmd_apply_patch + cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch +
-      cmd_spread_1_adjusted + cmd_spread_1_unadjusted
-      != 1)
+          cmd_apply_patch + cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch +
+          cmd_spread_1_adjusted + cmd_spread_1_unadjusted !=
+      1) {
     UsageProblem(
-        "Must have exactly one of:\n"
-        "  -supported -asm, -dis, -disadj, -gen or -apply, -genbsdiff"
-        " or -applybsdiff.");
+        "First argument must be one of:\n"
+        "  -supported, -asm, -dis, -disadj, -gen, -apply, -genbsdiff,"
+        " -applybsdiff, or -gen1[au].");
+  }
 
   while (repeat_count-- > 0) {
     if (cmd_sup) {
       if (values.size() != 1)
-        UsageProblem("-supported <executable_file>");
+        UsageProblem(kUsageSupported);
       return !Supported(values[0]);
     } else if (cmd_dis) {
-        if (values.size() != 2)
-          UsageProblem("-dis <executable_file> <courgette_file>");
-        Disassemble(values[0], values[1]);
+      if (values.size() != 2)
+        UsageProblem(kUsageDis);
+      Disassemble(values[0], values[1]);
     } else if (cmd_asm) {
       if (values.size() != 2)
-        UsageProblem("-asm <courgette_file_input> <executable_file_output>");
+        UsageProblem(kUsageAsm);
       Assemble(values[0], values[1]);
     } else if (cmd_disadj) {
       if (values.size() != 3)
-        UsageProblem("-disadj <executable_file> <model> <courgette_file>");
+        UsageProblem(kUsageDisadj);
       DisassembleAndAdjust(values[0], values[1], values[2]);
     } else if (cmd_make_patch) {
       if (values.size() != 3)
-        UsageProblem("-gen <old_file> <new_file> <patch_file>");
+        UsageProblem(kUsageGen);
       GenerateEnsemblePatch(values[0], values[1], values[2]);
     } else if (cmd_apply_patch) {
       if (values.size() != 3)
-        UsageProblem("-apply <old_file> <patch_file> <new_file>");
+        UsageProblem(kUsageApply);
       ApplyEnsemblePatch(values[0], values[1], values[2]);
     } else if (cmd_make_bsdiff_patch) {
       if (values.size() != 3)
-        UsageProblem("-genbsdiff <old_file> <new_file> <patch_file>");
+        UsageProblem(kUsageGenbsdiff);
       GenerateBSDiffPatch(values[0], values[1], values[2]);
     } else if (cmd_apply_bsdiff_patch) {
       if (values.size() != 3)
-        UsageProblem("-applybsdiff <old_file> <patch_file> <new_file>");
+        UsageProblem(kUsageApplybsdiff);
       ApplyBSDiffPatch(values[0], values[1], values[2]);
     } else if (cmd_spread_1_adjusted || cmd_spread_1_unadjusted) {
       if (values.size() != 3)
-        UsageProblem("-gen1[au] <old_file> <new_file> <patch_files_root>");
+        UsageProblem(kUsageGen1);
       DisassembleAdjustDiff(values[0], values[1], values[2],
                             cmd_spread_1_adjusted);
     } else {
diff --git a/courgette/encode_decode_unittest.cc b/courgette/encode_decode_unittest.cc
index 6e88ee3..7b705f12 100644
--- a/courgette/encode_decode_unittest.cc
+++ b/courgette/encode_decode_unittest.cc
@@ -9,10 +9,13 @@
 #include "courgette/assembly_program.h"
 #include "courgette/base_test_unittest.h"
 #include "courgette/courgette.h"
+#include "courgette/courgette_flow.h"
 #include "courgette/encoded_program.h"
 #include "courgette/program_detector.h"
 #include "courgette/streams.h"
 
+namespace courgette {
+
 class EncodeDecodeTest : public BaseTest {
  public:
   void TestAssembleToStreamDisassemble(const std::string& file,
@@ -22,57 +25,63 @@
 void EncodeDecodeTest::TestAssembleToStreamDisassemble(
     const std::string& file,
     size_t expected_encoded_length) const {
-  const uint8_t* original_buffer =
-      reinterpret_cast<const uint8_t*>(file.data());
+  const uint8_t* original_data = reinterpret_cast<const uint8_t*>(file.data());
   size_t original_length = file.length();
+  CourgetteFlow flow;
 
-  std::unique_ptr<courgette::AssemblyProgram> program;
-  const courgette::Status parse_status = courgette::ParseDetectedExecutable(
-      original_buffer, original_length, &program);
-  EXPECT_EQ(courgette::C_OK, parse_status);
+  // Convert executable to encoded assembly.
+  RegionBuffer original_buffer(Region(original_data, original_length));
+  flow.ReadAssemblyProgramFromBuffer(flow.ONLY, original_buffer, false);
+  EXPECT_EQ(C_OK, flow.status());
+  EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->program.get());
 
-  std::unique_ptr<courgette::EncodedProgram> encoded;
-  const courgette::Status encode_status = Encode(*program, &encoded);
-  EXPECT_EQ(courgette::C_OK, encode_status);
+  flow.CreateEncodedProgramFromAssemblyProgram(flow.ONLY);
+  EXPECT_EQ(C_OK, flow.status());
+  EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->encoded.get());
 
-  program.reset();
+  flow.DestroyAssemblyProgram(flow.ONLY);
+  EXPECT_EQ(C_OK, flow.status());
+  EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->program.get());
 
-  courgette::SinkStreamSet sinks;
-  const courgette::Status write_status =
-      WriteEncodedProgram(encoded.get(), &sinks);
-  EXPECT_EQ(courgette::C_OK, write_status);
+  flow.WriteSinkStreamSetFromEncodedProgram(flow.ONLY);
+  EXPECT_EQ(C_OK, flow.status());
 
-  encoded.reset();
+  flow.DestroyEncodedProgram(flow.ONLY);
+  EXPECT_EQ(C_OK, flow.status());
+  EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->encoded.get());
 
-  courgette::SinkStream sink;
-  bool can_collect = sinks.CopyTo(&sink);
-  EXPECT_TRUE(can_collect);
+  SinkStream sink;
+  flow.WriteSinkStreamFromSinkStreamSet(flow.ONLY, &sink);
+  EXPECT_EQ(C_OK, flow.status());
 
-  const void* buffer = sink.Buffer();
-  size_t length = sink.Length();
+  const void* encoded_data = sink.Buffer();
+  size_t encoded_length = sink.Length();
+  EXPECT_EQ(expected_encoded_length, encoded_length);
 
-  EXPECT_EQ(expected_encoded_length, length);
+  // Convert encoded assembly back to executable.
+  RegionBuffer encoded_buffer(Region(encoded_data, encoded_length));
+  flow.ReadSourceStreamSetFromBuffer(flow.ONLY, encoded_buffer);
+  EXPECT_EQ(C_OK, flow.status());
 
-  courgette::SourceStreamSet sources;
-  bool can_get_source_streams = sources.Init(buffer, length);
-  EXPECT_TRUE(can_get_source_streams);
+  flow.ReadEncodedProgramFromSourceStreamSet(flow.ONLY);
+  EXPECT_EQ(C_OK, flow.status());
+  EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->encoded.get());
 
-  std::unique_ptr<courgette::EncodedProgram> encoded2;
-  const courgette::Status read_status = ReadEncodedProgram(&sources, &encoded2);
-  EXPECT_EQ(courgette::C_OK, read_status);
+  SinkStream executable;
+  flow.WriteExecutableFromEncodedProgram(flow.ONLY, &executable);
+  EXPECT_EQ(C_OK, flow.status());
 
-  courgette::SinkStream assembled;
-  const courgette::Status assemble_status =
-      Assemble(encoded2.get(), &assembled);
-  EXPECT_EQ(courgette::C_OK, assemble_status);
+  flow.DestroyEncodedProgram(flow.ONLY);
+  EXPECT_EQ(C_OK, flow.status());
+  EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->encoded.get());
+  EXPECT_TRUE(flow.ok());
+  EXPECT_FALSE(flow.failed());
 
-  encoded2.reset();
+  const void* executable_data = executable.Buffer();
+  size_t executable_length = executable.Length();
+  EXPECT_EQ(original_length, executable_length);
 
-  const void* assembled_buffer = assembled.Buffer();
-  size_t assembled_length = assembled.Length();
-
-  EXPECT_EQ(original_length, assembled_length);
-  EXPECT_EQ(0, memcmp(original_buffer, assembled_buffer, original_length));
+  EXPECT_EQ(0, memcmp(original_data, executable_data, original_length));
 }
 
 TEST_F(EncodeDecodeTest, PE) {
@@ -99,3 +108,5 @@
   std::string file = FileContents("elf-armv7");
   TestAssembleToStreamDisassemble(file, 8531);
 }
+
+}  // namespace courgette
diff --git a/courgette/encoded_program_fuzz_unittest.cc b/courgette/encoded_program_fuzz_unittest.cc
index be32adf..108283516 100644
--- a/courgette/encoded_program_fuzz_unittest.cc
+++ b/courgette/encoded_program_fuzz_unittest.cc
@@ -17,10 +17,9 @@
 #include <memory>
 
 #include "base/test/test_suite.h"
-#include "courgette/assembly_program.h"
 #include "courgette/base_test_unittest.h"
 #include "courgette/courgette.h"
-#include "courgette/program_detector.h"
+#include "courgette/courgette_flow.h"
 #include "courgette/streams.h"
 
 class DecodeFuzzTest : public BaseTest {
@@ -41,32 +40,36 @@
 void DecodeFuzzTest::FuzzExe(const char* file_name) const {
   std::string file1 = FileContents(file_name);
 
-  const uint8_t* original_buffer =
-      reinterpret_cast<const uint8_t*>(file1.data());
+  const uint8_t* original_data = reinterpret_cast<const uint8_t*>(file1.data());
   size_t original_length = file1.length();
+  courgette::CourgetteFlow flow;
 
-  std::unique_ptr<courgette::AssemblyProgram> program;
-  const courgette::Status parse_status =
-      courgette::ParseDetectedExecutable(original_buffer, original_length,
-                                         &program);
-  EXPECT_EQ(courgette::C_OK, parse_status);
+  courgette::RegionBuffer original_buffer(
+      courgette::Region(original_data, original_length));
+  flow.ReadAssemblyProgramFromBuffer(flow.ONLY, original_buffer, false);
+  EXPECT_EQ(courgette::C_OK, flow.status());
+  EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->program.get());
 
-  std::unique_ptr<courgette::EncodedProgram> encoded;
-  const courgette::Status encode_status = Encode(*program, &encoded);
-  EXPECT_EQ(courgette::C_OK, encode_status);
+  flow.CreateEncodedProgramFromAssemblyProgram(flow.ONLY);
+  EXPECT_EQ(courgette::C_OK, flow.status());
+  EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->encoded.get());
 
-  program.reset();
+  flow.DestroyAssemblyProgram(flow.ONLY);
+  EXPECT_EQ(courgette::C_OK, flow.status());
+  EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->program.get());
 
-  courgette::SinkStreamSet sinks;
-  const courgette::Status write_status =
-      WriteEncodedProgram(encoded.get(), &sinks);
-  EXPECT_EQ(courgette::C_OK, write_status);
+  flow.WriteSinkStreamSetFromEncodedProgram(flow.ONLY);
+  EXPECT_EQ(courgette::C_OK, flow.status());
 
-  encoded.reset();
+  flow.DestroyEncodedProgram(flow.ONLY);
+  EXPECT_EQ(courgette::C_OK, flow.status());
+  EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->encoded.get());
 
   courgette::SinkStream sink;
-  bool can_collect = sinks.CopyTo(&sink);
-  EXPECT_TRUE(can_collect);
+  flow.WriteSinkStreamFromSinkStreamSet(flow.ONLY, &sink);
+  EXPECT_EQ(courgette::C_OK, flow.status());
+  EXPECT_TRUE(flow.ok());
+  EXPECT_FALSE(flow.failed());
 
   size_t length = sink.Length();
 
@@ -173,34 +176,27 @@
   }
 }
 
-bool DecodeFuzzTest::TryAssemble(const std::string& buffer,
+bool DecodeFuzzTest::TryAssemble(const std::string& file,
                                  std::string* output) const {
-  std::unique_ptr<courgette::EncodedProgram> encoded;
-  bool result = false;
+  courgette::CourgetteFlow flow;
+  courgette::RegionBuffer file_buffer(courgette::Region(
+      reinterpret_cast<const uint8_t*>(file.data()), file.length()));
+  flow.ReadSourceStreamSetFromBuffer(flow.ONLY, file_buffer);
+  if (flow.failed())
+    return false;
 
-  courgette::SourceStreamSet sources;
-  bool can_get_source_streams = sources.Init(buffer.c_str(), buffer.length());
-  if (can_get_source_streams) {
-    const courgette::Status read_status =
-        ReadEncodedProgram(&sources, &encoded);
-    if (read_status == courgette::C_OK) {
-      courgette::SinkStream assembled;
-      const courgette::Status assemble_status =
-          Assemble(encoded.get(), &assembled);
+  flow.ReadEncodedProgramFromSourceStreamSet(flow.ONLY);
+  if (flow.failed())
+    return false;
 
-      if (assemble_status == courgette::C_OK) {
-        const void* assembled_buffer = assembled.Buffer();
-        size_t assembled_length = assembled.Length();
+  courgette::SinkStream sink;
+  flow.WriteExecutableFromEncodedProgram(flow.ONLY, &sink);
+  if (flow.failed())
+    return false;
 
-        output->clear();
-        output->assign(reinterpret_cast<const char*>(assembled_buffer),
-                       assembled_length);
-        result = true;
-      }
-    }
-  }
-
-  return result;
+  output->clear();
+  output->assign(reinterpret_cast<const char*>(sink.Buffer()), sink.Length());
+  return true;
 }
 
 TEST_F(DecodeFuzzTest, All) {
diff --git a/courgette/patch_generator_x86_32.h b/courgette/patch_generator_x86_32.h
index 0c2f9f0..b03d7c1 100644
--- a/courgette/patch_generator_x86_32.h
+++ b/courgette/patch_generator_x86_32.h
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This is the transformation and adjustment for all executables.
-// The executable type is determined by ParseDetectedExecutable function.
-
 #ifndef COURGETTE_PATCH_GENERATOR_X86_32_H_
 #define COURGETTE_PATCH_GENERATOR_X86_32_H_
 
@@ -13,12 +10,16 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "courgette/assembly_program.h"
+#include "courgette/courgette_flow.h"
 #include "courgette/ensemble.h"
 #include "courgette/patcher_x86_32.h"
 #include "courgette/program_detector.h"
 
 namespace courgette {
 
+// PatchGeneratorX86_32 is the universal patch generator for all executables,
+// performing transformation and adjustment. The executable type is determined
+// by the program detector.
 class PatchGeneratorX86_32 : public TransformationPatchGenerator {
  public:
   PatchGeneratorX86_32(Element* old_element,
@@ -62,56 +63,26 @@
     if (!corrected_parameters->Empty())
       return C_GENERAL_ERROR;
 
-    // Generate old version of program using |corrected_parameters|.
-    // TODO(sra): refactor to use same code from patcher_.
-    std::unique_ptr<AssemblyProgram> old_program;
-    Status old_parse_status = ParseDetectedExecutableWithAnnotation(
-        old_element_->region().start(), old_element_->region().length(),
-        &old_program);
-    if (old_parse_status != C_OK) {
-      LOG(ERROR) << "Cannot parse an executable " << old_element_->Name();
-      return old_parse_status;
+    CourgetteFlow flow;
+    RegionBuffer old_buffer(old_element_->region());
+    RegionBuffer new_buffer(new_element_->region());
+    flow.ReadAssemblyProgramFromBuffer(flow.OLD, old_buffer, true);
+    flow.CreateEncodedProgramFromAssemblyProgram(flow.OLD);
+    flow.WriteSinkStreamSetFromEncodedProgram(flow.OLD,
+                                              old_transformed_element);
+    flow.DestroyEncodedProgram(flow.OLD);
+    flow.ReadAssemblyProgramFromBuffer(flow.NEW, new_buffer, true);
+    flow.AdjustNewAssemblyProgramToMatchOld();
+    flow.DestroyAssemblyProgram(flow.OLD);
+    flow.CreateEncodedProgramFromAssemblyProgram(flow.NEW);
+    flow.DestroyAssemblyProgram(flow.NEW);
+    flow.WriteSinkStreamSetFromEncodedProgram(flow.NEW,
+                                              new_transformed_element);
+    if (flow.failed()) {
+      LOG(ERROR) << flow.message() << " (" << old_element_->Name() << " => "
+                 << new_element_->Name() << ")";
     }
-
-    // TODO(huangs): Move the block below to right before |new_program| gets
-    // used, so we can reduce Courgette-gen peak memory.
-    std::unique_ptr<AssemblyProgram> new_program;
-    Status new_parse_status = ParseDetectedExecutableWithAnnotation(
-        new_element_->region().start(), new_element_->region().length(),
-        &new_program);
-    if (new_parse_status != C_OK) {
-      LOG(ERROR) << "Cannot parse an executable " << new_element_->Name();
-      return new_parse_status;
-    }
-
-    std::unique_ptr<EncodedProgram> old_encoded;
-    Status old_encode_status = Encode(*old_program, &old_encoded);
-    if (old_encode_status != C_OK)
-      return old_encode_status;
-
-    Status old_write_status =
-        WriteEncodedProgram(old_encoded.get(), old_transformed_element);
-
-    old_encoded.reset();
-
-    if (old_write_status != C_OK)
-      return old_write_status;
-
-    Status adjust_status = Adjust(*old_program, new_program.get());
-    old_program.reset();
-    if (adjust_status != C_OK)
-      return adjust_status;
-
-    std::unique_ptr<EncodedProgram> new_encoded;
-    Status new_encode_status = Encode(*new_program, &new_encoded);
-    if (new_encode_status != C_OK)
-      return new_encode_status;
-
-    new_program.reset();
-
-    Status new_write_status =
-        WriteEncodedProgram(new_encoded.get(), new_transformed_element);
-    return new_write_status;
+    return flow.status();
   }
 
   Status Reform(SourceStreamSet* transformed_element,
diff --git a/courgette/patcher_x86_32.h b/courgette/patcher_x86_32.h
index c5f4e3c0..fa822dc 100644
--- a/courgette/patcher_x86_32.h
+++ b/courgette/patcher_x86_32.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 COURGETTE_WIN32_X86_PATCHER_H_
-#define COURGETTE_WIN32_X86_PATCHER_H_
+#ifndef COURGETTE_PATCHER_X86_32_H_
+#define COURGETTE_PATCHER_X86_32_H_
 
 #include <stdint.h>
 
@@ -11,15 +11,15 @@
 
 #include "base/macros.h"
 #include "courgette/assembly_program.h"
+#include "courgette/courgette_flow.h"
 #include "courgette/encoded_program.h"
 #include "courgette/ensemble.h"
 #include "courgette/program_detector.h"
 
 namespace courgette {
 
-// PatcherX86_32 is the universal patcher for all executables.  The executable
-// type is determined by ParseDetectedExecutable function.
-//
+// PatcherX86_32 is the universal patcher for all executables. The executable
+// type is determined by the program detector.
 class PatcherX86_32 : public TransformationPatcher {
  public:
   explicit PatcherX86_32(const Region& region)
@@ -49,36 +49,26 @@
 
   Status Transform(SourceStreamSet* corrected_parameters,
                    SinkStreamSet* transformed_element) {
-    Status status;
-    if (!corrected_parameters->Empty())
-      return C_GENERAL_ERROR;   // Don't expect any corrected parameters.
-
-    std::unique_ptr<AssemblyProgram> program;
-    status = ParseDetectedExecutable(ensemble_region_.start() + base_offset_,
-                                     base_length_,
-                                     &program);
-    if (status != C_OK)
-      return status;
-
-    std::unique_ptr<EncodedProgram> encoded;
-    status = Encode(*program, &encoded);
-    if (status != C_OK)
-      return status;
-
-    program.reset();
-
-    return WriteEncodedProgram(encoded.get(), transformed_element);
+    CourgetteFlow flow;
+    RegionBuffer only_buffer(
+        Region(ensemble_region_.start() + base_offset_, base_length_));
+    flow.ReadAssemblyProgramFromBuffer(flow.ONLY, only_buffer, false);
+    flow.CreateEncodedProgramFromAssemblyProgram(flow.ONLY);
+    flow.DestroyAssemblyProgram(flow.ONLY);
+    flow.WriteSinkStreamSetFromEncodedProgram(flow.ONLY, transformed_element);
+    if (flow.failed())
+      LOG(ERROR) << flow.message();
+    return flow.status();
   }
 
   Status Reform(SourceStreamSet* transformed_element,
                 SinkStream* reformed_element) {
-    Status status;
-    std::unique_ptr<EncodedProgram> encoded_program;
-    status = ReadEncodedProgram(transformed_element, &encoded_program);
-    if (status != C_OK)
-      return status;
-
-    return Assemble(encoded_program.get(), reformed_element);
+    CourgetteFlow flow;
+    flow.ReadEncodedProgramFromSourceStreamSet(flow.ONLY, transformed_element);
+    flow.WriteExecutableFromEncodedProgram(flow.ONLY, reformed_element);
+    if (flow.failed())
+      LOG(ERROR) << flow.message();
+    return flow.status();
   }
 
  private:
@@ -90,6 +80,6 @@
   DISALLOW_COPY_AND_ASSIGN(PatcherX86_32);
 };
 
-}  // namespace
+}  // namespace courgette
 
-#endif  // COURGETTE_WIN32_X86_PATCHER_H_
+#endif  // COURGETTE_PATCHER_X86_32_H_
diff --git a/courgette/streams.h b/courgette/streams.h
index c23d710..fb7204e 100644
--- a/courgette/streams.h
+++ b/courgette/streams.h
@@ -33,6 +33,15 @@
 // Maximum number of streams in a stream set.
 static const unsigned int kMaxStreams = 10;
 
+// A simple interface for reading binary data.
+class BasicBuffer {
+ public:
+  BasicBuffer() {}
+  virtual ~BasicBuffer() {}
+  virtual const uint8_t* data() const = 0;
+  virtual size_t length() const = 0;
+};
+
 // A SourceStream allows a region of memory to be scanned by a sequence of Read
 // operations.  The stream does not own the memory.
 class SourceStream {
@@ -234,5 +243,6 @@
   DISALLOW_COPY_AND_ASSIGN(SinkStreamSet);
 };
 
-}  // namespace
+}  // namespace courgette
+
 #endif  // COURGETTE_STREAMS_H_
diff --git a/device/BUILD.gn b/device/BUILD.gn
index b9d73b20..3b481aa6 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("vr/features.gni")
+import("vr/features/features.gni")
 import("//build/config/features.gni")
 import("//testing/test.gni")
 
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index 66f5c096..a1ed8c4 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -2,23 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("features.gni")
-import("//build/buildflag_header.gni")
+import("features/features.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
 if (is_android) {
   import("//build/config/android/rules.gni")  # For generate_jni().
 }
 
-# Generate a buildflag header for compile-time checking of WebVR support.
-buildflag_header("features") {
-  header = "features.h"
-  flags = [
-    "ENABLE_VR=$enable_vr",
-    "ENABLE_OPENVR=$enable_openvr",
-  ]
-}
-
 component("vr") {
   output_name = "device_vr"
 
@@ -27,11 +17,9 @@
   ]
   defines = [ "DEVICE_VR_IMPLEMENTATION" ]
   deps = [
-    ":features",
+    ":mojo_bindings",
+    "features",
   ]
-  if (!is_ios) {
-    deps += [ ":mojo_bindings" ]
-  }
 
   if (!enable_vr) {
     sources += [
@@ -128,19 +116,17 @@
   }
 }
 
-if (!is_ios) {
-  mojom("mojo_bindings") {
-    sources = [
-      "vr_service.mojom",
-    ]
+mojom("mojo_bindings") {
+  sources = [
+    "vr_service.mojom",
+  ]
 
-    public_deps = [
-      "//gpu/ipc/common:interfaces",
-      "//mojo/common:common_custom_types",
-    ]
+  public_deps = [
+    "//gpu/ipc/common:interfaces",
+    "//mojo/common:common_custom_types",
+  ]
 
-    export_class_attribute = "DEVICE_VR_EXPORT"
-    export_define = "DEVICE_VR_IMPLEMENTATION=1"
-    export_header = "device/vr/vr_export.h"
-  }
+  export_class_attribute = "DEVICE_VR_EXPORT"
+  export_define = "DEVICE_VR_IMPLEMENTATION=1"
+  export_header = "device/vr/vr_export.h"
 }
diff --git a/device/vr/features/BUILD.gn b/device/vr/features/BUILD.gn
new file mode 100644
index 0000000..e8afb4a
--- /dev/null
+++ b/device/vr/features/BUILD.gn
@@ -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.
+
+import("features.gni")
+import("//build/buildflag_header.gni")
+
+# Generate a buildflag header for compile-time checking of WebVR support.
+buildflag_header("features") {
+  header = "features.h"
+  flags = [
+    "ENABLE_VR=$enable_vr",
+    "ENABLE_OPENVR=$enable_openvr",
+  ]
+}
diff --git a/device/vr/features.gni b/device/vr/features/features.gni
similarity index 100%
rename from device/vr/features.gni
rename to device/vr/features/features.gni
diff --git a/device/vr/vr_device_manager.cc b/device/vr/vr_device_manager.cc
index bd314f4c..af97c0a 100644
--- a/device/vr/vr_device_manager.cc
+++ b/device/vr/vr_device_manager.cc
@@ -10,7 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
 #include "build/build_config.h"
-#include "device/vr/features.h"
+#include "device/vr/features/features.h"
 
 #if defined(OS_ANDROID)
 #include "device/vr/android/gvr/gvr_device_provider.h"
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.cc b/extensions/browser/api/networking_private/networking_private_chromeos.cc
index 93d4103e..c030fda 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.cc
@@ -860,10 +860,14 @@
     case UIProxyConfig::MODE_PROXY_PER_SCHEME: {
       base::DictionaryValue* manual =
           EnsureDictionaryValue(::onc::proxy::kManual, proxy_settings);
-      SetManualProxy(manual, state, ::onc::proxy::kHttp, config.http_proxy);
-      SetManualProxy(manual, state, ::onc::proxy::kHttps, config.https_proxy);
-      SetManualProxy(manual, state, ::onc::proxy::kFtp, config.ftp_proxy);
-      SetManualProxy(manual, state, ::onc::proxy::kSocks, config.socks_proxy);
+      if (config.http_proxy.server.is_valid())
+        SetManualProxy(manual, state, ::onc::proxy::kHttp, config.http_proxy);
+      if (config.https_proxy.server.is_valid())
+        SetManualProxy(manual, state, ::onc::proxy::kHttps, config.https_proxy);
+      if (config.ftp_proxy.server.is_valid())
+        SetManualProxy(manual, state, ::onc::proxy::kFtp, config.ftp_proxy);
+      if (config.socks_proxy.server.is_valid())
+        SetManualProxy(manual, state, ::onc::proxy::kSocks, config.socks_proxy);
       break;
     }
     case UIProxyConfig::MODE_PAC_SCRIPT: {
diff --git a/extensions/common/api/networking_onc.idl b/extensions/common/api/networking_onc.idl
index 6e37363..960c7a9 100644
--- a/extensions/common/api/networking_onc.idl
+++ b/extensions/common/api/networking_onc.idl
@@ -263,9 +263,12 @@
     ClientCertificateType ClientCertType;
     DOMString? Identity;
     DOMString? Inner;
-    DOMString Outer;
+    // The outer EAP type. Required by ONC, but may not be provided when
+    // translating from Shill.
+    DOMString? Outer;
     DOMString? Password;
     boolean? SaveCredentials;
+    DOMString[]? ServerCAPEMs;
     DOMString[]? ServerCARefs;
     boolean? UseProactiveKeyCaching;
     boolean? UseSystemCAs;
diff --git a/extensions/common/api/networking_private.idl b/extensions/common/api/networking_private.idl
index 5b9d9bd..4e3be9b 100644
--- a/extensions/common/api/networking_private.idl
+++ b/extensions/common/api/networking_private.idl
@@ -233,9 +233,12 @@
     DOMString? ClientCertType;
     DOMString? Identity;
     DOMString? Inner;
-    DOMString Outer;
+    // The outer EAP type. Required by ONC, but may not be provided when
+    // translating from Shill.
+    DOMString? Outer;
     DOMString? Password;
     boolean? SaveCredentials;
+    DOMString[]? ServerCAPEMs;
     DOMString[]? ServerCARefs;
     boolean? UseProactiveKeyCaching;
     boolean? UseSystemCAs;
@@ -248,9 +251,12 @@
     ManagedDOMString? ClientCertType;
     ManagedDOMString? Identity;
     ManagedDOMString? Inner;
-    ManagedDOMString Outer;
+    // The outer EAP type. Required by ONC, but may not be provided when
+    // translating from Shill.
+    ManagedDOMString? Outer;
     ManagedDOMString? Password;
     ManagedBoolean? SaveCredentials;
+    ManagedDOMStringList? ServerCAPEMs;
     ManagedDOMStringList? ServerCARefs;
     ManagedBoolean? UseProactiveKeyCaching;
     ManagedBoolean? UseSystemCAs;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 03b4c76..571974c 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -11,6 +11,9 @@
 // Allow users to specify a custom buffer size for debugging purpose.
 const char kAudioBufferSize[] = "audio-buffer-size";
 
+// Command line flag name to set the autoplay policy.
+const char kAutoplayPolicy[] = "autoplay-policy";
+
 // Set number of threads to use for video decoding.
 const char kVideoThreads[] = "video-threads";
 
@@ -135,21 +138,25 @@
 const char kMSEAudioBufferSizeLimit[] = "mse-audio-buffer-size-limit";
 const char kMSEVideoBufferSizeLimit[] = "mse-video-buffer-size-limit";
 
+namespace autoplay {
+
+// Autoplay policy to require a user gesture in ordor to play for cross origin
+// iframes.
+const char kCrossOriginUserGestureRequiredPolicy[] =
+    "cross-origin-user-gesture-required";
+
+// Autoplay policy that does not require any user gesture.
+const char kNoUserGestureRequiredPolicy[] = "no-user-gesture-required";
+
+// Autoplay policy to require a user gesture in order to play.
+const char kUserGestureRequiredPolicy[] = "user-gesture-required";
+
+}  // namespace autoplay
+
 }  // namespace switches
 
 namespace media {
 
-#if defined(OS_WIN)
-// Enables video decode acceleration using the D3D11 video decoder api.
-// This is completely insecure - DO NOT USE except for testing.
-const base::Feature kD3D11VideoDecoding{"D3D11VideoDecoding",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Enables H264 HW encode acceleration using Media Foundation for Windows.
-const base::Feature kMediaFoundationH264Encoding{
-    "MediaFoundationH264Encoding", base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // defined(OS_WIN)
-
 // Use new audio rendering mixer.
 const base::Feature kNewAudioRenderingMixingStrategy{
     "NewAudioRenderingMixingStrategy", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -214,4 +221,15 @@
 
 #endif
 
+#if defined(OS_WIN)
+// Enables video decode acceleration using the D3D11 video decoder api.
+// This is completely insecure - DO NOT USE except for testing.
+const base::Feature kD3D11VideoDecoding{"D3D11VideoDecoding",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables H264 HW encode acceleration using Media Foundation for Windows.
+const base::Feature kMediaFoundationH264Encoding{
+    "MediaFoundationH264Encoding", base::FEATURE_ENABLED_BY_DEFAULT};
+#endif  // defined(OS_WIN)
+
 }  // namespace media
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index b2234d1a..f40ea604 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -17,6 +17,8 @@
 
 MEDIA_EXPORT extern const char kAudioBufferSize[];
 
+MEDIA_EXPORT extern const char kAutoplayPolicy[];
+
 MEDIA_EXPORT extern const char kVideoThreads[];
 
 MEDIA_EXPORT extern const char kEnableMediaSuspend[];
@@ -75,6 +77,14 @@
 MEDIA_EXPORT extern const char kMSEAudioBufferSizeLimit[];
 MEDIA_EXPORT extern const char kMSEVideoBufferSizeLimit[];
 
+namespace autoplay {
+
+MEDIA_EXPORT extern const char kCrossOriginUserGestureRequiredPolicy[];
+MEDIA_EXPORT extern const char kNoUserGestureRequiredPolicy[];
+MEDIA_EXPORT extern const char kUserGestureRequiredPolicy[];
+
+}  // namespace autoplay
+
 }  // namespace switches
 
 namespace media {
@@ -82,11 +92,7 @@
 // All features in alphabetical order. The features should be documented
 // alongside the definition of their values in the .cc file.
 
-#if defined(OS_WIN)
-MEDIA_EXPORT extern const base::Feature kD3D11VideoDecoding;
-MEDIA_EXPORT extern const base::Feature kMediaFoundationH264Encoding;
-#endif  // defined(OS_WIN)
-
+MEDIA_EXPORT extern const base::Feature kAutoplayPolicy;
 MEDIA_EXPORT extern const base::Feature kNewAudioRenderingMixingStrategy;
 MEDIA_EXPORT extern const base::Feature kOverlayFullscreenVideo;
 MEDIA_EXPORT extern const base::Feature kResumeBackgroundVideo;
@@ -103,6 +109,12 @@
 MEDIA_EXPORT extern const base::Feature kVideoFullscreenOrientationLock;
 MEDIA_EXPORT extern const base::Feature kMediaDrmPersistentLicense;
 #endif  // defined(OS_ANDROID)
+
+#if defined(OS_WIN)
+MEDIA_EXPORT extern const base::Feature kD3D11VideoDecoding;
+MEDIA_EXPORT extern const base::Feature kMediaFoundationH264Encoding;
+#endif  // defined(OS_WIN)
+
 }  // namespace media
 
 #endif  // MEDIA_BASE_MEDIA_SWITCHES_H_
diff --git a/mojo/README.md b/mojo/README.md
index 64d129f..3c4428d 100644
--- a/mojo/README.md
+++ b/mojo/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo
+# Mojo
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md
index 6fc2cce..f6f8f356 100644
--- a/mojo/edk/embedder/README.md
+++ b/mojo/edk/embedder/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo Embedder Development Kit (EDK)
+# Mojo Embedder Development Kit (EDK)
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/edk/system/node_channel.cc b/mojo/edk/system/node_channel.cc
index bed3848..0c438ebe 100644
--- a/mojo/edk/system/node_channel.cc
+++ b/mojo/edk/system/node_channel.cc
@@ -692,7 +692,7 @@
               MACH_PORT_NULL) {
             pending_relay_messages_.push(
                 std::make_pair(data->destination, std::move(message)));
-            break;
+            return;
           }
         }
   #endif
diff --git a/mojo/public/c/system/README.md b/mojo/public/c/system/README.md
index 6e4ad3d9..a668fc1 100644
--- a/mojo/public/c/system/README.md
+++ b/mojo/public/c/system/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo C System API
+# Mojo C System API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index ef11c693..f94e314 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -39,6 +39,7 @@
     "connection_error_callback.h",
     "connector.h",
     "disconnect_reason.h",
+    "equals_traits.h",
     "filter_chain.h",
     "interface_data_view.h",
     "interface_endpoint_client.h",
@@ -65,7 +66,6 @@
     "lib/control_message_handler.h",
     "lib/control_message_proxy.cc",
     "lib/control_message_proxy.h",
-    "lib/equals_traits.h",
     "lib/filter_chain.cc",
     "lib/fixed_buffer.cc",
     "lib/fixed_buffer.h",
diff --git a/mojo/public/cpp/bindings/README.md b/mojo/public/cpp/bindings/README.md
index 9a8e2b04..7426f043 100644
--- a/mojo/public/cpp/bindings/README.md
+++ b/mojo/public/cpp/bindings/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo C++ Bindings API
+# Mojo C++ Bindings API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
@@ -457,13 +457,222 @@
 message. Thus the `impl` above will first log our message and *then* see a
 connection error and break out of the run loop.
 
+### Enums
+
+[Mojom enums](/mojo/public/tools/bindings#Enumeration-Types) translate directly
+to equivalent strongly-typed C++11 enum classes with `int32_t` as the underlying
+type. The typename and value names are identical between Mojom and C++.
+
+For example, consider the following Mojom definition:
+
+```cpp
+module business.mojom;
+
+enum Department {
+  kEngineering,
+  kMarketng,
+  kSales,
+};
+```
+
+This translates to the following C++ definition:
+
+```cpp
+namespace business {
+namespace mojom {
+
+enum class Department : int32_t {
+  kEngineering,
+  kMarketing,
+  kSales,
+};
+
+}  // namespace mojom
+}  // namespace business
+```
+
+### Structs
+
+[Mojom structs](mojo/public/tools/bindings#Structs) can be used to define
+logical groupings of fields into a new composite type. Every Mojom struct
+elicits the generation of an identically named, representative C++ class, with
+identically named public fields of corresponding C++ types, and several helpful
+public methods.
+
+For example, consider the following Mojom struct:
+
+```cpp
+module business.mojom;
+
+struct Employee {
+  int64 id;
+  string username;
+  Department department;
+};
+```
+
+This would generate a C++ class like so:
+
+```cpp
+namespace business {
+namespace mojom {
+
+class Employee;
+
+using EmployeePtr = mojo::StructPtr<Employee>;
+
+class Employee {
+ public:
+  // Default constructor - applies default values, potentially ones specified
+  // explicitly within the Mojom.
+  Employee();
+
+  // Value constructor - an explicit argument for every field in the struct, in
+  // lexical Mojom definition order.
+  Employee(int64_t id, const std::string& username, Department department);
+
+  // Creates a new copy of this struct value
+  EmployeePtr Clone();
+
+  // Tests for equality with another struct value of the same type.
+  bool Equals(const Employee& other);
+
+  // Equivalent public fields with names identical to the Mojom.
+  int64_t id;
+  std::string username;
+  Department department;
+};
+
+}  // namespace mojom
+}  // namespace business
+```
+
+Note when used as a message parameter or as a field within another Mojom struct,
+a `struct` type is wrapped by the move-only `mojo::StructPtr` helper, which is
+roughly equivalent to a `std::unique_ptr` with some additional utility methods.
+This allows struct values to be nullable and struct types to be potentially
+self-referential.
+
+Every genereated struct class has a static `New()` method which returns a new
+`mojo::StructPtr<T>` wrapping a new instance of the class constructed by
+forwarding the arguments from `New`. For example:
+
+```cpp
+mojom::EmployeePtr e1 = mojom::Employee::New();
+e1->id = 42;
+e1->username = "mojo";
+e1->department = mojom::Department::kEngineering;
+```
+
+is equivalent to
+
+```cpp
+auto e1 = mojom::Employee::New(42, "mojo", mojom::Department::kEngineering);
+```
+
+Now if we define an interface like:
+
+```cpp
+interface EmployeeManager {
+  AddEmployee(Employee e);
+};
+```
+
+We'll get this C++ interface to implement:
+
+```cpp
+class EmployeeManager {
+ public:
+  virtual ~EmployeManager() {}
+
+  virtual void AddEmployee(EmployeePtr e) = 0;
+};
+```
+
+And we can send this message from C++ code as follows:
+
+```cpp
+mojom::EmployeManagerPtr manager = ...;
+manager->AddEmployee(
+    Employee::New(42, "mojo", mojom::Department::kEngineering));
+
+// or
+auto e = Employee::New(42, "mojo", mojom::Department::kEngineering);
+manager->AddEmployee(std::move(e));
+```
+
+### Unions
+
+Similarly to [structs](#Structs), tagged unions generate an identically named,
+representative C++ class which is typically wrapped in a `mojo::StructPtr<T>`.
+
+Unlike structs, all generated union fields are private and must be retrieved and
+manipulated using accessors. A field `foo` is accessible by `foo()` and
+settable by `set_foo()`. There is also a boolean `is_foo()` for each field which
+indicates whether the union is currently taking on the value of field `foo` in
+exclusion to all other union fields.
+
+Finally, every generated union class also has a nested `Tag` enum class which
+enumerates all of the named union fields. A Mojom union value's current type can
+be determined by calling the `which()` method which returns a `Tag`.
+
+For example, consider the following Mojom definitions:
+
+```cpp
+union Value {
+  int64 int_value;
+  float32 float_vlaue;
+  string string_value;
+};
+
+interface Dictionary {
+  AddValue(string key, Value value);
+};
+```
+
+This generates a the following C++ interface:
+
+```cpp
+class Value {
+ public:
+  virtual ~Value() {}
+
+  virtual void AddValue(const std::string& key, ValuePtr value) = 0;
+};
+```
+
+And we can use it like so:
+
+```cpp
+ValuePtr value = Value::New();
+value->set_int_value(42);
+CHECK(value->is_int_value());
+CHECK_EQ(value->which(), Value::Tag::INT_VALUE);
+
+value->set_float_value(42);
+CHECK(value->is_float_value());
+CHECK_EQ(value->which(), Value::Tag::FLOAT_VALUE);
+
+value->set_string_value("bananas");
+CHECK(value->is_string_value());
+CHECK_EQ(value->which(), Value::Tag::STRING_VALUE);
+```
+
+Finally, note that if a union value is not currently occupied by a given field,
+attempts to access that field will DCHECK:
+
+```cpp
+ValuePtr value = Value::New();
+value->set_int_value(42);
+LOG(INFO) << "Value is " << value->string_value();  // DCHECK!
+```
+
 ### Sending Interfaces Over Interfaces
 
-Now we know how to create interface pipes and use their Ptr and Request
-endpoints in some interesting ways. This still doesn't add up to interesting
-IPC! The bread and butter of Mojo IPC is the ability to transfer interface
-endpoints across other interfaces, so let's take a look at how to accomplish
-that.
+We know how to create interface pipes and use their Ptr and Request endpoints
+in some interesting ways. This still doesn't add up to interesting IPC! The
+bread and butter of Mojo IPC is the ability to transfer interface endpoints
+across other interfaces, so let's take a look at how to accomplish that.
 
 #### Sending Interface Requests
 
diff --git a/mojo/public/cpp/bindings/lib/equals_traits.h b/mojo/public/cpp/bindings/equals_traits.h
similarity index 74%
rename from mojo/public/cpp/bindings/lib/equals_traits.h
rename to mojo/public/cpp/bindings/equals_traits.h
index 53c7dce..5d5dfa2 100644
--- a/mojo/public/cpp/bindings/lib/equals_traits.h
+++ b/mojo/public/cpp/bindings/equals_traits.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 MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_EQUALS_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_EQUALS_TRAITS_H_
 
 #include <type_traits>
 #include <unordered_map>
@@ -13,7 +13,10 @@
 #include "mojo/public/cpp/bindings/lib/template_util.h"
 
 namespace mojo {
-namespace internal {
+
+// EqualsTraits<> allows you to specify comparison functions for mapped mojo
+// objects. By default objects can be compared if they implement operator==()
+// or have a method named Equals().
 
 template <typename T>
 struct HasEqualsMethod {
@@ -24,7 +27,7 @@
   static const bool value = sizeof(Test<T>(0)) == sizeof(char);
 
  private:
-  EnsureTypeIsComplete<T> check_t_;
+  internal::EnsureTypeIsComplete<T> check_t_;
 };
 
 template <typename T, bool has_equals_method = HasEqualsMethod<T>::value>
@@ -51,7 +54,9 @@
     if (!a || !b)
       return false;
 
-    return internal::Equals(*a, *b);
+    // NOTE: Not just Equals() because that's EqualsTraits<>::Equals() and we
+    // want mojo::Equals() for things like base::Optional<std::vector<T>>.
+    return mojo::Equals(*a, *b);
   }
 };
 
@@ -61,7 +66,7 @@
     if (a.size() != b.size())
       return false;
     for (size_t i = 0; i < a.size(); ++i) {
-      if (!internal::Equals(a[i], b[i]))
+      if (!mojo::Equals(a[i], b[i]))
         return false;
     }
     return true;
@@ -76,7 +81,7 @@
       return false;
     for (const auto& element : a) {
       auto iter = b.find(element.first);
-      if (iter == b.end() || !internal::Equals(element.second, iter->second))
+      if (iter == b.end() || !mojo::Equals(element.second, iter->second))
         return false;
     }
     return true;
@@ -88,7 +93,6 @@
   return EqualsTraits<T>::Equals(a, b);
 }
 
-}  // namespace internal
 }  // namespace mojo
 
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_EQUALS_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
index 07f969dd..f18274f 100644
--- a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
+++ b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
@@ -8,7 +8,7 @@
 #include <type_traits>
 
 #include "mojo/public/cpp/bindings/clone_traits.h"
-#include "mojo/public/cpp/bindings/lib/equals_traits.h"
+#include "mojo/public/cpp/bindings/equals_traits.h"
 #include "third_party/WebKit/Source/platform/wtf/HashMap.h"
 #include "third_party/WebKit/Source/platform/wtf/Optional.h"
 #include "third_party/WebKit/Source/platform/wtf/Vector.h"
@@ -39,15 +39,13 @@
   }
 };
 
-namespace internal {
-
 template <typename T>
 struct EqualsTraits<WTF::Vector<T>, false> {
   static bool Equals(const WTF::Vector<T>& a, const WTF::Vector<T>& b) {
     if (a.size() != b.size())
       return false;
     for (size_t i = 0; i < a.size(); ++i) {
-      if (!internal::Equals(a[i], b[i]))
+      if (!Equals(a[i], b[i]))
         return false;
     }
     return true;
@@ -65,14 +63,13 @@
 
     for (auto iter = a.begin(); iter != a_end; ++iter) {
       auto b_iter = b.find(iter->key);
-      if (b_iter == b_end || !internal::Equals(iter->value, b_iter->value))
+      if (b_iter == b_end || !Equals(iter->value, b_iter->value))
         return false;
     }
     return true;
   }
 };
 
-}  // namespace internal
 }  // namespace mojo
 
 #endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_CLONE_EQUALS_UTIL_H_
diff --git a/mojo/public/cpp/system/README.md b/mojo/public/cpp/system/README.md
index f169411..3b60c39 100644
--- a/mojo/public/cpp/system/README.md
+++ b/mojo/public/cpp/system/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo C++ System API
+# Mojo C++ System API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/public/java/bindings/README.md b/mojo/public/java/bindings/README.md
index 2831344f..5f9b492 100644
--- a/mojo/public/java/bindings/README.md
+++ b/mojo/public/java/bindings/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo Java Bindings API
+# Mojo Java Bindings API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/public/java/system/README.md b/mojo/public/java/system/README.md
index c62f30b..373009c5 100644
--- a/mojo/public/java/system/README.md
+++ b/mojo/public/java/system/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo Java System API
+# Mojo Java System API
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
diff --git a/mojo/public/js/README.md b/mojo/public/js/README.md
index f510b03..2193450 100644
--- a/mojo/public/js/README.md
+++ b/mojo/public/js/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojo JavaScript System and Bindings APIs
+# Mojo JavaScript System and Bindings APIs
 This document is a subset of the [Mojo documentation](/mojo).
 
 **NOTE:** The JavaScript APIs are currently in flux and will stabilize soon.
diff --git a/mojo/public/tools/bindings/README.md b/mojo/public/tools/bindings/README.md
index 7a62d7e..b224991 100644
--- a/mojo/public/tools/bindings/README.md
+++ b/mojo/public/tools/bindings/README.md
@@ -1,4 +1,4 @@
-# ![Mojo Graphic](https://goo.gl/e0Hpks) Mojom IDL and Bindings Generator
+# Mojom IDL and Bindings Generator
 This document is a subset of the [Mojo documentation](/mojo).
 
 [TOC]
@@ -256,6 +256,29 @@
 code, see
 [documentation for individual target languages](#Generated-Code-For-Target-Languages).
 
+### Unions
+
+Mojom supports tagged unions using the **union** keyword. A union is a
+collection of fields which may taken the value of any single one of those fields
+at a time. Thus they provide a way to represent a variant value type while
+minimizing storage requirements.
+
+Union fields may be of any type supported by [struct](#Structs) fields. For
+example:
+
+```cpp
+enum ExampleUnion {
+  string str;
+  StringPair pair;
+  int64 id;
+  array<uint64, 2> guid;
+  SampleInterface iface;
+};
+```
+
+For details on how unions like this translate to generated bindings code, see
+[documentation for individual target languages](#Generated-Code-For-Target-Languages).
+
 ### Enumeration Types
 
 Enumeration types may be defined using the **enum** keyword either directly
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
index 9cb28d4e..19d806d2 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -46,9 +46,9 @@
 #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
 #include "mojo/public/cpp/bindings/associated_interface_request.h"
 #include "mojo/public/cpp/bindings/clone_traits.h"
+#include "mojo/public/cpp/bindings/equals_traits.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/lib/equals_traits.h"
 #include "mojo/public/cpp/bindings/lib/control_message_handler.h"
 #include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
index feb86156..0ab7f27e 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
@@ -13,7 +13,7 @@
               T, {{struct.name}}>::value>::type*>
 bool {{struct.name}}::Equals(const T& other) const {
 {%- for field in struct.fields %}
-  if (!mojo::internal::Equals(this->{{field.name}}, other.{{field.name}}))
+  if (!mojo::Equals(this->{{field.name}}, other.{{field.name}}))
     return false;
 {%- endfor %}
   return true;
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
index 4c4851f..a29a1b7f 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
@@ -30,9 +30,9 @@
     case Tag::{{field.name|upper}}:
 {%-   if field.kind|is_object_kind or
          field.kind|is_any_handle_or_interface_kind %}
-      return mojo::internal::Equals(*(data_.{{field.name}}), *(other.data_.{{field.name}}));
+      return mojo::Equals(*(data_.{{field.name}}), *(other.data_.{{field.name}}));
 {%-   else %}
-      return mojo::internal::Equals(data_.{{field.name}}, other.data_.{{field.name}});
+      return mojo::Equals(data_.{{field.name}}, other.data_.{{field.name}});
 {%-   endif %}
 {%- endfor %}
   };
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index 6bda6d0e..a060b35 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -40,7 +40,6 @@
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source.h"
 #include "net/log/net_log_source_type.h"
-#include "net/quic/chromium/quic_http_stream.h"
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/client_socket_pool_manager.h"
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc
index 691d6fa45..30a09c3 100644
--- a/net/quic/chromium/quic_stream_factory.cc
+++ b/net/quic/chromium/quic_stream_factory.cc
@@ -623,7 +623,7 @@
   return factory_->GetTimeDelayForWaitingJob(server_id_);
 }
 
-std::unique_ptr<QuicHttpStream> QuicStreamRequest::CreateStream() {
+std::unique_ptr<HttpStream> QuicStreamRequest::CreateStream() {
   if (!session_)
     return nullptr;
   return base::MakeUnique<QuicHttpStream>(session_, http_server_properties_);
diff --git a/net/quic/chromium/quic_stream_factory.h b/net/quic/chromium/quic_stream_factory.h
index 28f66da..916c2f97 100644
--- a/net/quic/chromium/quic_stream_factory.h
+++ b/net/quic/chromium/quic_stream_factory.h
@@ -130,7 +130,7 @@
   // returns the amount of time waiting job should be delayed.
   base::TimeDelta GetTimeDelayForWaitingJob() const;
 
-  std::unique_ptr<QuicHttpStream> CreateStream();
+  std::unique_ptr<HttpStream> CreateStream();
 
   std::unique_ptr<BidirectionalStreamImpl> CreateBidirectionalStreamImpl();
 
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index 7246829..8ba2ff6 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -157,8 +157,8 @@
 
 class QuicHttpStreamPeer {
  public:
-  static QuicChromiumClientSession* GetSession(QuicHttpStream* stream) {
-    return stream->session_.get();
+  static QuicChromiumClientSession* GetSession(HttpStream* stream) {
+    return static_cast<QuicHttpStream*>(stream)->session_.get();
   }
 };
 
@@ -289,7 +289,7 @@
                               callback_.callback()));
 
     EXPECT_THAT(callback_.WaitForResult(), IsOk());
-    std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+    std::unique_ptr<HttpStream> stream = request.CreateStream();
     EXPECT_TRUE(stream.get());
     stream.reset();
 
@@ -437,7 +437,7 @@
     // posted by QuicChromiumClientSession::MigrateToSocket().
     base::RunLoop().RunUntilIdle();
 
-    std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+    std::unique_ptr<HttpStream> stream = request.CreateStream();
     EXPECT_TRUE(stream.get());
 
     // Cause QUIC stream to be created.
@@ -766,7 +766,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   QuicStreamRequest request2(factory_.get(), &http_server_properties_);
@@ -811,7 +811,7 @@
                                 /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                 callback_.callback()));
 
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -837,7 +837,7 @@
                                 /*cert_verify_flags=*/0, url_, "POST", net_log_,
                                 callback_.callback()));
 
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -860,7 +860,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
@@ -891,7 +891,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
@@ -922,7 +922,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
@@ -953,7 +953,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
@@ -979,7 +979,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
@@ -1009,7 +1009,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
@@ -1051,7 +1051,7 @@
   EXPECT_EQ(OK, request.Request(host_port_pair_, privacy_mode_,
                                 /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                 callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   TestCompletionCallback callback;
@@ -1059,7 +1059,7 @@
   EXPECT_EQ(OK, request2.Request(server2, privacy_mode_,
                                  /*cert_verify_flags=*/0, url2_, "GET",
                                  net_log_, callback.callback()));
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_EQ(GetActiveSession(host_port_pair_), GetActiveSession(server2));
@@ -1110,7 +1110,7 @@
                              /*cert_verify_flags=*/0, url2_, "GET", net_log_,
                              callback.callback()));
   EXPECT_EQ(OK, callback.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
@@ -1143,7 +1143,7 @@
   EXPECT_EQ(OK, request.Request(host_port_pair_, privacy_mode_,
                                 /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                 callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   TestCompletionCallback callback;
@@ -1151,7 +1151,7 @@
   EXPECT_EQ(OK, request2.Request(server2, privacy_mode_,
                                  /*cert_verify_flags=*/0, url2_, "GET",
                                  net_log_, callback.callback()));
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   factory_->OnSessionGoingAway(GetActiveSession(host_port_pair_));
@@ -1163,7 +1163,7 @@
   EXPECT_EQ(OK, request3.Request(server2, privacy_mode_,
                                  /*cert_verify_flags=*/0, url2_, "GET",
                                  net_log_, callback3.callback()));
-  std::unique_ptr<QuicHttpStream> stream3 = request3.CreateStream();
+  std::unique_ptr<HttpStream> stream3 = request3.CreateStream();
   EXPECT_TRUE(stream3.get());
 
   EXPECT_TRUE(HasActiveSession(server2));
@@ -1196,7 +1196,7 @@
   EXPECT_EQ(OK, request.Request(server1, privacy_mode_,
                                 /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                 callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   TestCompletionCallback callback;
@@ -1204,7 +1204,7 @@
   EXPECT_EQ(OK, request2.Request(server2, privacy_mode_,
                                  /*cert_verify_flags=*/0, url2_, "GET",
                                  net_log_, callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_EQ(GetActiveSession(server1), GetActiveSession(server2));
@@ -1240,7 +1240,7 @@
   EXPECT_EQ(OK, request.Request(server1, privacy_mode_,
                                 /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                 callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   TestCompletionCallback callback;
@@ -1248,7 +1248,7 @@
   EXPECT_EQ(OK, request2.Request(server2, privacy_mode_,
                                  /*cert_verify_flags=*/0, url2_, "GET",
                                  net_log_, callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_EQ(GetActiveSession(server1), GetActiveSession(server2));
@@ -1295,7 +1295,7 @@
   EXPECT_EQ(OK, request.Request(server1, privacy_mode_,
                                 /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                 callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   TestCompletionCallback callback;
@@ -1303,7 +1303,7 @@
   EXPECT_EQ(OK, request2.Request(server2, privacy_mode_,
                                  /*cert_verify_flags=*/0, url2_, "GET",
                                  net_log_, callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_NE(GetActiveSession(server1), GetActiveSession(server2));
@@ -1336,7 +1336,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Mark the session as going away.  Ensure that while it is still alive
@@ -1355,7 +1355,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
@@ -1388,7 +1388,7 @@
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   HttpRequestInfo request_info;
-  std::vector<std::unique_ptr<QuicHttpStream>> streams;
+  std::vector<std::unique_ptr<HttpStream>> streams;
   // The MockCryptoClientStream sets max_open_streams to be
   // kDefaultMaxStreamsPerConnection / 2.
   for (size_t i = 0; i < kDefaultMaxStreamsPerConnection / 2; i++) {
@@ -1402,7 +1402,7 @@
     } else {
       EXPECT_THAT(rv, IsOk());
     }
-    std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+    std::unique_ptr<HttpStream> stream = request.CreateStream();
     EXPECT_TRUE(stream);
     EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY,
                                            net_log_, CompletionCallback()));
@@ -1413,7 +1413,7 @@
   EXPECT_EQ(OK, request.Request(host_port_pair_, privacy_mode_,
                                 /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                 CompletionCallback()));
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream);
   EXPECT_EQ(ERR_IO_PENDING,
             stream->InitializeStream(&request_info, DEFAULT_PRIORITY, net_log_,
@@ -1494,7 +1494,7 @@
   EXPECT_EQ(OK, request2.Request(host_port_pair_, privacy_mode_,
                                  /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                  callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream = request2.CreateStream();
 
   EXPECT_TRUE(stream.get());
   stream.reset();
@@ -1527,7 +1527,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   HttpRequestInfo request_info;
   EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY,
                                          net_log_, CompletionCallback()));
@@ -1611,7 +1611,7 @@
   EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
 
   // Create QuicHttpStream.
-  std::unique_ptr<QuicHttpStream> stream = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream = request2.CreateStream();
   EXPECT_TRUE(stream.get());
   stream.reset();
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
@@ -1671,7 +1671,7 @@
   EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
 
   // Create QuicHttpStream.
-  std::unique_ptr<QuicHttpStream> stream = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream = request2.CreateStream();
   EXPECT_TRUE(stream.get());
   stream.reset();
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
@@ -1705,7 +1705,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   HttpRequestInfo request_info;
   EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY,
                                          net_log_, CompletionCallback()));
@@ -1772,7 +1772,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -1839,7 +1839,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
@@ -1902,7 +1902,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -1964,7 +1964,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
@@ -2012,7 +2012,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -2070,7 +2070,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created, but marked as non-migratable.
@@ -2119,7 +2119,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -2171,7 +2171,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created, but marked as non-migratable.
@@ -2219,7 +2219,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -2267,7 +2267,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Ensure that session is alive and active.
@@ -2305,7 +2305,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Ensure that session is alive and active.
@@ -2347,7 +2347,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -2422,7 +2422,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
@@ -2469,7 +2469,7 @@
   EXPECT_EQ(OK, request1.Request(server1, privacy_mode_,
                                  /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                  callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
+  std::unique_ptr<HttpStream> stream1 = request1.CreateStream();
   EXPECT_TRUE(stream1.get());
 
   // Create request and QuicHttpStream to create session2.
@@ -2477,7 +2477,7 @@
   EXPECT_EQ(OK, request2.Request(server2, privacy_mode_,
                                  /*cert_verify_flags=*/0, url2_, "GET",
                                  net_log_, callback_.callback()));
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   QuicChromiumClientSession* session1 = GetActiveSession(server1);
@@ -2563,7 +2563,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -2626,7 +2626,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
@@ -2687,7 +2687,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -2752,7 +2752,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
@@ -2805,7 +2805,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -2857,7 +2857,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created, but marked as non-migratable.
@@ -2909,7 +2909,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -2967,7 +2967,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3055,7 +3055,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3144,7 +3144,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created, but marked as non-migratable.
@@ -3208,7 +3208,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3287,7 +3287,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3367,7 +3367,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3464,7 +3464,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3562,7 +3562,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3642,7 +3642,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
@@ -3694,7 +3694,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3782,7 +3782,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
@@ -3847,7 +3847,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -3910,7 +3910,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -4051,7 +4051,7 @@
                             /*cert_verify_flags=*/0, url_, "GET", net_log_,
                             callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Cause QUIC stream to be created.
@@ -4106,7 +4106,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   HttpRequestInfo request_info;
   EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY,
                                          net_log_, CompletionCallback()));
@@ -4159,7 +4159,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   HttpRequestInfo request_info;
   EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY,
                                          net_log_, CompletionCallback()));
@@ -4287,7 +4287,7 @@
   // the CancelWaitForDataReady task hasn't been posted.
   ASSERT_EQ(0u, runner_->GetPostedTasks().size());
 
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -4334,7 +4334,7 @@
   EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
             session->connection()->ping_timeout());
 
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
   HttpRequestInfo request_info;
   EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY,
@@ -4365,7 +4365,7 @@
   EXPECT_EQ(QuicTime::Delta::FromSeconds(10),
             session2->connection()->ping_timeout());
 
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
   EXPECT_EQ(OK, stream2->InitializeStream(&request_info, DEFAULT_PRIORITY,
                                           net_log_, CompletionCallback()));
@@ -4437,7 +4437,7 @@
 
   EXPECT_EQ(OK, callback_.WaitForResult());
 
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   // Restore |race_cert_verification|.
@@ -4490,7 +4490,7 @@
   // yielded the read.
   EXPECT_EQ(1u, observer.executed_count());
 
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_FALSE(stream.get());  // Session is already closed.
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -4535,7 +4535,7 @@
   // yielded the read.
   EXPECT_EQ(1u, observer.executed_count());
 
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_FALSE(stream.get());  // Session is already closed.
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -4558,7 +4558,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumPushStreamsCreated(factory_.get()));
@@ -4604,7 +4604,7 @@
                             callback_.callback()));
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
   EXPECT_TRUE(stream.get());
 
   EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumPushStreamsCreated(factory_.get()));
@@ -4632,7 +4632,7 @@
   EXPECT_EQ(index->GetPromised(kDefaultUrl), nullptr);
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   EXPECT_TRUE(socket_data1.AllReadDataConsumed());
@@ -4663,7 +4663,7 @@
                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
+  std::unique_ptr<HttpStream> stream1 = request1.CreateStream();
   EXPECT_TRUE(stream1.get());
   EXPECT_TRUE(HasActiveSession(host_port_pair_));
 
@@ -4673,7 +4673,7 @@
   EXPECT_EQ(OK, request2.Request(destination2, privacy_mode_,
                                  /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                  callback2.callback()));
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   QuicChromiumClientSession* session1 =
@@ -4846,7 +4846,7 @@
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
 
-  std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
+  std::unique_ptr<HttpStream> stream1 = request1.CreateStream();
   EXPECT_TRUE(stream1.get());
   EXPECT_TRUE(HasActiveSession(origin1_));
 
@@ -4856,7 +4856,7 @@
   EXPECT_EQ(OK, request2.Request(destination, privacy_mode_,
                                  /*cert_verify_flags=*/0, url2, "GET", net_log_,
                                  callback2.callback()));
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   QuicChromiumClientSession* session1 =
@@ -4917,7 +4917,7 @@
                              /*cert_verify_flags=*/0, url1, "GET", net_log_,
                              callback_.callback()));
   EXPECT_EQ(OK, callback_.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
+  std::unique_ptr<HttpStream> stream1 = request1.CreateStream();
   EXPECT_TRUE(stream1.get());
   EXPECT_TRUE(HasActiveSession(origin1_));
 
@@ -4928,7 +4928,7 @@
                              /*cert_verify_flags=*/0, url2, "GET", net_log_,
                              callback2.callback()));
   EXPECT_EQ(OK, callback2.WaitForResult());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   // |request2| does not pool to the first session, because PrivacyMode does not
@@ -5000,7 +5000,7 @@
                              /*cert_verify_flags=*/0, url1, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
+  std::unique_ptr<HttpStream> stream1 = request1.CreateStream();
   EXPECT_TRUE(stream1.get());
   EXPECT_TRUE(HasActiveSession(origin1_));
 
@@ -5011,7 +5011,7 @@
                              /*cert_verify_flags=*/0, url2, "GET", net_log_,
                              callback2.callback()));
   EXPECT_THAT(callback2.WaitForResult(), IsOk());
-  std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream();
+  std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
   // |request2| does not pool to the first session, because the certificate does
diff --git a/ppapi/proxy/tcp_socket_resource.h b/ppapi/proxy/tcp_socket_resource.h
index 57da040..3266805d 100644
--- a/ppapi/proxy/tcp_socket_resource.h
+++ b/ppapi/proxy/tcp_socket_resource.h
@@ -14,8 +14,6 @@
 
 namespace ppapi {
 
-enum TCPSocketVersion;
-
 namespace proxy {
 
 class PPAPI_PROXY_EXPORT TCPSocketResource : public thunk::PPB_TCPSocket_API,
diff --git a/services/service_manager/public/cpp/standalone_service/mach_broker.cc b/services/service_manager/public/cpp/standalone_service/mach_broker.cc
index 8f8c670..a9cc00f 100644
--- a/services/service_manager/public/cpp/standalone_service/mach_broker.cc
+++ b/services/service_manager/public/cpp/standalone_service/mach_broker.cc
@@ -5,7 +5,6 @@
 #include "services/service_manager/public/cpp/standalone_service/mach_broker.h"
 
 #include "base/logging.h"
-#include "base/memory/singleton.h"
 
 namespace service_manager {
 
@@ -22,7 +21,8 @@
 
 // static
 MachBroker* MachBroker::GetInstance() {
-  return base::Singleton<MachBroker>::get();
+  static MachBroker* broker = new MachBroker;
+  return broker;
 }
 
 MachBroker::MachBroker() : broker_(kBootstrapPortName) {
diff --git a/services/service_manager/public/cpp/standalone_service/mach_broker.h b/services/service_manager/public/cpp/standalone_service/mach_broker.h
index bb96e9f..a85b0fc 100644
--- a/services/service_manager/public/cpp/standalone_service/mach_broker.h
+++ b/services/service_manager/public/cpp/standalone_service/mach_broker.h
@@ -7,10 +7,6 @@
 
 #include "base/mac/mach_port_broker.h"
 
-namespace base {
-template <typename T> struct DefaultSingletonTraits;
-}
-
 namespace service_manager {
 
 // A global singleton |MachBroker| is used by the service manager to provide
@@ -43,7 +39,6 @@
  private:
   MachBroker();
   ~MachBroker();
-  friend struct base::DefaultSingletonTraits<MachBroker>;
 
   base::MachPortBroker broker_;
 };
diff --git a/services/service_manager/public/cpp/test/BUILD.gn b/services/service_manager/public/cpp/test/BUILD.gn
index c1697a3..90f3aea 100644
--- a/services/service_manager/public/cpp/test/BUILD.gn
+++ b/services/service_manager/public/cpp/test/BUILD.gn
@@ -20,6 +20,7 @@
     "//mojo/edk/system",
     "//services/catalog:lib",
     "//services/service_manager/background:lib",
+    "//services/service_manager/public/cpp/standalone_service",
   ]
 
   if (is_android) {
diff --git a/services/service_manager/public/cpp/test/run_all_service_tests.cc b/services/service_manager/public/cpp/test/run_all_service_tests.cc
index 7b13364..41e53eb 100644
--- a/services/service_manager/public/cpp/test/run_all_service_tests.cc
+++ b/services/service_manager/public/cpp/test/run_all_service_tests.cc
@@ -18,13 +18,7 @@
 #endif
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
-#include "base/mac/mach_port_broker.h"
-#endif
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-namespace {
-base::MachPortBroker* g_mach_broker = nullptr;
-}
+#include "services/service_manager/public/cpp/standalone_service/mach_broker.h"
 #endif
 
 int main(int argc, char** argv) {
@@ -36,11 +30,8 @@
   mojo::edk::Init();
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
-  if (!g_mach_broker) {
-    g_mach_broker = new base::MachPortBroker("Service Tests");
-    CHECK(g_mach_broker->Init());
-    mojo::edk::SetMachPortProvider(g_mach_broker);
-  }
+  mojo::edk::SetMachPortProvider(
+      service_manager::MachBroker::GetInstance()->port_provider());
 #endif
 
 #if defined(OS_ANDROID)
diff --git a/services/ui/gpu/gpu_main.cc b/services/ui/gpu/gpu_main.cc
index 580e5b4fc..fed0107d 100644
--- a/services/ui/gpu/gpu_main.cc
+++ b/services/ui/gpu/gpu_main.cc
@@ -213,7 +213,8 @@
       image_factory);
 
   frame_sink_manager_ = base::MakeUnique<viz::MojoFrameSinkManager>(
-      true, display_provider_.get(), std::move(request), std::move(client));
+      true, display_provider_.get());
+  frame_sink_manager_->Connect(std::move(request), std::move(client));
 }
 
 void GpuMain::TearDownOnCompositorThread() {
diff --git a/testing/buildbot/filters/ash_mus_unittests.filter b/testing/buildbot/filters/ash_mus_unittests.filter
index 02224f2..ed9440f4c 100644
--- a/testing/buildbot/filters/ash_mus_unittests.filter
+++ b/testing/buildbot/filters/ash_mus_unittests.filter
@@ -1,17 +1,6 @@
 -AcceleratorControllerTest.DisallowedAtModalWindow
 -AcceleratorControllerTest.GlobalAccelerators
 -AcceleratorControllerTest.RotateScreen
--AppListPresenterDelegateTest.TinyDisplay
--AppListPresenterDelegateTest.HideOnFocusOut/0
--AppListPresenterDelegateTest.HideOnFocusOut/1
--AppListPresenterDelegateTest.RemainVisibleWhenFocusingToApplistContainer/0
--AppListPresenterDelegateTest.RemainVisibleWhenFocusingToApplistContainer/1
--AppListPresenterDelegateTest.ClickOutsideBubbleClosesBubble/0
--AppListPresenterDelegateTest.ClickOutsideBubbleClosesBubble/1
--AppListPresenterDelegateTest.TapOutsideBubbleClosesBubble/0
--AppListPresenterDelegateTest.TapOutsideBubbleClosesBubble/1
--AppListPresenterDelegateTest.NonPrimaryDisplay/0
--AppListPresenterDelegateTest.NonPrimaryDisplay/1
 -AshNativeCursorManagerTest.FractionalScale
 -AshNativeCursorManagerTest.LockCursor
 -AshNativeCursorManagerTest.SetCursor
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a156f5ca..5527629 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1451,14 +1451,13 @@
             ],
             "experiments": [
                 {
-                    "name": "EnabledSoftFetches",
+                    "name": "EnabledSoftFetchesChromeReader_Rest",
                     "params": {
-                        "fetching_interval_hours-wifi-active_ntp_user": "8",
-                        "scheduler_trigger_types": "persistent_scheduler_wake_up,ntp_opened",
-                        "send_top_languages": "true",
-                        "soft_fetching_interval_hours-active-active_ntp_user": "2",
-                        "soft_fetching_interval_hours-active-active_suggestions_consumer": "1",
-                        "soft_fetching_interval_hours-active-rare_ntp_user": "4"
+                        "soft_fetching_interval_hours-fallback-active_ntp_user": "10",
+                        "soft_fetching_interval_hours-fallback-active_suggestions_consumer": "1",
+                        "soft_fetching_interval_hours-fallback-rare_ntp_user": "4",
+                        "user_classifier_active_consumer_clicks_at_least_once_per_hours": "96",
+                        "user_classifier_rare_user_opens_ntp_at_most_once_per_hours": "66"
                     },
                     "enable_features": [
                         "NTPArticleSuggestions"
diff --git a/third_party/WebKit/LayoutTests/transforms/perspective-fixed-pos-descendant-expected.html b/third_party/WebKit/LayoutTests/transforms/perspective-fixed-pos-descendant-expected.html
new file mode 100644
index 0000000..9417b44
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/transforms/perspective-fixed-pos-descendant-expected.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<div style="width:100px; height:100px; perspective:100px; background:green;"></div>
+This test verifies CSS perspective correctly applies to fixed-position descendants. The test passes if a 100x100 green box completely occludes the red box behind.
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/transforms/perspective-fixed-pos-descendant.html b/third_party/WebKit/LayoutTests/transforms/perspective-fixed-pos-descendant.html
new file mode 100644
index 0000000..3a087c0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/transforms/perspective-fixed-pos-descendant.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<div style="width:100px; height:100px; perspective:100px; background:red;">
+  <div style="position:fixed; left:25px; top:25px; width:50px; height:50px; transform:translateZ(50px); background:green;"></div>
+</div>
+This test verifies CSS perspective correctly applies to fixed-position descendants. The test passes if a 100x100 green box completely occludes the red box behind.
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/webaudio/constructor/convolver.html b/third_party/WebKit/LayoutTests/webaudio/constructor/convolver.html
index 410869d2a..d4c7377 100644
--- a/third_party/WebKit/LayoutTests/webaudio/constructor/convolver.html
+++ b/third_party/WebKit/LayoutTests/webaudio/constructor/convolver.html
@@ -5,8 +5,8 @@
     <script src="../../resources/testharness.js"></script>
     <script src="../../resources/testharnessreport.js"></script>
     <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audio-testing.js"></script>
-    <script src="audionodeoptions.js"></script>
+    <script src="../resources/audit.js"></script>
+    <script src="new-audionodeoptions.js"></script>
   </head>
 
   <body>
@@ -15,151 +15,110 @@
 
       var audit = Audit.createTaskRunner();
 
-      audit.defineTask("initialize", function (taskDone) {
-        Should("context = new OfflineAudioContext(...)", function () {
-          context = new OfflineAudioContext(1, 1, 48000);
-        }).notThrow();
-        taskDone();
+      audit.define('initialize', (task, should) => {
+        context = initializeContext(should);
+        task.done();
       });
 
-      audit.defineTask("invalid constructor", function (taskDone) {
-        var node;
-        var success = true;
-
-        succes = Should("new ConvolverNode()", function () {
-          node = new ConvolverNode();
-        }).throw("TypeError");
-        success = Should("new ConvolverNode(1)", function () {
-          node = new ConvolverNode(1);
-        }).throw("TypeError") && success;
-        success = Should("new ConvolverNode(context, 42)", function () {
-          node = new ConvolverNode(context, 42);
-        }).throw("TypeError") && success;
-
-        Should("Invalid constructors", success)
-          .summarize(
-            "correctly threw errors",
-            "did not throw errors in all cases");
-        taskDone();
+      audit.define('invalid constructor', (task, should) => {
+        testInvalidConstructor(should, 'ConvolverNode', context);
+        task.done();
       });
 
-      audit.defineTask("default constructor", function (taskDone) {
-        var node;
-        var success = true;
+      audit.define('default constructor', (task, should) => {
+        let prefix = 'node0';
+        let node = testDefaultConstructor(should, 'ConvolverNode', context, {
+          prefix: prefix,
+          numberOfInputs: 1,
+          numberOfOutputs: 1,
+          channelCount: 2,
+          channelCountMode: 'clamped-max',
+          channelInterpretation: 'speakers'
+        });
 
-        success = Should("node0 = new ConvolverNode(context)", function () {
-          node = new ConvolverNode(context);
-        }).notThrow();
-        success = Should("node0 instanceOf ConvolverNode", node instanceof ConvolverNode)
-          .beEqualTo(true) && success;
-        success = Should("node0.normalize", node.normalize)
-          .beEqualTo(true) && success;
+        testDefaultAttributes(
+            should, node, prefix,
+            [{name: 'normalize', value: true}, {name: 'buffer', value: null}]);
 
-        success = Should("node0.channelCount", node.channelCount)
-          .beEqualTo(2) && success;
-        success = Should("node0.channelCountMode", node.channelCountMode)
-          .beEqualTo("clamped-max") && success;
-        success = Should("node0.channelInterpretation", node.channelInterpretation)
-          .beEqualTo("speakers") && success;
-
-        success = Should("new ConvolverNode(context)", success)
-            .summarize(
-                "constructed node with correct attributes",
-                "did not construct correct node correctly")
-
-        taskDone();
+        task.done();
       });
 
-      audit.defineTask("test AudioNodeOptions", function (taskDone) {
-        testAudioNodeOptions(context, "ConvolverNode", {
-          expectedChannelCount: {
-            value: 2,
+      audit.define('test AudioNodeOptions', (task, should) => {
+        testAudioNodeOptions(should, context, 'ConvolverNode', {
+          channelCount:
+              {value: 2, isFixed: true, errorType: 'NotSupportedError'},
+          channelCountMode: {
+            value: 'clamped-max',
             isFixed: true,
-            errorType: "NotSupportedError"
-          },
-          expectedChannelCountMode: {
-            value: "clamped-max",
-            isFixed: true,
-            errorType: "NotSupportedError"
+            errorType: 'NotSupportedError'
           },
         });
-        taskDone();
+        task.done();
       });
 
-      audit.defineTask("nullable buffer", function (taskDone) {
+      audit.define('nullable buffer', (task, should) => {
         var node;
-        var success = true;
+        var options = {buffer: null};
 
-        var options = { buffer: null };
-      
-        success = Should("node1 = new ConvolverNode(c, " + JSON.stringify(options), function () {
-          node = new ConvolverNode(context, options);
-        }).notThrow();
+        should(
+            () => {
+              node = new ConvolverNode(context, options);
+            },
+            'node1 = new ConvolverNode(c, ' + JSON.stringify(options))
+            .notThrow();
 
-        success = Should("node1.buffer", node.buffer)
-          .beEqualTo(null);
+        should(node.buffer, 'node1.buffer').beEqualTo(null);
 
-        Should("Null buffer in constructor handled", success)
-          .summarize(
-            "correctly",
-            "incorrectly");
-
-        taskDone();
+        task.done();
       });
 
-      audit.defineTask("construct with options", function (taskDone) {
+      audit.define('construct with options', (task, should) => {
         var buf = context.createBuffer(1, 1, context.sampleRate);
-        var options = {
-          buffer: buf,
-          disableNormalization: false
-        };
+        var options = {buffer: buf, disableNormalization: false};
 
-        var message = "node = new ConvolverNode(c, " + JSON.stringify(options) + ")";
+        var message =
+            'node = new ConvolverNode(c, ' + JSON.stringify(options) + ')';
 
         var node;
-        success = Should(message, function () {
+        should(() => {
           node = new ConvolverNode(context, options);
-        }).notThrow();
+        }, message).notThrow();
 
-        success = Should("node1 instanceOf ConvolverNode", node instanceof ConvolverNode)
-          .beEqualTo(true) && success;
-        success = Should("node1.buffer === <buf>", node.buffer ===
-        options.buffer)
-          .beEqualTo(true) && success;
-        success = Should("node1.normalize", node.normalize)
-          .beEqualTo(!options.disableNormalization) && success;
+        should(node instanceof ConvolverNode, 'node1 instanceOf ConvolverNode')
+            .beEqualTo(true);
+        should(node.buffer === options.buffer, 'node1.buffer === <buf>')
+            .beEqualTo(true);
+        should(node.normalize, 'node1.normalize')
+            .beEqualTo(!options.disableNormalization);
 
         options.buffer = null;
         options.disableNormalization = true;
 
-        message = "node2 = new ConvolverNode(, " + JSON.stringify(options) + ")";
+        message =
+            'node2 = new ConvolverNode(, ' + JSON.stringify(options) + ')';
 
-        success = Should(message, function () {
+        should(() => {
           node = new ConvolverNode(context, options);
-        }).notThrow() && success;
-        success = Should("node2.buffer", node.buffer).beEqualTo(null) && success;
-        success = Should("node2.normalize", node.normalize)
-          .beEqualTo(!options.disableNormalization) && success;
+        }, message).notThrow();
+        should(node.buffer, 'node2.buffer').beEqualTo(null);
+        should(node.normalize, 'node2.normalize')
+            .beEqualTo(!options.disableNormalization);
 
         options.disableNormalization = false;
-        message = "node3 = new ConvolverNode(context, " + JSON.stringify(options) + ")";
+        message = 'node3 = new ConvolverNode(context, ' +
+            JSON.stringify(options) + ')';
 
-        success = Should(message, function () {
+        should(() => {
           node = new ConvolverNode(context, options);
-        }).notThrow() && success;
-        success = Should("node3.buffer", node.buffer).beEqualTo(null) && success;
-        success = Should("node3.normalize", node.normalize)
-          .beEqualTo(!options.disableNormalization) && success;
+        }, message).notThrow();
+        should(node.buffer, 'node3.buffer').beEqualTo(null);
+        should(node.normalize, 'node3.normalize')
+            .beEqualTo(!options.disableNormalization);
 
-        Should("new ConvolverNode() with options", success)
-            .summarize(
-                "constructed with correct attributes",
-                "was not constructed correctly");
-          
-        taskDone();
+        task.done();
       });
 
-      audit.runTasks();
+      audit.run();
     </script>
   </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/constructor/gain.html b/third_party/WebKit/LayoutTests/webaudio/constructor/gain.html
index 022476c..3be0bc78 100644
--- a/third_party/WebKit/LayoutTests/webaudio/constructor/gain.html
+++ b/third_party/WebKit/LayoutTests/webaudio/constructor/gain.html
@@ -5,8 +5,8 @@
     <script src="../../resources/testharness.js"></script>
     <script src="../../resources/testharnessreport.js"></script>
     <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audio-testing.js"></script>
-    <script src="audionodeoptions.js"></script>
+    <script src="../resources/audit.js"></script>
+    <script src="new-audionodeoptions.js"></script>
   </head>
 
   <body>
@@ -15,102 +15,64 @@
 
       var audit = Audit.createTaskRunner();
 
-      audit.defineTask("initialize", function (taskDone) {
-        Should("context = new OfflineAudioContext(...)", function () {
-          context = new OfflineAudioContext(1, 1, 48000);
-        }).notThrow();
-        taskDone();
+      audit.define('initialize', (task, should) => {
+        context = initializeContext(should);
+        task.done();
       });
 
-      audit.defineTask("invalid constructor", function (taskDone) {
+      audit.define('invalid constructor', (task, should) => {
+        testInvalidConstructor(should, 'GainNode', context);
+        task.done();
+      });
+
+      audit.define('default constructor', (task, should) => {
+        let prefix = 'node0';
+        let node = testDefaultConstructor(should, 'GainNode', context, {
+          prefix: prefix,
+          numberOfInputs: 1,
+          numberOfOutputs: 1,
+          channelCount: 2,
+          channelCountMode: 'max',
+          channelInterpretation: 'speakers'
+        });
+
+        testDefaultAttributes(should, node, prefix, [{name: 'gain', value: 1}]);
+
+        task.done();
+      });
+
+      audit.define('test AudioNodeOptions', (task, should) => {
+        testAudioNodeOptions(should, context, 'GainNode');
+        task.done();
+      });
+
+      audit.define('constructor with options', (task, should) => {
         var node;
-        var success = true;
-
-        success = Should("new GainNode()", function () {
-          node = new GainNode();
-        }).throw("TypeError");
-        success = Should("new GainNode(1)", function () {
-          node = new GainNode(1);
-        }).throw("TypeError") && success;
-        success = Should("new GainNode(context, 42)", function () {
-          node = new GainNode(context, 42);
-        }).throw("TypeError") && success;
-
-        Should("Invalid constructors", success)
-            .summarize(
-                "correctly threw errors",
-                "did not throw errors in all cases");
-
-        taskDone();
-      });
-
-      audit.defineTask("default constructor", function (taskDone) {
-        var node;
-        var success = true;
-
-        success = Should("node0 = new GainNode(context)", function () {
-          node = new GainNode(context);
-        }).notThrow();
-        success = Should("node0 instanceof GainNode",
-            node instanceof GainNode)
-          .beEqualTo(true) && success;
-
-        success = Should("node0.gain.value", node.gain.value)
-          .beEqualTo(1) && success;
-
-        success = Should("node0.channelCount", node.channelCount)
-          .beEqualTo(2) && success;
-        success = Should("node0.channelCountMode", node.channelCountMode)
-          .beEqualTo("max") && success;
-        success = Should("node0.channelInterpretation", node.channelInterpretation)
-          .beEqualTo("speakers") && success;
-
-        Should("new GainNode(context)", success)
-            .summarize(
-                "constructed node with correct attributes",
-                "did not construct correct node correctly")
-
-        taskDone();
-      });
-
-      audit.defineTask("test AudioNodeOptions", function (taskDone) {
-        testAudioNodeOptions(context, "GainNode");
-        taskDone();
-      });
-
-      audit.defineTask("constructor with options", function (taskDone) {
-        var node;
-        var success = true;
         var options = {
           gain: -2,
         };
 
-        success = Should("node1 = new GainNode(c, " + JSON.stringify(options) + ")", function () {
-          node = new GainNode(context, options);
-        }).notThrow();
-        success = Should("node1 instanceof GainNode",
-            node instanceof GainNode)
-          .beEqualTo(true) && success;
+        should(
+            () => {
+              node = new GainNode(context, options);
+            },
+            'node1 = new GainNode(c, ' + JSON.stringify(options) + ')')
+            .notThrow();
+        should(node instanceof GainNode, 'node1 instanceof GainNode')
+            .beEqualTo(true);
 
-        success = Should("node1.gain.value", node.gain.value)
-          .beEqualTo(options.gain) && success;
+        should(node.gain.value, 'node1.gain.value').beEqualTo(options.gain);
 
-        success = Should("node1.channelCount", node.channelCount)
-          .beEqualTo(2) && success;
-        success = Should("node1.channelCountMode", node.channelCountMode)
-          .beEqualTo("max") && success;
-        success = Should("node1.channelInterpretation", node.channelInterpretation)
-          .beEqualTo("speakers") && success;
+        should(node.channelCount, 'node1.channelCount').beEqualTo(2);
+        should(node.channelCountMode, 'node1.channelCountMode')
+            .beEqualTo('max');
+        should(node.channelInterpretation, 'node1.channelInterpretation')
+            .beEqualTo('speakers');
 
-        Should("new GainNode() with options", success)
-          .summarize(
-            "constructed with correct attributes",
-            "was not constructed correctly");
-
-        taskDone();
+        task.done();
       });
 
-      audit.runTasks();
+      audit.run();
     </script>
   </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/constructor/panner.html b/third_party/WebKit/LayoutTests/webaudio/constructor/panner.html
index ec9cee33..3b082a7 100644
--- a/third_party/WebKit/LayoutTests/webaudio/constructor/panner.html
+++ b/third_party/WebKit/LayoutTests/webaudio/constructor/panner.html
@@ -5,7 +5,8 @@
     <script src="../../resources/testharness.js"></script>
     <script src="../../resources/testharnessreport.js"></script>
     <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audio-testing.js"></script>
+    <script src="../resources/audit.js"></script>
+    <script src="new-audionodeoptions.js"></script>
   </head>
 
   <body>
@@ -14,242 +15,190 @@
 
       var audit = Audit.createTaskRunner();
 
-      audit.defineTask("initialize", function (taskDone) {
-        Should("context = new OfflineAudioContext(...)", function () {
-          context = new OfflineAudioContext(1, 1, 48000);
-        }).notThrow();
-
-        taskDone();
+      audit.define('initialize', (task, should) => {
+        context = initializeContext(should);
+        task.done();
       });
 
-      audit.defineTask("invalid constructor", function (taskDone) {
-        var node;
-        var success = true;
-
-        success = Should("new PannerNode()", function () {
-          node = new PannerNode();
-        }).throw("TypeError");
-        success = Should("new PannerNode(1)", function () {
-          node = new PannerNode(1) && success;
-        }).throw("TypeError");
-        success = Should("new PannerNode(context, 42)", function () {
-          node = new PannerNode(context, 42) && success;
-        }).throw("TypeError");
-
-        Should("Invalid constructors", success)
-            .summarize(
-                "correctly threw errors",
-                "did not throw errors in all cases");
-
-        taskDone();
+      audit.define('invalid constructor', (task, should) => {
+        testInvalidConstructor(should, 'PannerNode', context);
+        task.done();
       });
 
-      audit.defineTask("default constructor", function (taskDone) {
-        var node;
-        var success = true;
+      audit.define('default constructor', (task, should) => {
+        let prefix = 'node0';
+        let node = testDefaultConstructor(should, 'PannerNode', context, {
+          prefix: prefix,
+          numberOfInputs: 1,
+          numberOfOutputs: 1,
+          channelCount: 2,
+          channelCountMode: 'clamped-max',
+          channelInterpretation: 'speakers'
+        });
 
-        success = Should("node0 = new PannerNode(context)", function () {
-          node = new PannerNode(context);
-        }).notThrow();
-        success = Should("node0 instanceof PannerNode", node instanceof PannerNode)
-          .beEqualTo(true) && success;
-
-        success = Should("node0.panningModel", node.panningModel)
-          .beEqualTo("equalpower") && success;
-        success = Should("node0.positionX.value", node.positionX.value)
-          .beEqualTo(0) && success;
-        success = Should("node0.positionY.value", node.positionY.value)
-          .beEqualTo(0) && success;
-        success = Should("node0.positionZ.value", node.positionZ.value)
-          .beEqualTo(0) && success;
-        success = Should("node0.orientationX.value", node.orientationX.value)
-          .beEqualTo(1) && success;
-        success = Should("node0.orientationY.value", node.orientationY.value)
-          .beEqualTo(0) && success;
-        success = Should("node0.orientationZ.value", node.orientationZ.value)
-          .beEqualTo(0) && success;
-        success = Should("node0.distanceModel", node.distanceModel)
-          .beEqualTo("inverse") && success;
-        success = Should("node0.refDistance", node.refDistance)
-          .beEqualTo(1) && success;
-        success = Should("node0.maxDistance", node.maxDistance)
-          .beEqualTo(10000) && success;
-        success = Should("node0.rolloffFactor", node.rolloffFactor)
-          .beEqualTo(1) && success;
-        success = Should("node0.coneInnerAngle", node.coneInnerAngle)
-          .beEqualTo(360) && success;
-        success = Should("node0.coneOuterAngle", node.coneOuterAngle)
-          .beEqualTo(360) && success;
-        success = Should("node0.coneOuterGain", node.coneOuterGain)
-          .beEqualTo(0) && success;
+        testDefaultAttributes(should, node, prefix, [
+          {name: 'panningModel', value: 'equalpower'},
+          {name: 'positionX', value: 0}, {name: 'positionY', value: 0},
+          {name: 'positionZ', value: 0}, {name: 'orientationX', value: 1},
+          {name: 'orientationY', value: 0}, {name: 'orientationZ', value: 0},
+          {name: 'distanceModel', value: 'inverse'},
+          {name: 'refDistance', value: 1}, {name: 'maxDistance', value: 10000},
+          {name: 'rolloffFactor', value: 1},
+          {name: 'coneInnerAngle', value: 360},
+          {name: 'coneOuterAngle', value: 360},
+          {name: 'coneOuterGain', value: 0}
+        ]);
 
         // Test the listener too, while we're at it.
-        success = Should("context.listener.positionX.value", context.listener.positionX.value)
-          .beEqualTo(0) && success;
-        success = Should("context.listener.positionY.value", context.listener.positionY.value)
-          .beEqualTo(0) && success;
-        success = Should("context.listener.positionZ.value", context.listener.positionZ.value)
-          .beEqualTo(0) && success;
-        success = Should("context.listener.forwardX.value", context.listener.forwardX.value)
-          .beEqualTo(0) && success;
-        success = Should("context.listener.forwardY.value", context.listener.forwardY.value)
-          .beEqualTo(0) && success;
-        success = Should("context.listener.forwardZ.value", context.listener.forwardZ.value)
-          .beEqualTo(-1) && success;
-        success = Should("context.listener.upX.value", context.listener.upX.value)
-          .beEqualTo(0) && success;
-        success = Should("context.listener.upY.value", context.listener.upY.value)
-          .beEqualTo(1) && success;
-        success = Should("context.listener.upZ.value", context.listener.upZ.value)
-          .beEqualTo(0) && success;
+        let listenerAttributes = [
+          {name: 'positionX', value: 0},
+          {name: 'positionY', value: 0},
+          {name: 'positionZ', value: 0},
+          {name: 'forwardX', value: 0},
+          {name: 'forwardY', value: 0},
+          {name: 'forwardZ', value: -1},
+          {name: 'upX', value: 0},
+          {name: 'upY', value: 1},
+          {name: 'upZ', value: 0},
+        ];
 
-        success = Should("node0.channelCount", node.channelCount)
-          .beEqualTo(2) && success;
-        success = Should("node0.channelCountMode", node.channelCountMode)
-          .beEqualTo("clamped-max") && success;
-        success = Should("node0.channelInterpretation", node.channelInterpretation)
-          .beEqualTo("speakers") && success;
+        listenerAttributes.forEach((item) => {
+          should(
+              context.listener[item.name].value,
+              'context.listener.' + item.name + '.value')
+              .beEqualTo(item.value);
+        });
 
-        Should("new PannerNode(context)", success)
-            .summarize(
-                "constructed node with correct attributes",
-                "did not construct correct node correctly")
-
-        taskDone();
+        task.done();
       });
 
-      audit.defineTask("test AudioNodeOptions", function (taskDone) {
+      audit.define('test AudioNodeOptions', (task, should) => {
         // Can't use testAudioNodeOptions because the constraints for this node
         // are not supported there.
         var node;
         var success = true;
 
         // Test that we can set the channel count to 1 or 2.
-        var options = {
-          channelCount: 1
-        };
-        success = Should("node1 = new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).notThrow() && success;
-        success = Should("node1.channelCount", node.channelCount)
-          .beEqualTo(options.channelCount) && success;
+        var options = {channelCount: 1};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'node1 = new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .notThrow();
+        should(node.channelCount, 'node1.channelCount')
+            .beEqualTo(options.channelCount);
 
-        options = {
-          channelCount: 2
-        };
-        success = Should("node2 = new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).notThrow() && success;
-        success = Should("node2.channelCount", node.channelCount)
-          .beEqualTo(options.channelCount) && success;
+        options = {channelCount: 2};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'node2 = new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .notThrow();
+        should(node.channelCount, 'node2.channelCount')
+            .beEqualTo(options.channelCount);
 
         // Test that other channel counts throw an error
-        options = {
-          channelCount: 0
-        };
-        success = Should("new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).throw("NotSupportedError") && success;
+        options = {channelCount: 0};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .throw('NotSupportedError');
 
-        options = {
-          channelCount: 3
-        };
-        success = Should("new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).throw("NotSupportedError") && success;
+        options = {channelCount: 3};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .throw('NotSupportedError');
 
-        options = {
-          channelCount: 99
-        };
-        success = Should("new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).throw("NotSupportedError") && success;
+        options = {channelCount: 99};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .throw('NotSupportedError');
 
         // Test channelCountMode.  A mode of "max" is illegal, but others are
         // ok.
-        options = {
-          channelCountMode: "clamped-max"
-        };
-        success = Should("node3 = new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).notThrow() && success;
-        success = Should("node3.channelCountMode", node.channelCountMode)
-          .beEqualTo(options.channelCountMode) && success;
+        options = {channelCountMode: 'clamped-max'};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'node3 = new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .notThrow();
+        should(node.channelCountMode, 'node3.channelCountMode')
+            .beEqualTo(options.channelCountMode);
 
-        options = {
-          channelCountMode: "explicit"
-        };
-        success = Should("node4 = new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).notThrow() && success;
-        success = Should("node4.channelCountMode", node.channelCountMode)
-          .beEqualTo(options.channelCountMode);
+        options = {channelCountMode: 'explicit'};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'node4 = new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .notThrow();
+        should(node.channelCountMode, 'node4.channelCountMode')
+            .beEqualTo(options.channelCountMode);
 
-        options = {
-          channelCountMode: "max"
-        };
-        success = Should("new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).throw("NotSupportedError") && success;
+        options = {channelCountMode: 'max'};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .throw('NotSupportedError');
 
-        options = {
-          channelCountMode: "foobar"
-        };
-        success = Should('new PannerNode(c, " + JSON.stringify(options) + ")',
-          function () {
-            node = new PannerNode(context, options);
-          }).throw("TypeError") && success;
+        options = {channelCountMode: 'foobar'};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'new PannerNode(c, " + JSON.stringify(options) + ")')
+            .throw('TypeError');
 
         // Test channelInterpretation.
-        options = {
-          channelInterpretation: "speakers"
-        };
-        success = Should("node5 = new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).notThrow() && success;
-        success = Should("node5.channelInterpretation", node.channelInterpretation)
-          .beEqualTo(options.channelInterpretation) && success;
+        options = {channelInterpretation: 'speakers'};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'node5 = new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .notThrow();
+        should(node.channelInterpretation, 'node5.channelInterpretation')
+            .beEqualTo(options.channelInterpretation);
 
-        options = {
-          channelInterpretation: "discrete"
-        };
-        success = Should("node6 = new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).notThrow() && success;
-        success = Should("node6.channelInterpretation", node.channelInterpretation)
-          .beEqualTo(options.channelInterpretation) && success;
+        options = {channelInterpretation: 'discrete'};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'node6 = new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .notThrow();
+        should(node.channelInterpretation, 'node6.channelInterpretation')
+            .beEqualTo(options.channelInterpretation);
 
-        options = {
-          channelInterpretation: "foobar"
-        };
-        success = Should("new PannerNode(c, " + JSON.stringify(options) + ")",
-          function () {
-            node = new PannerNode(context, options);
-          }).throw("TypeError") && success;
+        options = {channelInterpretation: 'foobar'};
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .throw('TypeError');
 
-        Should("AudioNodeOptions for PannerNode", success)
-          .summarize(
-            "were correctly handled",
-            "were not correctly handled");
-
-        taskDone();
+        task.done();
       });
 
-      audit.defineTask("constructor with options", function (taskDone) {
+      audit.define('constructor with options', (task, should) => {
         var node;
         var success = true;
         var options = {
-          panningModel: "HRTF",
+          panningModel: 'HRTF',
           // We use full double float values here to verify also that the actual
           // AudioParam value is properly rounded to a float.  The actual value
           // is immaterial as long as x != Math.fround(x).
@@ -259,7 +208,7 @@
           orientationX: -Math.SQRT2,
           orientationY: -2 * Math.SQRT2,
           orientationZ: -3 * Math.SQRT2,
-          distanceModel: "linear",
+          distanceModel: 'linear',
           // We use full double float values here to verify also that the actual
           // attribute is a double float.  The actual value is immaterial as
           // long as x != Math.fround(x).
@@ -271,57 +220,54 @@
           coneOuterGain: 6 * Math.PI
         };
 
-        success = Should("node = new PannerNode(c, " + JSON.stringify(options) + ")", function () {
-          node = new PannerNode(context, options);
-        }).notThrow();
-        success = Should("node instanceof PannerNode", node instanceof PannerNode)
-          .beEqualTo(true) && success;
+        should(
+            () => {
+              node = new PannerNode(context, options);
+            },
+            'node = new PannerNode(c, ' + JSON.stringify(options) + ')')
+            .notThrow();
+        should(node instanceof PannerNode, 'node instanceof PannerNode')
+            .beEqualTo(true);
 
-        success = Should("node.panningModel", node.panningModel)
-          .beEqualTo(options.panningModel) && success;
-        success = Should("node.positionX.value", node.positionX.value)
-          .beEqualTo(Math.fround(options.positionX)) && success;
-        success = Should("node.positionY.value", node.positionY.value)
-          .beEqualTo(Math.fround(options.positionY)) && success;
-        success = Should("node.positionZ.value", node.positionZ.value)
-          .beEqualTo(Math.fround(options.positionZ)) && success;
-        success = Should("node.orientationX.value", node.orientationX.value)
-          .beEqualTo(Math.fround(options.orientationX)) && success;
-        success = Should("node.orientationY.value", node.orientationY.value)
-          .beEqualTo(Math.fround(options.orientationY)) && success;
-        success = Should("node.orientationZ.value", node.orientationZ.value)
-          .beEqualTo(Math.fround(options.orientationZ)) && success;
-        success = Should("node.distanceModel", node.distanceModel)
-          .beEqualTo(options.distanceModel) && success;
-        success = Should("node.refDistance", node.refDistance)
-          .beEqualTo(options.refDistance) && success;
-        success = Should("node.maxDistance", node.maxDistance)
-          .beEqualTo(options.maxDistance) && success;
-        success = Should("node.rolloffFactor", node.rolloffFactor)
-          .beEqualTo(options.rolloffFactor) && success;
-        success = Should("node.coneInnerAngle", node.coneInnerAngle)
-          .beEqualTo(options.coneInnerAngle) && success;
-        success = Should("node.coneOuterAngle", node.coneOuterAngle)
-          .beEqualTo(options.coneOuterAngle) && success;
-        success = Should("node.coneOuterGain", node.coneOuterGain)
-          .beEqualTo(options.coneOuterGain) && success;
+        should(node.panningModel, 'node.panningModel')
+            .beEqualTo(options.panningModel);
+        should(node.positionX.value, 'node.positionX.value')
+            .beEqualTo(Math.fround(options.positionX));
+        should(node.positionY.value, 'node.positionY.value')
+            .beEqualTo(Math.fround(options.positionY));
+        should(node.positionZ.value, 'node.positionZ.value')
+            .beEqualTo(Math.fround(options.positionZ));
+        should(node.orientationX.value, 'node.orientationX.value')
+            .beEqualTo(Math.fround(options.orientationX));
+        should(node.orientationY.value, 'node.orientationY.value')
+            .beEqualTo(Math.fround(options.orientationY));
+        should(node.orientationZ.value, 'node.orientationZ.value')
+            .beEqualTo(Math.fround(options.orientationZ));
+        should(node.distanceModel, 'node.distanceModel')
+            .beEqualTo(options.distanceModel);
+        should(node.refDistance, 'node.refDistance')
+            .beEqualTo(options.refDistance);
+        should(node.maxDistance, 'node.maxDistance')
+            .beEqualTo(options.maxDistance);
+        should(node.rolloffFactor, 'node.rolloffFactor')
+            .beEqualTo(options.rolloffFactor);
+        should(node.coneInnerAngle, 'node.coneInnerAngle')
+            .beEqualTo(options.coneInnerAngle);
+        should(node.coneOuterAngle, 'node.coneOuterAngle')
+            .beEqualTo(options.coneOuterAngle);
+        should(node.coneOuterGain, 'node.coneOuterGain')
+            .beEqualTo(options.coneOuterGain);
 
-        success = Should("node.channelCount", node.channelCount)
-          .beEqualTo(2) && success;
-        success = Should("node.channelCountMode", node.channelCountMode)
-          .beEqualTo("clamped-max") && success;
-        success = Should("node.channelInterpretation", node.channelInterpretation)
-          .beEqualTo("speakers") && success;
+        should(node.channelCount, 'node.channelCount').beEqualTo(2);
+        should(node.channelCountMode, 'node.channelCountMode')
+            .beEqualTo('clamped-max');
+        should(node.channelInterpretation, 'node.channelInterpretation')
+            .beEqualTo('speakers');
 
-        Should("new PannerNode() with options", success)
-          .summarize(
-            "constructed with correct attributes",
-            "was not constructed correctly");
-
-        taskDone();
+        task.done();
       });
 
-      audit.runTasks();
+      audit.run();
     </script>
   </body>
 </html>
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 445b2ad..3a595e1c 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -1596,26 +1596,6 @@
       graphics_layer_->OffsetFromLayoutObject());
 }
 
-void CompositedLayerMapping::RegisterScrollingLayers() {
-  // Register fixed position layers and their containers with the scrolling
-  // coordinator.
-  ScrollingCoordinator* scrolling_coordinator =
-      owning_layer_.GetScrollingCoordinator();
-  if (!scrolling_coordinator)
-    return;
-
-  scrolling_coordinator->UpdateLayerPositionConstraint(&owning_layer_);
-
-  // Page scale is applied as a transform on the root layout view layer. Because
-  // the scroll layer is further up in the hierarchy, we need to avoid marking
-  // the root layout view layer as a container.
-  bool is_container =
-      owning_layer_.GetLayoutObject().CanContainFixedPositionObjects() &&
-      !owning_layer_.IsRootLayer();
-  scrolling_coordinator->SetLayerIsContainerForFixedPositionLayers(
-      graphics_layer_.get(), is_container);
-}
-
 void CompositedLayerMapping::UpdateInternalHierarchy() {
   // m_foregroundLayer has to be inserted in the correct order with child
   // layers, so it's not inserted here.
@@ -2490,6 +2470,37 @@
   }
 }
 
+void CompositedLayerMapping::RegisterScrollingLayers() {
+  // Register fixed position layers and their containers with the scrolling
+  // coordinator.
+  ScrollingCoordinator* scrolling_coordinator =
+      owning_layer_.GetScrollingCoordinator();
+  if (!scrolling_coordinator)
+    return;
+
+  scrolling_coordinator->UpdateLayerPositionConstraint(&owning_layer_);
+
+  // Page scale is applied as a transform on the root layout view layer. Because
+  // the scroll layer is further up in the hierarchy, we need to avoid marking
+  // the root layout view layer as a container.
+  bool is_container =
+      owning_layer_.GetLayoutObject().CanContainFixedPositionObjects() &&
+      !owning_layer_.IsRootLayer();
+  scrolling_coordinator->SetLayerIsContainerForFixedPositionLayers(
+      graphics_layer_.get(), is_container);
+  // Fixed-pos descendants inherits the space that has all CSS property applied,
+  // including perspective, overflow scroll/clip. Thus we also mark every layers
+  // below the main graphics layer so transforms implemented by them don't get
+  // skipped.
+  ApplyToGraphicsLayers(
+      this,
+      [scrolling_coordinator, is_container](GraphicsLayer* layer) {
+        scrolling_coordinator->SetLayerIsContainerForFixedPositionLayers(
+            layer, is_container);
+      },
+      kApplyToChildContainingLayers);
+}
+
 bool CompositedLayerMapping::UpdateSquashingLayers(
     bool needs_squashing_layers) {
   bool layers_changed = false;
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
index 37e7ff121..dc07c7f3 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
@@ -631,6 +631,7 @@
     super();
     this._groupBySetting =
         Common.settings.createSetting('timelineTreeGroupBy', Timeline.AggregatedTimelineTreeView.GroupBy.None);
+    this._groupByCombobox = new UI.ToolbarComboBox(this._onGroupByChanged.bind(this));
     this.init(filters);
     this._stackView = new Timeline.TimelineStackView(this);
     this._stackView.addEventListener(
@@ -639,6 +640,16 @@
 
   /**
    * @override
+   */
+  wasShown() {
+    var groupById = this._groupBySetting.get();
+    var option = this._groupByCombobox.options().find(option => option.value === groupById);
+    if (option)
+      this._groupByCombobox.select(option);
+  }
+
+  /**
+   * @override
    * @param {!Timeline.TimelineSelection} selection
    */
   updateContents(selection) {
@@ -712,7 +723,6 @@
    */
   populateToolbar(toolbar) {
     super.populateToolbar(toolbar);
-    this._groupByCombobox = new UI.ToolbarComboBox(this._onGroupByChanged.bind(this));
     /**
      * @param {string} name
      * @param {string} id
diff --git a/third_party/WebKit/Source/modules/media_controls/OWNERS b/third_party/WebKit/Source/modules/media_controls/OWNERS
new file mode 100644
index 0000000..9c2b5a9
--- /dev/null
+++ b/third_party/WebKit/Source/modules/media_controls/OWNERS
@@ -0,0 +1,5 @@
+avayvod@chromium.org
+mlamouri@chromium.org
+
+# TEAM: media-dev@chromium.org
+# COMPONENT: Blink>Media>Controls
diff --git a/third_party/WebKit/Source/platform/LengthBox.cpp b/third_party/WebKit/Source/platform/LengthBox.cpp
index a78defd..975087c 100644
--- a/third_party/WebKit/Source/platform/LengthBox.cpp
+++ b/third_party/WebKit/Source/platform/LengthBox.cpp
@@ -110,39 +110,4 @@
   return IsHorizontalWritingMode(writing_mode) ? bottom : left;
 }
 
-const Length& LengthBox::LogicalLeft(WritingMode writing_mode) const {
-  return LengthBox::LogicalLeft(writing_mode, left_, top_);
-}
-
-const Length& LengthBox::LogicalRight(WritingMode writing_mode) const {
-  return LengthBox::LogicalRight(writing_mode, right_, bottom_);
-}
-
-const Length& LengthBox::Before(WritingMode writing_mode) const {
-  return LengthBox::Before(writing_mode, top_, left_, right_);
-}
-
-const Length& LengthBox::After(WritingMode writing_mode) const {
-  return LengthBox::After(writing_mode, bottom_, left_, right_);
-}
-
-const Length& LengthBox::Start(WritingMode writing_mode,
-                               TextDirection direction) const {
-  return LengthBox::Start(writing_mode, direction, top_, left_, right_,
-                          bottom_);
-}
-
-const Length& LengthBox::end(WritingMode writing_mode,
-                             TextDirection direction) const {
-  return LengthBox::End(writing_mode, direction, top_, left_, right_, bottom_);
-}
-
-const Length& LengthBox::Over(WritingMode writing_mode) const {
-  return LengthBox::Over(writing_mode, top_, right_);
-}
-
-const Length& LengthBox::Under(WritingMode writing_mode) const {
-  return LengthBox::Under(writing_mode, bottom_, left_);
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/LengthBox.h b/third_party/WebKit/Source/platform/LengthBox.h
index f9927a70..4fb231c 100644
--- a/third_party/WebKit/Source/platform/LengthBox.h
+++ b/third_party/WebKit/Source/platform/LengthBox.h
@@ -92,16 +92,6 @@
   const Length& Top() const { return top_; }
   const Length& Bottom() const { return bottom_; }
 
-  const Length& LogicalLeft(WritingMode) const;
-  const Length& LogicalRight(WritingMode) const;
-
-  const Length& Before(WritingMode) const;
-  const Length& After(WritingMode) const;
-  const Length& Start(WritingMode, TextDirection) const;
-  const Length& end(WritingMode, TextDirection) const;
-  const Length& Over(WritingMode) const;
-  const Length& Under(WritingMode) const;
-
   bool operator==(const LengthBox& o) const {
     return left_ == o.left_ && right_ == o.right_ && top_ == o.top_ &&
            bottom_ == o.bottom_;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeState.cpp b/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeState.cpp
index 1b07e96..ce23989e 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeState.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeState.cpp
@@ -92,7 +92,12 @@
   bool clip_ancestor_of_effect =
       IsAncestorOf<ClipPaintPropertyNode>(clip_.Get(), effect_->OutputClip());
 
-  if (!effect_->IsRoot() && clip_ancestor_of_effect) {
+  if (!effect_->IsRoot() &&
+      (clip_ancestor_of_effect ||
+       // Effects that don't move pixels commute with all clips, so always apply
+       // them first when inside compatible transforms.
+       (!effect_->HasFilterThatMovesPixels() &&
+        !effect_transform_strict_ancestor_of_transform))) {
     return kEffect;
   }
   if (!clip_->IsRoot())
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeStateTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeStateTest.cpp
index e1218a9..6b1e01d 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeStateTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeStateTest.cpp
@@ -10,6 +10,47 @@
 
 class PropertyTreeStateTest : public ::testing::Test {};
 
+TEST_F(PropertyTreeStateTest, ClipBelowOutputClipOfEffect) {
+  RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+      ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+      FloatRoundedRect());
+
+  RefPtr<EffectPaintPropertyNode> effect = EffectPaintPropertyNode::Create(
+      EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+      ClipPaintPropertyNode::Root(), kColorFilterNone,
+      CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver);
+
+  PropertyTreeState state(TransformPaintPropertyNode::Root(), clip.Get(),
+                          effect.Get());
+  EXPECT_EQ(PropertyTreeState::kEffect, state.GetInnermostNode());
+
+  PropertyTreeStateIterator iterator(state);
+  EXPECT_EQ(PropertyTreeState::kClip, iterator.Next()->GetInnermostNode());
+  EXPECT_EQ(PropertyTreeState::kNone, iterator.Next()->GetInnermostNode());
+}
+
+TEST_F(PropertyTreeStateTest, ClipBelowOutputClipOfEffectMovingPixels) {
+  RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+      ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+      FloatRoundedRect());
+
+  CompositorFilterOperations operations;
+  operations.AppendBlurFilter(2);
+  RefPtr<EffectPaintPropertyNode> effect = EffectPaintPropertyNode::Create(
+      EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+      ClipPaintPropertyNode::Root(), kColorFilterNone, operations, 1.0,
+      SkBlendMode::kSrcOver);
+
+  PropertyTreeState state(TransformPaintPropertyNode::Root(), clip.Get(),
+                          effect.Get());
+  // If the effect moves pixels, the clip must happen first.
+  EXPECT_EQ(PropertyTreeState::kClip, state.GetInnermostNode());
+
+  PropertyTreeStateIterator iterator(state);
+  EXPECT_EQ(PropertyTreeState::kEffect, iterator.Next()->GetInnermostNode());
+  EXPECT_EQ(PropertyTreeState::kNone, iterator.Next()->GetInnermostNode());
+}
+
 TEST_F(PropertyTreeStateTest, TransformOnEffectOnClip) {
   RefPtr<TransformPaintPropertyNode> transform =
       TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
@@ -79,24 +120,27 @@
       CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver);
 
   PropertyTreeState state(transform.Get(), clip.Get(), effect.Get());
-  EXPECT_EQ(PropertyTreeState::kClip, state.GetInnermostNode());
+  EXPECT_EQ(PropertyTreeState::kEffect, state.GetInnermostNode());
 
   PropertyTreeStateIterator iterator(state);
-  EXPECT_EQ(PropertyTreeState::kEffect, iterator.Next()->GetInnermostNode());
+  EXPECT_EQ(PropertyTreeState::kClip, iterator.Next()->GetInnermostNode());
   EXPECT_EQ(PropertyTreeState::kTransform, iterator.Next()->GetInnermostNode());
   EXPECT_EQ(PropertyTreeState::kNone, iterator.Next()->GetInnermostNode());
 }
 
 TEST_F(PropertyTreeStateTest, ClipDescendantOfTransform) {
+  // CSS transform
   RefPtr<TransformPaintPropertyNode> transform =
       TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
                                          TransformationMatrix(),
                                          FloatPoint3D());
 
+  // Scroll transform
   RefPtr<TransformPaintPropertyNode> transform2 =
       TransformPaintPropertyNode::Create(
           transform.Get(), TransformationMatrix(), FloatPoint3D());
 
+  // CSS clip
   RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
       ClipPaintPropertyNode::Root(), transform2.Get(), FloatRoundedRect());
 
@@ -105,10 +149,11 @@
       ClipPaintPropertyNode::Root(), kColorFilterNone,
       CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver);
 
-  // Here the clip is inside of its own transform, but the transform is an
-  // ancestor of the clip's transform. This models situations such as
-  // a clip inside a scroller that applies to an absolute-positioned element
-  // which escapes the scroll transform but not the clip.
+  // Here the clip is inside of its own transform, but the
+  // PropertyTreeState of the content is an ancestor of the clip's transform./
+  // This models situations such as a CSS clip inside a scroller that applies to
+  // an absolute-positioned element which escapes the scroll transforms but not
+  // the clip.
   PropertyTreeState state(transform.Get(), clip.Get(), effect.Get());
   EXPECT_EQ(PropertyTreeState::kClip, state.GetInnermostNode());
 
@@ -119,11 +164,6 @@
 }
 
 TEST_F(PropertyTreeStateTest, EffectDescendantOfTransform) {
-  RefPtr<TransformPaintPropertyNode> transform =
-      TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
-                                         TransformationMatrix(),
-                                         FloatPoint3D());
-
   RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
       ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
       FloatRoundedRect());
@@ -138,11 +178,7 @@
       kColorFilterNone, CompositorFilterOperations(), 1.0,
       SkBlendMode::kSrcOver);
 
-  // Here the clip is inside of its own transform, but the transform is an
-  // ancestor of the clip's transform. This models situations such as
-  // a clip inside a scroller that applies to an absolute-positioned element
-  // which escapes the scroll transform but not the clip.
-  PropertyTreeState state(transform.Get(), clip.Get(), effect.Get());
+  PropertyTreeState state(transform2.Get(), clip.Get(), effect.Get());
   EXPECT_EQ(PropertyTreeState::kEffect, state.GetInnermostNode());
 
   PropertyTreeStateIterator iterator(state);
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index 90915e7..09be675f 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -1074,6 +1074,14 @@
   if (layer_tree_view_) {
     GetPage()->LayerTreeViewInitialized(*layer_tree_view_,
                                         local_root_->GetFrame()->View());
+
+    // TODO(kenrb): Currently GPU rasterization is always enabled for OOPIFs.
+    // This is okay because it is only necessarily to set the trigger to false
+    // for certain cases that affect the top-level frame, but it would be better
+    // to be consistent with the top-level frame. Ideally the logic should
+    // be moved from WebViewImpl into WebFrameWidget and used for all local
+    // frame roots. https://crbug.com/712794
+    layer_tree_view_->HeuristicsForGpuRasterizationUpdated(true);
   }
 
   // FIXME: only unittests, click to play, Android priting, and printing (for
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 31a751f..3fce7fb 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -2009,12 +2009,12 @@
 }
 
 IntSize WebViewTest::PrintICBSizeFromPageSize(const FloatSize& page_size) {
-  // This needs to match printingMinimumShrinkFactor in PrintContext.cpp. The
+  // This needs to match |kPrintingMinimumShrinkFactor| in PrintContext.cpp. The
   // layout is scaled by this factor for printing.
   constexpr float kMinimumShrinkFactor = 1.333f;
 
   // The expected layout size comes from the calculation done in
-  // resizePageRectsKeepingRatio which is used from PrintContext::begin to
+  // ResizePageRectsKeepingRatio() which is used from PrintContext::begin() to
   // scale the page size.
   const float ratio = page_size.Height() / (float)page_size.Width();
   const int icb_width = floor(page_size.Width() * kMinimumShrinkFactor);
diff --git a/third_party/ced/README.chromium b/third_party/ced/README.chromium
index 291a265..0a84776e 100644
--- a/third_party/ced/README.chromium
+++ b/third_party/ced/README.chromium
@@ -4,7 +4,7 @@
 Version: 2f40a850bcc5d6f7c1bfa02dbf42ad19d8220dc0
 License: Apache 2.0
 License File: LICENSE
-Security Critical: no
+Security Critical: yes
 
 Description:
 Compact Encoding Detection(CED for short) is a library written in C++ that
diff --git a/third_party/closure_compiler/externs/networking_private.js b/third_party/closure_compiler/externs/networking_private.js
index ecdd915..e911ff8 100644
--- a/third_party/closure_compiler/externs/networking_private.js
+++ b/third_party/closure_compiler/externs/networking_private.js
@@ -299,9 +299,10 @@
  *   ClientCertType: (string|undefined),
  *   Identity: (string|undefined),
  *   Inner: (string|undefined),
- *   Outer: string,
+ *   Outer: (string|undefined),
  *   Password: (string|undefined),
  *   SaveCredentials: (boolean|undefined),
+ *   ServerCAPEMs: (!Array<string>|undefined),
  *   ServerCARefs: (!Array<string>|undefined),
  *   UseProactiveKeyCaching: (boolean|undefined),
  *   UseSystemCAs: (boolean|undefined)
@@ -318,9 +319,10 @@
  *   ClientCertType: (!chrome.networkingPrivate.ManagedDOMString|undefined),
  *   Identity: (!chrome.networkingPrivate.ManagedDOMString|undefined),
  *   Inner: (!chrome.networkingPrivate.ManagedDOMString|undefined),
- *   Outer: !chrome.networkingPrivate.ManagedDOMString,
+ *   Outer: (!chrome.networkingPrivate.ManagedDOMString|undefined),
  *   Password: (!chrome.networkingPrivate.ManagedDOMString|undefined),
  *   SaveCredentials: (!chrome.networkingPrivate.ManagedBoolean|undefined),
+ *   ServerCAPEMs: (!chrome.networkingPrivate.ManagedDOMStringList|undefined),
  *   ServerCARefs: (!chrome.networkingPrivate.ManagedDOMStringList|undefined),
  *   UseProactiveKeyCaching: (!chrome.networkingPrivate.ManagedBoolean|undefined),
  *   UseSystemCAs: (!chrome.networkingPrivate.ManagedBoolean|undefined)
@@ -1224,6 +1226,7 @@
  *     trusted device.
  * @param {function(boolean):void} callback A callback function that indicates
  *     whether or not the device     is a trusted device.
+ * @deprecated Use networking.castPrivate API.
  * @see https://developer.chrome.com/extensions/networkingPrivate#method-verifyDestination
  */
 chrome.networkingPrivate.verifyDestination = function(properties, callback) {};
@@ -1237,6 +1240,7 @@
  * @param {string} networkGuid The GUID of the Cellular network to activate.
  * @param {function(string):void} callback A callback function that receives
  *     base64-encoded encrypted     credential data to send to a trusted device.
+ * @deprecated Use networking.castPrivate API.
  * @see https://developer.chrome.com/extensions/networkingPrivate#method-verifyAndEncryptCredentials
  */
 chrome.networkingPrivate.verifyAndEncryptCredentials = function(properties, networkGuid, callback) {};
@@ -1250,6 +1254,7 @@
  * @param {string} data A string containing the base64-encoded data to encrypt.
  * @param {function(string):void} callback A callback function that receives
  *     base64-encoded encrypted     data to send to a trusted device.
+ * @deprecated Use networking.castPrivate API.
  * @see https://developer.chrome.com/extensions/networkingPrivate#method-verifyAndEncryptData
  */
 chrome.networkingPrivate.verifyAndEncryptData = function(properties, data, callback) {};
@@ -1265,6 +1270,7 @@
  *     that the request failed     (e.g. MAC address lookup failed). 'Timeout'
  *     indicates that the lookup     timed out. Otherwise a valid status is
  *     returned (see     $(ref:getWifiTDLSStatus)).
+ * @deprecated Use networking.castPrivate API.
  * @see https://developer.chrome.com/extensions/networkingPrivate#method-setWifiTDLSEnabledState
  */
 chrome.networkingPrivate.setWifiTDLSEnabledState = function(ip_or_mac_address, enabled, callback) {};
@@ -1275,6 +1281,7 @@
  * @param {function(string):void} callback A callback function that receives a
  *     string with the current     TDLS status which can be 'Connected',
  *     'Disabled', 'Disconnected',     'Nonexistent', or 'Unknown'.
+ * @deprecated Use networking.castPrivate API.
  * @see https://developer.chrome.com/extensions/networkingPrivate#method-getWifiTDLSStatus
  */
 chrome.networkingPrivate.getWifiTDLSStatus = function(ip_or_mac_address, callback) {};
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 9284e4d..411a1a3 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -157,13 +157,13 @@
       'ClangToTLinuxLLD': 'clang_tot_lld_release_shared',
       'ClangToTLinuxUBSanVptr': 'clang_tot_edge_ubsan_no_recover_hack_static_release',
       'ClangToTMac': 'clang_tot_minimal_symbols_shared_release',
-      'ClangToTMac (dbg)': 'clang_tot_minimal_symbols_shared_debug',
+      'ClangToTMac (dbg)': 'clang_tot_shared_debug',
       'ClangToTMacASan': 'asan_disable_nacl_clang_tot_full_symbols_static_release',
       'ClangToTWin': 'clang_tot_official_minimal_symbols_static_release_x86',
-      'ClangToTWin(dbg)': 'clang_tot_minimal_symbols_shared_debug_x86',
+      'ClangToTWin(dbg)': 'clang_tot_shared_debug_x86',
       'ClangToTWin(dll)': 'clang_tot_minimal_symbols_shared_release_x86',
       'ClangToTWin64': 'clang_tot_official_minimal_symbols_static_release',
-      'ClangToTWin64(dbg)': 'clang_tot_minimal_symbols_shared_debug',
+      'ClangToTWin64(dbg)': 'clang_tot_shared_debug',
       'ClangToTWin64(dll)': 'clang_tot_shared_release',
       'ClangToTiOS': 'ios',
       'Closure Compilation Linux': 'closure_compilation',
@@ -1107,8 +1107,8 @@
       'clang_tot', 'full_symbols', 'shared', 'release',
     ],
 
-    'clang_tot_minimal_symbols_shared_debug': [
-      'clang_tot', 'minimal_symbols', 'shared', 'debug',
+    'clang_tot_shared_debug': [
+      'clang_tot', 'shared', 'debug',
     ],
 
     'clang_tot_full_symbols_shared_debug_use_lld': [
@@ -1119,8 +1119,8 @@
       'clang_tot', 'full_symbols', 'shared', 'debug', 'use_lld', 'x86',
     ],
 
-    'clang_tot_minimal_symbols_shared_debug_x86': [
-      'clang_tot', 'minimal_symbols', 'shared', 'debug', 'x86',
+    'clang_tot_shared_debug_x86': [
+      'clang_tot', 'shared', 'debug', 'x86',
     ],
 
     'clang_tot_shared_release_use_lld': [
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f8590d48..d19d7b15 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -103823,6 +103823,7 @@
   <int value="879992337" label="disable-pull-to-refresh-effect"/>
   <int value="880510010" label="enable-permissions-bubbles"/>
   <int value="884106779" label="supervised-user-safesites"/>
+  <int value="886907524" label="autoplay-policy"/>
   <int value="887011602" label="enable-spelling-auto-correct"/>
   <int value="892899792" label="MaterialDesignIncognitoNTP:disabled"/>
   <int value="902608487" label="AutofillUpstreamRequestCvcIfMissing:enabled"/>
diff --git a/ui/app_list/app_list_switches.cc b/ui/app_list/app_list_switches.cc
index 0f6d670..c583832 100644
--- a/ui/app_list/app_list_switches.cc
+++ b/ui/app_list/app_list_switches.cc
@@ -26,9 +26,6 @@
 // If set, the app list will be enabled as if enabled from CWS.
 const char kEnableAppList[] = "enable-app-list";
 
-// Enables the fullscreen app list.
-extern const char kEnableFullscreenAppList[] = "enable-fullscreen-app-list";
-
 // Enable/disable syncing of the app list independent of extensions.
 const char kEnableSyncAppList[] = "enable-sync-app-list";
 const char kDisableSyncAppList[] = "disable-sync-app-list";
@@ -90,10 +87,5 @@
   return !AnswerServerUrl().empty();
 }
 
-bool IsFullscreenAppListEnabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      kEnableFullscreenAppList);
-}
-
 }  // namespace switches
 }  // namespace app_list
diff --git a/ui/app_list/app_list_switches.h b/ui/app_list/app_list_switches.h
index b3cc9a9..d4fc5fb 100644
--- a/ui/app_list/app_list_switches.h
+++ b/ui/app_list/app_list_switches.h
@@ -18,7 +18,6 @@
 APP_LIST_EXPORT extern const char kCustomLauncherPage[];
 APP_LIST_EXPORT extern const char kDisableAppListDismissOnBlur[];
 APP_LIST_EXPORT extern const char kEnableAppList[];
-APP_LIST_EXPORT extern const char kEnableFullscreenAppList[];
 APP_LIST_EXPORT extern const char kEnableSyncAppList[];
 APP_LIST_EXPORT extern const char kDisableSyncAppList[];
 APP_LIST_EXPORT extern const char kEnableDriveSearchInChromeLauncher[];
@@ -42,8 +41,6 @@
 
 bool APP_LIST_EXPORT IsAnswerCardEnabled();
 
-bool APP_LIST_EXPORT IsFullscreenAppListEnabled();
-
 }  // namespace switches
 }  // namespace app_list
 
diff --git a/ui/app_list/demo/app_list_demo_views.cc b/ui/app_list/demo/app_list_demo_views.cc
index 8f0a806b..d21cdaf 100644
--- a/ui/app_list/demo/app_list_demo_views.cc
+++ b/ui/app_list/demo/app_list_demo_views.cc
@@ -60,8 +60,8 @@
   gfx::NativeView container = window_context;
 
   view_ = new app_list::AppListView(this);
-  view_->Initialize(container, 0);
-  view_->MaybeSetAnchorPoint(gfx::Point(300, 300));
+  view_->InitAsBubble(container, 0);
+  view_->SetAnchorPoint(gfx::Point(300, 300));
 
   // Populate some apps.
   GetTestModel()->PopulateApps(kInitialItems);
diff --git a/ui/app_list/presenter/app_list_presenter_impl_unittest.cc b/ui/app_list/presenter/app_list_presenter_impl_unittest.cc
index 6edb9875..372b8d16 100644
--- a/ui/app_list/presenter/app_list_presenter_impl_unittest.cc
+++ b/ui/app_list/presenter/app_list_presenter_impl_unittest.cc
@@ -44,7 +44,7 @@
             int current_apps_page) override {
     init_called_ = true;
     view_ = view;
-    view->Initialize(container_, current_apps_page);
+    view->InitAsBubble(container_, current_apps_page);
   }
   void OnShown(int64_t display_id) override { on_shown_called_ = true; }
   void OnDismissed() override { on_dismissed_called_ = true; }
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 1278828..2a1be91f 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -31,8 +31,6 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/image/image_skia.h"
@@ -131,7 +129,10 @@
 // An animation observer to hide the view at the end of the animation.
 class HideViewAnimationObserver : public ui::ImplicitAnimationObserver {
  public:
-  HideViewAnimationObserver() : frame_(NULL), target_(NULL) {}
+  HideViewAnimationObserver()
+      : frame_(NULL),
+        target_(NULL) {
+  }
 
   ~HideViewAnimationObserver() override {
     if (target_)
@@ -189,22 +190,37 @@
   RemoveAllChildViews(true);
 }
 
-void AppListView::Initialize(gfx::NativeView parent, int initial_apps_page) {
+void AppListView::InitAsBubble(gfx::NativeView parent, int initial_apps_page) {
   base::Time start_time = base::Time::Now();
+
   InitContents(parent, initial_apps_page);
+
   AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
-  set_color(kContentsBackgroundColor);
+  set_margins(gfx::Insets());
   set_parent_window(parent);
+  set_close_on_deactivate(false);
+  set_shadow(views::BubbleBorder::NO_ASSETS);
+  set_color(kContentsBackgroundColor);
+  // This creates the app list widget. (Before this, child widgets cannot be
+  // created.)
+  views::BubbleDialogDelegateView::CreateBubble(this);
 
-  if (switches::IsFullscreenAppListEnabled())
-    InitializeFullscreen(parent, initial_apps_page);
-  else
-    InitializeBubble(parent, initial_apps_page);
-
+  SetBubbleArrow(views::BubbleBorder::FLOAT);
+  // We can now create the internal widgets.
   InitChildWidgets();
+
+  aura::Window* window = GetWidget()->GetNativeWindow();
+  window->SetEventTargeter(base::MakeUnique<views::BubbleWindowTargeter>(this));
+
+  const int kOverlayCornerRadius =
+      GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius();
+  overlay_view_ = new AppListOverlayView(kOverlayCornerRadius);
+  overlay_view_->SetBoundsRect(GetContentsBounds());
   AddChildView(overlay_view_);
+
   if (delegate_)
     delegate_->ViewInitialized();
+
   UMA_HISTOGRAM_TIMES("Apps.AppListCreationTime",
                       base::Time::Now() - start_time);
 }
@@ -215,10 +231,8 @@
   GetBubbleFrameView()->SchedulePaint();
 }
 
-void AppListView::MaybeSetAnchorPoint(const gfx::Point& anchor_point) {
-  // if the AppListView is a bubble
-  if (!switches::IsFullscreenAppListEnabled())
-    SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
+void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) {
+  SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
 }
 
 void AppListView::SetDragAndDropHostOfCurrentAppList(
@@ -236,9 +250,7 @@
 }
 
 void AppListView::UpdateBounds() {
-  // if the AppListView is a bubble
-  if (!switches::IsFullscreenAppListEnabled())
-    SizeToContents();
+  SizeToContents();
 }
 
 void AppListView::SetAppListOverlayVisible(bool visible) {
@@ -292,10 +304,6 @@
   }
 }
 
-const char* AppListView::GetClassName() const {
-  return "AppListView";
-}
-
 bool AppListView::ShouldHandleSystemCommands() const {
   return true;
 }
@@ -335,6 +343,7 @@
   app_list_main_view_->SetPaintToLayer();
   app_list_main_view_->layer()->SetFillsBoundsOpaquely(false);
   app_list_main_view_->layer()->SetMasksToBounds(true);
+
   // This will be added to the |search_box_widget_| after the app list widget is
   // initialized.
   search_box_view_ = new SearchBoxView(app_list_main_view_, delegate_);
@@ -401,49 +410,9 @@
   app_list_main_view_->contents_view()->Layout();
 }
 
-void AppListView::InitializeFullscreen(gfx::NativeView parent,
-                                       int initial_apps_page) {
-  const gfx::Rect& display_work_area_bounds =
-      display::Screen::GetScreen()->GetDisplayNearestView(parent).work_area();
-  views::Widget* widget = new views::Widget;
-  views::Widget::InitParams app_list_overlay_view_params(
-      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-
-  app_list_overlay_view_params.parent = parent;
-  app_list_overlay_view_params.delegate = this;
-  app_list_overlay_view_params.opacity =
-      views::Widget::InitParams::TRANSLUCENT_WINDOW;
-  app_list_overlay_view_params.bounds = display_work_area_bounds;
-  widget->Init(app_list_overlay_view_params);
-  widget->GetLayer()->SetBackgroundBlur(10);
-
-  overlay_view_ = new AppListOverlayView(0 /* no corners */);
-}
-
-void AppListView::InitializeBubble(gfx::NativeView parent,
-                                   int initial_apps_page) {
-  set_margins(gfx::Insets());
-  set_close_on_deactivate(false);
-  set_shadow(views::BubbleBorder::NO_ASSETS);
-
-  // This creates the app list widget. (Before this, child widgets cannot be
-  // created.)
-  views::BubbleDialogDelegateView::CreateBubble(this);
-
-  SetBubbleArrow(views::BubbleBorder::FLOAT);
-  // We can now create the internal widgets.
-
-  aura::Window* window = GetWidget()->GetNativeWindow();
-  window->SetEventTargeter(base::MakeUnique<views::BubbleWindowTargeter>(this));
-
-  const int kOverlayCornerRadius =
-      GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius();
-  overlay_view_ = new AppListOverlayView(kOverlayCornerRadius);
-  overlay_view_->SetBoundsRect(GetContentsBounds());
-}
-
-void AppListView::OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
-                                           views::Widget* widget) const {
+void AppListView::OnBeforeBubbleWidgetInit(
+    views::Widget::InitParams* params,
+    views::Widget* widget) const {
   if (!params->native_widget) {
     views::ViewsDelegate* views_delegate = views::ViewsDelegate::GetInstance();
     if (views_delegate && !views_delegate->native_widget_factory().is_null()) {
@@ -472,7 +441,8 @@
   DCHECK(mask);
   DCHECK(GetBubbleFrameView());
 
-  mask->addRect(gfx::RectToSkRect(GetBubbleFrameView()->GetContentsBounds()));
+  mask->addRect(gfx::RectToSkRect(
+      GetBubbleFrameView()->GetContentsBounds()));
 }
 
 bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) {
@@ -505,8 +475,8 @@
     gfx::Rect speech_bounds = centered_bounds;
     int preferred_height = speech_view_->GetPreferredSize().height();
     speech_bounds.Inset(kSpeechUIMargin, kSpeechUIMargin);
-    speech_bounds.set_height(
-        std::min(speech_bounds.height(), preferred_height));
+    speech_bounds.set_height(std::min(speech_bounds.height(),
+                                      preferred_height));
     speech_bounds.Inset(-speech_view_->GetInsets());
     speech_view_->SetBoundsRect(speech_bounds);
   }
@@ -552,7 +522,8 @@
 
   animation_observer_->set_frame(GetBubbleFrameView());
   gfx::Transform speech_transform;
-  speech_transform.Translate(0, SkFloatToMScalar(kSpeechUIAppearingPosition));
+  speech_transform.Translate(
+      0, SkFloatToMScalar(kSpeechUIAppearingPosition));
   if (will_appear)
     speech_view_->layer()->SetTransform(speech_transform);
 
diff --git a/ui/app_list/views/app_list_view.h b/ui/app_list/views/app_list_view.h
index cbdb1d89..fb3b061e 100644
--- a/ui/app_list/views/app_list_view.h
+++ b/ui/app_list/views/app_list_view.h
@@ -38,18 +38,16 @@
   explicit AppListView(AppListViewDelegate* delegate);
   ~AppListView() override;
 
-  // Initializes the widget as a bubble or fullscreen view depending on the
-  // presence of a cmd line switch. parent and initial_apps_page are used for
-  // both the bubble and fullscreen initialization.
-  void Initialize(gfx::NativeView parent, int initial_apps_page);
+  // Initializes the widget.
+  void InitAsBubble(gfx::NativeView parent, int initial_apps_page);
 
   void SetBubbleArrow(views::BubbleBorder::Arrow arrow);
 
-  void MaybeSetAnchorPoint(const gfx::Point& anchor_point);
+  void SetAnchorPoint(const gfx::Point& anchor_point);
 
   // If |drag_and_drop_host| is not NULL it will be called upon drag and drop
   // operations outside the application list. This has to be called after
-  // Initialize was called since the app list object needs to exist so that
+  // InitAsBubble was called since the app list object needs to exist so that
   // it can set the host.
   void SetDragAndDropHostOfCurrentAppList(
       ApplicationDragAndDropHost* drag_and_drop_host);
@@ -71,7 +69,6 @@
   // Overridden from views::View:
   gfx::Size GetPreferredSize() const override;
   void OnPaint(gfx::Canvas* canvas) override;
-  const char* GetClassName() const override;
 
   // WidgetDelegate overrides:
   bool ShouldHandleSystemCommands() const override;
@@ -96,12 +93,6 @@
 
   void InitChildWidgets();
 
-  // Initializes the widget for fullscreen mode.
-  void InitializeFullscreen(gfx::NativeView parent, int initial_apps_page);
-
-  // Initializes the widget as a bubble.
-  void InitializeBubble(gfx::NativeView parent, int initial_apps_page);
-
   // Overridden from views::BubbleDialogDelegateView:
   void OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
                                 views::Widget* widget) const override;
@@ -126,8 +117,8 @@
   SpeechView* speech_view_;
 
   views::View* search_box_focus_host_;  // Owned by the views hierarchy.
-  views::Widget* search_box_widget_;    // Owned by the app list's widget.
-  SearchBoxView* search_box_view_;      // Owned by |search_box_widget_|.
+  views::Widget* search_box_widget_;  // Owned by the app list's widget.
+  SearchBoxView* search_box_view_;    // Owned by |search_box_widget_|.
 
   // A semi-transparent white overlay that covers the app list while dialogs are
   // open.
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc
index 56e2bdd..a93a966 100644
--- a/ui/app_list/views/app_list_view_unittest.cc
+++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -172,8 +172,8 @@
   view_ = new app_list::AppListView(delegate_.get());
 
   // Initialize centered around a point that ensures the window is wholly shown.
-  view_->Initialize(parent, 0);
-  view_->MaybeSetAnchorPoint(gfx::Point(300, 300));
+  view_->InitAsBubble(parent, 0);
+  view_->SetAnchorPoint(gfx::Point(300, 300));
 }
 
 AppListViewTestContext::~AppListViewTestContext() {
diff --git a/ui/aura/window.h b/ui/aura/window.h
index c50ccd0d..2996584 100644
--- a/ui/aura/window.h
+++ b/ui/aura/window.h
@@ -175,7 +175,7 @@
   // LayoutManager may adjust the bounds.
   void SetBounds(const gfx::Rect& new_bounds);
 
-  // Changes the bounds of the window in the screen coordinates.
+  // Changes the bounds of the window in the screen coordintates.
   // If present, the window's parent's LayoutManager may adjust the bounds.
   void SetBoundsInScreen(const gfx::Rect& new_bounds_in_screen_coords,
                          const display::Display& dst_display);
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 744fd65..43da095 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/ui.gni")
-import("//device/vr/features.gni")
+import("//device/vr/features/features.gni")
 import("//testing/test.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 
@@ -255,7 +255,7 @@
     "//base:base_static",
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
-    "//device/vr:features",
+    "//device/vr/features",
     "//skia",
     "//third_party/zlib",
   ]
diff --git a/ui/gfx/DEPS b/ui/gfx/DEPS
index bef0c572..a6891ad4 100644
--- a/ui/gfx/DEPS
+++ b/ui/gfx/DEPS
@@ -1,7 +1,7 @@
 include_rules = [
   "+base",
   "+cc/paint",
-  "+device/vr/features.h",
+  "+device/vr/features/features.h",
   "+skia/ext",
   "+third_party/harfbuzz-ng",
   "+third_party/skia",
diff --git a/ui/gfx/font_render_params.h b/ui/gfx/font_render_params.h
index af89e34..ade3672 100644
--- a/ui/gfx/font_render_params.h
+++ b/ui/gfx/font_render_params.h
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "build/build_config.h"
-#include "device/vr/features.h"
+#include "device/vr/features/features.h"
 #include "third_party/skia/include/core/SkFontLCDConfig.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/gfx_export.h"
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index de3ac38..fbce29e 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -581,26 +581,26 @@
     return;
   }
 
-  bool can_close = true;
-  if (non_client_view_)
-    can_close = non_client_view_->CanClose();
-  if (can_close) {
-    SaveWindowPlacement();
+  if (non_client_view_ && !non_client_view_->CanClose())
+    return;
 
-    // During tear-down the top-level focus manager becomes unavailable to
-    // GTK tabbed panes and their children, so normal deregistration via
-    // |FormManager::ViewRemoved()| calls are fouled.  We clear focus here
-    // to avoid these redundant steps and to avoid accessing deleted views
-    // that may have been in focus.
-    if (is_top_level() && focus_manager_.get())
-      focus_manager_->SetFocusedView(NULL);
+  // The actions below can cause this function to be called again, so mark
+  // |this| as closed early. See crbug.com/714334
+  widget_closed_ = true;
+  SaveWindowPlacement();
 
-    for (WidgetObserver& observer : observers_)
-      observer.OnWidgetClosing(this);
+  // During tear-down the top-level focus manager becomes unavailable to
+  // GTK tabbed panes and their children, so normal deregistration via
+  // |FocusManager::ViewRemoved()| calls are fouled.  We clear focus here
+  // to avoid these redundant steps and to avoid accessing deleted views
+  // that may have been in focus.
+  if (is_top_level() && focus_manager_.get())
+    focus_manager_->SetFocusedView(nullptr);
 
-    native_widget_->Close();
-    widget_closed_ = true;
-  }
+  for (WidgetObserver& observer : observers_)
+    observer.OnWidgetClosing(this);
+
+  native_widget_->Close();
 }
 
 void Widget::CloseNow() {
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 52845e5ed..fe51c6f 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -738,6 +738,8 @@
   void OnWidgetDestroying(Widget* widget) override {
     if (active_ == widget)
       active_ = nullptr;
+    if (widget_activated_ == widget)
+      widget_activated_ = nullptr;
     widget_closed_ = widget;
   }
 
@@ -808,11 +810,10 @@
   Widget* widget_to_close_on_hide_;
 };
 
-TEST_F(WidgetObserverTest, DISABLED_ActivationChange) {
+TEST_F(WidgetObserverTest, ActivationChange) {
   WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
-
-  Widget* toplevel1 = NewWidget();
-  Widget* toplevel2 = NewWidget();
+  WidgetAutoclosePtr toplevel1(NewWidget());
+  WidgetAutoclosePtr toplevel2(NewWidget());
 
   toplevel1->Show();
   toplevel2->Show();
@@ -822,20 +823,82 @@
   toplevel1->Activate();
 
   RunPendingMessages();
-  EXPECT_EQ(toplevel1, widget_activated());
+  EXPECT_EQ(toplevel1.get(), widget_activated());
 
   toplevel2->Activate();
   RunPendingMessages();
-  EXPECT_EQ(toplevel1, widget_deactivated());
-  EXPECT_EQ(toplevel2, widget_activated());
-  EXPECT_EQ(toplevel2, active());
+  EXPECT_EQ(toplevel1.get(), widget_deactivated());
+  EXPECT_EQ(toplevel2.get(), widget_activated());
+  EXPECT_EQ(toplevel2.get(), active());
 }
 
-TEST_F(WidgetObserverTest, DISABLED_VisibilityChange) {
-  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
+namespace {
 
-  Widget* child1 = NewWidget();
-  Widget* child2 = NewWidget();
+// This class simulates a focus manager that moves focus to a second widget when
+// the first one is closed. It simulates a situation where a sequence of widget
+// observers might try to call Widget::Close in response to a OnWidgetClosing().
+class WidgetActivationForwarder : public TestWidgetObserver {
+ public:
+  WidgetActivationForwarder(Widget* current_active_widget,
+                            Widget* widget_to_activate)
+      : TestWidgetObserver(current_active_widget),
+        widget_to_activate_(widget_to_activate) {}
+
+  ~WidgetActivationForwarder() override {}
+
+ private:
+  // WidgetObserver overrides:
+  void OnWidgetClosing(Widget* widget) override {
+    widget->OnNativeWidgetActivationChanged(false);
+    widget_to_activate_->Activate();
+  }
+  void OnWidgetActivationChanged(Widget* widget, bool active) override {
+    if (!active)
+      widget->Close();
+  }
+
+  Widget* widget_to_activate_;
+
+  DISALLOW_COPY_AND_ASSIGN(WidgetActivationForwarder);
+};
+
+// This class observes a widget and counts the number of times OnWidgetClosing
+// is called.
+class WidgetCloseCounter : public TestWidgetObserver {
+ public:
+  explicit WidgetCloseCounter(Widget* widget) : TestWidgetObserver(widget) {}
+
+  ~WidgetCloseCounter() override {}
+
+  int close_count() const { return close_count_; }
+
+ private:
+  // WidgetObserver overrides:
+  void OnWidgetClosing(Widget* widget) override { close_count_++; }
+
+  int close_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(WidgetCloseCounter);
+};
+
+}  // namespace
+
+// Makes sure close notifications aren't sent more than once when a Widget is
+// shutting down. Test for crbug.com/714334
+TEST_F(WidgetObserverTest, CloseReentrancy) {
+  Widget* widget1 = CreateTopLevelPlatformWidget();
+  Widget* widget2 = CreateTopLevelPlatformWidget();
+  WidgetCloseCounter counter(widget1);
+  WidgetActivationForwarder focus_manager(widget1, widget2);
+  widget1->Close();
+  EXPECT_EQ(1, counter.close_count());
+  widget2->Close();
+}
+
+TEST_F(WidgetObserverTest, VisibilityChange) {
+  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
+  WidgetAutoclosePtr child1(NewWidget());
+  WidgetAutoclosePtr child2(NewWidget());
 
   toplevel->Show();
   child1->Show();
@@ -844,16 +907,16 @@
   reset();
 
   child1->Hide();
-  EXPECT_EQ(child1, widget_hidden());
+  EXPECT_EQ(child1.get(), widget_hidden());
 
   child2->Hide();
-  EXPECT_EQ(child2, widget_hidden());
+  EXPECT_EQ(child2.get(), widget_hidden());
 
   child1->Show();
-  EXPECT_EQ(child1, widget_shown());
+  EXPECT_EQ(child1.get(), widget_shown());
 
   child2->Show();
-  EXPECT_EQ(child2, widget_shown());
+  EXPECT_EQ(child2.get(), widget_shown());
 }
 
 TEST_F(WidgetObserverTest, DestroyBubble) {
diff --git a/ui/webui/resources/cr_elements/hidden_style_css.html b/ui/webui/resources/cr_elements/hidden_style_css.html
new file mode 100644
index 0000000..4c22288
--- /dev/null
+++ b/ui/webui/resources/cr_elements/hidden_style_css.html
@@ -0,0 +1,15 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<!-- Common style for Material Design WebUI, such that the |hidden| attributes
+  works within Shadow DOM. -->
+<dom-module id="cr-hidden-style">
+  <template>
+    <style>
+      /* Included here so we don't have to include "iron-positioning" in every
+       * stylesheet. See crbug.com/498405. */
+      [hidden] {
+        display: none !important;
+      }
+    </style>
+  </template>
+</dom-module>
diff --git a/ui/webui/resources/cr_elements/shared_style_css.html b/ui/webui/resources/cr_elements/shared_style_css.html
index e00e4945..f4157f6 100644
--- a/ui/webui/resources/cr_elements/shared_style_css.html
+++ b/ui/webui/resources/cr_elements/shared_style_css.html
@@ -1,11 +1,12 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="hidden_style_css.html">
 
 <!-- Common styles for Material Design WebUI. Included directly in
   settings_shared_css.html. -->
 <dom-module id="cr-shared-style">
   <template>
-    <style>
+    <style include="cr-hidden-style">
       /* Chrome spinners should be blue. */
       paper-spinner {
         --paper-spinner-layer-1-color: var(--google-blue-500);
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp
index 8d96278..1f3a45ff 100644
--- a/ui/webui/resources/cr_elements_resources.grdp
+++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -146,6 +146,9 @@
              file="../../webui/resources/cr_elements/shared_style_css.html"
              type="chrome_html"
              flattenhtml="true" />
+  <structure name="IDR_CR_ELEMENTS_CR_HIDDEN_STYLE_CSS_HTML"
+             file="../../webui/resources/cr_elements/hidden_style_css.html"
+             type="chrome_html" />
   <structure name="IDR_CR_ELEMENTS_CR_SHARED_VARS_CSS_HTML"
              file="../../webui/resources/cr_elements/shared_vars_css.html"
              type="chrome_html" />