diff --git a/.gitignore b/.gitignore
index c01e2ae9..98d23f96 100644
--- a/.gitignore
+++ b/.gitignore
@@ -186,6 +186,7 @@
 /ios/third_party/material_roboto_font_loader_ios/src
 /ios/third_party/material_sprited_animation_view_ios/src
 /ios/third_party/material_text_accessibility_ios/src
+/ios/third_party/motion_animator_objc/src
 /ios/third_party/motion_interchange_objc/src
 /ios/third_party/ochamcrest/src
 /ios_internal
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 791d86e..213a2c76 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -5,7 +5,8 @@
 applies to our repos and organizations, issue trackers, mailing lists,
 blog content, and any other Chromium-supported communication group, as
 well as any private communication initiated in the context of these
-spaces.
+spaces. This code of conduct must be followed by everyone contributing to
+the Chromium project, regardless of affiliation or position.
 
 Simply put, community discussions should be
 
diff --git a/DEPS b/DEPS
index 02f64fd..46be599 100644
--- a/DEPS
+++ b/DEPS
@@ -227,6 +227,11 @@
       'condition': 'checkout_ios',
   },
 
+  'src/ios/third_party/motion_animator_objc/src': {
+      'url': Var('chromium_git') + '/external/github.com/material-motion/motion-animator-objc.git' + '@' + '60cf5680f1df8b181299c04ba22c32e388a7f0ba',
+      'condition': 'checkout_ios',
+  },
+
   'src/ios/third_party/ochamcrest/src': {
       'url': Var('chromium_git') + '/external/github.com/hamcrest/OCHamcrest.git' + '@' + '92d9c14d13bb864255e65c09383564653896916b',
       'condition': 'checkout_ios',
@@ -313,7 +318,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f6b946d71d43483dc701837aae4871927d0115bd',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6c5a1c4c3c97b568c79ee44a7b1d7ffac17e30d3',
 
   # DevTools node modules. Used on Linux buildbots only.
   'src/third_party/devtools-node-modules': {
diff --git a/ash/display/display_move_window_util.cc b/ash/display/display_move_window_util.cc
index 4a2cd6f..98fc0f57 100644
--- a/ash/display/display_move_window_util.cc
+++ b/ash/display/display_move_window_util.cc
@@ -9,6 +9,7 @@
 
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
+#include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/window.h"
 #include "ui/display/display.h"
@@ -141,6 +142,13 @@
   if (!window)
     return;
 
+  // When |window_list| is not empty, |window| can only be the first one of the
+  // fresh built list if it is in the list.
+  MruWindowTracker::WindowList window_list =
+      Shell::Get()->mru_window_tracker()->BuildWindowForCycleList();
+  if (window_list.empty() || window_list.front() != window)
+    return;
+
   display::Display origin_display =
       display::Screen::GetScreen()->GetDisplayNearestWindow(window);
   int64_t dest_display_id = GetNextDisplay(origin_display, direction);
diff --git a/ash/display/display_move_window_util_unittest.cc b/ash/display/display_move_window_util_unittest.cc
index 0ce5e24..2807be5 100644
--- a/ash/display/display_move_window_util_unittest.cc
+++ b/ash/display/display_move_window_util_unittest.cc
@@ -8,6 +8,7 @@
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_util.h"
 #include "base/macros.h"
 #include "ui/aura/test/test_windows.h"
@@ -248,4 +249,39 @@
             client.last_a11y_alert());
 }
 
+// Tests that moving window between displays is no-op if active window is not in
+// cycle window list.
+TEST_F(DisplayMoveWindowUtilTest, NoMovementIfNotInCycleWindowList) {
+  display::Screen* screen = display::Screen::GetScreen();
+  int64_t primary_id = screen->GetPrimaryDisplay().id();
+  display::DisplayIdList list =
+      display::test::CreateDisplayIdList2(primary_id, primary_id + 1);
+  // Layout: [p][1]
+  display::DisplayLayoutBuilder builder(primary_id);
+  builder.AddDisplayPlacement(list[1], primary_id,
+                              display::DisplayPlacement::RIGHT, 0);
+  display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
+      list, builder.Build());
+  UpdateDisplay("400x300,400x300");
+  EXPECT_EQ(2U, display_manager()->GetNumDisplays());
+  EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
+            display_manager()->GetDisplayAt(0).bounds());
+  EXPECT_EQ(gfx::Rect(400, 0, 400, 300),
+            display_manager()->GetDisplayAt(1).bounds());
+
+  // Create a window in app list container, which would be excluded in cycle
+  // window list.
+  std::unique_ptr<aura::Window> window = CreateChildWindow(
+      Shell::GetPrimaryRootWindow(), gfx::Rect(10, 20, 200, 100),
+      kShellWindowId_AppListContainer);
+  wm::ActivateWindow(window.get());
+  EXPECT_EQ(list[0], screen->GetDisplayNearestWindow(window.get()).id());
+
+  MruWindowTracker::WindowList cycle_window_list =
+      Shell::Get()->mru_window_tracker()->BuildWindowForCycleList();
+  EXPECT_TRUE(cycle_window_list.empty());
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kRight);
+  EXPECT_EQ(list[0], screen->GetDisplayNearestWindow(window.get()).id());
+}
+
 }  // namespace ash
diff --git a/ash/wm/OWNERS b/ash/wm/OWNERS
index c1e24dc9f..26780820 100644
--- a/ash/wm/OWNERS
+++ b/ash/wm/OWNERS
@@ -1,5 +1,3 @@
-pkotwicz@chromium.org
-varkha@chromium.org
 flackr@chromium.org
 
 # COMPONENT: UI>Shell>WindowManager
diff --git a/ash/wm/mru_window_tracker.cc b/ash/wm/mru_window_tracker.cc
index 5ee031d2..0a21166e 100644
--- a/ash/wm/mru_window_tracker.cc
+++ b/ash/wm/mru_window_tracker.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/wm/focus_rules.h"
 #include "ash/wm/switchable_windows.h"
@@ -121,6 +122,31 @@
                                  base::Bind(&IsWindowConsideredActivatable));
 }
 
+MruWindowTracker::WindowList MruWindowTracker::BuildWindowForCycleList() const {
+  MruWindowTracker::WindowList window_list = BuildMruWindowList();
+  // Exclude windows:
+  // - non user positionable windows, such as extension popups.
+  // - windows being dragged
+  // - the AppList window, which will hide as soon as cycling starts
+  //   anyway. It doesn't make sense to count it as a "switchable" window, yet
+  //   a lot of code relies on the MRU list returning the app window. If we
+  //   don't manually remove it, the window cycling UI won't crash or misbehave,
+  //   but there will be a flicker as the target window changes. Also exclude
+  //   unselectable windows such as extension popups.
+  auto window_is_ineligible = [](aura::Window* window) {
+    wm::WindowState* state = wm::GetWindowState(window);
+    return !state->IsUserPositionable() || state->is_dragged() ||
+           window->GetRootWindow()
+               ->GetChildById(kShellWindowId_AppListContainer)
+               ->Contains(window) ||
+           !window->GetProperty(kShowInOverviewKey);
+  };
+  window_list.erase(std::remove_if(window_list.begin(), window_list.end(),
+                                   window_is_ineligible),
+                    window_list.end());
+  return window_list;
+}
+
 void MruWindowTracker::SetIgnoreActivations(bool ignore) {
   ignore_window_activations_ = ignore;
 
diff --git a/ash/wm/mru_window_tracker.h b/ash/wm/mru_window_tracker.h
index c90da432..7af4150 100644
--- a/ash/wm/mru_window_tracker.h
+++ b/ash/wm/mru_window_tracker.h
@@ -34,6 +34,11 @@
   // modal dialog window is present.
   WindowList BuildWindowListIgnoreModal() const;
 
+  // This does the same thing as |BuildMruWindowList()| but with some
+  // exclusions. This list is used for cycling through by the keyboard via
+  // alt-tab.
+  WindowList BuildWindowForCycleList() const;
+
   // Starts or stops ignoring window activations. If no longer ignoring
   // activations the currently active window is moved to the front of the
   // MRU window list. Used by WindowCycleList to avoid adding all cycled
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc
index 687fbe26..0803bb1 100644
--- a/ash/wm/window_cycle_controller.cc
+++ b/ash/wm/window_cycle_controller.cc
@@ -8,7 +8,6 @@
 #include "ash/metrics/task_switch_source.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/cpp/window_properties.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
@@ -16,7 +15,6 @@
 #include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/window_cycle_event_filter.h"
 #include "ash/wm/window_cycle_list.h"
-#include "ash/wm/window_state.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 
@@ -59,27 +57,7 @@
 
 void WindowCycleController::StartCycling() {
   WindowCycleList::WindowList window_list =
-      Shell::Get()->mru_window_tracker()->BuildMruWindowList();
-  // Exclude windows:
-  // - non user positionable windows, such as extension popups.
-  // - windows being dragged
-  // - the AppList window, which will hide as soon as cycling starts
-  //   anyway. It doesn't make sense to count it as a "switchable" window, yet
-  //   a lot of code relies on the MRU list returning the app window. If we
-  //   don't manually remove it, the window cycling UI won't crash or misbehave,
-  //   but there will be a flicker as the target window changes. Also exclude
-  //   unselectable windows such as extension popups.
-  auto window_is_ineligible = [](aura::Window* window) {
-    wm::WindowState* state = wm::GetWindowState(window);
-    return !state->IsUserPositionable() || state->is_dragged() ||
-           window->GetRootWindow()
-               ->GetChildById(kShellWindowId_AppListContainer)
-               ->Contains(window) ||
-           !window->GetProperty(kShowInOverviewKey);
-  };
-  window_list.erase(std::remove_if(window_list.begin(), window_list.end(),
-                                   window_is_ineligible),
-                    window_list.end());
+      Shell::Get()->mru_window_tracker()->BuildWindowForCycleList();
 
   active_window_before_window_cycle_ = GetActiveWindow(window_list);
 
diff --git a/base/process/process_win.cc b/base/process/process_win.cc
index 005a68e..c3b59924 100644
--- a/base/process/process_win.cc
+++ b/base/process/process_win.cc
@@ -132,19 +132,22 @@
 }
 
 bool Process::Terminate(int exit_code, bool wait) const {
+  constexpr DWORD kWaitMs = 60 * 1000;
+
   // exit_code cannot be implemented.
   DCHECK(IsValid());
   bool result = (::TerminateProcess(Handle(), exit_code) != FALSE);
   if (result) {
     // The process may not end immediately due to pending I/O
-    if (wait && ::WaitForSingleObject(Handle(), 60 * 1000) != WAIT_OBJECT_0)
+    if (wait && ::WaitForSingleObject(Handle(), kWaitMs) != WAIT_OBJECT_0)
       DPLOG(ERROR) << "Error waiting for process exit";
     Exited(exit_code);
   } else {
     // The process can't be terminated, perhaps because it has already
-    // exited.
+    // exited or is in the process of exiting. A non-zero timeout is necessary
+    // here for the same reasons as above.
     DPLOG(ERROR) << "Unable to terminate process";
-    if (::WaitForSingleObject(Handle(), 0) == WAIT_OBJECT_0) {
+    if (::WaitForSingleObject(Handle(), kWaitMs) == WAIT_OBJECT_0) {
       DWORD actual_exit;
       Exited(::GetExitCodeProcess(Handle(), &actual_exit) ? actual_exit
                                                           : exit_code);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index 4d5e30e..8562af0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -19,7 +19,11 @@
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.datausage.ExternalDataUseObserver;
 import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
+import org.chromium.chrome.browser.feedback.AsyncFeedbackSource;
+import org.chromium.chrome.browser.feedback.FeedbackCollector;
 import org.chromium.chrome.browser.feedback.FeedbackReporter;
+import org.chromium.chrome.browser.feedback.FeedbackSource;
+import org.chromium.chrome.browser.feedback.FeedbackSourceProvider;
 import org.chromium.chrome.browser.gsa.GSAHelper;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.historyreport.AppIndexingReporter;
@@ -331,4 +335,12 @@
     public PartnerBrowserCustomizations.Provider getCustomizationProvider() {
         return new PartnerBrowserCustomizations.ProviderPackage();
     }
+
+    /**
+     * @return A {@link FeedbackSourceProvider} that can provide additional {@link FeedbackSource}s
+     * and {@link AsyncFeedbackSource}s to be used by a {@link FeedbackCollector}.
+     */
+    public FeedbackSourceProvider getAdditionalFeedbackSources() {
+        return new FeedbackSourceProvider() {};
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
index 1eadfce..722ca8b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
@@ -80,14 +80,14 @@
         DownloadNotificationUmaHelper.recordServiceStoppedHistogram(
                 DownloadNotificationUmaHelper.ServiceStopped.STOPPED, true /* withForeground */);
 
-        boolean notificationDetachedOrKilled =
-                (detachNotification && isSdkAtLeast24()) || killNotification;
+        boolean notificationDetached = detachNotification && isSdkAtLeast24();
+        boolean notificationDetachedOrKilled = notificationDetached || killNotification;
 
         // Reset pinned notification if notification is properly detached or killed.
         if (notificationDetachedOrKilled) clearPinnedNotificationId();
 
         // Detach notification from foreground if possible.
-        if (detachNotification && isSdkAtLeast24()) {
+        if (notificationDetached) {
             stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_DETACH);
             return true;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
index b70ce44..ba6b5fb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
@@ -244,24 +244,31 @@
         Preconditions.checkNotNull(mBoundService);
         mIsServiceBound = false;
 
+        // For pre-Lollipop phones (API < 21), we need to kill the notification in the pause case
+        // because otherwise the notification gets stuck in the ongoing state.
+        boolean needAdjustNotificationPreLollipop =
+                isPreLollipop() && downloadStatus == DownloadStatus.PAUSE;
+
         // Pause: only try to detach, do not kill notification.
         // Complete/failed: try to detach, if that doesn't work, kill.
         // Cancel: don't even try to detach, just kill.
+
         boolean detachNotification = downloadStatus == DownloadStatus.PAUSE
                 || downloadStatus == DownloadStatus.COMPLETE
                 || downloadStatus == DownloadStatus.FAIL;
         boolean killNotification = downloadStatus == DownloadStatus.CANCEL
                 || downloadStatus == DownloadStatus.COMPLETE
-                || downloadStatus == DownloadStatus.FAIL;
+                || downloadStatus == DownloadStatus.FAIL || needAdjustNotificationPreLollipop;
 
         boolean notificationHandledProperly =
                 stopAndUnbindServiceInternal(detachNotification, killNotification);
         mBoundService = null;
 
-        // If the download is completed,  need to relaunch the notification so it is no longer
-        // pinned to the foreground service.
-        if ((downloadStatus == DownloadStatus.COMPLETE || downloadStatus == DownloadStatus.FAIL)
-                && Build.VERSION.SDK_INT < 24) {
+        // Relaunch notification so it is no longer pinned to the foreground service when the
+        // download is completed/failed or if a pre-Lollipop adjustment is needed.
+        if (((downloadStatus == DownloadStatus.COMPLETE || downloadStatus == DownloadStatus.FAIL)
+                    && Build.VERSION.SDK_INT < 24)
+                || needAdjustNotificationPreLollipop) {
             relaunchPinnedNotification();
         }
 
@@ -290,4 +297,9 @@
     void setBoundService(DownloadForegroundService service) {
         mBoundService = service;
     }
+
+    @VisibleForTesting
+    boolean isPreLollipop() {
+        return Build.VERSION.SDK_INT < 21;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/feedback/FeedbackCollector.java b/chrome/android/java/src/org/chromium/chrome/browser/feedback/FeedbackCollector.java
index 1d469ec..33d8899 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/feedback/FeedbackCollector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/feedback/FeedbackCollector.java
@@ -15,6 +15,7 @@
 import org.chromium.base.CollectionUtil;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.profiles.Profile;
 
 import java.util.ArrayList;
@@ -74,6 +75,7 @@
 
         // This is the list of all synchronous sources of feedback.  Please add new synchronous
         // entries here.
+        sources.addAll(AppHooks.get().getAdditionalFeedbackSources().getSynchronousSources());
         sources.add(new UrlFeedbackSource(url));
         sources.add(new VariationsFeedbackSource(profile));
         sources.add(new DataReductionProxyFeedbackSource(profile));
@@ -91,6 +93,7 @@
 
         // This is the list of all asynchronous sources of feedback.  Please add new asynchronous
         // entries here.
+        sources.addAll(AppHooks.get().getAdditionalFeedbackSources().getAsynchronousSources());
         sources.add(new ConnectivityFeedbackSource(profile));
         sources.add(new SystemInfoFeedbackSource());
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/feedback/FeedbackSourceProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/feedback/FeedbackSourceProvider.java
new file mode 100644
index 0000000..b485d9ed
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/feedback/FeedbackSourceProvider.java
@@ -0,0 +1,24 @@
+// 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.
+
+package org.chromium.chrome.browser.feedback;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Used for an external component to be able to provide extra feedback sources to the
+ * FeedbackCollector.  In general it is best to add the sources directly to FeedbackCollector, but
+ * if there are DEPS or repository reasons for not doing that, they can be provided through here.
+ */
+public interface FeedbackSourceProvider {
+    // clang-format off
+    // TODO(crbug.com/781015): Clang isn't formatting this correctly.
+    /** @return A list of {@link FeedbackSource}s to add to a {@link FeedbackCollector}. */
+    default Collection<FeedbackSource> getSynchronousSources() { return new ArrayList<>(); }
+
+    /** @return A list of {@link AsyncFeedbackSource}s to add to a {@link FeedbackCollector}. */
+    default Collection<AsyncFeedbackSource> getAsynchronousSources() { return new ArrayList<>(); }
+        // clang-format on
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java
index dcaacd5..0ea8cec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java
@@ -147,10 +147,11 @@
      * @param profile The profile to use for starting the AutocompleteController.
      * @param omniboxText The text displayed in the omnibox.
      * @param url The url of the currently loaded web page.
+     * @param url The title of the currently loaded web page.
      * @param focusedFromFakebox Whether the user entered the omnibox by tapping the fakebox on the
      *                           native NTP. This should be false on all other pages.
      */
-    public void startZeroSuggest(Profile profile, String omniboxText, String url,
+    public void startZeroSuggest(Profile profile, String omniboxText, String url, String title,
             boolean focusedFromFakebox) {
         if (profile == null || TextUtils.isEmpty(url)) return;
 
@@ -162,8 +163,8 @@
         mNativeAutocompleteControllerAndroid = nativeInit(profile);
         if (mNativeAutocompleteControllerAndroid != 0) {
             if (mUseCachedZeroSuggestResults) mWaitingForSuggestionsToCache = true;
-            nativeOnOmniboxFocused(
-                    mNativeAutocompleteControllerAndroid, omniboxText, url, focusedFromFakebox);
+            nativeOnOmniboxFocused(mNativeAutocompleteControllerAndroid, omniboxText, url, title,
+                    focusedFromFakebox);
         }
     }
 
@@ -356,7 +357,7 @@
             boolean focusedFromFakebox, long elapsedTimeSinceModified,
             int completedLength, WebContents webContents);
     private native void nativeOnOmniboxFocused(long nativeAutocompleteControllerAndroid,
-            String omniboxText, String currentUrl, boolean focusedFromFakebox);
+            String omniboxText, String currentUrl, String currentTitle, boolean focusedFromFakebox);
     private native void nativeDeleteSuggestion(long nativeAutocompleteControllerAndroid,
             int selectedIndex);
     private native String nativeUpdateMatchDestinationURLWithQueryFormulationTime(
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 cb7f7c42..70216777 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
@@ -1128,7 +1128,7 @@
         if (mNativeInitialized && mUrlHasFocus && mToolbarDataProvider.hasTab()) {
             mAutocomplete.startZeroSuggest(mToolbarDataProvider.getProfile(),
                     mUrlBar.getTextWithAutocomplete(), mToolbarDataProvider.getCurrentUrl(),
-                    mUrlFocusedFromFakebox);
+                    getCurrentTab().getTitle(), mUrlFocusedFromFakebox);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/AndroidVSyncHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/AndroidVSyncHelper.java
index a28b975..3470263b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/AndroidVSyncHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/AndroidVSyncHelper.java
@@ -4,8 +4,11 @@
 
 package org.chromium.chrome.browser.vr_shell;
 
+import android.content.Context;
 import android.view.Choreographer;
+import android.view.WindowManager;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 
@@ -43,5 +46,13 @@
         Choreographer.getInstance().removeFrameCallback(mCallback);
     }
 
+    @CalledByNative
+    private float getRefreshRate() {
+        Context context = ContextUtils.getApplicationContext();
+        WindowManager windowManager =
+                (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        return windowManager.getDefaultDisplay().getRefreshRate();
+    }
+
     private native void nativeOnVSync(long nativeAndroidVSyncHelper, long frameTimeNanos);
 }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 97ce81fe..fccb374c 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -415,6 +415,7 @@
   "java/src/org/chromium/chrome/browser/feedback/FeedbackCollector.java",
   "java/src/org/chromium/chrome/browser/feedback/FeedbackReporter.java",
   "java/src/org/chromium/chrome/browser/feedback/FeedbackSource.java",
+  "java/src/org/chromium/chrome/browser/feedback/FeedbackSourceProvider.java",
   "java/src/org/chromium/chrome/browser/feedback/HistogramFeedbackSource.java",
   "java/src/org/chromium/chrome/browser/feedback/IMEFeedbackSource.java",
   "java/src/org/chromium/chrome/browser/feedback/LowEndDeviceFeedbackSource.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
index 880f24d..f3dbf6c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
@@ -211,7 +211,9 @@
         mDownloadServiceManager.updateDownloadStatus(
                 mContext, DownloadStatus.PAUSE, FAKE_DOWNLOAD_1, mNotification);
         assertFalse(mDownloadServiceManager.mIsServiceBound);
-        assertFalse(mDownloadServiceManager.mIsNotificationKilled);
+
+        assertEquals(mDownloadServiceManager.isPreLollipop(),
+                mDownloadServiceManager.mIsNotificationKilled);
         assertTrue(mDownloadServiceManager.mIsNotificationDetached);
 
         // Service restarts and then is cancelled, so notification is killed.
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index d29bf1f..d6802fe2 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1140,10 +1140,20 @@
       </if>
 
       <!-- Framebust / Blocked Redirection intervention message -->
+      <message name="IDS_REDIRECT_BLOCKED_MESSAGE" desc="The message stating that a redirect (noun) was blocked on this page. This will be followed on a separate line with the address the user was being redirected to.">
+         Chromium stopped this site from taking you to
+      </message>
       <if expr="is_android">
-        <!-- TODO(https://crbug.com/754754) only implemented in Android for now. -->
-        <message name="IDS_REDIRECT_BLOCKED_MESSAGE" desc="The message stating that a redirect (noun) was blocked on this page. This will be followed on a separate line with the address the user was being redirected to.">
-           Chromium stopped this site from taking you to
+        <message name="IDS_REDIRECT_BLOCKED_SHORT_MESSAGE" desc="The short message stating that a redirect (noun) was blocked on this page.">
+           Redirect blocked.
+        </message>
+      </if>
+      <if expr="not is_android">
+        <message name="IDS_REDIRECT_BLOCKED_TITLE" desc="The short message stating that a redirect (noun) was blocked on this page. Same as IDS_REDIRECT_BLOCKED_SHORT_MESSAGE but without the period.">
+           Redirect blocked
+        </message>
+        <message name="IDS_REDIRECT_BLOCKED_TOOLTIP" desc="Tooltip text that appears when the user hovers over the &quot;Redirect blocked&quot; icon in the address bar. It means that a redirect (noun) was blocked by Chrome on this page.">
+           Redirect blocked on this page.
         </message>
       </if>
 
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 5e18bdd..ee79ac8 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1157,10 +1157,20 @@
       </if>
 
       <!-- Framebust / Blocked Redirection intervention message -->
+      <message name="IDS_REDIRECT_BLOCKED_MESSAGE" desc="The message stating that a redirect (noun) was blocked on this page. This will be followed on a separate line with the address the user was being redirected to.">
+         Chrome stopped this site from taking you to
+      </message>
       <if expr="is_android">
-        <!-- TODO(https://crbug.com/754754) only implemented in Android for now. -->
-        <message name="IDS_REDIRECT_BLOCKED_MESSAGE" desc="The message stating that a redirect (noun) was blocked on this page. This will be followed on a separate line with the address the user was being redirected to.">
-           Chrome stopped this site from taking you to
+        <message name="IDS_REDIRECT_BLOCKED_SHORT_MESSAGE" desc="The short message stating that a redirect (noun) was blocked on this page.">
+           Redirect blocked.
+        </message>
+      </if>
+      <if expr="not is_android">
+        <message name="IDS_REDIRECT_BLOCKED_TITLE" desc="The short message stating that a redirect (noun) was blocked on this page. Same as IDS_REDIRECT_BLOCKED_SHORT_MESSAGE but without the period.">
+           Redirect blocked
+        </message>
+        <message name="IDS_REDIRECT_BLOCKED_TOOLTIP" desc="Tooltip text that appears when the user hovers over the &quot;Redirect blocked&quot; icon in the address bar. It means that a redirect (noun) was blocked by Chrome on this page.">
+           Redirect blocked on this page.
         </message>
       </if>
 
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 31d3041..676ef6e 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -16,10 +16,11 @@
     "add.icon",
     "apps.icon",
     "blocked_badge.icon",
-    "browser_tools_animated.1x.icon",
-    "browser_tools_animated.icon",
+    "blocked_redirect.icon",
     "browser_tools.1x.icon",
     "browser_tools.icon",
+    "browser_tools_animated.1x.icon",
+    "browser_tools_animated.icon",
     "browser_tools_error.icon",
     "browser_tools_update.icon",
     "caret_down.1x.icon",
diff --git a/chrome/app/vector_icons/blocked_redirect.icon b/chrome/app/vector_icons/blocked_redirect.icon
new file mode 100644
index 0000000..2961e701
--- /dev/null
+++ b/chrome/app/vector_icons/blocked_redirect.icon
@@ -0,0 +1,31 @@
+// 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.
+
+MOVE_TO, 32, 12.02f,
+LINE_TO, 16, 12,
+R_CUBIC_TO, -2.2f, 0, -4, 1.8f, -4, 4,
+R_V_LINE_TO, 6,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, -6,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 16,
+R_V_LINE_TO, -6,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, 6,
+R_CUBIC_TO, 0, 2.2f, 1.8f, 4, 4, 4,
+R_H_LINE_TO, 16,
+R_CUBIC_TO, 2.2f, 0, 4, -1.8f, 4, -4,
+V_LINE_TO, 16,
+R_CUBIC_TO, 0, -2.2f, -1.8f, -3.98f, -4, -3.98f,
+CLOSE,
+MOVE_TO, 24, 24,
+R_LINE_TO, -6, -6,
+R_V_LINE_TO, 4,
+R_H_LINE_TO, -6,
+R_V_LINE_TO, 4,
+R_H_LINE_TO, 6,
+R_V_LINE_TO, 4,
+CLOSE,
+END
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5cd3211..1f798d4 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -779,6 +779,8 @@
     "net/profile_network_context_service.h",
     "net/profile_network_context_service_factory.cc",
     "net/profile_network_context_service_factory.h",
+    "net/proxy_config_monitor.cc",
+    "net/proxy_config_monitor.h",
     "net/proxy_service_factory.cc",
     "net/proxy_service_factory.h",
     "net/quota_policy_channel_id_store.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7b4d9f0..4ab76d4 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3137,8 +3137,8 @@
 
     {"omnibox-display-title-for-current-url",
      flag_descriptions::kOmniboxDisplayTitleForCurrentUrlName,
-     flag_descriptions::kOmniboxDisplayTitleForCurrentUrlDescription,
-     kOsDesktop, FEATURE_VALUE_TYPE(omnibox::kDisplayTitleForCurrentUrl)},
+     flag_descriptions::kOmniboxDisplayTitleForCurrentUrlDescription, kOsAll,
+     FEATURE_VALUE_TYPE(omnibox::kDisplayTitleForCurrentUrl)},
 
     {"force-color-profile", flag_descriptions::kForceColorProfileName,
      flag_descriptions::kForceColorProfileDescription, kOsAll,
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
index 3ad1b302..7fa03c5 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.cc
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -180,6 +180,7 @@
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& j_omnibox_text,
     const JavaParamRef<jstring>& j_current_url,
+    const JavaParamRef<jstring>& j_current_title,
     jboolean focused_from_fakebox) {
   if (!autocomplete_controller_)
     return;
@@ -190,6 +191,7 @@
     return;
 
   base::string16 url = ConvertJavaStringToUTF16(env, j_current_url);
+  base::string16 current_title = ConvertJavaStringToUTF16(env, j_current_title);
   const GURL current_url = GURL(url);
   base::string16 omnibox_text = ConvertJavaStringToUTF16(env, j_omnibox_text);
 
@@ -204,6 +206,7 @@
                              ClassifyPage(current_url, focused_from_fakebox),
                              ChromeAutocompleteSchemeClassifier(profile_));
   input_.set_current_url(current_url);
+  input_.set_current_title(current_title);
   input_.set_from_omnibox_focus(true);
   autocomplete_controller_->Start(input_);
 }
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.h b/chrome/browser/android/omnibox/autocomplete_controller_android.h
index a879f1f..e604b837 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.h
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.h
@@ -53,6 +53,7 @@
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& j_omnibox_text,
       const base::android::JavaParamRef<jstring>& j_current_url,
+      const base::android::JavaParamRef<jstring>& j_current_title,
       jboolean focused_from_fakebox);
   void Stop(JNIEnv* env,
             const base::android::JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/android/vr_shell/android_vsync_helper.cc b/chrome/browser/android/vr_shell/android_vsync_helper.cc
index 277af265..0587d3d 100644
--- a/chrome/browser/android/vr_shell/android_vsync_helper.cc
+++ b/chrome/browser/android/vr_shell/android_vsync_helper.cc
@@ -6,6 +6,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/callback_helpers.h"
+#include "base/logging.h"
 #include "jni/AndroidVSyncHelper_jni.h"
 
 using base::android::AttachCurrentThread;
@@ -17,6 +18,9 @@
   JNIEnv* env = AttachCurrentThread();
   j_object_.Reset(
       Java_AndroidVSyncHelper_create(env, reinterpret_cast<jlong>(this)));
+  float refresh_rate = Java_AndroidVSyncHelper_getRefreshRate(env, j_object_);
+  display_vsync_interval_ = base::TimeDelta::FromSecondsD(1.0 / refresh_rate);
+  DVLOG(1) << "display_vsync_interval_=" << display_vsync_interval_;
 }
 
 AndroidVSyncHelper::~AndroidVSyncHelper() {
diff --git a/chrome/browser/android/vr_shell/android_vsync_helper.h b/chrome/browser/android/vr_shell/android_vsync_helper.h
index 4c04df9..a12246b 100644
--- a/chrome/browser/android/vr_shell/android_vsync_helper.h
+++ b/chrome/browser/android/vr_shell/android_vsync_helper.h
@@ -24,11 +24,18 @@
 
   void RequestVSync(const base::Callback<void(base::TimeTicks)>& callback);
   void CancelVSyncRequest();
+
+  // The last interval will be a multiple of the actual refresh interval, use
+  // with care.
   base::TimeDelta LastVSyncInterval() { return last_interval_; }
 
+  // Nominal display VSync interval from Java Display.getRefreshRate()
+  base::TimeDelta DisplayVSyncInterval() { return display_vsync_interval_; }
+
  private:
   base::TimeTicks last_vsync_;
   base::TimeDelta last_interval_;
+  base::TimeDelta display_vsync_interval_;
   base::Callback<void(base::TimeTicks)> callback_;
 
   base::android::ScopedJavaGlobalRef<jobject> j_object_;
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index e1c73fc..86d5647 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/vr/model/camera_model.h"
 #include "chrome/browser/vr/model/model.h"
 #include "chrome/browser/vr/pose_util.h"
+#include "chrome/browser/vr/sliding_average.h"
 #include "chrome/browser/vr/ui.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
 #include "chrome/browser/vr/ui_scene.h"
@@ -227,8 +228,9 @@
       binding_(this),
       browser_(browser_interface),
       fps_meter_(new vr::FPSMeter()),
-      webvr_js_time_(new vr::SlidingAverage(kWebVRSlidingAverageSize)),
-      webvr_render_time_(new vr::SlidingAverage(kWebVRSlidingAverageSize)),
+      webvr_js_time_(new vr::SlidingTimeDeltaAverage(kWebVRSlidingAverageSize)),
+      webvr_render_time_(
+          new vr::SlidingTimeDeltaAverage(kWebVRSlidingAverageSize)),
       weak_ptr_factory_(this) {
   GvrInit(gvr_api);
 }
@@ -1160,12 +1162,8 @@
         webvr_time_pose_[frame_index % kPoseRingBufferSize];
     base::TimeTicks js_submit_time =
         webvr_time_js_submit_[frame_index % kPoseRingBufferSize];
-    int64_t pose_to_js_submit_us =
-        (js_submit_time - pose_time).InMicroseconds();
-    webvr_js_time_->AddSample(pose_to_js_submit_us);
-    int64_t js_submit_to_gvr_submit_us =
-        (now - js_submit_time).InMicroseconds();
-    webvr_render_time_->AddSample(js_submit_to_gvr_submit_us);
+    webvr_js_time_->AddSample(js_submit_time - pose_time);
+    webvr_render_time_->AddSample(now - js_submit_time);
   }
 
   // After saving the timestamp, fps will be available via GetFPS().
@@ -1364,22 +1362,22 @@
   }
 }
 
-int64_t VrShellGl::GetPredictedFrameTimeNanos() {
-  int64_t frame_time_micros =
-      vsync_helper_.LastVSyncInterval().InMicroseconds();
+base::TimeDelta VrShellGl::GetPredictedFrameTime() {
+  base::TimeDelta frame_interval = vsync_helper_.DisplayVSyncInterval();
   // If we aim to submit at vsync, that frame will start scanning out
   // one vsync later. Add a half frame to split the difference between
   // left and right eye.
-  int64_t js_micros = webvr_js_time_->GetAverageOrDefault(frame_time_micros);
-  int64_t render_micros =
-      webvr_render_time_->GetAverageOrDefault(frame_time_micros);
-  int64_t overhead_micros = frame_time_micros * 3 / 2;
-  int64_t expected_frame_micros = js_micros + render_micros + overhead_micros;
+  base::TimeDelta js_time = webvr_js_time_->GetAverageOrDefault(frame_interval);
+  base::TimeDelta render_time =
+      webvr_render_time_->GetAverageOrDefault(frame_interval);
+  base::TimeDelta overhead_time = frame_interval * 3 / 2;
+  base::TimeDelta expected_frame_time = js_time + render_time + overhead_time;
   TRACE_COUNTER2("gpu", "WebVR frame time (ms)", "javascript",
-                 js_micros / 1000.0, "rendering", render_micros / 1000.0);
+                 js_time.InMilliseconds(), "rendering",
+                 render_time.InMilliseconds());
   TRACE_COUNTER1("gpu", "WebVR pose prediction (ms)",
-                 expected_frame_micros / 1000.0);
-  return expected_frame_micros * 1000;
+                 expected_frame_time.InMilliseconds());
+  return expected_frame_time;
 }
 
 void VrShellGl::SendVSync(base::TimeTicks time, GetVSyncCallback callback) {
@@ -1387,7 +1385,7 @@
 
   TRACE_EVENT1("input", "VrShellGl::SendVSync", "frame", frame_index);
 
-  int64_t prediction_nanos = GetPredictedFrameTimeNanos();
+  int64_t prediction_nanos = GetPredictedFrameTime().InMicroseconds() * 1000;
 
   gfx::Transform head_mat;
   device::mojom::VRPosePtr pose =
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 827bde0..8896d79 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -51,7 +51,7 @@
 namespace vr {
 class BrowserUiInterface;
 class FPSMeter;
-class SlidingAverage;
+class SlidingTimeDeltaAverage;
 class Ui;
 }  // namespace vr
 
@@ -155,7 +155,7 @@
   void OnWebVrTimeoutImminent();
   void OnWebVrFrameTimedOut();
 
-  int64_t GetPredictedFrameTimeNanos();
+  base::TimeDelta GetPredictedFrameTime();
 
   void OnVSync(base::TimeTicks frame_time);
 
@@ -255,8 +255,8 @@
 
   std::unique_ptr<vr::FPSMeter> fps_meter_;
 
-  std::unique_ptr<vr::SlidingAverage> webvr_js_time_;
-  std::unique_ptr<vr::SlidingAverage> webvr_render_time_;
+  std::unique_ptr<vr::SlidingTimeDeltaAverage> webvr_js_time_;
+  std::unique_ptr<vr::SlidingTimeDeltaAverage> webvr_render_time_;
 
   gfx::Point3F pointer_start_;
 
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index 33386a9e..c188c6ef 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -314,7 +314,6 @@
     : weak_java_obj_(env, obj),
       personal_data_manager_(PersonalDataManagerFactory::GetForProfile(
           ProfileManager::GetActiveUserProfile())),
-      address_normalizer_(AddressNormalizerFactory::GetInstance()),
       subkey_requester_(base::MakeUnique<ChromeMetadataSource>(
                             I18N_ADDRESS_VALIDATION_DATA_URL,
                             g_browser_process->system_request_context()),
@@ -695,8 +694,8 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& unused_obj,
     const base::android::JavaParamRef<jstring>& jregion_code) {
-  address_normalizer_->LoadRulesForRegion(
-      ConvertJavaStringToUTF8(env, jregion_code));
+  AddressNormalizer* normalizer = AddressNormalizerFactory::GetInstance();
+  normalizer->LoadRulesForRegion(ConvertJavaStringToUTF8(env, jregion_code));
 }
 
 void PersonalDataManagerAndroid::LoadRulesForSubKeys(
@@ -717,7 +716,8 @@
   PopulateNativeProfileFromJava(jprofile, env, &profile);
 
   // Start the normalization.
-  address_normalizer_->NormalizeAddressAsync(
+  AddressNormalizer* normalizer = AddressNormalizerFactory::GetInstance();
+  normalizer->NormalizeAddressAsync(
       profile, jtimeout_seconds,
       base::BindOnce(&OnAddressNormalized,
                      ScopedJavaGlobalRef<jobject>(jdelegate)));
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.h b/chrome/browser/autofill/android/personal_data_manager_android.h
index 86224af..e13609a 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.h
+++ b/chrome/browser/autofill/android/personal_data_manager_android.h
@@ -14,12 +14,9 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/personal_data_manager_observer.h"
 #include "components/autofill/core/browser/subkey_requester.h"
-#include "third_party/libaddressinput/chromium/chrome_address_validator.h"
 
 namespace autofill {
 
-class AddressNormalizer;
-
 // Android wrapper of the PersonalDataManager which provides access from the
 // Java layer. Note that on Android, there's only a single profile, and
 // therefore a single instance of this wrapper.
@@ -380,9 +377,6 @@
   // Pointer to the PersonalDataManager for the main profile.
   PersonalDataManager* personal_data_manager_;
 
-  // The address validator used to normalize addresses.
-  AddressNormalizer* address_normalizer_;
-
   // Used for subkey request.
   SubKeyRequester subkey_requester_;
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f4e6950..8437b48 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1973,12 +1973,22 @@
   return prefs && prefs->GetBoolean(prefs::kDataSaverEnabled);
 }
 
-std::unique_ptr<net::HttpRequestHeaders>
-ChromeContentBrowserClient::GetAdditionalNavigationRequestHeaders(
-    content::BrowserContext* context,
-    const GURL& url) const {
-  return client_hints::GetAdditionalNavigationRequestClientHintsHeaders(context,
-                                                                        url);
+void ChromeContentBrowserClient::NavigationRequestStarted(
+    int frame_tree_node_id,
+    const GURL& url,
+    std::unique_ptr<net::HttpRequestHeaders>* extra_headers,
+    int* extra_load_flags) {
+  WebContents* web_contents =
+      WebContents::FromFrameTreeNodeId(frame_tree_node_id);
+  *extra_headers =
+      client_hints::GetAdditionalNavigationRequestClientHintsHeaders(
+          web_contents->GetBrowserContext(), url);
+  prerender::PrerenderContents* prerender_contents =
+      prerender::PrerenderContents::FromWebContents(web_contents);
+  if (prerender_contents) {
+    if (prerender_contents->prerender_mode() == prerender::PREFETCH_ONLY)
+      *extra_load_flags = net::LOAD_PREFETCH;
+  }
 }
 
 bool ChromeContentBrowserClient::AllowAppCache(
@@ -3709,6 +3719,22 @@
 #endif
 }
 
+bool ChromeContentBrowserClient::AllowRenderingMhtmlOverHttp(
+    content::NavigationUIData* navigation_ui_data) const {
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
+  // It is OK to load the saved offline copy, in MHTML format.
+  ChromeNavigationUIData* chrome_navigation_ui_data =
+      static_cast<ChromeNavigationUIData*>(navigation_ui_data);
+  if (!chrome_navigation_ui_data)
+    return false;
+  offline_pages::OfflinePageNavigationUIData* offline_page_data =
+      chrome_navigation_ui_data->GetOfflinePageNavigationUIData();
+  return offline_page_data && offline_page_data->is_offline_page();
+#else
+  return false;
+#endif
+}
+
 // Static; handles rewriting Web UI URLs.
 bool ChromeContentBrowserClient::HandleWebUI(
     GURL* url,
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 19b64a9..03606bc 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -160,9 +160,11 @@
   std::string GetAcceptLangs(content::BrowserContext* context) override;
   const gfx::ImageSkia* GetDefaultFavicon() override;
   bool IsDataSaverEnabled(content::BrowserContext* context) override;
-  std::unique_ptr<net::HttpRequestHeaders>
-  GetAdditionalNavigationRequestHeaders(content::BrowserContext* context,
-                                        const GURL& url) const override;
+  void NavigationRequestStarted(
+      int frame_tree_node_id,
+      const GURL& url,
+      std::unique_ptr<net::HttpRequestHeaders>* extra_headers,
+      int* extra_load_flags) override;
   bool AllowAppCache(const GURL& manifest_url,
                      const GURL& first_party,
                      content::ResourceContext* context) override;
@@ -385,6 +387,8 @@
       content::RenderFrameHost* frame_host,
       const GURL& frame_url,
       NonNetworkURLLoaderFactoryMap* factories) override;
+  bool AllowRenderingMhtmlOverHttp(
+      content::NavigationUIData* navigation_ui_data) const override;
 
  protected:
   static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context);
diff --git a/chrome/browser/chromeos/app_mode/kiosk_external_updater.cc b/chrome/browser/chromeos/app_mode/kiosk_external_updater.cc
index 8bd9af9..156f504 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_external_updater.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_external_updater.cc
@@ -105,10 +105,13 @@
     disks::DiskMountManager::GetInstance()->RemoveObserver(this);
 }
 
-void KioskExternalUpdater::OnDiskEvent(
+void KioskExternalUpdater::OnAutoMountableDiskEvent(
     disks::DiskMountManager::DiskEvent event,
-    const disks::DiskMountManager::Disk* disk) {
-}
+    const disks::DiskMountManager::Disk& disk) {}
+
+void KioskExternalUpdater::OnBootDeviceDiskEvent(
+    disks::DiskMountManager::DiskEvent event,
+    const disks::DiskMountManager::Disk& disk) {}
 
 void KioskExternalUpdater::OnDeviceEvent(
     disks::DiskMountManager::DeviceEvent event,
diff --git a/chrome/browser/chromeos/app_mode/kiosk_external_updater.h b/chrome/browser/chromeos/app_mode/kiosk_external_updater.h
index e52a6ec..22c72649 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_external_updater.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_external_updater.h
@@ -57,8 +57,12 @@
   };
 
   // disks::DiskMountManager::Observer overrides.
-  void OnDiskEvent(disks::DiskMountManager::DiskEvent event,
-                   const disks::DiskMountManager::Disk* disk) override;
+  void OnAutoMountableDiskEvent(
+      disks::DiskMountManager::DiskEvent event,
+      const disks::DiskMountManager::Disk& disk) override;
+  void OnBootDeviceDiskEvent(
+      disks::DiskMountManager::DiskEvent event,
+      const disks::DiskMountManager::Disk& disk) override;
   void OnDeviceEvent(disks::DiskMountManager::DeviceEvent event,
                      const std::string& device_path) override;
   void OnMountEvent(
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
index a343963..eb4b2a0 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
@@ -35,6 +35,7 @@
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_session_runner.h"
 #include "components/arc/arc_util.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_arc_session.h"
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
@@ -212,6 +213,7 @@
       ArcServiceManager::Get()->arc_bridge_service();
   ASSERT_TRUE(arc_bridge_service);
   arc_bridge_service->auth()->SetInstance(&auth_instance);
+  WaitForInstanceReady(arc_bridge_service->auth());
 
   base::RunLoop run_loop;
   auth_instance.RequestAccountInfo(run_loop.QuitClosure());
@@ -224,6 +226,8 @@
             auth_instance.account_info()->account_type);
   EXPECT_FALSE(auth_instance.account_info()->enrollment_token);
   EXPECT_FALSE(auth_instance.account_info()->is_managed);
+
+  arc_bridge_service->auth()->SetInstance(nullptr, 0);
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
index 063e216..d767b690 100644
--- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/bluetooth/bluetooth_type_converters.h"
 #include "components/arc/common/bluetooth.mojom.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_bluetooth_instance.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
@@ -90,12 +91,13 @@
         std::make_unique<bluez::FakeBluetoothGattDescriptorClient>());
 
     arc_bridge_service_ = std::make_unique<ArcBridgeService>();
-    fake_bluetooth_instance_ = std::make_unique<FakeBluetoothInstance>();
-    arc_bridge_service_->bluetooth()->SetInstance(
-        fake_bluetooth_instance_.get(), 2);
     // TODO(hidehiko): Use Singleton instance tied to BrowserContext.
     arc_bluetooth_bridge_ = std::make_unique<ArcBluetoothBridge>(
         nullptr, arc_bridge_service_.get());
+    fake_bluetooth_instance_ = std::make_unique<FakeBluetoothInstance>();
+    arc_bridge_service_->bluetooth()->SetInstance(
+        fake_bluetooth_instance_.get(), 2);
+    WaitForInstanceReady(arc_bridge_service_->bluetooth());
 
     device::BluetoothAdapterFactory::GetAdapter(base::Bind(
         &ArcBluetoothBridgeTest::OnAdapterInitialized, base::Unretained(this)));
@@ -103,6 +105,12 @@
     get_adapter_run_loop_.Run();
   }
 
+  void TearDown() override {
+    arc_bridge_service_->bluetooth()->SetInstance(nullptr, 0);
+    arc_bluetooth_bridge_.reset();
+    arc_bridge_service_.reset();
+  }
+
   // Helper methods for multi advertisement tests.
   int32_t ReserveAdvertisementHandle() {
     constexpr int kSentinelHandle = -2;
diff --git a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
index c53a4118..7c0f966e 100644
--- a/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
+++ b/chrome/browser/chromeos/arc/enterprise/arc_cert_store_bridge_browsertest.cc
@@ -31,6 +31,7 @@
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/common/cert_store.mojom.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/policy/policy_constants.h"
 #include "content/public/browser/browser_thread.h"
 #include "crypto/scoped_test_system_nss_key_slot.h"
@@ -134,11 +135,12 @@
     instance_ = std::make_unique<FakeArcCertStoreInstance>();
     ASSERT_TRUE(arc_bridge());
     arc_bridge()->cert_store()->SetInstance(instance_.get());
+    WaitForInstanceReady(arc_bridge()->cert_store());
   }
 
   void TearDownOnMainThread() override {
     ASSERT_TRUE(arc_bridge());
-    arc_bridge()->cert_store()->SetInstance(nullptr);
+    arc_bridge()->cert_store()->SetInstance(nullptr, 0);
     instance_.reset();
 
     // Since ArcServiceLauncher is (re-)set up with profile() in
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
index edaa17b..6969b57 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
@@ -17,6 +17,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_file_system_instance.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -56,10 +57,17 @@
         profile_.get(), &CreateArcFileSystemOperationRunnerForTesting);
     arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
         &fake_file_system_);
+    WaitForInstanceReady(
+        arc_service_manager_->arc_bridge_service()->file_system());
 
     async_file_util_ = std::make_unique<ArcContentFileSystemAsyncFileUtil>();
   }
 
+  void TearDown() override {
+    arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
+        nullptr, 0);
+  }
+
  protected:
   storage::FileSystemURL ExternalFileURLToFileSystemURL(const GURL& url) {
     base::FilePath mount_point_virtual_path =
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader_unittest.cc
index 9d76ff1..940e1f3 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader_unittest.cc
@@ -15,6 +15,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_file_system_instance.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -83,6 +84,13 @@
         profile_.get(), &CreateFileSystemOperationRunnerForTesting);
     arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
         &fake_file_system_);
+    WaitForInstanceReady(
+        arc_service_manager_->arc_bridge_service()->file_system());
+  }
+
+  void TearDown() override {
+    arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
+        nullptr, 0);
   }
 
  private:
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_unittest.cc
index 53a251b..3448826 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/common/file_system.mojom.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_file_system_instance.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -130,9 +131,10 @@
         profile_.get(), &CreateFileSystemOperationRunnerForTesting);
     arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
         &fake_file_system_);
+    WaitForInstanceReady(
+        arc_service_manager_->arc_bridge_service()->file_system());
 
     // Run the message loop until FileSystemInstance::Init() is called.
-    base::RunLoop().RunUntilIdle();
     ASSERT_TRUE(fake_file_system_.InitCalled());
 
     root_ = std::make_unique<ArcDocumentsProviderRoot>(
@@ -142,6 +144,9 @@
 
   void TearDown() override {
     root_.reset();
+    arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
+        nullptr);
+
     // Run all pending tasks before destroying testing profile.
     base::RunLoop().RunUntilIdle();
   }
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge_unittest.cc
index 23a513e..b2050aa 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge_unittest.cc
@@ -19,6 +19,7 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_virtual_file_provider_client.h"
 #include "components/arc/arc_bridge_service.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_file_system_instance.h"
 #include "components/drive/chromeos/fake_file_system.h"
 #include "components/drive/service/fake_drive_service.h"
@@ -56,9 +57,10 @@
     Profile* profile =
         profile_manager_->CreateTestingProfile(kTestingProfileName);
 
-    arc_bridge_service_.file_system()->SetInstance(&fake_file_system_);
     arc_file_system_bridge_ =
         std::make_unique<ArcFileSystemBridge>(profile, &arc_bridge_service_);
+    arc_bridge_service_.file_system()->SetInstance(&fake_file_system_);
+    WaitForInstanceReady(arc_bridge_service_.file_system());
 
     // Create the drive integration service for the profile.
     integration_service_factory_callback_ =
@@ -72,6 +74,7 @@
 
   void TearDown() override {
     integration_service_factory_scope_.reset();
+    arc_bridge_service_.file_system()->SetInstance(nullptr, 0);
     arc_file_system_bridge_.reset();
     profile_manager_.reset();
     chromeos::DBusThreadManager::Shutdown();
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
index c64e0a7..ae0db71 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/common/file_system.mojom.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_file_system_instance.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -48,13 +49,16 @@
         profile_.get(), arc_service_manager_->arc_bridge_service());
     arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
         &file_system_instance_);
+    WaitForInstanceReady(
+        arc_service_manager_->arc_bridge_service()->file_system());
 
     // Run the message loop until FileSystemInstance::Init() is called.
-    base::RunLoop().RunUntilIdle();
     ASSERT_TRUE(file_system_instance_.InitCalled());
   }
 
   void TearDown() override {
+    arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
+        nullptr, 0);
     // Explicitly calls Shutdown() to detach from services.
     if (runner_)
       runner_->Shutdown();
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc
index e13db3c0..7e3d875 100644
--- a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc
@@ -28,6 +28,7 @@
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_util.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_intent_helper_instance.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
@@ -231,17 +232,19 @@
     EXPECT_CALL(provider_, IsInitializationComplete(_))
         .WillRepeatedly(Return(true));
     policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
-    fake_intent_helper_instance_.reset(new FakeIntentHelperInstance());
   }
 
   void SetUpOnMainThread() override {
     SetupNetworkEnvironment();
     RunUntilIdle();
 
+    fake_intent_helper_instance_ = std::make_unique<FakeIntentHelperInstance>();
     ArcServiceManager::Get()
         ->arc_bridge_service()
         ->intent_helper()
         ->SetInstance(fake_intent_helper_instance_.get());
+    WaitForInstanceReady(
+        ArcServiceManager::Get()->arc_bridge_service()->intent_helper());
   }
 
   void TearDownOnMainThread() override {
@@ -249,6 +252,7 @@
         ->arc_bridge_service()
         ->intent_helper()
         ->SetInstance(nullptr);
+    fake_intent_helper_instance_.reset();
   }
 
   void UpdatePolicy(const policy::PolicyMap& policy) {
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_bridge_unittest.cc b/chrome/browser/chromeos/arc/policy/arc_policy_bridge_unittest.cc
index 7aa583a..c1d7fe0 100644
--- a/chrome/browser/chromeos/arc/policy/arc_policy_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/policy/arc_policy_bridge_unittest.cc
@@ -15,6 +15,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_prefs.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_policy_instance.h"
 #include "components/policy/core/common/mock_policy_service.h"
 #include "components/policy/core/common/policy_map.h"
@@ -126,9 +127,6 @@
     EXPECT_CALL(policy_service_, AddObserver(policy::POLICY_DOMAIN_CHROME, _))
         .Times(1);
 
-    policy_instance_ = std::make_unique<FakePolicyInstance>();
-    bridge_service_->policy()->SetInstance(policy_instance_.get());
-
     // Setting up user profile for ReportCompliance() tests.
     chromeos::FakeChromeUserManager* const fake_user_manager =
         new chromeos::FakeChromeUserManager();
@@ -148,6 +146,15 @@
     policy_bridge_ = std::make_unique<ArcPolicyBridge>(
         profile_, bridge_service_.get(), &policy_service_);
     policy_bridge_->OverrideIsManagedForTesting(true);
+
+    policy_instance_ = std::make_unique<FakePolicyInstance>();
+    bridge_service_->policy()->SetInstance(policy_instance_.get());
+    WaitForInstanceReady(bridge_service_->policy());
+  }
+
+  void TearDown() override {
+    bridge_service_->policy()->SetInstance(nullptr, 0);
+    policy_instance_.reset();
   }
 
  protected:
diff --git a/chrome/browser/chromeos/arc/user_session/arc_user_session_service_browsertest.cc b/chrome/browser/chromeos/arc/user_session/arc_user_session_service_browsertest.cc
index 0215672..12c0962 100644
--- a/chrome/browser/chromeos/arc/user_session/arc_user_session_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/user_session/arc_user_session_service_browsertest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "chrome/browser/chromeos/arc/user_session/arc_user_session_service.h"
@@ -9,6 +11,7 @@
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_util.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_intent_helper_instance.h"
 #include "components/session_manager/core/session_manager.h"
 
@@ -49,17 +52,16 @@
     arc::SetArcAvailableCommandLineForTesting(command_line);
   }
 
-  void SetUpInProcessBrowserTestFixture() override {
-    fake_intent_helper_instance_.reset(new FakeIntentHelperInstance());
-  }
-
   void SetUpOnMainThread() override {
     RunUntilIdle();
 
+    fake_intent_helper_instance_ = std::make_unique<FakeIntentHelperInstance>();
     ArcServiceManager::Get()
         ->arc_bridge_service()
         ->intent_helper()
         ->SetInstance(fake_intent_helper_instance_.get());
+    WaitForInstanceReady(
+        ArcServiceManager::Get()->arc_bridge_service()->intent_helper());
   }
 
   void TearDownOnMainThread() override {
@@ -67,6 +69,7 @@
         ->arc_bridge_service()
         ->intent_helper()
         ->SetInstance(nullptr);
+    fake_intent_helper_instance_.reset(nullptr);
   }
 
  protected:
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service_unittest.cc b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service_unittest.cc
index 66d1c9d..55f682e8 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service_unittest.cc
@@ -20,6 +20,7 @@
 #include "chromeos/dbus/fake_cras_audio_client.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_util.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_arc_session.h"
 #include "components/arc/test/fake_voice_interaction_framework_instance.h"
 #include "components/prefs/pref_service.h"
@@ -191,6 +192,7 @@
         std::make_unique<FakeVoiceInteractionFrameworkInstance>();
     arc_bridge_service_->voice_interaction_framework()->SetInstance(
         framework_instance_.get());
+    WaitForInstanceReady(arc_bridge_service_->voice_interaction_framework());
     // Flushing is required for the AttachClient call to get through to the
     // highligther controller.
     FlushHighlighterControllerMojo();
@@ -202,6 +204,7 @@
   }
 
   void TearDown() override {
+    arc_bridge_service_->voice_interaction_framework()->SetInstance(nullptr);
     framework_instance_.reset();
     framework_service_.reset();
     arc_bridge_service_.reset();
@@ -378,6 +381,7 @@
   // The client should become attached again.
   arc_bridge_service()->voice_interaction_framework()->SetInstance(
       framework_instance());
+  WaitForInstanceReady(arc_bridge_service()->voice_interaction_framework());
   FlushHighlighterControllerMojo();
   EXPECT_TRUE(highlighter_controller()->client_attached());
 
diff --git a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service_unittest.cc b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service_unittest.cc
index 648e6c9..ce93bc5 100644
--- a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service_unittest.cc
@@ -24,6 +24,7 @@
 #include "chromeos/cryptohome/system_salt_getter.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_wallpaper_instance.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
@@ -95,13 +96,15 @@
     chromeos::WallpaperManager::Initialize();
 
     // Arc services
-    wallpaper_instance_ = std::make_unique<arc::FakeWallpaperInstance>();
     arc_service_manager_.set_browser_context(&testing_profile_);
-    arc_service_manager_.arc_bridge_service()->wallpaper()->SetInstance(
-        wallpaper_instance_.get());
     service_ =
         arc::ArcWallpaperService::GetForBrowserContext(&testing_profile_);
     ASSERT_TRUE(service_);
+    wallpaper_instance_ = std::make_unique<arc::FakeWallpaperInstance>();
+    arc_service_manager_.arc_bridge_service()->wallpaper()->SetInstance(
+        wallpaper_instance_.get());
+    WaitForInstanceReady(
+        arc_service_manager_.arc_bridge_service()->wallpaper());
 
     // Salt
     std::vector<uint8_t> salt = {0x01, 0x02, 0x03};
@@ -109,6 +112,10 @@
   }
 
   void TearDown() override {
+    arc_service_manager_.arc_bridge_service()->wallpaper()->SetInstance(
+        nullptr);
+    wallpaper_instance_.reset();
+
     chromeos::WallpaperManager::Shutdown();
     TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
     AshTestBase::TearDown();
diff --git a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
index 664bad8..91f0699 100644
--- a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
+++ b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
@@ -153,8 +153,10 @@
 void FakeDiskMountManager::InvokeDiskEventForTest(
     chromeos::disks::DiskMountManager::DiskEvent event,
     const chromeos::disks::DiskMountManager::Disk* disk) {
-  for (auto& observer : observers_)
-    observer.OnDiskEvent(event, disk);
+  for (auto& observer : observers_) {
+    disk->IsAutoMountable() ? observer.OnAutoMountableDiskEvent(event, *disk)
+                            : observer.OnBootDeviceDiskEvent(event, *disk);
+  }
 }
 
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.cc b/chrome/browser/chromeos/file_manager/volume_manager.cc
index b08419d..6f0c1dc 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager.cc
@@ -530,30 +530,30 @@
   DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, *Volume::CreateForDrive(profile_));
 }
 
-void VolumeManager::OnDiskEvent(
+void VolumeManager::OnAutoMountableDiskEvent(
     chromeos::disks::DiskMountManager::DiskEvent event,
-    const chromeos::disks::DiskMountManager::Disk* disk) {
+    const chromeos::disks::DiskMountManager::Disk& disk) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // Disregard hidden devices.
-  if (disk->is_hidden())
+  if (disk.is_hidden())
     return;
 
   switch (event) {
     case chromeos::disks::DiskMountManager::DISK_ADDED:
     case chromeos::disks::DiskMountManager::DISK_CHANGED: {
-      if (disk->device_path().empty()) {
-        DVLOG(1) << "Empty system path for " << disk->device_path();
+      if (disk.device_path().empty()) {
+        DVLOG(1) << "Empty system path for " << disk.device_path();
         return;
       }
 
       bool mounting = false;
-      if (disk->mount_path().empty() && disk->has_media() &&
+      if (disk.mount_path().empty() && disk.has_media() &&
           !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
         // TODO(crbug.com/774890): Remove |mount_label| when the issue gets
         // resolved. Currently we suggest a mount point name, because in case
         // when disk's name contains '#', content will not load in Files App.
-        std::string mount_label = disk->device_label();
+        std::string mount_label = disk.device_label();
         std::replace(mount_label.begin(), mount_label.end(), '#', '_');
 
         // If disk is not mounted yet and it has media and there is no policy
@@ -561,7 +561,7 @@
         // Initiate disk mount operation. MountPath auto-detects the filesystem
         // format if the second argument is empty. The third argument (mount
         // label) is not used in a disk mount operation.
-        disk_mount_manager_->MountPath(disk->device_path(), std::string(),
+        disk_mount_manager_->MountPath(disk.device_path(), std::string(),
                                        mount_label, chromeos::MOUNT_TYPE_DEVICE,
                                        GetExternalStorageAccessMode(profile_));
         mounting = true;
@@ -569,27 +569,30 @@
 
       // Notify to observers.
       for (auto& observer : observers_)
-        observer.OnDiskAdded(*disk, mounting);
+        observer.OnDiskAdded(disk, mounting);
       return;
     }
 
     case chromeos::disks::DiskMountManager::DISK_REMOVED:
       // If the disk is already mounted, unmount it.
-      if (!disk->mount_path().empty()) {
+      if (!disk.mount_path().empty()) {
         disk_mount_manager_->UnmountPath(
-            disk->mount_path(),
-            chromeos::UNMOUNT_OPTIONS_LAZY,
+            disk.mount_path(), chromeos::UNMOUNT_OPTIONS_LAZY,
             chromeos::disks::DiskMountManager::UnmountPathCallback());
       }
 
       // Notify to observers.
       for (auto& observer : observers_)
-        observer.OnDiskRemoved(*disk);
+        observer.OnDiskRemoved(disk);
       return;
   }
   NOTREACHED();
 }
 
+void VolumeManager::OnBootDeviceDiskEvent(
+    chromeos::disks::DiskMountManager::DiskEvent event,
+    const chromeos::disks::DiskMountManager::Disk& disk) {}
+
 void VolumeManager::OnDeviceEvent(
     chromeos::disks::DiskMountManager::DeviceEvent event,
     const std::string& device_path) {
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.h b/chrome/browser/chromeos/file_manager/volume_manager.h
index cb095dc..d99a2435d 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager.h
+++ b/chrome/browser/chromeos/file_manager/volume_manager.h
@@ -287,9 +287,12 @@
   void OnFileSystemBeingUnmounted() override;
 
   // chromeos::disks::DiskMountManager::Observer overrides.
-  void OnDiskEvent(
+  void OnAutoMountableDiskEvent(
       chromeos::disks::DiskMountManager::DiskEvent event,
-      const chromeos::disks::DiskMountManager::Disk* disk) override;
+      const chromeos::disks::DiskMountManager::Disk& disk) override;
+  void OnBootDeviceDiskEvent(
+      chromeos::disks::DiskMountManager::DiskEvent event,
+      const chromeos::disks::DiskMountManager::Disk& disk) override;
   void OnDeviceEvent(chromeos::disks::DiskMountManager::DeviceEvent event,
                      const std::string& device_path) override;
   void OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,
diff --git a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
index 0d7f15f..464ee23e 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
@@ -256,8 +256,31 @@
   ASSERT_EQ(0U, observer.events().size());
   volume_manager()->RemoveObserver(&observer);
 }
+TEST_F(VolumeManagerTest, OnBootDeviceDiskEvent) {
+  LoggingObserver observer;
+  volume_manager()->AddObserver(&observer);
 
-TEST_F(VolumeManagerTest, OnDiskEvent_Hidden) {
+  const chromeos::disks::DiskMountManager::Disk disk(
+      "device1", "", false, "", "", "", "", "", "", "", "", "", "",
+      chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, true, false, false,
+      "", "");
+
+  volume_manager()->OnBootDeviceDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_ADDED, disk);
+  EXPECT_EQ(0U, observer.events().size());
+
+  volume_manager()->OnBootDeviceDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_REMOVED, disk);
+  EXPECT_EQ(0U, observer.events().size());
+
+  volume_manager()->OnBootDeviceDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_CHANGED, disk);
+  EXPECT_EQ(0U, observer.events().size());
+
+  volume_manager()->RemoveObserver(&observer);
+}
+
+TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_Hidden) {
   LoggingObserver observer;
   volume_manager()->AddObserver(&observer);
 
@@ -267,22 +290,22 @@
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
       kIsHidden, "", "");
 
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_ADDED, &kDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_ADDED, kDisk);
   EXPECT_EQ(0U, observer.events().size());
 
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_REMOVED, &kDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_REMOVED, kDisk);
   EXPECT_EQ(0U, observer.events().size());
 
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_CHANGED, &kDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_CHANGED, kDisk);
   EXPECT_EQ(0U, observer.events().size());
 
   volume_manager()->RemoveObserver(&observer);
 }
 
-TEST_F(VolumeManagerTest, OnDiskEvent_Added) {
+TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_Added) {
   // Enable external storage.
   profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled, false);
 
@@ -294,8 +317,8 @@
       "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
       false, "", "");
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_ADDED, &kEmptyDevicePathDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_ADDED, kEmptyDevicePathDisk);
   EXPECT_EQ(0U, observer.events().size());
 
   const bool kHasMedia = true;
@@ -303,8 +326,8 @@
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, kHasMedia, false, false,
       false, "", "");
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_ADDED, &kMediaDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_ADDED, kMediaDisk);
   ASSERT_EQ(1U, observer.events().size());
   const LoggingObserver::Event& event = observer.events()[0];
   EXPECT_EQ(LoggingObserver::Event::DISK_ADDED, event.type);
@@ -322,7 +345,7 @@
   volume_manager()->RemoveObserver(&observer);
 }
 
-TEST_F(VolumeManagerTest, OnDiskEvent_AddedNonMounting) {
+TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_AddedNonMounting) {
   // Enable external storage.
   profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled, false);
 
@@ -336,8 +359,8 @@
         "device1", "mounted", false, "", "", "", "", "", "", "", "", "", "",
         chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, kHasMedia, false, false,
         false, "", "");
-    volume_manager()->OnDiskEvent(
-        chromeos::disks::DiskMountManager::DISK_ADDED, &kMountedMediaDisk);
+    volume_manager()->OnAutoMountableDiskEvent(
+        chromeos::disks::DiskMountManager::DISK_ADDED, kMountedMediaDisk);
     ASSERT_EQ(1U, observer.events().size());
     const LoggingObserver::Event& event = observer.events()[0];
     EXPECT_EQ(LoggingObserver::Event::DISK_ADDED, event.type);
@@ -359,8 +382,8 @@
         "device1", "", false, "", "", "", "", "", "", "", "", "", "",
         chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, kWithoutMedia, false,
         false, false, "", "");
-    volume_manager()->OnDiskEvent(
-        chromeos::disks::DiskMountManager::DISK_ADDED, &kNoMediaDisk);
+    volume_manager()->OnAutoMountableDiskEvent(
+        chromeos::disks::DiskMountManager::DISK_ADDED, kNoMediaDisk);
     ASSERT_EQ(1U, observer.events().size());
     const LoggingObserver::Event& event = observer.events()[0];
     EXPECT_EQ(LoggingObserver::Event::DISK_ADDED, event.type);
@@ -384,8 +407,8 @@
         "device1", "", false, "", "", "", "", "", "", "", "", "", "",
         chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, kHasMedia, false, false,
         false, "", "");
-    volume_manager()->OnDiskEvent(
-        chromeos::disks::DiskMountManager::DISK_ADDED, &kMediaDisk);
+    volume_manager()->OnAutoMountableDiskEvent(
+        chromeos::disks::DiskMountManager::DISK_ADDED, kMediaDisk);
     ASSERT_EQ(1U, observer.events().size());
     const LoggingObserver::Event& event = observer.events()[0];
     EXPECT_EQ(LoggingObserver::Event::DISK_ADDED, event.type);
@@ -398,7 +421,7 @@
   }
 }
 
-TEST_F(VolumeManagerTest, OnDiskEvent_Removed) {
+TEST_F(VolumeManagerTest, OnDiskAutoMountableEvent_Removed) {
   LoggingObserver observer;
   volume_manager()->AddObserver(&observer);
 
@@ -406,8 +429,8 @@
       "device1", "mount_path", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
       false, "", "");
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_REMOVED, &kMountedDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_REMOVED, kMountedDisk);
 
   ASSERT_EQ(1U, observer.events().size());
   const LoggingObserver::Event& event = observer.events()[0];
@@ -423,7 +446,7 @@
   volume_manager()->RemoveObserver(&observer);
 }
 
-TEST_F(VolumeManagerTest, OnDiskEvent_RemovedNotMounted) {
+TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_RemovedNotMounted) {
   LoggingObserver observer;
   volume_manager()->AddObserver(&observer);
 
@@ -431,8 +454,8 @@
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
       false, "", "");
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_REMOVED, &kNotMountedDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_REMOVED, kNotMountedDisk);
 
   ASSERT_EQ(1U, observer.events().size());
   const LoggingObserver::Event& event = observer.events()[0];
@@ -444,7 +467,7 @@
   volume_manager()->RemoveObserver(&observer);
 }
 
-TEST_F(VolumeManagerTest, OnDiskEvent_Changed) {
+TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_Changed) {
   // Changed event should cause mounting (if possible).
   LoggingObserver observer;
   volume_manager()->AddObserver(&observer);
@@ -453,8 +476,8 @@
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false, false,
       "", "");
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_CHANGED, &kDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_CHANGED, kDisk);
 
   EXPECT_EQ(1U, observer.events().size());
   EXPECT_EQ(1U, disk_mount_manager_->mount_requests().size());
@@ -466,7 +489,7 @@
   volume_manager()->RemoveObserver(&observer);
 }
 
-TEST_F(VolumeManagerTest, OnDiskEvent_ChangedInReadonly) {
+TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_ChangedInReadonly) {
   profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageReadOnly, true);
 
   // Changed event should cause mounting (if possible).
@@ -477,8 +500,8 @@
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false, false,
       "", "");
-  volume_manager()->OnDiskEvent(chromeos::disks::DiskMountManager::DISK_CHANGED,
-                                &kDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_CHANGED, kDisk);
 
   EXPECT_EQ(1U, observer.events().size());
   EXPECT_EQ(1U, disk_mount_manager_->mount_requests().size());
@@ -783,10 +806,10 @@
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false, false,
       "", "");
-  volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_ADDED, &kMediaDisk);
-  secondary.volume_manager()->OnDiskEvent(
-      chromeos::disks::DiskMountManager::DISK_ADDED, &kMediaDisk);
+  volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_ADDED, kMediaDisk);
+  secondary.volume_manager()->OnAutoMountableDiskEvent(
+      chromeos::disks::DiskMountManager::DISK_ADDED, kMediaDisk);
 
   // The profile with external storage enabled should have mounted the volume.
   bool has_volume_mounted = false;
diff --git a/chrome/browser/chromeos/fileapi/recent_arc_media_source_unittest.cc b/chrome/browser/chromeos/fileapi/recent_arc_media_source_unittest.cc
index fe176b3..e365f975 100644
--- a/chrome/browser/chromeos/fileapi/recent_arc_media_source_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/recent_arc_media_source_unittest.cc
@@ -19,6 +19,7 @@
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/common/file_system.mojom.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_file_system_instance.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -78,6 +79,11 @@
     source_ = base::MakeUnique<RecentArcMediaSource>(profile_.get());
   }
 
+  void TearDown() override {
+    arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
+        nullptr);
+  }
+
  protected:
   void AddDocumentsToFakeFileSystemInstance() {
     auto root_doc =
@@ -104,6 +110,8 @@
   void EnableFakeFileSystemInstance() {
     arc_service_manager_->arc_bridge_service()->file_system()->SetInstance(
         &fake_file_system_);
+    arc::WaitForInstanceReady(
+        arc_service_manager_->arc_bridge_service()->file_system());
   }
 
   std::vector<RecentFile> GetRecentFiles() {
diff --git a/chrome/browser/chromeos/note_taking_helper_unittest.cc b/chrome/browser/chromeos/note_taking_helper_unittest.cc
index f54550e4..a29d442 100644
--- a/chrome/browser/chromeos/note_taking_helper_unittest.cc
+++ b/chrome/browser/chromeos/note_taking_helper_unittest.cc
@@ -37,6 +37,7 @@
 #include "components/arc/arc_util.h"
 #include "components/arc/common/intent_helper.mojom.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_intent_helper_instance.h"
 #include "components/crx_file/id_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
@@ -218,6 +219,10 @@
 
   void TearDown() override {
     if (initialized_) {
+      arc::ArcServiceManager::Get()
+          ->arc_bridge_service()
+          ->intent_helper()
+          ->SetInstance(nullptr);
       NoteTakingHelper::Shutdown();
       intent_helper_bridge_.reset();
       arc_test_.TearDown();
@@ -279,6 +284,8 @@
         ->arc_bridge_service()
         ->intent_helper()
         ->SetInstance(&intent_helper_);
+    WaitForInstanceReady(
+        arc::ArcServiceManager::Get()->arc_bridge_service()->intent_helper());
 
     if (flags & ENABLE_PALETTE) {
       base::CommandLine::ForCurrentProcess()->AppendSwitch(
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc
index b42ac80..1aa115d 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings.cc
@@ -54,6 +54,10 @@
 #include "storage/common/fileapi/file_system_types.h"
 #include "url/origin.h"
 
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
+#endif
+
 using content::BrowserThread;
 using content::NavigationController;
 using content::NavigationEntry;
@@ -732,6 +736,21 @@
   OnContentBlocked(CONTENT_SETTINGS_TYPE_SOUND);
 }
 
+void TabSpecificContentSettings::OnFramebustBlocked(const GURL& blocked_url) {
+#if !defined(OS_ANDROID)
+  FramebustBlockTabHelper* framebust_block_tab_helper =
+      FramebustBlockTabHelper::FromWebContents(web_contents());
+  if (!framebust_block_tab_helper)
+    return;
+
+  framebust_block_tab_helper->AddBlockedUrl(blocked_url);
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
+      content::Source<WebContents>(web_contents()),
+      content::NotificationService::NoDetails());
+#endif  // !defined(OS_ANDROID)
+}
+
 void TabSpecificContentSettings::SetPepperBrokerAllowed(bool allowed) {
   if (allowed) {
     OnContentAllowed(CONTENT_SETTINGS_TYPE_PPAPI_BROKER);
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.h b/chrome/browser/content_settings/tab_specific_content_settings.h
index def6bc82..247a3d3 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.h
+++ b/chrome/browser/content_settings/tab_specific_content_settings.h
@@ -203,6 +203,9 @@
   // Called when audio has been blocked on the page.
   void OnAudioBlocked();
 
+  // Updates the blocked framebust icon in the location bar.
+  void OnFramebustBlocked(const GURL& blocked_url);
+
   // Returns whether a particular kind of content has been blocked for this
   // page.
   bool IsContentBlocked(ContentSettingsType content_type) const;
diff --git a/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc b/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
index 23278aec..b62b194 100644
--- a/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
+++ b/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
@@ -227,10 +227,9 @@
   // The |manager| should have |download| in its list of downloads.
   void DeleteDownloadAndWaitForFlush(DownloadItem* download,
                                      DownloadManager* manager) {
-    scoped_refptr<content::DownloadTestFlushObserver> flush_observer(
-        new content::DownloadTestFlushObserver(manager));
+    content::DownloadTestFlushObserver flush_observer(manager);
     download->Remove();
-    flush_observer->WaitForFlush();
+    flush_observer.WaitForFlush();
   }
 
  protected:
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index f052ba9..d144111c 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -55,7 +55,6 @@
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "components/proxy_config/pref_proxy_config_tracker.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_thread.h"
@@ -334,11 +333,6 @@
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
   allow_gssapi_library_load_ = connector->IsActiveDirectoryManaged();
 #endif
-  pref_proxy_config_tracker_.reset(
-      ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
-          local_state));
-  system_proxy_config_service_ = ProxyServiceFactory::CreateProxyConfigService(
-      pref_proxy_config_tracker_.get());
   ChromeNetworkDelegate::InitializePrefsOnUIThread(
       &system_enable_referrers_,
       nullptr,
@@ -394,7 +388,6 @@
   // be multiply constructed.
   BrowserThread::SetIOThreadDelegate(nullptr);
 
-  pref_proxy_config_tracker_->DetachFromPrefService();
   DCHECK(!globals_);
 
   // Destroy the old distributor to check that the observers list it holds is
@@ -588,7 +581,6 @@
   // Shutdown the HistogramWatcher on the IO thread.
   net::NetworkChangeNotifier::ShutdownHistogramWatcher();
 
-  system_proxy_config_service_.reset();
   delete globals_;
   globals_ = nullptr;
 
@@ -730,9 +722,8 @@
   return pac_https_url_stripping_enabled_.GetValue();
 }
 
-void IOThread::SetUpProxyConfigService(
-    content::URLRequestContextBuilderMojo* builder,
-    std::unique_ptr<net::ProxyConfigService> proxy_config_service) const {
+void IOThread::SetUpProxyService(
+    content::URLRequestContextBuilderMojo* builder) const {
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
 
@@ -757,7 +748,6 @@
       PacHttpsUrlStrippingEnabled()
           ? net::ProxyService::SanitizeUrlPolicy::SAFE
           : net::ProxyService::SanitizeUrlPolicy::UNSAFE);
-  builder->set_proxy_config_service(std::move(proxy_config_service));
 }
 
 content::mojom::NetworkService* IOThread::GetNetworkServiceOnUIThread() {
@@ -826,8 +816,7 @@
 
   builder->set_ct_verifier(std::move(ct_verifier));
 
-  SetUpProxyConfigService(builder.get(),
-                          std::move(system_proxy_config_service_));
+  SetUpProxyService(builder.get());
 
   globals_->network_service = content::NetworkService::Create(
       std::move(network_service_request_), net_log_);
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index 5a4c264d..33e4ce8e 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -35,7 +35,6 @@
 #include "net/base/network_change_notifier.h"
 #include "net/nqe/network_quality_estimator.h"
 
-class PrefProxyConfigTracker;
 class PrefService;
 class PrefRegistrySimple;
 class SystemNetworkContextManager;
@@ -76,7 +75,6 @@
 class HttpAuthHandlerFactory;
 class HttpAuthPreferences;
 class NetworkQualityEstimator;
-class ProxyConfigService;
 class RTTAndThroughputEstimatesObserver;
 class SSLConfigService;
 class URLRequestContext;
@@ -212,12 +210,9 @@
   bool WpadQuickCheckEnabled() const;
   bool PacHttpsUrlStrippingEnabled() const;
 
-  // Configures |builder|'s ProxyService to use the specified
-  // |proxy_config_service| and sets a number of proxy-related options based on
-  // prefs, policies, and the command line.
-  void SetUpProxyConfigService(
-      content::URLRequestContextBuilderMojo* builder,
-      std::unique_ptr<net::ProxyConfigService> proxy_config_service) const;
+  // Configures |builder|'s ProxyService based on prefs, policies, and the
+  // command line.
+  void SetUpProxyService(content::URLRequestContextBuilderMojo* builder) const;
 
   // Gets a pointer to the NetworkService. Can only be called on the UI thread.
   // When out-of-process NetworkService is enabled, this is a reference to the
@@ -326,12 +321,6 @@
   std::unique_ptr<ssl_config::SSLConfigServiceManager>
       ssl_config_service_manager_;
 
-  // These member variables are initialized by a task posted to the IO thread,
-  // which gets posted by calling certain member functions of IOThread.
-  std::unique_ptr<net::ProxyConfigService> system_proxy_config_service_;
-
-  std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
-
   scoped_refptr<net::URLRequestContextGetter>
       system_url_request_context_getter_;
 
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index 1e8fc59..1d0aafb 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -992,20 +992,3 @@
   return ProfileIOData::FromResourceContext(resource_context)->
       CreateClientCertStore();
 }
-
-bool ChromeResourceDispatcherHostDelegate::AllowRenderingMhtmlOverHttp(
-    net::URLRequest* request) const {
-#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
-  // It is OK to load the saved offline copy, in MHTML format.
-  const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
-  ChromeNavigationUIData* navigation_data =
-      static_cast<ChromeNavigationUIData*>(info->GetNavigationUIData());
-  if (!navigation_data)
-    return false;
-  offline_pages::OfflinePageNavigationUIData* offline_page_data =
-      navigation_data->GetOfflinePageNavigationUIData();
-  return offline_page_data && offline_page_data->is_offline_page();
-#else
-  return false;
-#endif
-}
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
index c5e300a..b1026c5 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
@@ -93,7 +93,6 @@
       net::URLRequest* request) const override;
   std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
       content::ResourceContext* resource_context) override;
-  bool AllowRenderingMhtmlOverHttp(net::URLRequest* request) const override;
 
   // Called on the UI thread. Allows switching out the
   // ExternalProtocolHandler::Delegate for testing code.
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index c94d9e9..3ef47eea 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <string>
 
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -11,6 +12,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/profile_network_context_service_factory.h"
@@ -19,6 +21,9 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/network_session_configurator/common/network_switches.h"
+#include "components/prefs/pref_service.h"
+#include "components/proxy_config/proxy_config_dictionary.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
@@ -34,6 +39,7 @@
 #include "content/public/test/test_url_loader_client.h"
 #include "mojo/common/data_pipe_utils.h"
 #include "net/base/filename_util.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -134,6 +140,49 @@
     return StorageType::kNone;
   }
 
+  // Sets the proxy preference on a PrefService based on the NetworkContextType,
+  // and waits for it to be applied.
+  void SetProxyPref(const net::HostPortPair& host_port_pair) {
+    // Get the correct PrefService.
+    PrefService* pref_service = nullptr;
+    switch (GetParam().network_context_type) {
+      case NetworkContextType::kSystem:
+        pref_service = g_browser_process->local_state();
+        break;
+      case NetworkContextType::kProfile:
+        pref_service = browser()->profile()->GetPrefs();
+        break;
+      case NetworkContextType::kIncognitoProfile:
+        // Incognito uses the non-incognito prefs.
+        pref_service =
+            browser()->profile()->GetOffTheRecordProfile()->GetPrefs();
+        break;
+    }
+
+    pref_service->Set(proxy_config::prefs::kProxy,
+                      *ProxyConfigDictionary::CreateFixedServers(
+                          host_port_pair.ToString(), std::string()));
+
+    // Wait for the new ProxyConfig to be passed over the pipe. Needed because
+    // Mojo doesn't guarantee ordering of events on different Mojo pipes, and
+    // requests are sent on a separate pipe from ProxyConfigs.
+    switch (GetParam().network_context_type) {
+      case NetworkContextType::kSystem:
+        g_browser_process->system_network_context_manager()
+            ->FlushProxyConfigMonitorForTesting();
+        break;
+      case NetworkContextType::kProfile:
+        ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
+            ->FlushProxyConfigMonitorForTesting();
+        break;
+      case NetworkContextType::kIncognitoProfile:
+        ProfileNetworkContextServiceFactory::GetForContext(
+            browser()->profile()->GetOffTheRecordProfile())
+            ->FlushProxyConfigMonitorForTesting();
+        break;
+    }
+  }
+
  private:
   content::mojom::NetworkContext* network_context_ = nullptr;
   content::mojom::URLLoaderFactory* loader_factory_ = nullptr;
@@ -330,6 +379,28 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, ProxyConfig) {
+  SetProxyPref(embedded_test_server()->host_port_pair());
+
+  std::unique_ptr<content::ResourceRequest> request =
+      std::make_unique<content::ResourceRequest>();
+  // This URL should be directed to the test server because of the proxy.
+  request->url = GURL("http://jabberwocky.com:1872/echo");
+
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create(std::move(request),
+                                       TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      loader_factory(), simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
+
+  EXPECT_EQ(net::OK, simple_loader->NetError());
+  ASSERT_TRUE(simple_loader_helper.response_body());
+  EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
+}
+
 class NetworkContextConfigurationFixedPortBrowserTest
     : public NetworkContextConfigurationBrowserTest {
  public:
@@ -342,8 +413,6 @@
         base::StringPrintf("%u", embedded_test_server()->port()));
     LOG(WARNING) << base::StringPrintf("%u", embedded_test_server()->port());
   }
-
- private:
 };
 
 // Test that the command line switch makes it to the network service and is
@@ -370,6 +439,42 @@
   EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
 }
 
+class NetworkContextConfigurationProxyOnStartBrowserTest
+    : public NetworkContextConfigurationBrowserTest {
+ public:
+  NetworkContextConfigurationProxyOnStartBrowserTest() {}
+  ~NetworkContextConfigurationProxyOnStartBrowserTest() override {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(
+        switches::kProxyServer,
+        embedded_test_server()->host_port_pair().ToString());
+  }
+};
+
+// Test that when there's a proxy configuration at startup, the initial requests
+// use that configuration.
+IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationProxyOnStartBrowserTest,
+                       TestInitialProxyConfig) {
+  std::unique_ptr<content::ResourceRequest> request =
+      std::make_unique<content::ResourceRequest>();
+  // This URL should be directed to the test server because of the proxy.
+  request->url = GURL("http://jabberwocky.com:1872/echo");
+
+  content::SimpleURLLoaderTestHelper simple_loader_helper;
+  std::unique_ptr<content::SimpleURLLoader> simple_loader =
+      content::SimpleURLLoader::Create(std::move(request),
+                                       TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      loader_factory(), simple_loader_helper.GetCallback());
+  simple_loader_helper.WaitForCallback();
+
+  EXPECT_EQ(net::OK, simple_loader->NetError());
+  ASSERT_TRUE(simple_loader_helper.response_body());
+  EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
+}
+
 // Instiates tests with a prefix indicating which NetworkContext is being
 // tested, and a suffix of "/0" if the network service is disabled and "/1" if
 // it's enabled.
@@ -398,5 +503,7 @@
 INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(NetworkContextConfigurationBrowserTest);
 INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
     NetworkContextConfigurationFixedPortBrowserTest);
+INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
+    NetworkContextConfigurationProxyOnStartBrowserTest);
 
 }  // namespace
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 0a339aed..3d3d057 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -28,57 +28,8 @@
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
 #include "net/net_features.h"
 
-namespace {
-
-content::mojom::NetworkContextParamsPtr CreateMainNetworkContextParams(
-    Profile* profile) {
-  // TODO(mmenke): Set up parameters here.
-  content::mojom::NetworkContextParamsPtr network_context_params =
-      CreateDefaultNetworkContextParams();
-
-  network_context_params->context_name = std::string("main");
-
-  // Always enable the HTTP cache.
-  network_context_params->http_cache_enabled = true;
-
-  // Configure on-disk storage for non-OTR profiles. OTR profiles just use
-  // default behavior (in memory storage, default sizes).
-  PrefService* prefs = profile->GetPrefs();
-  if (!profile->IsOffTheRecord()) {
-    // Configure the HTTP cache path and size.
-    base::FilePath base_cache_path;
-    chrome::GetUserCacheDirectory(profile->GetPath(), &base_cache_path);
-    base::FilePath disk_cache_dir = prefs->GetFilePath(prefs::kDiskCacheDir);
-    if (!disk_cache_dir.empty())
-      base_cache_path = disk_cache_dir.Append(base_cache_path.BaseName());
-    network_context_params->http_cache_path =
-        base_cache_path.Append(chrome::kCacheDirname);
-    network_context_params->http_cache_max_size =
-        prefs->GetInteger(prefs::kDiskCacheSize);
-
-    // Currently this just contains HttpServerProperties, but that will likely
-    // change.
-    network_context_params->http_server_properties_path =
-        profile->GetPath().Append(chrome::kNetworkPersistentStateFilename);
-  }
-
-  // NOTE(mmenke): Keep these protocol handlers and
-  // ProfileIOData::SetUpJobFactoryDefaultsForBuilder in sync with
-  // ProfileIOData::IsHandledProtocol().
-  // TODO(mmenke): Find a better way of handling tracking supported schemes.
-  network_context_params->enable_data_url_support = true;
-  network_context_params->enable_file_url_support = true;
-#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
-  network_context_params->enable_ftp_url_support = true;
-#endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
-
-  return network_context_params;
-}
-
-}  // namespace
-
 ProfileNetworkContextService::ProfileNetworkContextService(Profile* profile)
-    : profile_(profile) {
+    : profile_(profile), proxy_config_monitor_(profile) {
   quic_allowed_.Init(
       prefs::kQuicAllowed, profile->GetPrefs(),
       base::Bind(&ProfileNetworkContextService::DisableQuicIfNotAllowed,
@@ -105,7 +56,7 @@
 
   content::mojom::NetworkContextPtr network_context;
   content::GetNetworkService()->CreateNetworkContext(
-      MakeRequest(&network_context), CreateMainNetworkContextParams(profile_));
+      MakeRequest(&network_context), CreateMainNetworkContextParams());
   return network_context;
 }
 
@@ -124,7 +75,7 @@
   }
 
   if (!base::FeatureList::IsEnabled(features::kNetworkService)) {
-    *network_context_params = CreateMainNetworkContextParams(profile_);
+    *network_context_params = CreateMainNetworkContextParams();
     return;
   }
 
@@ -149,3 +100,54 @@
 
   g_browser_process->system_network_context_manager()->DisableQuic();
 }
+
+void ProfileNetworkContextService::FlushProxyConfigMonitorForTesting() {
+  proxy_config_monitor_.FlushForTesting();
+}
+
+content::mojom::NetworkContextParamsPtr
+ProfileNetworkContextService::CreateMainNetworkContextParams() {
+  // TODO(mmenke): Set up parameters here.
+  content::mojom::NetworkContextParamsPtr network_context_params =
+      CreateDefaultNetworkContextParams();
+
+  network_context_params->context_name = std::string("main");
+
+  // Always enable the HTTP cache.
+  network_context_params->http_cache_enabled = true;
+
+  // Configure on-disk storage for non-OTR profiles. OTR profiles just use
+  // default behavior (in memory storage, default sizes).
+  PrefService* prefs = profile_->GetPrefs();
+  if (!profile_->IsOffTheRecord()) {
+    // Configure the HTTP cache path and size.
+    base::FilePath base_cache_path;
+    chrome::GetUserCacheDirectory(profile_->GetPath(), &base_cache_path);
+    base::FilePath disk_cache_dir = prefs->GetFilePath(prefs::kDiskCacheDir);
+    if (!disk_cache_dir.empty())
+      base_cache_path = disk_cache_dir.Append(base_cache_path.BaseName());
+    network_context_params->http_cache_path =
+        base_cache_path.Append(chrome::kCacheDirname);
+    network_context_params->http_cache_max_size =
+        prefs->GetInteger(prefs::kDiskCacheSize);
+
+    // Currently this just contains HttpServerProperties, but that will likely
+    // change.
+    network_context_params->http_server_properties_path =
+        profile_->GetPath().Append(chrome::kNetworkPersistentStateFilename);
+  }
+
+  // NOTE(mmenke): Keep these protocol handlers and
+  // ProfileIOData::SetUpJobFactoryDefaultsForBuilder in sync with
+  // ProfileIOData::IsHandledProtocol().
+  // TODO(mmenke): Find a better way of handling tracking supported schemes.
+  network_context_params->enable_data_url_support = true;
+  network_context_params->enable_file_url_support = true;
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+  network_context_params->enable_ftp_url_support = true;
+#endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
+
+  proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get());
+
+  return network_context_params;
+}
diff --git a/chrome/browser/net/profile_network_context_service.h b/chrome/browser/net/profile_network_context_service.h
index cdc9f63..8de0d08 100644
--- a/chrome/browser/net/profile_network_context_service.h
+++ b/chrome/browser/net/profile_network_context_service.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_NET_PROFILE_NETWORK_CONTEXT_SERVICE_H_
 
 #include "base/macros.h"
+#include "chrome/browser/net/proxy_config_monitor.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_member.h"
 #include "content/public/common/network_service.mojom.h"
@@ -53,12 +54,21 @@
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
+  // Flushes all pending proxy configuration changes.
+  void FlushProxyConfigMonitorForTesting();
+
  private:
   // Checks |quic_allowed_|, and disables QUIC if needed.
   void DisableQuicIfNotAllowed();
 
+  // Creates parameters for the NetworkContext. May only be called once, since
+  // it initializes some class members.
+  content::mojom::NetworkContextParamsPtr CreateMainNetworkContextParams();
+
   Profile* const profile_;
 
+  ProxyConfigMonitor proxy_config_monitor_;
+
   // This is a NetworkContext interface that uses ProfileIOData's
   // NetworkContext. If the network service is disabled, ownership is passed to
   // StoragePartition when CreateMainNetworkContext is called.  Otherwise,
diff --git a/chrome/browser/net/proxy_config_monitor.cc b/chrome/browser/net/proxy_config_monitor.cc
new file mode 100644
index 0000000..620ff27
--- /dev/null
+++ b/chrome/browser/net/proxy_config_monitor.cc
@@ -0,0 +1,109 @@
+// 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 "chrome/browser/net/proxy_config_monitor.h"
+
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/net/proxy_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#endif  // defined(OS_CHROMEOS)
+
+ProxyConfigMonitor::ProxyConfigMonitor(Profile* profile) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(profile);
+
+// If this is the ChromeOS sign-in profile, just create the tracker from global
+// state.
+#if defined(OS_CHROMEOS)
+  if (chromeos::ProfileHelper::IsSigninProfile(profile)) {
+    pref_proxy_config_tracker_.reset(
+        ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
+            g_browser_process->local_state()));
+  }
+#endif  // defined(OS_CHROMEOS)
+
+  if (!pref_proxy_config_tracker_) {
+    pref_proxy_config_tracker_.reset(
+        ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile(
+            profile->GetPrefs(), g_browser_process->local_state()));
+  }
+
+  proxy_config_service_ = ProxyServiceFactory::CreateProxyConfigService(
+      pref_proxy_config_tracker_.get());
+
+  proxy_config_service_->AddObserver(this);
+}
+
+ProxyConfigMonitor::ProxyConfigMonitor() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  pref_proxy_config_tracker_.reset(
+      ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
+          g_browser_process->local_state()));
+
+  proxy_config_service_ = ProxyServiceFactory::CreateProxyConfigService(
+      pref_proxy_config_tracker_.get());
+
+  proxy_config_service_->AddObserver(this);
+}
+
+ProxyConfigMonitor::~ProxyConfigMonitor() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  proxy_config_service_->RemoveObserver(this);
+  pref_proxy_config_tracker_->DetachFromPrefService();
+}
+
+void ProxyConfigMonitor::AddToNetworkContextParams(
+    content::mojom::NetworkContextParams* network_context_params) {
+  content::mojom::ProxyConfigClientPtr proxy_config_client;
+  network_context_params->proxy_config_client_request =
+      mojo::MakeRequest(&proxy_config_client);
+  proxy_config_client_set_.AddPtr(std::move(proxy_config_client));
+
+  binding_set_.AddBinding(
+      this,
+      mojo::MakeRequest(&network_context_params->proxy_config_poller_client));
+  net::ProxyConfig proxy_config;
+  net::ProxyConfigService::ConfigAvailability availability =
+      proxy_config_service_->GetLatestProxyConfig(&proxy_config);
+  if (availability != net::ProxyConfigService::CONFIG_PENDING)
+    network_context_params->initial_proxy_config = proxy_config;
+}
+
+void ProxyConfigMonitor::FlushForTesting() {
+  proxy_config_client_set_.FlushForTesting();
+}
+
+void ProxyConfigMonitor::OnProxyConfigChanged(
+    const net::ProxyConfig& config,
+    net::ProxyConfigService::ConfigAvailability availability) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  proxy_config_client_set_.ForAllPtrs(
+      [config,
+       availability](content::mojom::ProxyConfigClient* proxy_config_client) {
+        switch (availability) {
+          case net::ProxyConfigService::CONFIG_VALID:
+            proxy_config_client->OnProxyConfigUpdated(config);
+            break;
+          case net::ProxyConfigService::CONFIG_UNSET:
+            proxy_config_client->OnProxyConfigUpdated(
+                net::ProxyConfig::CreateDirect());
+            break;
+          case net::ProxyConfigService::CONFIG_PENDING:
+            NOTREACHED();
+            break;
+        }
+      });
+}
+
+void ProxyConfigMonitor::OnLazyProxyConfigPoll() {
+  proxy_config_service_->OnLazyPoll();
+}
diff --git a/chrome/browser/net/proxy_config_monitor.h b/chrome/browser/net/proxy_config_monitor.h
new file mode 100644
index 0000000..c58bfcf
--- /dev/null
+++ b/chrome/browser/net/proxy_config_monitor.h
@@ -0,0 +1,75 @@
+// 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 CHROME_BROWSER_NET_PROXY_CONFIG_MONITOR_H_
+#define CHROME_BROWSER_NET_PROXY_CONFIG_MONITOR_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "content/public/common/network_service.mojom.h"
+#include "content/public/common/proxy_config.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "net/proxy/proxy_config_service.h"
+
+namespace net {
+class ProxyConfig;
+}
+
+class Profile;
+class PrefProxyConfigTracker;
+
+// Tracks the ProxyConfig to use, and passes any updates to a NetworkContext's
+// ProxyConfigClient.
+class ProxyConfigMonitor : public net::ProxyConfigService::Observer,
+                           public content::mojom::ProxyConfigPollerClient {
+ public:
+  // Creates a ProxyConfigMonitor that gets proxy settings from |profile| and
+  // watches for changes. The created ProxyConfigMonitor must be destroyed
+  // before |profile|.
+  explicit ProxyConfigMonitor(Profile* profile);
+
+  // Creates a ProxyConfigMonitor that gets proxy settings from the
+  // BrowserProcess's |local_state_|, for use with NetworkContexts not
+  // assocaited with a profile. Must be destroyed before the BrowserProcess's
+  // |local_state_|.
+  ProxyConfigMonitor();
+
+  ~ProxyConfigMonitor() override;
+
+  // Populates proxy-related fields of |network_context_params|. Updated
+  // ProxyConfigs will be sent to a NetworkContext created with those params
+  // whenever the configuration changes. Can be called more than once to inform
+  // multiple NetworkContexts of proxy changes.
+  void AddToNetworkContextParams(
+      content::mojom::NetworkContextParams* network_context_params);
+
+  // Flushes all pending data on the pipe, blocking the current thread until
+  // they're received, to allow tests to wait until all pending proxy
+  // configuration changes have been applied.
+  void FlushForTesting();
+
+ private:
+  // net::ProxyConfigService::Observer implementation:
+  void OnProxyConfigChanged(
+      const net::ProxyConfig& config,
+      net::ProxyConfigService::ConfigAvailability availability) override;
+
+  // content::mojom::ProxyConfigPollerClient implementation:
+  void OnLazyProxyConfigPoll() override;
+
+  std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
+  // Monitors global and Profile prefs related to proxy configuration.
+  std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
+
+  mojo::BindingSet<content::mojom::ProxyConfigPollerClient> binding_set_;
+
+  mojo::InterfacePtrSet<content::mojom::ProxyConfigClient>
+      proxy_config_client_set_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProxyConfigMonitor);
+};
+
+#endif  // CHROME_BROWSER_NET_PROXY_CONFIG_MONITOR_H_
diff --git a/chrome/browser/net/proxy_service_factory.cc b/chrome/browser/net/proxy_service_factory.cc
index 17c0e53..5ce2abc 100644
--- a/chrome/browser/net/proxy_service_factory.cc
+++ b/chrome/browser/net/proxy_service_factory.cc
@@ -35,7 +35,7 @@
   // include command line and configuration policy).
 
   base_service = net::ProxyService::CreateSystemProxyConfigService(
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
+      BrowserThread::GetTaskRunnerForThread(BrowserThread::UI));
 #endif  // !defined(OS_CHROMEOS)
 
   return tracker->CreateTrackingProxyConfigService(std::move(base_service));
@@ -47,12 +47,10 @@
     PrefService* profile_prefs,
     PrefService* local_state_prefs) {
 #if defined(OS_CHROMEOS)
-  return new chromeos::ProxyConfigServiceImpl(
-      profile_prefs, local_state_prefs,
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
+  return new chromeos::ProxyConfigServiceImpl(profile_prefs, local_state_prefs,
+                                              nullptr);
 #else
-  return new PrefProxyConfigTrackerImpl(
-      profile_prefs, BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
+  return new PrefProxyConfigTrackerImpl(profile_prefs, nullptr);
 #endif  // defined(OS_CHROMEOS)
 }
 
@@ -61,12 +59,9 @@
 ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
     PrefService* local_state_prefs) {
 #if defined(OS_CHROMEOS)
-  return new chromeos::ProxyConfigServiceImpl(
-      nullptr, local_state_prefs,
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
+  return new chromeos::ProxyConfigServiceImpl(nullptr, local_state_prefs,
+                                              nullptr);
 #else
-  return new PrefProxyConfigTrackerImpl(
-      local_state_prefs,
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
+  return new PrefProxyConfigTrackerImpl(local_state_prefs, nullptr);
 #endif  // defined(OS_CHROMEOS)
 }
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 590efe8..12398d9 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -28,25 +28,6 @@
 
 namespace {
 
-content::mojom::NetworkContextParamsPtr CreateNetworkContextParams() {
-  // TODO(mmenke): Set up parameters here (in memory cookie store, etc).
-  content::mojom::NetworkContextParamsPtr network_context_params =
-      CreateDefaultNetworkContextParams();
-
-  network_context_params->context_name = std::string("system");
-
-  network_context_params->http_cache_enabled = false;
-
-  // These are needed for PAC scripts that use file or data URLs (Or FTP URLs?).
-  network_context_params->enable_data_url_support = true;
-  network_context_params->enable_file_url_support = true;
-#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
-  network_context_params->enable_ftp_url_support = true;
-#endif
-
-  return network_context_params;
-}
-
 // Called on IOThread to disable QUIC for HttpNetworkSessions not using the
 // network service. Note that re-enabling QUIC dynamically is not supported for
 // simpliciy and requires a browser restart.
@@ -101,7 +82,13 @@
     content::mojom::NetworkContextParamsPtr* network_context_params,
     bool* is_quic_allowed) {
   *network_context_request = mojo::MakeRequest(&io_thread_network_context_);
-  *network_context_params = CreateNetworkContextParams();
+  if (!base::FeatureList::IsEnabled(features::kNetworkService)) {
+    *network_context_params = CreateNetworkContextParams();
+  } else {
+    // Just use defaults if the network service is enabled, since
+    // CreateNetworkContextParams() can only be called once.
+    *network_context_params = CreateDefaultNetworkContextParams();
+  }
   *is_quic_allowed = is_quic_allowed_;
 }
 
@@ -140,3 +127,29 @@
       base::BindOnce(&DisableQuicOnIOThread, io_thread,
                      base::Unretained(safe_browsing_service)));
 }
+
+void SystemNetworkContextManager::FlushProxyConfigMonitorForTesting() {
+  proxy_config_monitor_.FlushForTesting();
+}
+
+content::mojom::NetworkContextParamsPtr
+SystemNetworkContextManager::CreateNetworkContextParams() {
+  // TODO(mmenke): Set up parameters here (in memory cookie store, etc).
+  content::mojom::NetworkContextParamsPtr network_context_params =
+      CreateDefaultNetworkContextParams();
+
+  network_context_params->context_name = std::string("system");
+
+  network_context_params->http_cache_enabled = false;
+
+  // These are needed for PAC scripts that use file or data URLs (Or FTP URLs?).
+  network_context_params->enable_data_url_support = true;
+  network_context_params->enable_file_url_support = true;
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+  network_context_params->enable_ftp_url_support = true;
+#endif
+
+  proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get());
+
+  return network_context_params;
+}
diff --git a/chrome/browser/net/system_network_context_manager.h b/chrome/browser/net/system_network_context_manager.h
index 529031c2b..515e605 100644
--- a/chrome/browser/net/system_network_context_manager.h
+++ b/chrome/browser/net/system_network_context_manager.h
@@ -8,8 +8,11 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "chrome/browser/net/proxy_config_monitor.h"
 #include "content/public/common/network_service.mojom.h"
 
+class ProxyConfigMonitor;
+
 // Responsible for creating and managing access to the system NetworkContext.
 // Lives on the UI thread. The NetworkContext this owns is intended for requests
 // not associated with a profile. It stores no data on disk, and has no HTTP
@@ -62,7 +65,16 @@
   // NetworkService, and for those using the network service (if enabled).
   void DisableQuic();
 
+  // Flushes all pending proxy configuration changes.
+  void FlushProxyConfigMonitorForTesting();
+
  private:
+  // Creates parameters for the NetworkContext. May only be called once, since
+  // it initializes some class members.
+  content::mojom::NetworkContextParamsPtr CreateNetworkContextParams();
+
+  ProxyConfigMonitor proxy_config_monitor_;
+
   // NetworkContext using the network service, if the network service is
   // enabled. nullptr, otherwise.
   content::mojom::NetworkContextPtr network_service_network_context_;
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index 704abbb..23501fa6 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -29,12 +29,14 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/appcache_service.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/url_loader_interceptor.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
 #include "net/dns/mock_host_resolver.h"
@@ -161,6 +163,15 @@
                            expected_final_status);
   }
 
+  content::StoragePartition* GetStoragePartition() {
+    return browser()
+        ->tab_strip_model()
+        ->GetActiveWebContents()
+        ->GetMainFrame()
+        ->GetProcess()
+        ->GetStoragePartition();
+  }
+
  private:
   // Schedule a task to retrieve AppCacheInfo from |appcache_service|. This sets
   // |found_manifest| if an appcache exists for |manifest_url|. |callback| will
@@ -221,29 +232,38 @@
 
 // Check that the LOAD_PREFETCH flag is set.
 IN_PROC_BROWSER_TEST_P(NoStatePrefetchBrowserTest, PrefetchLoadFlag) {
-  // TODO(jam): use URLLoaderFactory for subresource.
-  // This will need a separate code path for network service:
-  //   -add way for the
-  //        storage_partition->GetNetworkContext()->CreateURLLoaderFactory
-  //        to return test defined factoory
-  //   -also add a URLLoader unittest to verify that if ResourceRequest has that
-  //        flag it is passed to the net::URLRequest
-  RequestCounter main_counter;
-  RequestCounter script_counter;
-  auto verify_prefetch_only = base::Bind([](net::URLRequest* request) {
-    EXPECT_TRUE(request->load_flags() & net::LOAD_PREFETCH);
-  });
+  GURL prefetch_page = src_server()->GetURL(kPrefetchPage);
+  GURL prefetch_script = src_server()->GetURL(kPrefetchScript);
 
-  prerender::test_utils::InterceptRequestAndCount(
-      src_server()->GetURL(kPrefetchPage), &main_counter, verify_prefetch_only);
-  prerender::test_utils::InterceptRequestAndCount(
-      src_server()->GetURL(kPrefetchScript), &script_counter,
-      verify_prefetch_only);
+  bool use_interceptor_for_frame_requests =
+      base::FeatureList::IsEnabled(features::kNetworkService);
+  if (!use_interceptor_for_frame_requests) {
+    // Until http://crbug.com/747130 is fixed, navigation requests won't go
+    // through URLLoader.
+    prerender::test_utils::InterceptRequest(
+        prefetch_page,
+        base::Bind([](net::URLRequest* request) {
+          EXPECT_TRUE(request->load_flags() & net::LOAD_PREFETCH);
+        }));
+  }
+
+  content::URLLoaderInterceptor interceptor(
+      base::Bind(
+          [](const GURL& prefetch_page, const GURL& prefetch_script,
+             content::URLLoaderInterceptor::RequestParams* params) {
+            if (params->url_request.url == prefetch_page ||
+                params->url_request.url == prefetch_script) {
+              EXPECT_TRUE(params->url_request.load_flags & net::LOAD_PREFETCH);
+            }
+            return false;
+          },
+          prefetch_page, prefetch_script),
+      GetStoragePartition(), use_interceptor_for_frame_requests, true);
 
   std::unique_ptr<TestPrerender> test_prerender =
       PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
-  main_counter.WaitForCount(1);
-  script_counter.WaitForCount(1);
+  WaitForRequestCount(prefetch_page, 1);
+  WaitForRequestCount(prefetch_script, 1);
 
   // Verify that the page load did not happen.
   test_prerender->WaitForLoads(0);
@@ -384,18 +404,33 @@
       "/server-redirect/?" + net::EscapeQueryParamValue(kPrefetchPage, false);
   GURL redirect_url = src_server()->GetURL(redirect_path);
   GURL page_url = src_server()->GetURL(kPrefetchPage);
-  RequestCounter redirect_counter;
   auto verify_prefetch_only = base::Bind([](net::URLRequest* request) {
     EXPECT_TRUE(request->load_flags() & net::LOAD_PREFETCH);
   });
-  prerender::test_utils::InterceptRequestAndCount(
-      redirect_url, &redirect_counter, verify_prefetch_only);
-  RequestCounter page_counter;
-  prerender::test_utils::InterceptRequestAndCount(page_url, &page_counter,
-                                                  verify_prefetch_only);
+
+  bool use_interceptor = false;
+  if (base::FeatureList::IsEnabled(features::kNetworkService)) {
+    use_interceptor = true;
+  } else {
+    // Until http://crbug.com/747130 is fixed, navigation requests won't go
+    // through URLLoader.
+    prerender::test_utils::InterceptRequest(page_url, verify_prefetch_only);
+  }
+
+  content::URLLoaderInterceptor interceptor(
+      base::Bind(
+          [](const GURL& page_url,
+             content::URLLoaderInterceptor::RequestParams* params) {
+            if (params->url_request.url == page_url)
+              EXPECT_TRUE(params->url_request.load_flags & net::LOAD_PREFETCH);
+            return false;
+          },
+          redirect_url),
+      GetStoragePartition(), use_interceptor, false);
+
   PrefetchFromFile(redirect_path, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
-  redirect_counter.WaitForCount(1);
-  page_counter.WaitForCount(1);
+  WaitForRequestCount(redirect_url, 1);
+  WaitForRequestCount(page_url, 1);
 }
 
 // Checks that a subresource 301 redirect is followed.
diff --git a/chrome/browser/prerender/prerender_resource_throttle.cc b/chrome/browser/prerender/prerender_resource_throttle.cc
index c90cad0..e12cdcf9 100644
--- a/chrome/browser/prerender/prerender_resource_throttle.cc
+++ b/chrome/browser/prerender/prerender_resource_throttle.cc
@@ -85,7 +85,6 @@
 
 PrerenderResourceThrottle::PrerenderResourceThrottle(net::URLRequest* request)
     : request_(request),
-      load_flags_(net::LOAD_NORMAL),
       prerender_throttle_info_(new PrerenderThrottleInfo()) {
 // Priorities for prerendering requests are lowered, to avoid competing with
 // other page loads, except on Android where this is less likely to be a
@@ -170,7 +169,6 @@
 
 void PrerenderResourceThrottle::ResumeHandler() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  request_->SetLoadFlags(request_->load_flags() | load_flags_);
   Resume();
 }
 
@@ -201,10 +199,6 @@
     prerender_throttle_info->Set(prerender_contents->prerender_mode(),
                                  prerender_contents->origin(),
                                  prerender_contents->prerender_manager());
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::BindOnce(&PrerenderResourceThrottle::SetPrerenderMode, throttle,
-                       prerender_contents->prerender_mode()));
 
     // Abort any prerenders that spawn requests that use unsupported HTTP
     // methods or schemes.
@@ -329,9 +323,4 @@
   return PrerenderContents::FromWebContents(web_contents_getter.Run());
 }
 
-void PrerenderResourceThrottle::SetPrerenderMode(PrerenderMode mode) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  load_flags_ = (mode == PREFETCH_ONLY) ? net::LOAD_PREFETCH : net::LOAD_NORMAL;
-}
-
 }  // namespace prerender
diff --git a/chrome/browser/prerender/prerender_resource_throttle.h b/chrome/browser/prerender/prerender_resource_throttle.h
index 0302ec5..a117d9bd 100644
--- a/chrome/browser/prerender/prerender_resource_throttle.h
+++ b/chrome/browser/prerender/prerender_resource_throttle.h
@@ -89,11 +89,7 @@
       const content::ResourceRequestInfo::WebContentsGetter&
           web_contents_getter);
 
-  // Sets the prerender mode. Must be called before |ResumeHandler()|.
-  void SetPrerenderMode(PrerenderMode mode);
-
   net::URLRequest* request_;
-  int load_flags_;  // Load flags to be OR'ed with the existing request flags.
 
   // The throttle changes most request priorities to IDLE during prerendering.
   // The priority is reset back to the original priority when prerendering is
diff --git a/chrome/browser/prerender/prerender_test_utils.cc b/chrome/browser/prerender/prerender_test_utils.cc
index 300db3d0..2964947 100644
--- a/chrome/browser/prerender/prerender_test_utils.cc
+++ b/chrome/browser/prerender/prerender_test_utils.cc
@@ -107,16 +107,19 @@
 class CountingInterceptorWithCallback : public net::URLRequestInterceptor {
  public:
   // Inserts the interceptor object to intercept requests to |url|.  Can be
-  // called on any thread. Assumes that |counter| lives on the UI thread.  The
-  // |callback_io| will be called on IO thread with the net::URLrequest
-  // provided.
+  // called on any thread. Assumes that |counter| (if non-null) lives on the UI
+  // thread.  The |callback_io| will be called on IO thread with the
+  // net::URLrequest provided.
   static void Initialize(const GURL& url,
                          RequestCounter* counter,
                          base::Callback<void(net::URLRequest*)> callback_io) {
+    base::WeakPtr<RequestCounter> weakptr;
+    if (counter)
+      weakptr = counter->AsWeakPtr();
     content::BrowserThread::PostTask(
         content::BrowserThread::IO, FROM_HERE,
         base::BindOnce(&CountingInterceptorWithCallback::CreateAndAddOnIO, url,
-                       counter->AsWeakPtr(), callback_io));
+                       weakptr, callback_io));
   }
 
   // net::URLRequestInterceptor:
@@ -853,6 +856,11 @@
       url, base::MakeUnique<CountingInterceptor>(file, counter));
 }
 
+void InterceptRequest(const GURL& url,
+                      base::Callback<void(net::URLRequest*)> callback_io) {
+  CountingInterceptorWithCallback::Initialize(url, nullptr, callback_io);
+}
+
 void InterceptRequestAndCount(
     const GURL& url,
     RequestCounter* counter,
diff --git a/chrome/browser/prerender/prerender_test_utils.h b/chrome/browser/prerender/prerender_test_utils.h
index 00d9fe3d..8d93a2d 100644
--- a/chrome/browser/prerender/prerender_test_utils.h
+++ b/chrome/browser/prerender/prerender_test_utils.h
@@ -434,6 +434,12 @@
     const base::WeakPtr<RequestCounter>& counter);
 
 // When the |url| hits the net::URLRequestFilter (on the IO thread), executes
+// the |callback_io| providing the request to it. Does not modify the behavior
+// or the request job.
+void InterceptRequest(const GURL& url,
+                      base::Callback<void(net::URLRequest*)> callback_io);
+
+// When the |url| hits the net::URLRequestFilter (on the IO thread), executes
 // the |callback_io| providing the request to it, also pings the |counter| on UI
 // thread. Does not modify the behavior or the request job.
 void InterceptRequestAndCount(
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
index 50c467c9..5fa6170 100644
--- a/chrome/browser/printing/print_job.cc
+++ b/chrome/browser/printing/print_job.cc
@@ -373,7 +373,6 @@
       break;
     }
     case JobEventDetails::NEW_DOC:
-    case JobEventDetails::NEW_PAGE:
     case JobEventDetails::JOB_DONE:
     case JobEventDetails::ALL_PAGES_REQUESTED: {
       // Don't care.
diff --git a/chrome/browser/printing/print_job.h b/chrome/browser/printing/print_job.h
index 239456b..b4979af6 100644
--- a/chrome/browser/printing/print_job.h
+++ b/chrome/browser/printing/print_job.h
@@ -186,9 +186,6 @@
     // A new document started printing.
     NEW_DOC,
 
-    // A new page started printing.
-    NEW_PAGE,
-
     // A page is done printing.
     PAGE_DONE,
 
diff --git a/chrome/browser/printing/print_job_manager.cc b/chrome/browser/printing/print_job_manager.cc
index 60fcd135..44b60df 100644
--- a/chrome/browser/printing/print_job_manager.cc
+++ b/chrome/browser/printing/print_job_manager.cc
@@ -138,7 +138,6 @@
     case JobEventDetails::USER_INIT_DONE:
     case JobEventDetails::USER_INIT_CANCELED:
     case JobEventDetails::DEFAULT_INIT_DONE:
-    case JobEventDetails::NEW_PAGE:
     case JobEventDetails::PAGE_DONE:
     case JobEventDetails::DOC_DONE:
     case JobEventDetails::ALL_PAGES_REQUESTED: {
diff --git a/chrome/browser/printing/print_job_worker.cc b/chrome/browser/printing/print_job_worker.cc
index 6f185aef..3ba4ffe 100644
--- a/chrome/browser/printing/print_job_worker.cc
+++ b/chrome/browser/printing/print_job_worker.cc
@@ -403,13 +403,6 @@
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   DCHECK_NE(page_number_, PageNumber::npos());
 
-  // Signal everyone that the page is about to be printed.
-  owner_->PostTask(
-      FROM_HERE,
-      base::Bind(&NotificationCallback, base::RetainedRef(owner_),
-                 JobEventDetails::NEW_PAGE, printing_context_->job_id(),
-                 base::RetainedRef(document_), base::RetainedRef(page)));
-
   // Preprocess.
   if (printing_context_->NewPage() != PrintingContext::OK) {
     OnFailure();
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
index bdc6e4d8..d13aa8e 100644
--- a/chrome/browser/printing/print_view_manager_base.cc
+++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -363,7 +363,6 @@
       break;
     }
     case JobEventDetails::NEW_DOC:
-    case JobEventDetails::NEW_PAGE:
     case JobEventDetails::PAGE_DONE:
     case JobEventDetails::DOC_DONE: {
       // Don't care about the actual printing process.
diff --git a/chrome/browser/printing/printing_layout_browsertest.cc b/chrome/browser/printing/printing_layout_browsertest.cc
index 3ac9b9bb..ce02d320 100644
--- a/chrome/browser/printing/printing_layout_browsertest.cc
+++ b/chrome/browser/printing/printing_layout_browsertest.cc
@@ -98,7 +98,6 @@
       case printing::JobEventDetails::NEW_DOC:
       case printing::JobEventDetails::USER_INIT_DONE:
       case printing::JobEventDetails::DEFAULT_INIT_DONE:
-      case printing::JobEventDetails::NEW_PAGE:
       case printing::JobEventDetails::PAGE_DONE:
       case printing::JobEventDetails::DOC_DONE:
       case printing::JobEventDetails::ALL_PAGES_REQUESTED: {
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index ffe884f..6ef2a78 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/download/download_core_service_factory.h"
 #include "chrome/browser/io_thread.h"
 #include "chrome/browser/net/chrome_url_request_context_getter.h"
-#include "chrome/browser/net/proxy_service_factory.h"
 #include "chrome/browser/permissions/permission_manager.h"
 #include "chrome/browser/permissions/permission_manager_factory.h"
 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
@@ -53,7 +52,6 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/prefs/json_pref_store.h"
-#include "components/proxy_config/pref_proxy_config_tracker.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/browser_thread.h"
@@ -73,7 +71,6 @@
 
 #if defined(OS_ANDROID)
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/proxy_config/proxy_prefs.h"
 #else  // !defined(OS_ANDROID)
 #include "chrome/browser/ui/zoom/chrome_zoom_level_otr_delegate.h"
 #include "components/zoom/zoom_event_manager.h"
@@ -223,9 +220,6 @@
       base::BindOnce(&NotifyOTRProfileDestroyedOnIOThread, profile_, this));
 #endif
 
-  if (pref_proxy_config_tracker_)
-    pref_proxy_config_tracker_->DetachFromPrefService();
-
   // Clears any data the network stack contains that may be related to the
   // OTR session.
   g_browser_process->io_thread()->ChangedToOnTheRecord();
@@ -527,12 +521,6 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
-PrefProxyConfigTracker* OffTheRecordProfileImpl::GetProxyConfigTracker() {
-  if (!pref_proxy_config_tracker_)
-    pref_proxy_config_tracker_.reset(CreateProxyConfigTracker());
-  return pref_proxy_config_tracker_.get();
-}
-
 chrome_browser_net::Predictor* OffTheRecordProfileImpl::GetNetworkPredictor() {
   // We do not store information about websites visited in OTR profiles which
   // is necessary for a Predictor, so we do not have a Predictor at all.
@@ -610,14 +598,3 @@
       ->OnDefaultZoomLevelChanged();
 }
 #endif  // !defined(OS_ANDROID)
-
-PrefProxyConfigTracker* OffTheRecordProfileImpl::CreateProxyConfigTracker() {
-#if defined(OS_CHROMEOS)
-  if (chromeos::ProfileHelper::IsSigninProfile(this)) {
-    return ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
-        g_browser_process->local_state());
-  }
-#endif  // defined(OS_CHROMEOS)
-  return ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile(
-      GetPrefs(), g_browser_process->local_state());
-}
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.h b/chrome/browser/profiles/off_the_record_profile_impl.h
index 93a5931..60816549 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.h
+++ b/chrome/browser/profiles/off_the_record_profile_impl.h
@@ -88,8 +88,6 @@
   void InitChromeOSPreferences() override;
 #endif  // defined(OS_CHROMEOS)
 
-  PrefProxyConfigTracker* GetProxyConfigTracker() override;
-
   chrome_browser_net::Predictor* GetNetworkPredictor() override;
   GURL GetHomePage() override;
 
@@ -122,11 +120,6 @@
   void TrackZoomLevelsFromParent();
 #endif  // !defined(OS_ANDROID)
 
-#if defined(OS_ANDROID)
-  void UseSystemProxy();
-#endif  // defined(OS_ANDROID)
-
-  PrefProxyConfigTracker* CreateProxyConfigTracker();
 #if !defined(OS_ANDROID)
   // Callback function for tracking parent's zoom level changes.
   void OnParentZoomLevelChanged(
@@ -151,8 +144,6 @@
 
   base::FilePath last_selected_directory_;
 
-  std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
-
   DISALLOW_COPY_AND_ASSIGN(OffTheRecordProfileImpl);
 };
 
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index 1cd9fe03..255c865 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -23,7 +23,6 @@
 #endif
 
 class ExtensionSpecialStoragePolicy;
-class PrefProxyConfigTracker;
 class PrefService;
 class TestingProfile;
 
@@ -251,10 +250,6 @@
   virtual void InitChromeOSPreferences() = 0;
 #endif  // defined(OS_CHROMEOS)
 
-  // Returns the helper object that provides the proxy configuration service
-  // access to the the proxy configuration possibly defined by preferences.
-  virtual PrefProxyConfigTracker* GetProxyConfigTracker() = 0;
-
   // Returns the Predictor object used for dns prefetch.
   virtual chrome_browser_net::Predictor* GetNetworkPredictor() = 0;
 
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 1dba4b4..5e6ae44 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -53,7 +53,6 @@
 #include "chrome/browser/download/download_core_service_factory.h"
 #include "chrome/browser/media/media_device_id_salt.h"
 #include "chrome/browser/net/predictor.h"
-#include "chrome/browser/net/proxy_service_factory.h"
 #include "chrome/browser/permissions/permission_manager.h"
 #include "chrome/browser/permissions/permission_manager_factory.h"
 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
@@ -108,7 +107,6 @@
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/proxy_config/pref_proxy_config_tracker.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "components/ssl_config/ssl_config_service_manager.h"
@@ -749,9 +747,6 @@
   BrowserContextDependencyManager::GetInstance()->DestroyBrowserContextServices(
       this);
 
-  if (pref_proxy_config_tracker_)
-    pref_proxy_config_tracker_->DetachFromPrefService();
-
   // This causes the Preferences file to be written to disk.
   if (prefs_loaded)
     SetExitType(EXIT_NORMAL);
@@ -1252,12 +1247,6 @@
 
 #endif  // defined(OS_CHROMEOS)
 
-PrefProxyConfigTracker* ProfileImpl::GetProxyConfigTracker() {
-  if (!pref_proxy_config_tracker_)
-    pref_proxy_config_tracker_.reset(CreateProxyConfigTracker());
-  return pref_proxy_config_tracker_.get();
-}
-
 chrome_browser_net::Predictor* ProfileImpl::GetNetworkPredictor() {
   return predictor_;
 }
@@ -1352,17 +1341,6 @@
   *max_size = prefs_->GetInteger(prefs::kMediaCacheSize);
 }
 
-PrefProxyConfigTracker* ProfileImpl::CreateProxyConfigTracker() {
-#if defined(OS_CHROMEOS)
-  if (chromeos::ProfileHelper::IsSigninProfile(this)) {
-    return ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
-        g_browser_process->local_state());
-  }
-#endif  // defined(OS_CHROMEOS)
-  return ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile(
-      GetPrefs(), g_browser_process->local_state());
-}
-
 std::unique_ptr<domain_reliability::DomainReliabilityMonitor>
 ProfileImpl::CreateDomainReliabilityMonitor(PrefService* local_state) {
   domain_reliability::DomainReliabilityService* service =
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index 22759afcd..025ad0a 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -150,8 +150,6 @@
   void InitChromeOSPreferences() override;
 #endif  // defined(OS_CHROMEOS)
 
-  PrefProxyConfigTracker* GetProxyConfigTracker() override;
-
  private:
 #if defined(OS_CHROMEOS)
   friend class chromeos::KioskTest;
@@ -193,8 +191,6 @@
 
   void GetMediaCacheParameters(base::FilePath* cache_path, int* max_size);
 
-  PrefProxyConfigTracker* CreateProxyConfigTracker();
-
   std::unique_ptr<domain_reliability::DomainReliabilityMonitor>
   CreateDomainReliabilityMonitor(PrefService* local_state);
 
@@ -262,8 +258,6 @@
   std::unique_ptr<chromeos::LocaleChangeGuard> locale_change_guard_;
 #endif
 
-  std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
-
   // TODO(mmenke):  This should be removed from the Profile, and use a
   // BrowserContextKeyedService instead.
   // See https://crbug.com/713733
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 07ca9b0..f03c54f3 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -39,7 +39,6 @@
 #include "chrome/browser/net/loading_predictor_observer.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/profile_network_context_service_factory.h"
-#include "chrome/browser/net/proxy_service_factory.h"
 #include "chrome/browser/policy/cloud/policy_header_service_factory.h"
 #include "chrome/browser/policy/policy_helpers.h"
 #include "chrome/browser/predictors/loading_predictor.h"
@@ -80,6 +79,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/resource_context.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/proxy_config_traits.h"
 #include "content/public/network/ignore_errors_cert_verifier.h"
 #include "content/public/network/url_request_context_builder_mojo.h"
 #include "extensions/features/features.h"
@@ -97,9 +97,6 @@
 #include "net/http/transport_security_persister.h"
 #include "net/net_features.h"
 #include "net/nqe/network_quality_estimator.h"
-#include "net/proxy/proxy_config_service_fixed.h"
-#include "net/proxy/proxy_script_fetcher_impl.h"
-#include "net/proxy/proxy_service.h"
 #include "net/reporting/reporting_service.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/ssl/client_cert_store.h"
@@ -465,8 +462,6 @@
   }
 #endif
 
-  params->proxy_config_service = ProxyServiceFactory::CreateProxyConfigService(
-      profile->GetProxyConfigTracker());
 #if defined(OS_CHROMEOS)
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
   if (user_manager) {
@@ -1107,8 +1102,7 @@
   builder->set_shared_http_auth_handler_factory(
       io_thread_globals->system_request_context->http_auth_handler_factory());
 
-  io_thread->SetUpProxyConfigService(
-      builder.get(), std::move(profile_params_->proxy_config_service));
+  io_thread->SetUpProxyService(builder.get());
 
   builder->set_network_delegate(std::move(network_delegate));
 
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 32807e7..86ced5e0 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -82,7 +82,6 @@
 class ClientCertStore;
 class CookieStore;
 class HttpTransactionFactory;
-class ProxyConfigService;
 class ReportingService;
 class ReportSender;
 class SSLConfigService;
@@ -341,11 +340,6 @@
     // and then passed to the list of request_interceptors on the IO thread.
     std::unique_ptr<net::URLRequestInterceptor> new_tab_page_interceptor;
 
-    // We need to initialize the ProxyConfigService from the UI thread
-    // because on linux it relies on initializing things through gconf,
-    // and needs to be on the main thread.
-    std::unique_ptr<net::ProxyConfigService> proxy_config_service;
-
 #if defined(OS_CHROMEOS)
     std::unique_ptr<policy::PolicyCertVerifier> policy_cert_verifier;
     std::string username_hash;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
index c7892f5..4054234c4 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -1312,3 +1312,33 @@
         .replay();
   });
 });
+
+TEST_F('BackgroundTest', 'NodeVsSubnode', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(function(root) {/*!
+    <a href="#">test</a>
+  */}, function(root) {
+    var link = root.find({role: RoleType.LINK});
+    function outputLinkRange(start, end) {
+      return function() {
+        new Output().withSpeech(new cursors.Range(new cursors.Cursor(link, start),
+            new cursors.Cursor(link, end))).go();
+      };
+    }
+
+    mockFeedback.call(outputLinkRange(0, 0))
+        .expectSpeech('test', 'Link')
+        .call(outputLinkRange(0, 1))
+        .expectSpeech('t')
+        .call(outputLinkRange(1, 1))
+        .expectSpeech('test', 'Link')
+        .call(outputLinkRange(1, 2))
+        .expectSpeech('e')
+        .call(outputLinkRange(1, 3))
+        .expectNextSpeechUtteranceIsNot('Link')
+        .expectSpeech('es')
+        .call(outputLinkRange(0, 4))
+        .expectSpeech('test', 'Link')
+        .replay();
+  });
+});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
index edb1d3f7..58ec5483e 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
@@ -666,12 +666,15 @@
   },
 
   /**
-   * Returns true if this range covers a single node's text content or less.
+   * Returns true if this range covers less than a node.
    * @return {boolean}
    */
   isSubNode: function() {
-    return this.start.node === this.end.node && this.start.index > -1 &&
-        this.end.index > -1;
+    var startIndex = this.start.index;
+    var endIndex = this.end.index;
+    return this.start.node === this.end.node && startIndex != -1 &&
+        endIndex != -1 && startIndex != endIndex &&
+        (startIndex != 0 || endIndex != this.start.getText().length);
   },
 
   /**
diff --git a/chrome/browser/resources/print_preview/new/app.html b/chrome/browser/resources/print_preview/new/app.html
index bde13b1..83bdf21 100644
--- a/chrome/browser/resources/print_preview/new/app.html
+++ b/chrome/browser/resources/print_preview/new/app.html
@@ -82,7 +82,8 @@
             document-info="[[documentInfo]]"
             hidden$="[[!settings.scaling.available]]">
         </print-preview-scaling-settings>
-        <print-preview-other-options-settings settings="{{settings}}">
+        <print-preview-other-options-settings settings="{{settings}}"
+            hidden$="[[!settings.otherOptions.available]]">
         </print-preview-other-options-settings>
         <print-preview-advanced-options-settings settings="{{settings}}"
             hidden$="[[!settings.vendorItems.available]]">
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index 2925555..ee102c1 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -49,6 +49,7 @@
      *   headerFooter: !print_preview_new.Setting,
      *   rasterize: !print_preview_new.Setting,
      *   vendorItems: !print_preview_new.Setting,
+     *   otherOptions: !print_preview_new.Setting,
      * }}
      */
     settings: {
@@ -154,6 +155,14 @@
           available: true,
           updatesPreview: false,
         },
+        // This does not represent a real setting value, and is used only to
+        // expose the availability of the other options settings section.
+        otherOptions: {
+          value: null,
+          valid: true,
+          available: true,
+          updatesPreview: false,
+        },
       },
     },
 
@@ -301,7 +310,16 @@
         'settings.selectionOnly.available',
         this.documentInfo.isModifiable && this.documentInfo.hasSelection);
     this.set('settings.headerFooter.available', this.documentInfo.isModifiable);
-    this.set('settings.rasterize.available', !this.documentInfo.isModifiable);
+    this.set(
+        'settings.rasterize.available',
+        !this.documentInfo.isModifiable && !cr.isWindows && !cr.isMac);
+    this.set(
+        'settings.otherOptions.available',
+        this.settings.duplex.available ||
+            this.settings.cssBackground.available ||
+            this.settings.selectionOnly.available ||
+            this.settings.headerFooter.available ||
+            this.settings.rasterize.available);
   },
 
   /** @param {?print_preview.CddCapabilities} caps The printer capabilities. */
diff --git a/chrome/browser/resources/print_preview/new/other_options_settings.html b/chrome/browser/resources/print_preview/new/other_options_settings.html
index 71349530..f6397776 100644
--- a/chrome/browser/resources/print_preview/new/other_options_settings.html
+++ b/chrome/browser/resources/print_preview/new/other_options_settings.html
@@ -11,31 +11,35 @@
     <print-preview-settings-section class="multirow-controls">
       <span slot="title" id="options-label">$i18n{optionsLabel}</span>
       <div slot="controls" class="checkbox">
-        <div id="header-footer-container">
+        <div id="header-footer-container"
+            hidden$="[[!settings.headerFooter.available]]">
           <label aria-live="polite">
             <input type="checkbox">
             <span>$i18n{optionHeaderFooter}</span>
           </label>
         </div>
-        <div id="duplex-container">
+        <div id="duplex-container" hidden$="[[!settings.duplex.available]]">
           <label aria-live="polite">
             <input type="checkbox" checked="{{duplexValue_::change}}">
             <span>$i18n{optionTwoSided}</span>
           </label>
         </div>
-        <div id="css-background-container">
+        <div id="css-background-container"
+            hidden$="[[!settings.cssBackground.available]]">
           <label aria-live="polite">
             <input type="checkbox">
             <span>$i18n{optionBackgroundColorsAndImages}</span>
           </label>
         </div>
-        <div id="rasterize-container">
+        <div id="rasterize-container"
+            hidden$="[[!settings.rasterize.available]]">
           <label aria-live="polite">
             <input type="checkbox">
             <span>$i18n{optionRasterize}</span>
           </label>
         </div>
-        <div id="selection-only-container">
+        <div id="selection-only-container"
+            hidden$="[[!settings.selectionOnly.available]]">
           <label aria-live="polite">
             <input type="checkbox">
             <span>$i18n{optionSelectionOnly}</span>
diff --git a/chrome/browser/sync/test/integration/sync_arc_package_helper.cc b/chrome/browser/sync/test/integration/sync_arc_package_helper.cc
index 18c5240..4bce447 100644
--- a/chrome/browser/sync/test/integration/sync_arc_package_helper.cc
+++ b/chrome/browser/sync/test/integration/sync_arc_package_helper.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_package_syncable_service.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/arc/connection_holder.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_app_instance.h"
 
 namespace arc {
@@ -130,6 +131,7 @@
   arc_app_list_prefs->app_connection_holder()->SetInstance(nullptr);
   arc_app_list_prefs->app_connection_holder()->SetInstance(
       instance_map_[profile].get());
+  WaitForInstanceReady(arc_app_list_prefs->app_connection_holder());
   // OnPackageListRefreshed will be called when AppInstance is ready.
   // For fakeAppInstance we use SendRefreshPackageList to make sure that
   // OnPackageListRefreshed will be called.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 26a7a14..290e389d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -685,6 +685,8 @@
       "apps/chrome_app_window_client.h",
       "apps/directory_access_confirmation_dialog.cc",
       "apps/directory_access_confirmation_dialog.h",
+      "blocked_content/framebust_block_tab_helper.cc",
+      "blocked_content/framebust_block_tab_helper.h",
       "blocked_content/popunder_preventer.cc",
       "blocked_content/popunder_preventer.h",
       "bluetooth/bluetooth_chooser_controller.cc",
diff --git a/chrome/browser/ui/app_list/arc/arc_app_test.cc b/chrome/browser/ui/app_list/arc/arc_app_test.cc
index ee475f0..2917de0a 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_test.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_test.cc
@@ -22,6 +22,7 @@
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_session_runner.h"
 #include "components/arc/arc_util.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_app_instance.h"
 #include "components/arc/test/fake_arc_session.h"
 #include "components/user_manager/scoped_user_manager.h"
@@ -113,6 +114,7 @@
   app_instance_.reset(new arc::FakeAppInstance(arc_app_list_pref_));
   arc_service_manager_->arc_bridge_service()->app()->SetInstance(
       app_instance_.get());
+  WaitForInstanceReady(arc_service_manager_->arc_bridge_service()->app());
 }
 
 void ArcAppTest::WaitForDefaultApps() {
@@ -206,6 +208,7 @@
   bridge_service->app()->SetInstance(nullptr);
   app_instance_ = base::MakeUnique<arc::FakeAppInstance>(arc_app_list_pref_);
   bridge_service->app()->SetInstance(app_instance_.get());
+  WaitForInstanceReady(bridge_service->app());
 }
 
 const user_manager::User* ArcAppTest::CreateUserAndLogin() {
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index 46411ae..e3b94b57 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -1946,7 +1946,6 @@
                            display::kInvalidDisplayId);
 
   EXPECT_FALSE(launcher1.app_launched());
-  EXPECT_FALSE(launcher2.app_launched());
 
   arc_test()->WaitForDefaultApps();
 
diff --git a/chrome/browser/ui/app_list/test/fake_profile.cc b/chrome/browser/ui/app_list/test/fake_profile.cc
index f56f04d..98392a6 100644
--- a/chrome/browser/ui/app_list/test/fake_profile.cc
+++ b/chrome/browser/ui/app_list/test/fake_profile.cc
@@ -186,10 +186,6 @@
 void FakeProfile::OnLogin() {}
 void FakeProfile::InitChromeOSPreferences() {}
 
-PrefProxyConfigTracker* FakeProfile::GetProxyConfigTracker() {
-  return nullptr;
-}
-
 chrome_browser_net::Predictor* FakeProfile::GetNetworkPredictor() {
   return nullptr;
 }
diff --git a/chrome/browser/ui/app_list/test/fake_profile.h b/chrome/browser/ui/app_list/test/fake_profile.h
index 08b1847..6933a603 100644
--- a/chrome/browser/ui/app_list/test/fake_profile.h
+++ b/chrome/browser/ui/app_list/test/fake_profile.h
@@ -86,7 +86,6 @@
   void OnLogin() override;
   void InitChromeOSPreferences() override;
 
-  PrefProxyConfigTracker* GetProxyConfigTracker() override;
   chrome_browser_net::Predictor* GetNetworkPredictor() override;
   GURL GetHomePage() override;
   bool WasCreatedByVersionOrLater(const std::string& version) override;
diff --git a/chrome/browser/ui/blocked_content/framebust_block_tab_helper.cc b/chrome/browser/ui/blocked_content/framebust_block_tab_helper.cc
new file mode 100644
index 0000000..7a4dcac6
--- /dev/null
+++ b/chrome/browser/ui/blocked_content/framebust_block_tab_helper.cc
@@ -0,0 +1,44 @@
+// 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 "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
+
+#include "base/logging.h"
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(FramebustBlockTabHelper);
+
+FramebustBlockTabHelper::~FramebustBlockTabHelper() = default;
+
+// static
+void FramebustBlockTabHelper::AddBlockedUrl(const GURL& blocked_url) {
+  blocked_urls_.push_back(blocked_url);
+
+  if (observer_)
+    observer_->OnBlockedUrlAdded(blocked_url);
+}
+
+bool FramebustBlockTabHelper::HasBlockedUrls() const {
+  return !blocked_urls_.empty();
+}
+
+void FramebustBlockTabHelper::OnBlockedUrlClicked(size_t index) {
+  web_contents_->OpenURL(content::OpenURLParams(
+      blocked_urls_[index], content::Referrer(),
+      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_LINK, false));
+  blocked_urls_.clear();
+}
+
+void FramebustBlockTabHelper::SetObserver(Observer* observer) {
+  DCHECK(!observer_);
+  observer_ = observer;
+}
+
+void FramebustBlockTabHelper::ClearObserver() {
+  DCHECK(observer_);
+  observer_ = nullptr;
+}
+
+FramebustBlockTabHelper::FramebustBlockTabHelper(
+    content::WebContents* web_contents)
+    : web_contents_(web_contents) {}
diff --git a/chrome/browser/ui/blocked_content/framebust_block_tab_helper.h b/chrome/browser/ui/blocked_content/framebust_block_tab_helper.h
new file mode 100644
index 0000000..ae47410
--- /dev/null
+++ b/chrome/browser/ui/blocked_content/framebust_block_tab_helper.h
@@ -0,0 +1,74 @@
+// 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 CHROME_BROWSER_UI_BLOCKED_CONTENT_FRAMEBUST_BLOCK_TAB_HELPER_H_
+#define CHROME_BROWSER_UI_BLOCKED_CONTENT_FRAMEBUST_BLOCK_TAB_HELPER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "url/gurl.h"
+
+// A tab helper that keeps track of blocked Framebusts that happened on each
+// page. Only used for the desktop version of the blocked Framebust UI.
+class FramebustBlockTabHelper
+    : public content::WebContentsUserData<FramebustBlockTabHelper> {
+ public:
+  // There is expected to be at most one observer at a time.
+  class Observer {
+   public:
+    // Called whenever a new URL was blocked.
+    virtual void OnBlockedUrlAdded(const GURL& blocked_url) = 0;
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
+  ~FramebustBlockTabHelper() override;
+
+  // Shows the blocked Framebust icon in the Omnibox for the |blocked_url|.
+  // If the icon is already visible, that URL is instead added to the vector of
+  // currently blocked URLs and the bubble view is updated.
+  void AddBlockedUrl(const GURL& blocked_url);
+
+  // Returns true if at least one Framebust was blocked on this page.
+  bool HasBlockedUrls() const;
+
+  // Handles navigating to the blocked URL specified by |index| and clearing the
+  // vector of blocked URLs.
+  void OnBlockedUrlClicked(size_t index);
+
+  // Sets and clears the current observer.
+  void SetObserver(Observer* observer);
+  void ClearObserver();
+
+  // Returns all of the currently blocked URLs.
+  const std::vector<GURL>& blocked_urls() const { return blocked_urls_; }
+
+  // Remembers if the animation has run.
+  void set_animation_has_run() { animation_has_run_ = true; }
+  bool animation_has_run() const { return animation_has_run_; }
+
+ private:
+  friend class content::WebContentsUserData<FramebustBlockTabHelper>;
+
+  explicit FramebustBlockTabHelper(content::WebContents* web_contents);
+
+  content::WebContents* web_contents_;
+
+  // Remembers all the currently blocked URLs. This is cleared on each
+  // navigation.
+  std::vector<GURL> blocked_urls_;
+
+  // Remembers if the animation has run.
+  bool animation_has_run_ = false;
+
+  // Points to the unique observer of this class.
+  Observer* observer_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(FramebustBlockTabHelper);
+};
+
+#endif  // CHROME_BROWSER_UI_BLOCKED_CONTENT_FRAMEBUST_BLOCK_TAB_HELPER_H_
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 0fc32db..4a247ecb 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -89,6 +89,7 @@
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
+#include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
 #include "chrome/browser/ui/blocked_content/popup_tracker.h"
 #include "chrome/browser/ui/bluetooth/bluetooth_chooser_controller.h"
@@ -1399,6 +1400,14 @@
     sound_content_setting_observer->OnAudioStateChanged(is_audible);
 }
 
+void Browser::OnDidBlockFramebust(content::WebContents* web_contents,
+                                  const GURL& url) {
+  TabSpecificContentSettings* content_settings =
+      TabSpecificContentSettings::FromWebContents(web_contents);
+  DCHECK(content_settings);
+  content_settings->OnFramebustBlocked(url);
+}
+
 bool Browser::IsMouseLocked() const {
   return exclusive_access_manager_->mouse_lock_controller()->IsMouseLocked();
 }
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index fa3047d..3b0c514 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -487,6 +487,8 @@
                                          const GURL& resource_url) override;
   void OnAudioStateChanged(content::WebContents* web_contents,
                            bool is_audible) override;
+  void OnDidBlockFramebust(content::WebContents* web_contents,
+                           const GURL& url) override;
 
   bool is_type_tabbed() const { return type_ == TYPE_TABBED; }
   bool is_type_popup() const { return type_ == TYPE_POPUP; }
diff --git a/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa_browsertest.mm
index 73d4f45..35b7eddc 100644
--- a/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa_browsertest.mm
+++ b/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa_browsertest.mm
@@ -105,6 +105,12 @@
   for (const auto& model : models) {
     ContentSettingBubbleModel* bubble =
         model->CreateBubbleModel(nullptr, web_contents(), profile());
+
+    // Skip this test for the ContentSettingFramebustBlockBubbleModel, which
+    // doesn't have a Cocoa version.
+    if (bubble->AsFramebustBlockBubbleModel())
+      continue;
+
     ContentSettingBubbleController* controller = CreateBubbleController(bubble);
     // No bubble except the one for media should have media menus.
     if (!bubble->AsMediaStreamBubbleModel())
diff --git a/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.mm b/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.mm
index 032edcf..6ba07af5 100644
--- a/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.mm
@@ -299,7 +299,9 @@
     return true;
   }
 
-  if (chrome::ShowAllDialogsWithViewsToolkit()) {
+  // The blocked Framebust bubble only has a toolkit-views implementation.
+  if (chrome::ShowAllDialogsWithViewsToolkit() ||
+      model->AsFramebustBlockBubbleModel()) {
     gfx::Point origin = gfx::ScreenPointFromNSPoint(anchor);
     NSWindow* bubble = chrome::ContentSettingBubbleViewsBridge::Show(
         [web_contents->GetTopLevelNativeWindow() contentView], model,
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 1440dea..7d7775c 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -38,6 +38,7 @@
 #include "chrome/common/insecure_content_renderer.mojom.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/render_messages.h"
+#include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/content_settings/core/browser/content_settings_utils.h"
@@ -1588,6 +1589,88 @@
         CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS);
 }
 
+// ContentSettingFramebustBlockBubbleModel -------------------------------------
+
+ContentSettingFramebustBlockBubbleModel::
+    ContentSettingFramebustBlockBubbleModel(Delegate* delegate,
+                                            WebContents* web_contents,
+                                            Profile* profile)
+    : ContentSettingBubbleModel(delegate, web_contents, profile) {
+  if (!web_contents)
+    return;
+
+  set_manage_text_style(ContentSettingBubbleModel::ManageTextStyle::kNone);
+  set_show_learn_more(false);
+  set_title(l10n_util::GetStringUTF16(IDS_REDIRECT_BLOCKED_MESSAGE));
+  set_done_button_text(l10n_util::GetStringUTF16(IDS_OK));
+
+  auto* helper = FramebustBlockTabHelper::FromWebContents(web_contents);
+
+  // Build the blocked urls list.
+  for (const auto& blocked_url : helper->blocked_urls())
+    AddListItem(CreateListItem(blocked_url));
+
+  helper->SetObserver(this);
+}
+
+ContentSettingFramebustBlockBubbleModel::
+    ~ContentSettingFramebustBlockBubbleModel() {
+  if (web_contents())
+    FramebustBlockTabHelper::FromWebContents(web_contents())->ClearObserver();
+}
+
+void ContentSettingFramebustBlockBubbleModel::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  // The order is important because ContentSettingBubbleModel::Observer() clears
+  // the value of |web_contents()|.
+  if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED)
+    FramebustBlockTabHelper::FromWebContents(web_contents())->ClearObserver();
+  ContentSettingBubbleModel::Observe(type, source, details);
+}
+
+void ContentSettingFramebustBlockBubbleModel::OnListItemClicked(
+    int index,
+    int event_flags) {
+  if (!web_contents())
+    return;
+
+  FramebustBlockTabHelper::FromWebContents(web_contents())
+      ->OnBlockedUrlClicked(index);
+}
+
+ContentSettingFramebustBlockBubbleModel*
+ContentSettingFramebustBlockBubbleModel::AsFramebustBlockBubbleModel() {
+  return this;
+}
+
+void ContentSettingFramebustBlockBubbleModel::OnBlockedUrlAdded(
+    const GURL& blocked_url) {
+  AddListItem(CreateListItem(blocked_url));
+}
+
+ContentSettingBubbleModel::ListItem
+ContentSettingFramebustBlockBubbleModel::CreateListItem(const GURL& url) {
+  // Skip empty URLS.
+  base::string16 title = !url.spec().empty()
+                             ? base::UTF8ToUTF16(url.spec())
+                             : l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE);
+
+  const bool use_md = ui::MaterialDesignController::IsSecondaryUiMaterial();
+  if (use_md) {
+    // Format the title to include the unicode single dot bullet code-point
+    // \u2022 and two spaces.
+    title = l10n_util::GetStringFUTF16(IDS_LIST_BULLET, title);
+  }
+
+  gfx::Image image =
+      use_md ? gfx::Image()
+             : ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+                   IDR_DEFAULT_FAVICON);
+  return ListItem(image, title, true, 0);
+}
+
 // ContentSettingBubbleModel ---------------------------------------------------
 
 // static
@@ -1716,6 +1799,11 @@
   return nullptr;
 }
 
+ContentSettingFramebustBlockBubbleModel*
+ContentSettingBubbleModel::AsFramebustBlockBubbleModel() {
+  return nullptr;
+}
+
 void ContentSettingBubbleModel::AddListItem(const ListItem& item) {
   bubble_content_.list_items.push_back(item);
   if (owner_)
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index debc711b..db94491c 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -15,7 +15,9 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "build/build_config.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
+#include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
 #include "chrome/common/custom_handlers/protocol_handler.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -52,12 +54,14 @@
 //       ContentSettingPopupBubbleModel              - popups
 //   ContentSettingSubresourceFilterBubbleModel  - filtered subresources
 //   ContentSettingDownloadsBubbleModel          - automatic downloads
+//   ContentSettingFramebustBlockBubbleModel     - blocked framebusts
 
 // Forward declaration necessary for downcasts.
 class ContentSettingMediaStreamBubbleModel;
 class ContentSettingSimpleBubbleModel;
 class ContentSettingSubresourceFilterBubbleModel;
 class ContentSettingDownloadsBubbleModel;
+class ContentSettingFramebustBlockBubbleModel;
 
 // This model provides data for ContentSettingBubble, and also controls
 // the action triggered when the allow / block radio buttons are triggered.
@@ -203,6 +207,10 @@
   // Cast this bubble into ContentSettingDownloadsBubbleModel if possible.
   virtual ContentSettingDownloadsBubbleModel* AsDownloadsBubbleModel();
 
+  // Cast this bubble into ContentSettingFramebustBlockBubbleModel if possible.
+  virtual ContentSettingFramebustBlockBubbleModel*
+  AsFramebustBlockBubbleModel();
+
   // Sets the Rappor service used for testing.
   void SetRapporServiceImplForTesting(
       rappor::RapporServiceImpl* rappor_service) {
@@ -443,4 +451,36 @@
   DISALLOW_COPY_AND_ASSIGN(ContentSettingDownloadsBubbleModel);
 };
 
+#if !defined(OS_ANDROID)
+// The model for the blocked Framebust bubble.
+class ContentSettingFramebustBlockBubbleModel
+    : public ContentSettingBubbleModel,
+      public FramebustBlockTabHelper::Observer {
+ public:
+  ContentSettingFramebustBlockBubbleModel(Delegate* delegate,
+                                          content::WebContents* web_contents,
+                                          Profile* profile);
+
+  ~ContentSettingFramebustBlockBubbleModel() override;
+
+  // content::NotificationObserver:
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
+
+  // ContentSettingBubbleModel:
+  void OnListItemClicked(int index, int event_flags) override;
+  ContentSettingFramebustBlockBubbleModel* AsFramebustBlockBubbleModel()
+      override;
+
+  // FramebustBlockTabHelper::Observer:
+  void OnBlockedUrlAdded(const GURL& blocked_url) override;
+
+ private:
+  ListItem CreateListItem(const GURL& url);
+
+  DISALLOW_COPY_AND_ASSIGN(ContentSettingFramebustBlockBubbleModel);
+};
+#endif  // !defined(OS_ANDROID)
+
 #endif  // CHROME_BROWSER_UI_CONTENT_SETTINGS_CONTENT_SETTING_BUBBLE_MODEL_H_
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.cc b/chrome/browser/ui/content_settings/content_setting_image_model.cc
index f9944c0..fb39987f 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.cc
@@ -16,7 +16,9 @@
 #include "chrome/browser/plugins/plugin_utils.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
 #include "chrome/common/chrome_features.h"
+#include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/vector_icons/vector_icons.h"
@@ -40,6 +42,7 @@
 //     ContentSettingDownloadsImageModel         - automatic downloads
 //   ContentSettingMediaImageModel             - media
 //   ContentSettingSubresourceFilterImageModel - deceptive content
+//   ContentSettingFramebustBlockImageModel    - blocked framebust
 
 class ContentSettingBlockedImageModel : public ContentSettingSimpleImageModel {
  public:
@@ -470,6 +473,51 @@
   }
 }
 
+// Blocked Framebust -----------------------------------------------------------
+
+ContentSettingFramebustBlockImageModel::ContentSettingFramebustBlockImageModel()
+    : ContentSettingImageModel() {}
+
+void ContentSettingFramebustBlockImageModel::UpdateFromWebContents(
+    WebContents* web_contents) {
+  set_visible(false);
+
+  if (!web_contents)
+    return;
+
+  // Early exit if no blocked Framebust.
+  if (!FramebustBlockTabHelper::FromWebContents(web_contents)->HasBlockedUrls())
+    return;
+
+  set_icon(kBlockedRedirectIcon, kBlockedBadgeIcon);
+  set_explanatory_string_id(IDS_REDIRECT_BLOCKED_TITLE);
+  set_tooltip(l10n_util::GetStringUTF16(IDS_REDIRECT_BLOCKED_TOOLTIP));
+  set_visible(true);
+}
+
+ContentSettingBubbleModel*
+ContentSettingFramebustBlockImageModel::CreateBubbleModel(
+    ContentSettingBubbleModel::Delegate* delegate,
+    WebContents* web_contents,
+    Profile* profile) {
+  return new ContentSettingFramebustBlockBubbleModel(delegate, web_contents,
+                                                     profile);
+}
+
+bool ContentSettingFramebustBlockImageModel::ShouldRunAnimation(
+    WebContents* web_contents) {
+  return web_contents && !FramebustBlockTabHelper::FromWebContents(web_contents)
+                              ->animation_has_run();
+}
+
+void ContentSettingFramebustBlockImageModel::SetAnimationHasRun(
+    WebContents* web_contents) {
+  if (!web_contents)
+    return;
+  FramebustBlockTabHelper::FromWebContents(web_contents)
+      ->set_animation_has_run();
+}
+
 // Protocol handlers -----------------------------------------------------------
 
 ContentSettingRPHImageModel::ContentSettingRPHImageModel()
@@ -612,6 +660,12 @@
     }
     result.push_back(std::move(model));
   }
+
+  // The framebust blocking UI reuses the code for displaying an icon with
+  // ContentSettingImageModel and ContentSettingBubbleModel, even though
+  // framebust blocking is not a content setting.
+  result.push_back(base::MakeUnique<ContentSettingFramebustBlockImageModel>());
+
   return result;
 }
 
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.h b/chrome/browser/ui/content_settings/content_setting_image_model.h
index 727d9c3..2b5e571f 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.h
@@ -140,4 +140,22 @@
   DISALLOW_COPY_AND_ASSIGN(ContentSettingSubresourceFilterImageModel);
 };
 
+class ContentSettingFramebustBlockImageModel : public ContentSettingImageModel {
+ public:
+  ContentSettingFramebustBlockImageModel();
+
+  void UpdateFromWebContents(content::WebContents* web_contents) override;
+
+  ContentSettingBubbleModel* CreateBubbleModel(
+      ContentSettingBubbleModel::Delegate* delegate,
+      content::WebContents* web_contents,
+      Profile* profile) override;
+
+  bool ShouldRunAnimation(content::WebContents* web_contents) override;
+  void SetAnimationHasRun(content::WebContents* web_contents) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ContentSettingFramebustBlockImageModel);
+};
+
 #endif  // CHROME_BROWSER_UI_CONTENT_SETTINGS_CONTENT_SETTING_IMAGE_MODEL_H_
diff --git a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
new file mode 100644
index 0000000..e31dd911
--- /dev/null
+++ b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
+#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "ui/events/event_constants.h"
+#include "url/gurl.h"
+
+class ContentSettingFramebustBlockBubbleModelTest
+    : public InProcessBrowserTest {
+ public:
+  content::WebContents* GetWebContents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  void WaitForNavigation() {
+    content::TestNavigationObserver observer(GetWebContents());
+    observer.Wait();
+  }
+};
+
+// Tests that clicking an item in the list of blocked URLs trigger a navigation
+// to that URL.
+IN_PROC_BROWSER_TEST_F(ContentSettingFramebustBlockBubbleModelTest,
+                       AllowRedirection) {
+  const GURL blocked_urls[] = {
+      GURL(chrome::kChromeUIHistoryURL), GURL(chrome::kChromeUISettingsURL),
+      GURL(chrome::kChromeUIVersionURL),
+  };
+
+  // Signal that a blocked redirection happened.
+  auto* helper = FramebustBlockTabHelper::FromWebContents(GetWebContents());
+  for (const GURL& url : blocked_urls)
+    helper->AddBlockedUrl(url);
+  EXPECT_TRUE(helper->HasBlockedUrls());
+
+  // Simulate clicking on the second blocked URL.
+  ContentSettingFramebustBlockBubbleModel framebust_block_bubble_model(
+      browser()->content_setting_bubble_model_delegate(), GetWebContents(),
+      browser()->profile());
+  framebust_block_bubble_model.OnListItemClicked(/* index = */ 1,
+                                                 ui::EF_LEFT_MOUSE_BUTTON);
+  WaitForNavigation();
+
+  EXPECT_FALSE(helper->HasBlockedUrls());
+  EXPECT_EQ(blocked_urls[1], GetWebContents()->GetLastCommittedURL());
+}
diff --git a/chrome/browser/ui/passwords/destination_file_system.cc b/chrome/browser/ui/passwords/destination_file_system.cc
index 0a14712..730d01f 100644
--- a/chrome/browser/ui/passwords/destination_file_system.cc
+++ b/chrome/browser/ui/passwords/destination_file_system.cc
@@ -7,12 +7,27 @@
 #include <utility>
 
 #include "base/files/file_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 
 DestinationFileSystem::DestinationFileSystem(base::FilePath destination_path)
     : destination_path_(std::move(destination_path)) {}
 
-bool DestinationFileSystem::Write(const std::string& data) {
-  return base::WriteFile(destination_path_, data.c_str(), data.size());
+base::string16 DestinationFileSystem::Write(const std::string& data) {
+  if (base::WriteFile(destination_path_, data.c_str(), data.size())) {
+    return base::string16();
+  } else {
+#if defined(OS_WIN)
+    base::string16 folder_name(destination_path_.DirName().BaseName().value());
+#else
+    base::string16 folder_name(
+        base::UTF8ToUTF16(destination_path_.DirName().BaseName().value()));
+#endif
+    return l10n_util::GetStringFUTF16(
+        IDS_PASSWORD_MANAGER_EXPORT_TO_FILE_FAILED, std::move(folder_name));
+  }
 }
 
 const base::FilePath& DestinationFileSystem::GetDestinationPathForTesting() {
diff --git a/chrome/browser/ui/passwords/destination_file_system.h b/chrome/browser/ui/passwords/destination_file_system.h
index a6ea278..8ae9167 100644
--- a/chrome/browser/ui/passwords/destination_file_system.h
+++ b/chrome/browser/ui/passwords/destination_file_system.h
@@ -17,7 +17,7 @@
   ~DestinationFileSystem() override = default;
 
   // password_manager::Destination
-  bool Write(const std::string& data) override;
+  base::string16 Write(const std::string& data) override;
 
   // Get this instance's target.
   const base::FilePath& GetDestinationPathForTesting();
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 78fa05c..d6977ad 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -117,6 +117,10 @@
 #include "components/zoom/zoom_controller.h"
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
+#endif
+
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
 #include "chrome/browser/offline_pages/offline_page_tab_helper.h"
 #include "chrome/browser/offline_pages/recent_tab_helper.h"
@@ -280,6 +284,7 @@
   extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
       web_contents);
   extensions::WebNavigationTabObserver::CreateForWebContents(web_contents);
+  FramebustBlockTabHelper::CreateForWebContents(web_contents);
   HungPluginTabHelper::CreateForWebContents(web_contents);
   JavaScriptDialogTabHelper::CreateForWebContents(web_contents);
   ManagePasswordsUIController::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc b/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
index 609badf..bf1da98 100644
--- a/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
@@ -21,6 +21,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/common/app.mojom.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_app_instance.h"
 #include "content/public/test/test_utils.h"
 
@@ -62,6 +63,7 @@
     app_instance_.reset(new arc::FakeAppInstance(arc_app_list_pref_));
     arc_app_list_pref_->app_connection_holder()->SetInstance(
         app_instance_.get());
+    WaitForInstanceReady(arc_app_list_pref_->app_connection_holder());
 
     // In this setup, we have one app and one shortcut which share one package.
     mojom::AppInfo app;
@@ -88,6 +90,8 @@
   }
 
   void TearDownOnMainThread() override {
+    arc_app_list_pref_->app_connection_holder()->SetInstance(nullptr);
+    app_instance_.reset();
     ArcSessionManager::Get()->Shutdown();
   }
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index df8ef2b..766633a 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -1726,7 +1726,6 @@
     }
   }
 
-  button->SetMinSize(gfx::Size());
   button->set_context_menu_controller(this);
   button->set_drag_controller(this);
   if (node->is_url()) {
diff --git a/chrome/browser/ui/views/page_info/permission_selector_row.cc b/chrome/browser/ui/views/page_info/permission_selector_row.cc
index 2fba9b4..6d41fc87 100644
--- a/chrome/browser/ui/views/page_info/permission_selector_row.cc
+++ b/chrome/browser/ui/views/page_info/permission_selector_row.cc
@@ -400,14 +400,12 @@
 
   // Update the menu button text to reflect the new setting.
   if (menu_button_) {
-    menu_button_->SetText(PageInfoUI::PermissionActionToUIString(
-        profile_, permission.type, permission.setting,
-        permission.default_setting, content_settings::SETTING_SOURCE_USER));
-    menu_button_->SizeToPreferredSize();
     // Re-layout will be done at the |PageInfoBubbleView| level, since
     // that view may need to resize itself to accomodate the new sizes of its
     // contents.
-    menu_button_->InvalidateLayout();
+    menu_button_->SetText(PageInfoUI::PermissionActionToUIString(
+        profile_, permission.type, permission.setting,
+        permission.default_setting, content_settings::SETTING_SOURCE_USER));
   } else if (combobox_) {
     bool use_default = permission.setting == CONTENT_SETTING_DEFAULT;
     combobox_->UpdateSelectedIndex(use_default);
diff --git a/chrome/browser/ui/views/profiles/avatar_button.cc b/chrome/browser/ui/views/profiles/avatar_button.cc
index cb4ba02f..2de5f57 100644
--- a/chrome/browser/ui/views/profiles/avatar_button.cc
+++ b/chrome/browser/ui/views/profiles/avatar_button.cc
@@ -449,9 +449,6 @@
           : gfx::ShadowValues(
                 10, gfx::ShadowValue(gfx::Vector2d(), 2.0f, SK_ColorDKGRAY)));
 
-  // We want the button to resize if the new text is shorter.
-  SetMinSize(gfx::Size());
-
   if (use_generic_button) {
     SetImage(views::Button::STATE_NORMAL, generic_avatar_);
   } else if (error_controller_.HasAvatarError()) {
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index 421f6955..38ebd8d59 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -971,28 +971,15 @@
 }
 
 void TranslateBubbleView::UpdateAdvancedView() {
-  DCHECK(source_language_combobox_);
-  DCHECK(target_language_combobox_);
-  DCHECK(advanced_done_button_);
-
-  base::string16 source_language_name =
-      model_->GetLanguageNameAt(model_->GetOriginalLanguageIndex());
-  base::string16 target_language_name =
-      model_->GetLanguageNameAt(model_->GetTargetLanguageIndex());
-
   // "Always translate" checkbox doesn't exist in an incognito window.
   if (advanced_always_translate_checkbox_) {
     advanced_always_translate_checkbox_->SetText(
         l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ALWAYS));
   }
 
-  base::string16 label;
-  if (model_->IsPageTranslatedInCurrentLanguages())
-    label = l10n_util::GetStringUTF16(IDS_DONE);
-  else
-    label = l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ACCEPT);
-  advanced_done_button_->SetText(label);
-  advanced_done_button_->SizeToPreferredSize();
-  if (advanced_view_)
-    advanced_view_->Layout();
+  DCHECK(advanced_done_button_);
+  advanced_done_button_->SetText(
+      l10n_util::GetStringUTF16(model_->IsPageTranslatedInCurrentLanguages()
+                                    ? IDS_DONE
+                                    : IDS_TRANSLATE_BUBBLE_ACCEPT));
 }
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index e385d48..5927cd3 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -153,12 +153,16 @@
     "renderers/textured_quad_renderer.h",
     "renderers/web_vr_renderer.cc",
     "renderers/web_vr_renderer.h",
+    "sample_queue.cc",
+    "sample_queue.h",
     "service/vr_device_manager.cc",
     "service/vr_device_manager.h",
     "service/vr_display_host.cc",
     "service/vr_display_host.h",
     "service/vr_service_impl.cc",
     "service/vr_service_impl.h",
+    "sliding_average.cc",
+    "sliding_average.h",
     "speech_recognizer.cc",
     "speech_recognizer.h",
     "target_property.cc",
@@ -245,6 +249,7 @@
     "gltf_parser_unittest.cc",
     "pose_util_unittest.cc",
     "service/vr_device_manager_unittest.cc",
+    "sliding_average_unittest.cc",
     "speech_recognizer_unittest.cc",
     "test/fake_ui_element_renderer.cc",
     "test/fake_ui_element_renderer.h",
diff --git a/chrome/browser/vr/elements/suggestion.cc b/chrome/browser/vr/elements/suggestion.cc
index 625f4095..9d82fc4 100644
--- a/chrome/browser/vr/elements/suggestion.cc
+++ b/chrome/browser/vr/elements/suggestion.cc
@@ -9,8 +9,7 @@
 namespace vr {
 
 Suggestion::Suggestion(base::Callback<void(GURL)> navigate_callback)
-    : LinearLayout(LinearLayout::kRight),
-      navigate_callback_(navigate_callback) {
+    : navigate_callback_(navigate_callback) {
   EventHandlers event_handlers;
   event_handlers.button_up =
       base::Bind(&Suggestion::HandleButtonClick, base::Unretained(this));
diff --git a/chrome/browser/vr/elements/suggestion.h b/chrome/browser/vr/elements/suggestion.h
index 570de22..fd5c7b9 100644
--- a/chrome/browser/vr/elements/suggestion.h
+++ b/chrome/browser/vr/elements/suggestion.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_VR_ELEMENTS_SUGGESTION_H_
 #define CHROME_BROWSER_VR_ELEMENTS_SUGGESTION_H_
 
-#include "chrome/browser/vr/elements/linear_layout.h"
+#include "chrome/browser/vr/elements/ui_element.h"
 #include "url/gurl.h"
 
 namespace vr {
@@ -15,7 +15,7 @@
 // to bind both necessitates lambda capture, which base::Bind cannot do. This
 // workaround simplifies the problem. If UI elements gain the ability to use the
 // VR UI to browser interface, we could eliminate this class.
-class Suggestion : public LinearLayout {
+class Suggestion : public UiElement {
  public:
   explicit Suggestion(base::Callback<void(GURL)> navigate_callback);
   ~Suggestion() override;
diff --git a/chrome/browser/vr/elements/ui_element_type.cc b/chrome/browser/vr/elements/ui_element_type.cc
index a9b25aaf..c2d209c 100644
--- a/chrome/browser/vr/elements/ui_element_type.cc
+++ b/chrome/browser/vr/elements/ui_element_type.cc
@@ -17,6 +17,13 @@
     "kTypeButtonForeground",
     "kTypeButtonHitTarget",
     "kTypeScaledDepthAdjuster",
+    "kTypeOmniboxSuggestionBackground",
+    "kTypeOmniboxSuggestionLayout",
+    "kTypeOmniboxSuggestionTextLayout",
+    "kTypeOmniboxSuggestionIconField",
+    "kTypeOmniboxSuggestionIcon",
+    "kTypeOmniboxSuggestionContentText",
+    "kTypeOmniboxSuggestionDescriptionText",
 };
 
 static_assert(
diff --git a/chrome/browser/vr/elements/ui_element_type.h b/chrome/browser/vr/elements/ui_element_type.h
index dcb13c5b..1ecdd6d 100644
--- a/chrome/browser/vr/elements/ui_element_type.h
+++ b/chrome/browser/vr/elements/ui_element_type.h
@@ -17,6 +17,13 @@
   kTypeButtonForeground,
   kTypeButtonHitTarget,
   kTypeScaledDepthAdjuster,
+  kTypeOmniboxSuggestionBackground,
+  kTypeOmniboxSuggestionLayout,
+  kTypeOmniboxSuggestionTextLayout,
+  kTypeOmniboxSuggestionIconField,
+  kTypeOmniboxSuggestionIcon,
+  kTypeOmniboxSuggestionContentText,
+  kTypeOmniboxSuggestionDescriptionText,
 
   // This must be last.
   kNumUiElementTypes,
diff --git a/chrome/browser/vr/fps_meter.cc b/chrome/browser/vr/fps_meter.cc
index c4a79526..3ed0967 100644
--- a/chrome/browser/vr/fps_meter.cc
+++ b/chrome/browser/vr/fps_meter.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/vr/fps_meter.h"
 
-#include <algorithm>
-
 namespace vr {
 
 namespace {
@@ -14,28 +12,6 @@
 
 }  // namespace
 
-SampleQueue::SampleQueue(size_t window_size) : window_size_(window_size) {
-  samples_.reserve(window_size);
-}
-
-SampleQueue::~SampleQueue() = default;
-
-void SampleQueue::AddSample(int64_t value) {
-  sum_ += value;
-
-  if (samples_.size() < window_size_) {
-    samples_.push_back(value);
-  } else {
-    sum_ -= samples_[current_index_];
-    samples_[current_index_] = value;
-  }
-
-  ++current_index_;
-  if (current_index_ >= window_size_) {
-    current_index_ = 0;
-  }
-}
-
 FPSMeter::FPSMeter() : frame_times_(kDefaultNumFrameTimes) {}
 
 FPSMeter::FPSMeter(size_t window_size) : frame_times_(window_size) {}
@@ -70,18 +46,4 @@
   return (frame_times_.GetCount() * 1.0e6) / frame_times_.GetSum();
 }
 
-SlidingAverage::SlidingAverage(size_t window_size) : values_(window_size) {}
-
-SlidingAverage::~SlidingAverage() = default;
-
-void SlidingAverage::AddSample(int64_t value) {
-  values_.AddSample(value);
-}
-
-int64_t SlidingAverage::GetAverageOrDefault(int64_t default_value) const {
-  if (values_.GetCount() == 0)
-    return default_value;
-  return values_.GetSum() / values_.GetCount();
-}
-
 }  // namespace vr
diff --git a/chrome/browser/vr/fps_meter.h b/chrome/browser/vr/fps_meter.h
index 4fd9082..57900d9 100644
--- a/chrome/browser/vr/fps_meter.h
+++ b/chrome/browser/vr/fps_meter.h
@@ -5,34 +5,12 @@
 #ifndef CHROME_BROWSER_VR_FPS_METER_H_
 #define CHROME_BROWSER_VR_FPS_METER_H_
 
-#include <vector>
-
 #include "base/macros.h"
 #include "base/time/time.h"
+#include "chrome/browser/vr/sample_queue.h"
 
 namespace vr {
 
-class SampleQueue {
- public:
-  explicit SampleQueue(size_t window_size);
-  ~SampleQueue();
-
-  int64_t GetSum() const { return sum_; }
-
-  void AddSample(int64_t value);
-
-  size_t GetCount() const { return samples_.size(); }
-
-  // Get sliding window size for tests.
-  size_t GetWindowSize() const { return window_size_; }
-
- private:
-  int64_t sum_ = 0;
-  size_t current_index_ = 0;
-  size_t window_size_;
-  std::vector<int64_t> samples_;
-};
-
 // Computes fps based on submitted frame times.
 class FPSMeter {
  public:
@@ -55,19 +33,6 @@
   DISALLOW_COPY_AND_ASSIGN(FPSMeter);
 };
 
-class SlidingAverage {
- public:
-  explicit SlidingAverage(size_t window_size);
-  ~SlidingAverage();
-
-  void AddSample(int64_t value);
-  int64_t GetAverageOrDefault(int64_t default_value) const;
-  int64_t GetAverage() const { return GetAverageOrDefault(0); }
-
- private:
-  SampleQueue values_;
-};
-
 }  // namespace vr
 
 #endif  // CHROME_BROWSER_VR_FPS_METER_H_
diff --git a/chrome/browser/vr/fps_meter_unittest.cc b/chrome/browser/vr/fps_meter_unittest.cc
index a7e575d..b1625c1 100644
--- a/chrome/browser/vr/fps_meter_unittest.cc
+++ b/chrome/browser/vr/fps_meter_unittest.cc
@@ -70,37 +70,4 @@
   }
 }
 
-TEST(SlidingAverage, Basics) {
-  SlidingAverage meter(5);
-
-  // No values yet
-  EXPECT_EQ(42, meter.GetAverageOrDefault(42));
-  EXPECT_EQ(0, meter.GetAverage());
-
-  meter.AddSample(100);
-  EXPECT_EQ(100, meter.GetAverageOrDefault(42));
-  EXPECT_EQ(100, meter.GetAverage());
-
-  meter.AddSample(200);
-  EXPECT_EQ(150, meter.GetAverage());
-
-  meter.AddSample(10);
-  EXPECT_EQ(103, meter.GetAverage());
-
-  meter.AddSample(10);
-  EXPECT_EQ(80, meter.GetAverage());
-
-  meter.AddSample(10);
-  EXPECT_EQ(66, meter.GetAverage());
-
-  meter.AddSample(10);
-  EXPECT_EQ(48, meter.GetAverage());
-
-  meter.AddSample(10);
-  EXPECT_EQ(10, meter.GetAverage());
-
-  meter.AddSample(110);
-  EXPECT_EQ(30, meter.GetAverage());
-}
-
 }  // namespace vr
diff --git a/chrome/browser/vr/model/color_scheme.cc b/chrome/browser/vr/model/color_scheme.cc
index 52d4b65..2474154 100644
--- a/chrome/browser/vr/model/color_scheme.cc
+++ b/chrome/browser/vr/model/color_scheme.cc
@@ -111,6 +111,11 @@
   normal_scheme.timeout_message_background = 0xFF444444;
   normal_scheme.timeout_message_foreground = normal_scheme.spinner_color;
   normal_scheme.speech_recognition_circle_background = 0xFF4285F4;
+  normal_scheme.omnibox_background = 0xFFEEEEEE;
+  normal_scheme.omnibox_icon = 0xA6000000;
+  normal_scheme.omnibox_text = 0xFF595959;
+  normal_scheme.omnibox_suggestion_content = 0xFF595959;
+  normal_scheme.omnibox_suggestion_description = 0xFF5595FE;
 
   g_fullscreen_scheme.Get() = normal_scheme;
   ColorScheme& fullscreen_scheme = g_fullscreen_scheme.Get();
diff --git a/chrome/browser/vr/model/color_scheme.h b/chrome/browser/vr/model/color_scheme.h
index 6c3708a..e094bc39 100644
--- a/chrome/browser/vr/model/color_scheme.h
+++ b/chrome/browser/vr/model/color_scheme.h
@@ -88,11 +88,9 @@
 
   UrlBarColors url_bar;
 
-  // Screen dimmer colors.
   SkColor dimmer_outer;
   SkColor dimmer_inner;
 
-  // Splash screen colors.
   SkColor splash_screen_background;
   SkColor splash_screen_text_color;
 
@@ -100,11 +98,16 @@
   SkColor spinner_background;
   SkColor spinner_color;
 
-  // Timeout UI colors
   SkColor timeout_message_background;
   SkColor timeout_message_foreground;
 
   SkColor speech_recognition_circle_background;
+
+  SkColor omnibox_background;
+  SkColor omnibox_icon;
+  SkColor omnibox_text;
+  SkColor omnibox_suggestion_content;
+  SkColor omnibox_suggestion_description;
 };
 
 }  // namespace vr
diff --git a/chrome/browser/vr/sample_queue.cc b/chrome/browser/vr/sample_queue.cc
new file mode 100644
index 0000000..c2ca777
--- /dev/null
+++ b/chrome/browser/vr/sample_queue.cc
@@ -0,0 +1,31 @@
+// 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 "chrome/browser/vr/sample_queue.h"
+
+namespace vr {
+
+SampleQueue::SampleQueue(size_t window_size) : window_size_(window_size) {
+  samples_.reserve(window_size);
+}
+
+SampleQueue::~SampleQueue() = default;
+
+void SampleQueue::AddSample(int64_t value) {
+  sum_ += value;
+
+  if (samples_.size() < window_size_) {
+    samples_.push_back(value);
+  } else {
+    sum_ -= samples_[current_index_];
+    samples_[current_index_] = value;
+  }
+
+  ++current_index_;
+  if (current_index_ >= window_size_) {
+    current_index_ = 0;
+  }
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/sample_queue.h b/chrome/browser/vr/sample_queue.h
new file mode 100644
index 0000000..c74f316
--- /dev/null
+++ b/chrome/browser/vr/sample_queue.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VR_SAMPLE_QUEUE_H_
+#define CHROME_BROWSER_VR_SAMPLE_QUEUE_H_
+
+#include <vector>
+
+#include "base/macros.h"
+
+namespace vr {
+
+// Manages a fixed-size queue of samples including their current sum. Old
+// samples are automatically dropped when an added sample would exceed the
+// requested size.
+class SampleQueue {
+ public:
+  explicit SampleQueue(size_t window_size);
+  ~SampleQueue();
+
+  int64_t GetSum() const { return sum_; }
+
+  void AddSample(int64_t value);
+
+  size_t GetCount() const { return samples_.size(); }
+
+  // Get sliding window size for tests.
+  size_t GetWindowSize() const { return window_size_; }
+
+ private:
+  int64_t sum_ = 0;
+  size_t current_index_ = 0;
+  size_t window_size_;
+  std::vector<int64_t> samples_;
+  DISALLOW_COPY_AND_ASSIGN(SampleQueue);
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_SAMPLE_QUEUE_H_
diff --git a/chrome/browser/vr/sliding_average.cc b/chrome/browser/vr/sliding_average.cc
new file mode 100644
index 0000000..4852063
--- /dev/null
+++ b/chrome/browser/vr/sliding_average.cc
@@ -0,0 +1,38 @@
+// 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 "chrome/browser/vr/sliding_average.h"
+
+namespace vr {
+
+SlidingAverage::SlidingAverage(size_t window_size) : values_(window_size) {}
+
+SlidingAverage::~SlidingAverage() = default;
+
+void SlidingAverage::AddSample(int64_t value) {
+  values_.AddSample(value);
+}
+
+int64_t SlidingAverage::GetAverageOrDefault(int64_t default_value) const {
+  if (values_.GetCount() == 0)
+    return default_value;
+  return values_.GetSum() / values_.GetCount();
+}
+
+SlidingTimeDeltaAverage::SlidingTimeDeltaAverage(size_t window_size)
+    : sample_microseconds_(window_size) {}
+
+SlidingTimeDeltaAverage::~SlidingTimeDeltaAverage() = default;
+
+void SlidingTimeDeltaAverage::AddSample(base::TimeDelta value) {
+  sample_microseconds_.AddSample(value.InMicroseconds());
+}
+
+base::TimeDelta SlidingTimeDeltaAverage::GetAverageOrDefault(
+    base::TimeDelta default_value) const {
+  return base::TimeDelta::FromMicroseconds(
+      sample_microseconds_.GetAverageOrDefault(default_value.InMicroseconds()));
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/sliding_average.h b/chrome/browser/vr/sliding_average.h
new file mode 100644
index 0000000..7589673b
--- /dev/null
+++ b/chrome/browser/vr/sliding_average.h
@@ -0,0 +1,50 @@
+// 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 CHROME_BROWSER_VR_SLIDING_AVERAGE_H_
+#define CHROME_BROWSER_VR_SLIDING_AVERAGE_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "chrome/browser/vr/sample_queue.h"
+
+namespace vr {
+
+class SlidingAverage {
+ public:
+  explicit SlidingAverage(size_t window_size);
+  ~SlidingAverage();
+
+  void AddSample(int64_t value);
+  int64_t GetAverageOrDefault(int64_t default_value) const;
+  int64_t GetAverage() const { return GetAverageOrDefault(0); }
+  size_t GetCount() const { return values_.GetCount(); }
+
+ private:
+  SampleQueue values_;
+  DISALLOW_COPY_AND_ASSIGN(SlidingAverage);
+};
+
+class SlidingTimeDeltaAverage {
+ public:
+  explicit SlidingTimeDeltaAverage(size_t window_size);
+  virtual ~SlidingTimeDeltaAverage();
+
+  virtual void AddSample(base::TimeDelta value);
+  base::TimeDelta GetAverageOrDefault(base::TimeDelta default_value) const;
+  base::TimeDelta GetAverage() const {
+    return GetAverageOrDefault(base::TimeDelta());
+  }
+  size_t GetCount() const { return sample_microseconds_.GetCount(); }
+
+ private:
+  SlidingAverage sample_microseconds_;
+  DISALLOW_COPY_AND_ASSIGN(SlidingTimeDeltaAverage);
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_SLIDING_AVERAGE_H_
diff --git a/chrome/browser/vr/sliding_average_unittest.cc b/chrome/browser/vr/sliding_average_unittest.cc
new file mode 100644
index 0000000..a2632ff8
--- /dev/null
+++ b/chrome/browser/vr/sliding_average_unittest.cc
@@ -0,0 +1,60 @@
+// 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 "chrome/browser/vr/sliding_average.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace vr {
+
+TEST(SlidingAverage, Basics) {
+  SlidingAverage meter(5);
+
+  // No values yet
+  EXPECT_EQ(42, meter.GetAverageOrDefault(42));
+  EXPECT_EQ(0, meter.GetAverage());
+
+  meter.AddSample(100);
+  EXPECT_EQ(100, meter.GetAverageOrDefault(42));
+  EXPECT_EQ(100, meter.GetAverage());
+
+  meter.AddSample(200);
+  EXPECT_EQ(150, meter.GetAverage());
+
+  meter.AddSample(10);
+  EXPECT_EQ(103, meter.GetAverage());
+
+  meter.AddSample(10);
+  EXPECT_EQ(80, meter.GetAverage());
+
+  meter.AddSample(10);
+  EXPECT_EQ(66, meter.GetAverage());
+
+  meter.AddSample(10);
+  EXPECT_EQ(48, meter.GetAverage());
+
+  meter.AddSample(10);
+  EXPECT_EQ(10, meter.GetAverage());
+
+  meter.AddSample(110);
+  EXPECT_EQ(30, meter.GetAverage());
+}
+
+TEST(SlidingTimeDeltaAverage, Basics) {
+  SlidingTimeDeltaAverage meter(5);
+
+  EXPECT_EQ(base::TimeDelta::FromSeconds(42),
+            meter.GetAverageOrDefault(base::TimeDelta::FromSeconds(42)));
+  EXPECT_EQ(base::TimeDelta(), meter.GetAverage());
+
+  meter.AddSample(base::TimeDelta::FromSeconds(100));
+  EXPECT_EQ(base::TimeDelta::FromSeconds(100),
+            meter.GetAverageOrDefault(base::TimeDelta::FromSeconds(42)));
+  EXPECT_EQ(base::TimeDelta::FromSeconds(100), meter.GetAverage());
+
+  meter.AddSample(base::TimeDelta::FromMilliseconds(200000));
+  EXPECT_EQ(base::TimeDelta::FromSeconds(150), meter.GetAverage());
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/testapp/vr_test_context.cc b/chrome/browser/vr/testapp/vr_test_context.cc
index 287b042..f3726a2 100644
--- a/chrome/browser/vr/testapp/vr_test_context.cc
+++ b/chrome/browser/vr/testapp/vr_test_context.cc
@@ -316,9 +316,12 @@
   for (int i = 0; i < num_suggestions; i++) {
     result->suggestions.emplace_back(OmniboxSuggestion(
         base::UTF8ToUTF16("Suggestion ") + base::IntToString16(i + 1),
-        base::UTF8ToUTF16("Description text"),
+        base::UTF8ToUTF16(
+            "Very lengthy description of the suggestion that would wrap "
+            "if not truncated through some other means."),
         AutocompleteMatch::Type::VOICE_SUGGEST, GURL("http://www.test.com/")));
   }
+  model_->omnibox_input_active = true;
   ui_->SetOmniboxSuggestions(std::move(result));
 }
 
diff --git a/chrome/browser/vr/ui_scene_constants.h b/chrome/browser/vr/ui_scene_constants.h
index 8f3eddd..66140bba 100644
--- a/chrome/browser/vr/ui_scene_constants.h
+++ b/chrome/browser/vr/ui_scene_constants.h
@@ -184,19 +184,24 @@
 static constexpr float kReticleWidth = 0.025f;
 static constexpr float kReticleHeight = 0.025f;
 
-static constexpr float kOmniboxContainerWidth = 4.5;
-static constexpr float kOmniboxContainerHeight = 12 * 0.02f;
-static constexpr float kOmniboxContainerCornerRadius = 0.01;
-static constexpr float kOmniboxContainerVeriticalOffset = 0.7;
-static constexpr float kOmniboxTextHeight = 0.05;
-static constexpr float kOmniboxCloseButtonVerticalOffset = -0.5;
-static constexpr float kSuggestionGap = 0.01f;
-static constexpr float kSuggestionLineGap = 0.01f;
-static constexpr float kSuggestionIconGap = 0.01f;
-static constexpr float kSuggestionIconSize = 0.1f;
-static constexpr float kSuggestionTextFieldWidth = 0.3f;
-static constexpr float kSuggestionContentTextHeight = 0.02f;
-static constexpr float kSuggestionDescriptionTextHeight = 0.015f;
+static constexpr float kOmniboxWidthDMM = 0.848f;
+static constexpr float kOmniboxHeightDMM = 0.088f;
+static constexpr float kOmniboxVerticalOffsetDMM = -0.1f;
+static constexpr float kOmniboxTextHeightDMM = 0.032f;
+static constexpr float kOmniboxTextMarginDMM = 0.024f;
+static constexpr float kOmniboxCloseButtonDiameterDMM = 0.088f;
+static constexpr float kOmniboxCloseButtonVerticalOffsetDMM = -0.5;
+
+static constexpr float kSuggestionHeightDMM = 0.088f;
+static constexpr float kSuggestionGapDMM = 0.008f;
+static constexpr float kSuggestionLineGapDMM = 0.01f;
+static constexpr float kSuggestionIconSizeDMM = 0.036f;
+static constexpr float kSuggestionIconFieldWidthDMM = 0.104f;
+static constexpr float kSuggestionRightMarginDMM = 0.024f;
+static constexpr float kSuggestionTextFieldWidthDMM =
+    kOmniboxWidthDMM - kSuggestionIconFieldWidthDMM - kSuggestionRightMarginDMM;
+static constexpr float kSuggestionContentTextHeightDMM = 0.024f;
+static constexpr float kSuggestionDescriptionTextHeightDMM = 0.020f;
 
 static constexpr int kControllerFadeInMs = 200;
 static constexpr int kControllerFadeOutMs = 550;
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc
index 7855cb0..6f7236d7 100644
--- a/chrome/browser/vr/ui_scene_creator.cc
+++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -81,46 +81,76 @@
                             Model* model,
                             SuggestionBinding* element_binding) {
   auto icon = base::MakeUnique<VectorIcon>(100);
-  icon->SetVisible(true);
-  icon->SetSize(kSuggestionIconSize, kSuggestionIconSize);
   icon->set_draw_phase(kPhaseForeground);
+  icon->set_type(kTypeOmniboxSuggestionIcon);
+  icon->set_hit_testable(false);
+  icon->SetSize(kSuggestionIconSizeDMM, kSuggestionIconSizeDMM);
+  BindColor(model, icon.get(), &ColorScheme::omnibox_icon,
+            &VectorIcon::SetColor);
   VectorIcon* p_icon = icon.get();
 
-  auto content_text = base::MakeUnique<Text>(512, kSuggestionContentTextHeight);
+  auto icon_box = base::MakeUnique<UiElement>();
+  icon_box->set_draw_phase(kPhaseNone);
+  icon_box->set_type(kTypeOmniboxSuggestionIconField);
+  icon_box->SetSize(kSuggestionIconFieldWidthDMM, kSuggestionHeightDMM);
+  icon_box->AddChild(std::move(icon));
+
+  auto content_text =
+      base::MakeUnique<Text>(1024, kSuggestionContentTextHeightDMM);
   content_text->set_draw_phase(kPhaseForeground);
-  content_text->SetVisible(true);
-  content_text->SetSize(kSuggestionTextFieldWidth, 0);
+  content_text->set_type(kTypeOmniboxSuggestionContentText);
+  content_text->SetSize(kSuggestionTextFieldWidthDMM, 0);
   content_text->SetTextAlignment(UiTexture::kTextAlignmentLeft);
+  content_text->SetMultiLine(false);
+  BindColor(model, content_text.get(), &ColorScheme::omnibox_suggestion_content,
+            &Text::SetColor);
   Text* p_content_text = content_text.get();
 
   auto description_text =
-      base::MakeUnique<Text>(512, kSuggestionDescriptionTextHeight);
-  description_text->SetSize(kSuggestionTextFieldWidth, 0);
+      base::MakeUnique<Text>(1024, kSuggestionDescriptionTextHeightDMM);
   description_text->set_draw_phase(kPhaseForeground);
-  description_text->SetVisible(true);
+  description_text->set_type(kTypeOmniboxSuggestionDescriptionText);
+  description_text->SetSize(kSuggestionTextFieldWidthDMM, 0);
   description_text->SetTextAlignment(UiTexture::kTextAlignmentLeft);
+  description_text->SetMultiLine(false);
+  BindColor(model, description_text.get(),
+            &ColorScheme::omnibox_suggestion_description, &Text::SetColor);
   Text* p_description_text = description_text.get();
 
   auto text_layout = base::MakeUnique<LinearLayout>(LinearLayout::kDown);
-  text_layout->set_margin(kSuggestionLineGap);
+  text_layout->set_type(kTypeOmniboxSuggestionTextLayout);
+  text_layout->set_margin(kSuggestionLineGapDMM);
   text_layout->AddChild(std::move(content_text));
   text_layout->AddChild(std::move(description_text));
   text_layout->SetVisible(true);
 
+  auto right_margin = base::MakeUnique<UiElement>();
+  right_margin->set_draw_phase(kPhaseNone);
+  right_margin->SetSize(kSuggestionRightMarginDMM, kSuggestionHeightDMM);
+
+  auto suggestion_layout = base::MakeUnique<LinearLayout>(LinearLayout::kRight);
+  suggestion_layout->set_type(kTypeOmniboxSuggestionLayout);
+  suggestion_layout->AddChild(std::move(icon_box));
+  suggestion_layout->AddChild(std::move(text_layout));
+  suggestion_layout->AddChild(std::move(right_margin));
+
+  auto background = base::MakeUnique<Rect>();
+  background->set_type(kTypeOmniboxSuggestionBackground);
+  background->set_draw_phase(kPhaseForeground);
+  background->set_bounds_contain_children(true);
+  background->SetColor(SK_ColorGREEN);
+  background->AddChild(std::move(suggestion_layout));
+  BindColor(model, background.get(), &ColorScheme::omnibox_background,
+            &Rect::SetColor);
+
   auto suggestion = base::MakeUnique<Suggestion>(base::Bind(
       [](UiBrowserInterface* browser, Model* m, GURL gurl) {
         browser->Navigate(gurl);
         m->omnibox_input_active = false;
       },
       base::Unretained(browser), base::Unretained(model)));
-
-  suggestion->set_margin(kSuggestionIconGap);
-  suggestion->SetVisible(true);
-  suggestion->AddChild(std::move(icon));
-  suggestion->AddChild(std::move(text_layout));
-  Suggestion* p_suggestion = suggestion.get();
-
-  element_binding->set_view(suggestion.get());
+  suggestion->set_bounds_contain_children(true);
+  suggestion->AddChild(std::move(background));
 
   element_binding->bindings().push_back(
       VR_BIND_FUNC(base::string16, SuggestionBinding, element_binding,
@@ -134,8 +164,9 @@
               SetIcon(AutocompleteMatch::TypeToVectorIcon(value))));
   element_binding->bindings().push_back(VR_BIND_FUNC(
       GURL, SuggestionBinding, element_binding, model()->destination,
-      Suggestion, p_suggestion, set_destination));
+      Suggestion, suggestion.get(), set_destination));
 
+  element_binding->set_view(suggestion.get());
   scene->AddUiElement(kOmniboxSuggestions, std::move(suggestion));
 }
 
@@ -1004,18 +1035,19 @@
   omnibox_root->set_draw_phase(kPhaseNone);
   omnibox_root->SetVisible(false);
   omnibox_root->set_hit_testable(false);
-  omnibox_root->SetTranslate(0, kUrlBarVerticalOffset, -kUrlBarDistance);
   omnibox_root->SetTransitionedProperties({OPACITY});
   omnibox_root->AddBinding(VR_BIND_FUNC(bool, Model, model_,
                                         omnibox_input_active, UiElement,
                                         omnibox_root.get(), SetVisible));
-  scene_->AddUiElement(k2dBrowsingRoot, std::move(omnibox_root));
+
+  auto scaler = base::MakeUnique<ScaledDepthAdjuster>(kUrlBarDistance);
+  scaler->AddChild(std::move(omnibox_root));
+  scene_->AddUiElement(k2dBrowsingRoot, std::move(scaler));
 
   auto omnibox_container = base::MakeUnique<Rect>();
   omnibox_container->set_name(kOmniboxContainer);
   omnibox_container->set_draw_phase(kPhaseForeground);
-  omnibox_container->SetSize(kOmniboxContainerWidth, kOmniboxContainerHeight);
-  omnibox_container->set_corner_radius(kOmniboxContainerCornerRadius);
+  omnibox_container->SetSize(kOmniboxWidthDMM, kOmniboxHeightDMM);
   omnibox_container->SetColor(SK_ColorWHITE);
   omnibox_container->SetTransitionedProperties({TRANSFORM});
   omnibox_container->AddBinding(base::MakeUnique<Binding<bool>>(
@@ -1023,16 +1055,22 @@
                  base::Unretained(model_)),
       base::Bind(
           [](UiElement* e, const bool& v) {
-            float y_offset = v ? kOmniboxContainerVeriticalOffset : 0;
+            float y_offset = v ? kOmniboxVerticalOffsetDMM : 0;
             e->SetTranslate(0, y_offset, 0);
           },
           omnibox_container.get())));
   scene_->AddUiElement(kOmniboxRoot, std::move(omnibox_container));
 
-  auto omnibox_text_field = base::MakeUnique<TextInput>(
-      512, kOmniboxTextHeight, kSuggestionTextFieldWidth);
+  float width = kOmniboxWidthDMM - 2 * kOmniboxTextMarginDMM;
+  auto omnibox_text_field =
+      base::MakeUnique<TextInput>(1024, kOmniboxTextHeightDMM, width);
+  omnibox_text_field->SetSize(width, 0);
   omnibox_text_field->set_name(kOmniboxTextField);
   omnibox_text_field->set_draw_phase(kPhaseForeground);
+  omnibox_text_field->set_x_anchoring(LEFT);
+  omnibox_text_field->set_x_centering(LEFT);
+  omnibox_text_field->SetTranslate(kOmniboxTextMarginDMM, 0, 0);
+
   omnibox_text_field->SetTextChangedCallback(base::Bind(
       &UiBrowserInterface::StartAutocomplete, base::Unretained(browser_)));
   scene_->AddUiElement(kOmniboxContainer, std::move(omnibox_text_field));
@@ -1042,10 +1080,10 @@
       base::Bind([](Model* m) { m->omnibox_input_active = false; },
                  base::Unretained(model_)),
       vector_icons::kClose16Icon);
-  close_button->SetSize(kCloseButtonWidth, kCloseButtonHeight);
-  close_button->set_hover_offset(kButtonZOffsetHoverDMM * kCloseButtonDistance);
-  close_button->SetTranslate(0, kOmniboxCloseButtonVerticalOffset, 0);
-  close_button->SetSize(kCloseButtonWidth, kCloseButtonHeight);
+  close_button->SetSize(kOmniboxCloseButtonDiameterDMM,
+                        kOmniboxCloseButtonDiameterDMM);
+  close_button->SetTranslate(0, kOmniboxCloseButtonVerticalOffsetDMM, 0);
+  close_button->set_hover_offset(kButtonZOffsetHoverDMM);
   BindButtonColors(model_, close_button.get(), &ColorScheme::button_colors,
                    &Button::SetButtonColors);
   scene_->AddUiElement(kOmniboxContainer, std::move(close_button));
@@ -1060,11 +1098,13 @@
   auto suggestions_layout = base::MakeUnique<LinearLayout>(LinearLayout::kUp);
   suggestions_layout->set_name(kOmniboxSuggestions);
   suggestions_layout->set_draw_phase(kPhaseNone);
+  suggestions_layout->set_hit_testable(false);
   suggestions_layout->set_y_anchoring(TOP);
   suggestions_layout->set_y_centering(BOTTOM);
-  suggestions_layout->set_margin(kSuggestionGap);
+  suggestions_layout->SetTranslate(0, kSuggestionGapDMM, 0);
   suggestions_layout->AddBinding(base::MakeUnique<SuggestionSetBinding>(
       &model_->omnibox_suggestions, added_callback, removed_callback));
+
   scene_->AddUiElement(kOmniboxContainer, std::move(suggestions_layout));
 }
 
diff --git a/chrome/browser/vr/ui_scene_creator.h b/chrome/browser/vr/ui_scene_creator.h
index 157fd517..3d7ce7d 100644
--- a/chrome/browser/vr/ui_scene_creator.h
+++ b/chrome/browser/vr/ui_scene_creator.h
@@ -39,7 +39,6 @@
   void CreateViewportAwareRoot();
   void CreateUrlBar();
   void CreateOmnibox();
-  void CreateSuggestionList();
   void CreateWebVrUrlToast();
   void CreateCloseButton();
   void CreateExitPrompt();
diff --git a/chrome/browser/vr/ui_unittest.cc b/chrome/browser/vr/ui_unittest.cc
index 880ab0e..5f294aba 100644
--- a/chrome/browser/vr/ui_unittest.cc
+++ b/chrome/browser/vr/ui_unittest.cc
@@ -67,7 +67,6 @@
     kUrlBar,
     kOmniboxContainer,
     kOmniboxTextField,
-    kOmniboxSuggestions,
     kLoadingIndicator,
     kWebVrTimeoutSpinner,
     kWebVrTimeoutMessage,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ad5a5e1..68b17f7 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -733,6 +733,7 @@
       "../browser/ui/collected_cookies_browsertest.cc",
       "../browser/ui/content_settings/content_setting_bubble_model_browsertest.cc",
       "../browser/ui/content_settings/content_setting_image_model_browsertest.cc",
+      "../browser/ui/content_settings/framebust_block_browsertest.cc",
       "../browser/ui/exclusive_access/fullscreen_controller_browsertest.cc",
       "../browser/ui/extensions/extension_enable_flow_browsertest.cc",
       "../browser/ui/extensions/extension_installed_bubble_browsertest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
index 6ad7a1e..abf06ab2 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
@@ -165,7 +165,7 @@
         }
 
         @Override
-        public void startZeroSuggest(Profile profile, String omniboxText, String url,
+        public void startZeroSuggest(Profile profile, String omniboxText, String url, String title,
                 boolean focusedFromFakebox) {
             mZeroSuggestCalledCount++;
         }
@@ -212,9 +212,8 @@
                 boolean preventInlineAutocomplete, boolean focusedFromFakebox) {}
 
         @Override
-        public void startZeroSuggest(Profile profile, String omniboxText, String url,
-                boolean focusedFromFakebox) {
-        }
+        public void startZeroSuggest(Profile profile, String omniboxText, String url, String title,
+                boolean focusedFromFakebox) {}
 
         @Override
         public void stop(boolean clear) {}
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index de348e3..32c7334 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -35,7 +35,6 @@
 #include "chrome/browser/history/chrome_history_client.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/web_history_service_factory.h"
-#include "chrome/browser/net/proxy_service_factory.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/policy/schema_registry_service.h"
@@ -78,7 +77,6 @@
 #include "components/policy/core/common/policy_service_impl.h"
 #include "components/policy/core/common/schema.h"
 #include "components/prefs/testing_pref_store.h"
-#include "components/proxy_config/pref_proxy_config_tracker.h"
 #include "components/sync/model/fake_sync_change_processor.h"
 #include "components/sync/model/sync_error_factory_mock.h"
 #include "components/sync_preferences/pref_service_mock_factory.h"
@@ -537,9 +535,6 @@
   if (host_content_settings_map_.get())
     host_content_settings_map_->ShutdownOnUIThread();
 
-  if (pref_proxy_config_tracker_.get())
-    pref_proxy_config_tracker_->DetachFromPrefService();
-
   // Shutdown storage partitions before we post a task to delete
   // the resource context.
   ShutdownStoragePartitions();
@@ -888,16 +883,6 @@
   last_selected_directory_ = path;
 }
 
-PrefProxyConfigTracker* TestingProfile::GetProxyConfigTracker() {
-  if (!pref_proxy_config_tracker_.get()) {
-    // TestingProfile is used in unit tests, where local state is not available.
-    pref_proxy_config_tracker_.reset(
-        ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile(GetPrefs(),
-                                                                   NULL));
-  }
-  return pref_proxy_config_tracker_.get();
-}
-
 void TestingProfile::BlockUntilHistoryProcessesPendingRequests() {
   history::HistoryService* history_service =
       HistoryServiceFactory::GetForProfile(this,
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index c7b1e207..f86e5d9 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -328,8 +328,6 @@
   void InitChromeOSPreferences() override {}
 #endif  // defined(OS_CHROMEOS)
 
-  PrefProxyConfigTracker* GetProxyConfigTracker() override;
-
   // Schedules a task on the history backend and runs a nested loop until the
   // task is processed.  This has the effect of blocking the caller until the
   // history service processes all pending requests.
@@ -407,9 +405,6 @@
       extension_special_storage_policy_;
 #endif
 
-  // The proxy prefs tracker.
-  std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
-
   // The path to this profile. This will be valid in either of the two above
   // cases.
   base::FilePath profile_path_;
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index 88c2d02..2d814bb6 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -87,3 +87,7 @@
 TEST_F('PrintPreviewSettingsSectionsTest', 'Scaling', function() {
   this.runMochaTest(settings_sections_tests.TestNames.Scaling);
 });
+
+TEST_F('PrintPreviewSettingsSectionsTest', 'Other', function() {
+  this.runMochaTest(settings_sections_tests.TestNames.Other);
+});
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js
index deddf1a..8d8d082 100644
--- a/chrome/test/data/webui/print_preview/settings_section_test.js
+++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -12,6 +12,7 @@
     Margins: 'margins',
     Dpi: 'dpi',
     Scaling: 'scaling',
+    Other: 'other',
   };
 
   const suiteName = 'SettingsSectionsTests';
@@ -261,6 +262,62 @@
       setPdfDestination();
       expectEquals(true, scalingElement.hidden);
     });
+
+    test(assert(TestNames.Other), function() {
+      const optionsElement = page.$$('print-preview-other-options-settings');
+      const headerFooter = optionsElement.$$('#header-footer-container');
+      const duplex = optionsElement.$$('#duplex-container');
+      const cssBackground = optionsElement.$$('#css-background-container');
+      const rasterize = optionsElement.$$('#rasterize-container');
+      const selectionOnly = optionsElement.$$('#selection-only-container');
+
+      // Start with HTML + duplex capability.
+      setPdfDocument(false);
+      let capabilities = getCdd();
+      page.set('destination.capabilities', capabilities);
+      expectEquals(false, optionsElement.hidden);
+      expectEquals(false, headerFooter.hidden);
+      expectEquals(false, duplex.hidden);
+      expectEquals(false, cssBackground.hidden);
+      expectEquals(true, rasterize.hidden);
+      expectEquals(true, selectionOnly.hidden);
+
+      // Add a selection.
+      let info = new print_preview.DocumentInfo();
+      info.init(true, 'title', true);
+      page.set('documentInfo', info);
+      expectEquals(false, optionsElement.hidden);
+      expectEquals(false, selectionOnly.hidden);
+
+      // Remove duplex capability.
+      capabilities = getCdd();
+      delete capabilities.printer.duplex;
+      page.set('destination.capabilities', capabilities);
+      expectEquals(false, optionsElement.hidden);
+      expectEquals(true, duplex.hidden);
+
+      // PDF
+      setPdfDocument(true);
+      expectEquals(cr.isWindows || cr.isMac, optionsElement.hidden);
+      expectEquals(true, headerFooter.hidden);
+      expectEquals(true, duplex.hidden);
+      expectEquals(true, cssBackground.hidden);
+      expectEquals(cr.isWindows || cr.isMac, rasterize.hidden);
+      expectEquals(true, selectionOnly.hidden);
+
+      // Add a selection - should do nothing for PDFs.
+      info = new print_preview.DocumentInfo();
+      info.init(false, 'title', true);
+      page.set('documentInfo', info);
+      expectEquals(cr.isWindows || cr.isMac, optionsElement.hidden);
+      expectEquals(true, selectionOnly.hidden);
+
+      // Add duplex.
+      capabilities = getCdd();
+      page.set('destination.capabilities', capabilities);
+      expectEquals(false, optionsElement.hidden);
+      expectEquals(false, duplex.hidden);
+    });
   });
 
   return {
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 01b164c8..20d8489 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -34,6 +34,9 @@
   if (!is_android) {
     deps += [ ":cast_shell" ]
   }
+  if (is_linux) {
+    deps += [ "//chromecast/tracing" ]
+  }
   if (chromecast_branding != "public") {
     deps += [ "//chromecast/internal:all" ]
   }
@@ -514,7 +517,9 @@
     ]
 
     if (chromecast_branding != "public") {
-      sources += [ "$root_gen_dir/chromecast/internal/webui/app_strings_${locale}.pak" ]
+      sources += [
+        "$root_gen_dir/chromecast/internal/webui/app_strings_${locale}.pak",
+      ]
       deps += [ "//chromecast/internal/webui:chromecast_app_strings" ]
 
       if (enable_chromecast_webui) {
diff --git a/chromecast/base/chromecast_switches.cc b/chromecast/base/chromecast_switches.cc
index 5dbf6e4..7d79080 100644
--- a/chromecast/base/chromecast_switches.cc
+++ b/chromecast/base/chromecast_switches.cc
@@ -69,6 +69,7 @@
 const char kAlsaEnableUpsampling[] = "alsa-enable-upsampling";
 
 // Optional flag to set a fixed sample rate for the alsa device.
+// Deprecated: Use --audio-output-sample-rate instead.
 const char kAlsaFixedOutputSampleRate[] = "alsa-fixed-output-sample-rate";
 
 // Name of the simple mixer control element that the ALSA-based media library
@@ -96,6 +97,11 @@
 // value is 2.
 const char kAudioOutputChannels[] = "audio-output-channels";
 
+// Specify fixed sample rate for audio output stream. If this flag is not
+// specified the StreamMixer will choose sample rate based on the sample rate of
+// the media stream.
+const char kAudioOutputSampleRate[] = "audio-output-sample-rate";
+
 // Some platforms typically have very little 'free' memory, but plenty is
 // available in buffers+cached.  For such platforms, configure this amount
 // as the portion of buffers+cached memory that should be treated as
diff --git a/chromecast/base/chromecast_switches.h b/chromecast/base/chromecast_switches.h
index 8d3949a2..e887b95f 100644
--- a/chromecast/base/chromecast_switches.h
+++ b/chromecast/base/chromecast_switches.h
@@ -38,6 +38,9 @@
 extern const char kAcceptResourceProvider[];
 
 // ALSA-based CMA switches. (Only valid for audio products.)
+// TODO(sergeyu): kAlsaEnableUpsampling and kAlsaCheckCloseTimeout are
+// implemented in StreamMixer, which is not ALSA-specific - it's also used on
+// Fuchsia. Rename these flags.
 extern const char kAlsaOutputBufferSize[];
 extern const char kAlsaOutputPeriodSize[];
 extern const char kAlsaOutputStartThreshold[];
@@ -51,6 +54,7 @@
 extern const char kAlsaMuteElementName[];
 extern const char kMaxOutputVolumeDba1m[];
 extern const char kAudioOutputChannels[];
+extern const char kAudioOutputSampleRate[];
 
 // Memory pressure switches
 extern const char kMemPressureSystemReservedKb[];
diff --git a/chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.cc b/chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.cc
index 3656a6bc..2db91d4 100644
--- a/chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.cc
+++ b/chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.cc
@@ -143,10 +143,6 @@
   alsa_ = std::move(alsa);
 }
 
-bool MixerOutputStreamAlsa::IsFixedSampleRate() {
-  return fixed_sample_rate_ != kInvalidSampleRate;
-}
-
 bool MixerOutputStreamAlsa::Start(int sample_rate, int channels) {
   if (!alsa_) {
     alsa_ = std::make_unique<AlsaWrapper>();
@@ -427,22 +423,9 @@
     LOG(DFATAL) << "ALSA avail min must be no larger than the buffer size";
     alsa_avail_min_ = alsa_period_size_;
   }
-
-  fixed_sample_rate_ = GetSwitchValueNonNegativeInt(
-      switches::kAlsaFixedOutputSampleRate, kInvalidSampleRate);
-  if (fixed_sample_rate_ != kInvalidSampleRate) {
-    LOG(INFO) << "Setting fixed sample rate to " << fixed_sample_rate_;
-  }
 }
 
 int MixerOutputStreamAlsa::DetermineOutputRate(int requested_sample_rate) {
-  if (fixed_sample_rate_ != kInvalidSampleRate) {
-    LOG(INFO) << "Requested output rate is " << requested_sample_rate;
-    LOG(INFO) << "Cannot change rate since it is fixed to "
-              << fixed_sample_rate_;
-    return fixed_sample_rate_;
-  }
-
   unsigned int unsigned_output_sample_rate = requested_sample_rate;
 
   // Try the requested sample rate. If the ALSA driver doesn't know how to deal
diff --git a/chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.h b/chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.h
index eda0cc1..03f6fd2a 100644
--- a/chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.h
+++ b/chromecast/media/cma/backend/alsa/mixer_output_stream_alsa.h
@@ -27,7 +27,6 @@
   void SetAlsaWrapperForTest(std::unique_ptr<AlsaWrapper> alsa);
 
   // MixerOutputStream interface.
-  bool IsFixedSampleRate() override;
   bool Start(int requested_sample_rate, int channels) override;
   bool GetTimeUntilUnderrun(base::TimeDelta* result) override;
   int GetSampleRate() override;
@@ -57,9 +56,6 @@
 
   void UpdateRenderingDelay(int newly_pushed_frames);
 
-  // Value of --alsa-fixed-output-sample-rate flag if any.
-  int fixed_sample_rate_ = kInvalidSampleRate;
-
   std::unique_ptr<AlsaWrapper> alsa_;
 
   snd_pcm_t* pcm_ = nullptr;
diff --git a/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.cc b/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.cc
index b01d810..262a71f 100644
--- a/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.cc
+++ b/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.cc
@@ -39,10 +39,6 @@
     fuchsia_audio_manager_free(manager_);
 }
 
-bool MixerOutputStreamFuchsia::IsFixedSampleRate() {
-  return false;
-}
-
 bool MixerOutputStreamFuchsia::Start(int requested_sample_rate, int channels) {
   DCHECK(!stream_);
 
diff --git a/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h b/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h
index dfcc2a2..7d3bb07 100644
--- a/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h
+++ b/chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h
@@ -21,7 +21,6 @@
   ~MixerOutputStreamFuchsia() override;
 
   // MixerOutputStream interface.
-  bool IsFixedSampleRate() override;
   bool Start(int requested_sample_rate, int channels) override;
   bool GetTimeUntilUnderrun(base::TimeDelta* result) override;
   int GetSampleRate() override;
diff --git a/chromecast/media/cma/backend/mixer_output_stream.h b/chromecast/media/cma/backend/mixer_output_stream.h
index 4331076b..334cba1 100644
--- a/chromecast/media/cma/backend/mixer_output_stream.h
+++ b/chromecast/media/cma/backend/mixer_output_stream.h
@@ -26,9 +26,6 @@
 
   virtual ~MixerOutputStream() {}
 
-  // Returns true if the sample rate is fixed.
-  virtual bool IsFixedSampleRate() = 0;
-
   // Start the stream. Caller must call GetSampleRate() to get the actual sample
   // rate selected for the stream. It may be different from
   // |requested_sample_rate|, e.g. if IsFixedSampleRate() is true, or the device
diff --git a/chromecast/media/cma/backend/stream_mixer.cc b/chromecast/media/cma/backend/stream_mixer.cc
index f39ba03e..057a699 100644
--- a/chromecast/media/cma/backend/stream_mixer.cc
+++ b/chromecast/media/cma/backend/stream_mixer.cc
@@ -11,7 +11,6 @@
 #include <utility>
 
 #include "base/bind_helpers.h"
-#include "base/command_line.h"
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
@@ -152,13 +151,27 @@
          num_output_channels_ == 1);
 
   low_sample_rate_cutoff_ =
-      chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false)
+      GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false)
           ? kLowSampleRateCutoff
           : MixerOutputStream::kInvalidSampleRate;
 
-  // Read post-processing configuration file
-  PostProcessingPipelineParser pipeline_parser;
+  fixed_sample_rate_ = GetSwitchValueNonNegativeInt(
+      switches::kAudioOutputSampleRate, MixerOutputStream::kInvalidSampleRate);
 
+#if defined(USE_ALSA)
+  if (fixed_sample_rate_ == MixerOutputStream::kInvalidSampleRate) {
+    fixed_sample_rate_ =
+        GetSwitchValueNonNegativeInt(switches::kAlsaFixedOutputSampleRate,
+                                     MixerOutputStream::kInvalidSampleRate);
+  }
+#endif  // defined(USE_ALSA)
+
+  if (fixed_sample_rate_ != MixerOutputStream::kInvalidSampleRate) {
+    LOG(INFO) << "Setting fixed sample rate to " << fixed_sample_rate_;
+  }
+
+  // Read post-processing configuration file.
+  PostProcessingPipelineParser pipeline_parser;
   CreatePostProcessors(&pipeline_parser);
 
   // TODO(jyw): command line flag for filter frame alignment.
@@ -166,10 +179,10 @@
       << "Alignment must be a power of 2.";
 
   // --accept-resource-provider should imply a check close timeout of 0.
-  int default_close_timeout = chromecast::GetSwitchValueBoolean(
-                                  switches::kAcceptResourceProvider, false)
-                                  ? 0
-                                  : kDefaultCheckCloseTimeoutMs;
+  int default_close_timeout =
+      GetSwitchValueBoolean(switches::kAcceptResourceProvider, false)
+          ? 0
+          : kDefaultCheckCloseTimeoutMs;
   check_close_timeout_ = GetSwitchValueInt(switches::kAlsaCheckCloseTimeout,
                                            default_close_timeout);
 }
@@ -275,17 +288,20 @@
 bool StreamMixer::Start() {
   DCHECK(mixer_task_runner_->BelongsToCurrentThread());
 
-  int requested_sample_rate = requested_output_samples_per_second_;
-
   if (!output_)
     output_ = MixerOutputStream::Create();
 
-  if (low_sample_rate_cutoff_ != MixerOutputStream::kInvalidSampleRate &&
-      requested_sample_rate < low_sample_rate_cutoff_) {
+  int requested_sample_rate;
+  if (fixed_sample_rate_ != MixerOutputStream::kInvalidSampleRate) {
+    requested_sample_rate = fixed_sample_rate_;
+  } else if (low_sample_rate_cutoff_ != MixerOutputStream::kInvalidSampleRate &&
+             requested_output_samples_per_second_ < low_sample_rate_cutoff_) {
     requested_sample_rate =
         output_samples_per_second_ != MixerOutputStream::kInvalidSampleRate
             ? output_samples_per_second_
             : kLowSampleRateFallback;
+  } else {
+    requested_sample_rate = requested_output_samples_per_second_;
   }
 
   if (!output_->Start(requested_sample_rate, num_output_channels_)) {
@@ -357,7 +373,8 @@
   // If the new input is a primary one, we may need to change the output
   // sample rate to match its input sample rate.
   // We only change the output rate if it is not set to a fixed value.
-  if (input->primary() && output_ && !output_->IsFixedSampleRate()) {
+  if (input->primary() && output_ &&
+      fixed_sample_rate_ != MixerOutputStream::kInvalidSampleRate) {
     CheckChangeOutputRate(input->input_samples_per_second());
   }
 
diff --git a/chromecast/media/cma/backend/stream_mixer.h b/chromecast/media/cma/backend/stream_mixer.h
index 9282525..517ddaf 100644
--- a/chromecast/media/cma/backend/stream_mixer.h
+++ b/chromecast/media/cma/backend/stream_mixer.h
@@ -281,6 +281,7 @@
   int requested_output_samples_per_second_ = 0;
   int output_samples_per_second_ = 0;
   int low_sample_rate_cutoff_ = 0;
+  int fixed_sample_rate_ = 0;
 
   State state_;
 
diff --git a/chromecast/tracing/BUILD.gn b/chromecast/tracing/BUILD.gn
new file mode 100644
index 0000000..4893411
--- /dev/null
+++ b/chromecast/tracing/BUILD.gn
@@ -0,0 +1,51 @@
+# 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.
+
+import("//chromecast/chromecast.gni")
+import("//testing/test.gni")
+
+assert(!is_fuchsia)
+
+cast_source_set("system_tracing_common") {
+  sources = [
+    "system_tracing_common.cc",
+    "system_tracing_common.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
+cast_executable("traced") {
+  sources = [
+    "ftrace.cc",
+    "ftrace.h",
+    "tracing_service_main.cc",
+  ]
+
+  deps = [
+    ":system_tracing_common",
+    "//base",
+  ]
+}
+
+cast_source_set("system_tracer") {
+  sources = [
+    "system_tracer.cc",
+    "system_tracer.h",
+  ]
+
+  public_deps = [
+    ":system_tracing_common",
+    "//base",
+  ]
+}
+
+group("tracing") {
+  deps = [
+    ":system_tracing_common",
+    ":traced",
+  ]
+}
diff --git a/chromecast/tracing/OWNERS b/chromecast/tracing/OWNERS
new file mode 100644
index 0000000..35a2760
--- /dev/null
+++ b/chromecast/tracing/OWNERS
@@ -0,0 +1 @@
+spang@chromium.org
diff --git a/chromecast/tracing/ftrace.cc b/chromecast/tracing/ftrace.cc
new file mode 100644
index 0000000..6f38bfc
--- /dev/null
+++ b/chromecast/tracing/ftrace.cc
@@ -0,0 +1,205 @@
+// 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 "chromecast/tracing/ftrace.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "base/files/file_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/trace_event/common/trace_event_common.h"
+#include "chromecast/tracing/system_tracing_common.h"
+
+namespace chromecast {
+namespace tracing {
+namespace {
+
+const char kPathTracefs[] = "/sys/kernel/tracing";
+const char kTraceFileTracingOn[] = "tracing_on";
+const char kTraceFileTraceMarker[] = "trace_marker";
+const char kTraceFileSetEvent[] = "set_event";
+const char kTraceFileTraceClock[] = "trace_clock";
+const char kTraceFileTrace[] = "trace";
+const char kTraceFileBufferSizeKb[] = "buffer_size_kb";
+
+const char kBufferSizeKbRunning[] = "7040";
+const char kBufferSizeKbIdle[] = "1408";
+
+// TODO(spang): Load these lists from a configuration file.
+const char* const kGfxEvents[] = {
+    "i915:i915_flip_request",        "i915:i915_flip_complete",
+    "i915:i915_gem_object_pwrite",   "i915:intel_gpu_freq_change",
+    "exynos:exynos_flip_request",    "exynos:exynos_flip_complete",
+    "exynos:exynos_page_flip_state", "drm:drm_vblank_event",
+};
+
+const char* const kInputEvents[] = {
+    "irq:irq_threaded_handler_entry", "irq:irq_threaded_handler_exit",
+};
+
+const char* const kIrqEvents[] = {
+    "irq:irq_handler_exit", "irq:irq_handler_entry",
+};
+
+const char* const kPowerEvents[] = {
+    "power:cpu_idle",
+    "power:cpu_frequency",
+    "mali:mali_dvfs_set_clock",
+    "mali:mali_dvfs_set_voltage",
+    "cpufreq_interactive:cpufreq_interactive_boost",
+    "cpufreq_interactive:cpufreq_interactive_unboost",
+    "exynos_busfreq:exynos_busfreq_target_int",
+    "exynos_busfreq:exynos_busfreq_target_mif",
+};
+
+const char* const kSchedEvents[] = {
+    "sched:sched_switch", "sched:sched_wakeup",
+};
+
+const char* const kWorkqEvents[] = {
+    "workqueue:workqueue_execute_start", "workqueue:workqueue_execute_end",
+};
+
+void AddCategoryEvents(const std::string& category,
+                       std::vector<std::string>* events) {
+  if (category == "gfx") {
+    std::copy(kGfxEvents, kGfxEvents + arraysize(kGfxEvents),
+              std::back_inserter(*events));
+    return;
+  }
+  if (category == "input") {
+    std::copy(kInputEvents, kInputEvents + arraysize(kInputEvents),
+              std::back_inserter(*events));
+    return;
+  }
+  if (category == TRACE_DISABLED_BY_DEFAULT("irq")) {
+    std::copy(kIrqEvents, kIrqEvents + arraysize(kIrqEvents),
+              std::back_inserter(*events));
+    return;
+  }
+  if (category == "power") {
+    std::copy(kPowerEvents, kPowerEvents + arraysize(kPowerEvents),
+              std::back_inserter(*events));
+    return;
+  }
+  if (category == "sched") {
+    std::copy(kSchedEvents, kSchedEvents + arraysize(kSchedEvents),
+              std::back_inserter(*events));
+    return;
+  }
+  if (category == "workq") {
+    std::copy(kWorkqEvents, kWorkqEvents + arraysize(kWorkqEvents),
+              std::back_inserter(*events));
+    return;
+  }
+
+  LOG(WARNING) << "Unrecognized category: " << category;
+}
+
+bool WriteTracingFile(const char* trace_file, base::StringPiece contents) {
+  base::FilePath path = base::FilePath(kPathTracefs).Append(trace_file);
+
+  if (!base::WriteFile(path, contents.data(), contents.size())) {
+    PLOG(ERROR) << "write: " << path;
+    return false;
+  }
+
+  return true;
+}
+
+bool EnableTraceEvent(base::StringPiece event) {
+  base::FilePath path = base::FilePath(kPathTracefs).Append(kTraceFileSetEvent);
+
+  // Enabling events returns EINVAL if the event does not exist. It is normal
+  // for driver specific events to be missing when the driver is not built in.
+  if (!base::AppendToFile(path, event.data(), event.size()) &&
+      errno != EINVAL) {
+    PLOG(ERROR) << "write: " << path;
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+bool IsValidCategory(base::StringPiece str) {
+  for (size_t i = 0; i < kCategoryCount; ++i) {
+    base::StringPiece category(kCategories[i]);
+    if (category == str)
+      return true;
+  }
+
+  return false;
+}
+
+bool StartFtrace(const std::vector<std::string>& categories) {
+  if (categories.size() == 0) {
+    LOG(ERROR) << "No categories to enable";
+    return false;
+  }
+
+  std::vector<std::string> events;
+  for (const auto& category : categories)
+    AddCategoryEvents(category, &events);
+
+  if (events.size() == 0) {
+    LOG(ERROR) << "No events to enable";
+    return false;
+  }
+
+  // Disable tracing and clear events.
+  if (!WriteTracingFile(kTraceFileTracingOn, "0"))
+    return false;
+  if (!WriteTracingFile(kTraceFileSetEvent, "\n"))
+    return false;
+
+  // Use CLOCK_MONOTONIC so that kernel timestamps align with std::steady_clock
+  // and base::TimeTicks.
+  if (!WriteTracingFile(kTraceFileTraceClock, "mono"))
+    return false;
+
+  for (const auto& event : events)
+    EnableTraceEvent(event);
+
+  if (!WriteTracingFile(kTraceFileBufferSizeKb, kBufferSizeKbRunning))
+    return false;
+  if (!WriteTracingFile(kTraceFileTracingOn, "1"))
+    return false;
+
+  return true;
+}
+
+bool WriteFtraceTimeSyncMarker() {
+  return WriteTracingFile(kTraceFileTraceMarker,
+                          "trace_event_clock_sync: parent_ts=0");
+}
+
+bool StopFtrace() {
+  if (!WriteTracingFile(kTraceFileTracingOn, "0"))
+    return false;
+  return true;
+}
+
+base::ScopedFD GetFtraceData() {
+  base::FilePath path = base::FilePath(kPathTracefs).Append(kTraceFileTrace);
+  base::ScopedFD trace_data(HANDLE_EINTR(
+      open(path.value().c_str(), O_RDONLY | O_CLOEXEC | O_NONBLOCK)));
+  if (!trace_data.is_valid())
+    PLOG(ERROR) << "open: " << path.value();
+  return trace_data;
+}
+
+bool ClearFtrace() {
+  if (!WriteTracingFile(kTraceFileBufferSizeKb, kBufferSizeKbIdle))
+    return false;
+  if (!WriteTracingFile(kTraceFileTrace, "0"))
+    return false;
+  return true;
+}
+
+}  // namespace tracing
+}  // namespace chromecast
diff --git a/chromecast/tracing/ftrace.h b/chromecast/tracing/ftrace.h
new file mode 100644
index 0000000..92adc68
--- /dev/null
+++ b/chromecast/tracing/ftrace.h
@@ -0,0 +1,50 @@
+// 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 CHROMECAST_TRACING_FTRACE_H_
+#define CHROMECAST_TRACING_FTRACE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/strings/string_piece.h"
+
+namespace chromecast {
+namespace tracing {
+
+// Returns true if |category| is valid for system tracing.
+bool IsValidCategory(base::StringPiece category);
+
+// Starts ftrace for the specified categories.
+//
+// This must be paired with StopFtrace() or the kernel will continue tracing
+// indefinitely. Returns false if an error occurs writing to tracefs - this
+// usually indicates a configuration issue (e.g. tracefs not mounted).
+bool StartFtrace(const std::vector<std::string>& categories);
+
+// Writes time synchronization marker.
+//
+// This is used by trace-viewer to align ftrace clock with userspace
+// tracing. Since CLOCK_MONOTONIC is used in both cases, this merely
+// writes a zero offset. Call it at the end of tracing right before
+// StopFtrace(). Returns false if an error occurs writing to tracefs.
+bool WriteFtraceTimeSyncMarker();
+
+// Stops ftrace.
+//
+// This is safe to call even if tracing isn't started. Returns false if an error
+// occurs writing to tracefs.
+bool StopFtrace();
+
+// Opens ftrace buffer for reading.
+base::ScopedFD GetFtraceData();
+
+// Clears ftrace buffer. Returns false if an error occurs writing to tracefs.
+bool ClearFtrace();
+
+}  // namespace tracing
+}  // namespace chromecast
+
+#endif  // CHROMECAST_TRACING_FTRACE_H_
diff --git a/chromecast/tracing/system_tracer.cc b/chromecast/tracing/system_tracer.cc
new file mode 100644
index 0000000..bb487bb7
--- /dev/null
+++ b/chromecast/tracing/system_tracer.cc
@@ -0,0 +1,200 @@
+// 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 "chromecast/tracing/system_tracer.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_libevent.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket.h"
+#include "base/trace_event/trace_config.h"
+#include "chromecast/tracing/system_tracing_common.h"
+
+namespace chromecast {
+namespace {
+
+constexpr size_t kBufferSize = 1UL << 16;
+
+base::ScopedFD CreateClientSocket() {
+  base::ScopedFD socket_fd(
+      socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
+  if (!socket_fd.is_valid()) {
+    PLOG(ERROR) << "socket";
+    return base::ScopedFD();
+  }
+
+  struct sockaddr_un addr =
+      chromecast::tracing::GetSystemTracingSocketAddress();
+
+  if (connect(socket_fd.get(), reinterpret_cast<struct sockaddr*>(&addr),
+              sizeof(addr))) {
+    PLOG(ERROR) << "connect: " << addr.sun_path;
+    return base::ScopedFD();
+  }
+
+  return socket_fd;
+}
+
+}  // namespace
+
+SystemTracer::SystemTracer() : buffer_(new char[kBufferSize]) {}
+
+SystemTracer::~SystemTracer() {
+  Cleanup();
+}
+
+void SystemTracer::StartTracing(base::StringPiece categories,
+                                StartTracingCallback callback) {
+  start_tracing_callback_ = std::move(callback);
+  if (state_ != State::INITIAL) {
+    FailStartTracing();
+    return;
+  }
+
+  if (categories.size() == 0) {
+    // No relevant categories are enabled.
+    FailStartTracing();
+    return;
+  }
+
+  connection_fd_ = CreateClientSocket();
+  if (!connection_fd_.is_valid()) {
+    FailStartTracing();
+    return;
+  }
+
+  if (!base::UnixDomainSocket::SendMsg(connection_fd_.get(), categories.data(),
+                                       categories.size(), std::vector<int>())) {
+    PLOG(ERROR) << "sendmsg";
+    FailStartTracing();
+    return;
+  }
+
+  connection_watcher_ = base::FileDescriptorWatcher::WatchReadable(
+      connection_fd_.get(),
+      base::BindRepeating(&SystemTracer::ReceiveStartAckAndTracePipe,
+                          base::Unretained(this)));
+  state_ = State::STARTING;
+}
+
+void SystemTracer::StopTracing(const StopTracingCallback& callback) {
+  stop_tracing_callback_ = callback;
+  if (state_ != State::TRACING) {
+    FailStopTracing();
+    return;
+  }
+
+  char stop_tracing_message[] = {0};
+  if (!base::UnixDomainSocket::SendMsg(
+          connection_fd_.get(), stop_tracing_message,
+          sizeof(stop_tracing_message), std::vector<int>())) {
+    PLOG(ERROR) << "sendmsg";
+    FailStopTracing();
+    return;
+  }
+
+  trace_pipe_watcher_ = base::FileDescriptorWatcher::WatchReadable(
+      trace_pipe_fd_.get(), base::BindRepeating(&SystemTracer::ReceiveTraceData,
+                                                base::Unretained(this)));
+  state_ = State::READING;
+}
+
+void SystemTracer::ReceiveStartAckAndTracePipe() {
+  DCHECK_EQ(state_, State::STARTING);
+
+  std::vector<base::ScopedFD> fds;
+  ssize_t received = base::UnixDomainSocket::RecvMsg(
+      connection_fd_.get(), buffer_.get(), kBufferSize, &fds);
+  if (received == 0) {
+    LOG(ERROR) << "EOF from server";
+    FailStartTracing();
+    return;
+  }
+  if (received < 0) {
+    PLOG(ERROR) << "recvmsg";
+    FailStartTracing();
+    return;
+  }
+  if (fds.size() != 1) {
+    LOG(ERROR) << "Start ack missing trace pipe";
+    FailStartTracing();
+    return;
+  }
+
+  trace_pipe_fd_ = std::move(fds[0]);
+  connection_watcher_.reset();
+  state_ = State::TRACING;
+  std::move(start_tracing_callback_).Run(Status::OK);
+}
+
+void SystemTracer::ReceiveTraceData() {
+  DCHECK_EQ(state_, State::READING);
+
+  for (;;) {
+    ssize_t bytes =
+        HANDLE_EINTR(read(trace_pipe_fd_.get(), buffer_.get(), kBufferSize));
+    if (bytes < 0) {
+      if (errno == EAGAIN)
+        return;  // Wait for more data.
+      PLOG(ERROR) << "read: trace";
+      FailStopTracing();
+      return;
+    }
+
+    if (bytes == 0) {
+      FinishTracing();
+      return;
+    }
+
+    trace_data_.append(buffer_.get(), bytes);
+
+    static constexpr size_t kPartialTraceDataSize = 1UL << 20;  // 1 MiB
+    if (trace_data_.size() > kPartialTraceDataSize) {
+      SendPartialTraceData();
+      return;
+    }
+  }
+}
+
+void SystemTracer::FailStartTracing() {
+  std::move(start_tracing_callback_).Run(Status::FAIL);
+  Cleanup();
+}
+
+void SystemTracer::FailStopTracing() {
+  stop_tracing_callback_.Run(Status::FAIL, "");
+  Cleanup();
+}
+
+void SystemTracer::SendPartialTraceData() {
+  DCHECK_EQ(state_, State::READING);
+  stop_tracing_callback_.Run(Status::KEEP_GOING, std::move(trace_data_));
+  trace_data_ = "";
+}
+
+void SystemTracer::FinishTracing() {
+  DCHECK_EQ(state_, State::READING);
+  stop_tracing_callback_.Run(Status::OK, std::move(trace_data_));
+  Cleanup();
+}
+
+void SystemTracer::Cleanup() {
+  connection_watcher_.reset();
+  connection_fd_.reset();
+  trace_pipe_watcher_.reset();
+  trace_pipe_fd_.reset();
+  start_tracing_callback_.Reset();
+  stop_tracing_callback_.Reset();
+  state_ = State::FINISHED;
+}
+
+}  // namespace chromecast
diff --git a/chromecast/tracing/system_tracer.h b/chromecast/tracing/system_tracer.h
new file mode 100644
index 0000000..89d4f09
--- /dev/null
+++ b/chromecast/tracing/system_tracer.h
@@ -0,0 +1,88 @@
+// 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 CHROMECAST_TRACING_SYSTEM_TRACER_H_
+#define CHROMECAST_TRACING_SYSTEM_TRACER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/message_loop/message_pump_libevent.h"
+
+namespace chromecast {
+
+class SystemTracer {
+ public:
+  SystemTracer();
+  ~SystemTracer();
+
+  enum class Status {
+    OK,
+    KEEP_GOING,
+    FAIL,
+  };
+
+  using StartTracingCallback = base::OnceCallback<void(Status)>;
+  using StopTracingCallback =
+      base::RepeatingCallback<void(Status status, std::string trace_data)>;
+
+  // Start system tracing for categories in |categories| (comma separated).
+  void StartTracing(base::StringPiece categories,
+                    StartTracingCallback callback);
+
+  // Stop system tracing.
+  //
+  // This will call |callback| on the current thread with the trace data. If
+  // |status| is Status::KEEP_GOING, another call will be made with additional
+  // data.
+  void StopTracing(const StopTracingCallback& callback);
+
+ private:
+  enum class State {
+    INITIAL,   // Not yet started.
+    STARTING,  // Sent start message, waiting for ack.
+    TRACING,   // Tracing, not yet requested stop.
+    READING,   // Trace stopped, reading output.
+    FINISHED,  // All done.
+  };
+
+  void ReceiveStartAckAndTracePipe();
+  void ReceiveTraceData();
+  void FailStartTracing();
+  void FailStopTracing();
+  void SendPartialTraceData();
+  void FinishTracing();
+  void Cleanup();
+
+  // Current state of tracing attempt.
+  State state_ = State::INITIAL;
+
+  // Unix socket connection to tracing daemon.
+  base::ScopedFD connection_fd_;
+  std::unique_ptr<base::FileDescriptorWatcher::Controller> connection_watcher_;
+
+  // Pipe for trace data.
+  base::ScopedFD trace_pipe_fd_;
+  std::unique_ptr<base::FileDescriptorWatcher::Controller> trace_pipe_watcher_;
+
+  // Read buffer (of size kBufferSize).
+  std::unique_ptr<char[]> buffer_;
+
+  // Callbacks for StartTracing() and StopTracing().
+  StartTracingCallback start_tracing_callback_;
+  StopTracingCallback stop_tracing_callback_;
+
+  // Trace data.
+  std::string trace_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemTracer);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_TRACING_SYSTEM_TRACER_H_
diff --git a/chromecast/tracing/system_tracing_common.cc b/chromecast/tracing/system_tracing_common.cc
new file mode 100644
index 0000000..c88f721
--- /dev/null
+++ b/chromecast/tracing/system_tracing_common.cc
@@ -0,0 +1,36 @@
+// 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 "chromecast/tracing/system_tracing_common.h"
+
+#include <string.h>
+#include "base/macros.h"
+#include "base/trace_event/common/trace_event_common.h"
+
+namespace chromecast {
+namespace tracing {
+namespace {
+
+const char kSocketPath[] = "/dev/socket/tracing/tracing";
+
+}  // namespace
+
+const char* const kCategories[] = {
+    "gfx",   "input", TRACE_DISABLED_BY_DEFAULT("irq"),
+    "power", "sched", "workq"};
+
+const size_t kCategoryCount = arraysize(kCategories);
+
+sockaddr_un GetSystemTracingSocketAddress() {
+  struct sockaddr_un addr;
+  memset(&addr, 0, sizeof(addr));
+  static_assert(sizeof(kSocketPath) <= sizeof(addr.sun_path),
+                "Address too long");
+  strncpy(addr.sun_path, kSocketPath, sizeof(addr.sun_path) - 1);
+  addr.sun_family = AF_UNIX;
+  return addr;
+}
+
+}  // namespace tracing
+}  // namespace chromecast
diff --git a/chromecast/tracing/system_tracing_common.h b/chromecast/tracing/system_tracing_common.h
new file mode 100644
index 0000000..d7e3a8f
--- /dev/null
+++ b/chromecast/tracing/system_tracing_common.h
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_TRACING_SYSTEM_TRACING_COMMON_H_
+#define CHROMECAST_TRACING_SYSTEM_TRACING_COMMON_H_
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+namespace chromecast {
+namespace tracing {
+
+extern const char* const kCategories[];
+
+extern const size_t kCategoryCount;
+
+sockaddr_un GetSystemTracingSocketAddress();
+
+}  // namespace tracing
+}  // namespace chromecast
+
+#endif  // CHROMECAST_TRACING_SYSTEM_TRACING_COMMON_H_
diff --git a/chromecast/tracing/tracing_service_main.cc b/chromecast/tracing/tracing_service_main.cc
new file mode 100644
index 0000000..7a754b0
--- /dev/null
+++ b/chromecast/tracing/tracing_service_main.cc
@@ -0,0 +1,412 @@
+// 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 <fcntl.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <memory>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chromecast/tracing/ftrace.h"
+#include "chromecast/tracing/system_tracing_common.h"
+
+namespace chromecast {
+namespace tracing {
+namespace {
+
+constexpr size_t kMessageSize = 4096;
+
+base::ScopedFD CreateServerSocket() {
+  struct sockaddr_un addr = GetSystemTracingSocketAddress();
+
+  if (unlink(addr.sun_path) != 0 && errno != ENOENT)
+    PLOG(ERROR) << "unlink: " << addr.sun_path;
+
+  base::ScopedFD socket_fd(
+      socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
+  if (!socket_fd.is_valid()) {
+    PLOG(ERROR) << "socket";
+    return base::ScopedFD();
+  }
+
+  if (bind(socket_fd.get(), reinterpret_cast<struct sockaddr*>(&addr),
+           sizeof(addr))) {
+    PLOG(ERROR) << "bind: " << addr.sun_path;
+    return base::ScopedFD();
+  }
+
+  if (chmod(addr.sun_path, 0666))
+    PLOG(WARNING) << "chmod: " << addr.sun_path;
+
+  static constexpr int kBacklog = 10;
+  if (listen(socket_fd.get(), kBacklog)) {
+    PLOG(ERROR) << "listen: " << addr.sun_path;
+    return base::ScopedFD();
+  }
+
+  return socket_fd;
+}
+
+std::vector<std::string> ParseCategories(base::StringPiece message) {
+  std::vector<std::string> requested_categories = base::SplitString(
+      message, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+  std::vector<std::string> categories;
+  for (const auto& category : requested_categories) {
+    if (IsValidCategory(category))
+      categories.push_back(category);
+    else
+      LOG(WARNING) << "Unrecognized category: " << category;
+  }
+  return categories;
+}
+
+class TraceCopyTask : public base::MessagePumpLibevent::Watcher {
+ public:
+  // Read 64 kB at a time (standard pipe capacity).
+  static constexpr size_t kCopyBufferSize = 1UL << 16;
+
+  enum class Status {
+    SUCCESS,
+    FAILURE,
+  };
+
+  TraceCopyTask(base::ScopedFD in_fd,
+                base::ScopedFD out_fd,
+                base::OnceCallback<void(Status, size_t)> callback)
+      : buffer_(new char[kCopyBufferSize]),
+        in_fd_(std::move(in_fd)),
+        out_fd_(std::move(out_fd)),
+        out_watcher_(FROM_HERE),
+        callback_(std::move(callback)) {}
+  virtual ~TraceCopyTask() {}
+
+  void Start() {
+    base::MessageLoopForIO::current()->WatchFileDescriptor(
+        out_fd_.get(), true /* persistent */,
+        base::MessageLoopForIO::WATCH_WRITE, &out_watcher_, this);
+  }
+
+  // base::MessagePumpLibevent::Watcher:
+  void OnFileCanReadWithoutBlocking(int fd) override { NOTREACHED(); }
+  void OnFileCanWriteWithoutBlocking(int fd) override {
+    DCHECK_EQ(out_fd_.get(), fd);
+    CopyTraceData();
+  }
+
+ private:
+  void CopyTraceData() {
+    for (;;) {
+      if (read_ == written_) {
+        total_copied_ += read_;
+        read_ = written_ = 0;
+
+        // Read trace data from debugfs.
+        ssize_t read_bytes =
+            HANDLE_EINTR(read(in_fd_.get(), buffer_.get(), kCopyBufferSize));
+        if (read_bytes == 0) {
+          // EOF, we're done;
+          Finish(Status::SUCCESS);
+          return;
+        } else if (read_bytes < 0) {
+          PLOG(ERROR) << "read: trace";
+          Finish(Status::FAILURE);
+          return;
+        }
+
+        read_ = read_bytes;
+      }
+
+      // Write trace data to output pipe.
+      ssize_t written_bytes = HANDLE_EINTR(
+          write(out_fd_.get(), buffer_.get() + written_, read_ - written_));
+      if (written_bytes < 0) {
+        if (errno == EAGAIN)
+          return;  // Wait for more space.
+        PLOG(ERROR) << "write: pipe";
+        Finish(Status::FAILURE);
+        return;
+      }
+      written_ += written_bytes;
+    }
+  }
+
+  void Finish(Status status) {
+    out_watcher_.StopWatchingFileDescriptor();
+    in_fd_.reset();
+    out_fd_.reset();
+    buffer_.reset();
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback_), status, total_copied_));
+  }
+
+  std::unique_ptr<char[]> buffer_;
+  size_t read_ = 0;
+  size_t written_ = 0;
+  size_t total_copied_ = 0;
+
+  // Trace data file.
+  base::ScopedFD in_fd_;
+
+  // Pipe for trace data.
+  base::ScopedFD out_fd_;
+  base::MessagePumpLibevent::FileDescriptorWatcher out_watcher_;
+
+  // Callback for when copy finishes.
+  base::OnceCallback<void(Status, size_t)> callback_;
+};
+
+class TraceConnection : public base::MessagePumpLibevent::Watcher {
+ public:
+  TraceConnection(base::ScopedFD connection_fd, base::OnceClosure callback)
+      : recv_buffer_(new char[kMessageSize]),
+        connection_fd_(std::move(connection_fd)),
+        connection_watcher_(FROM_HERE),
+        callback_(std::move(callback)),
+        weak_ptr_factory_(this) {}
+  virtual ~TraceConnection() {}
+
+  void Init() {
+    base::MessageLoopForIO::current()->WatchFileDescriptor(
+        connection_fd_.get(), true /* persistent */,
+        base::MessageLoopForIO::WATCH_READ, &connection_watcher_, this);
+  }
+
+  // base::MessagePumpLibevent::Watcher:
+  void OnFileCanReadWithoutBlocking(int fd) override {
+    DCHECK_EQ(connection_fd_.get(), fd);
+    ReceiveClientMessage();
+  }
+  void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
+
+ private:
+  enum class State {
+    INITIAL,        // Waiting for init message
+    TRACING,        // Ftrace started.
+    COPYING,        // Ftrace stopped, copying trace data.
+    COPY_COMPLETE,  // Ftrace stopped, all data written to pipe.
+    FINISHED,       // All done.
+  };
+
+  void ReceiveClientMessage() {
+    std::vector<base::ScopedFD> fds;
+    ssize_t bytes = base::UnixDomainSocket::RecvMsg(
+        connection_fd_.get(), recv_buffer_.get(), kMessageSize, &fds);
+    if (bytes < 0) {
+      PLOG(ERROR) << "recvmsg";
+      Finish();
+      return;
+    } else if (bytes == 0) {
+      LOG(INFO) << "connection closed";
+      Finish();
+    } else {
+      base::StringPiece message(recv_buffer_.get(), bytes);
+      HandleClientMessage(message);
+    }
+  }
+
+  void HandleClientMessage(base::StringPiece message) {
+    if (state_ == State::INITIAL) {
+      std::vector<std::string> categories = ParseCategories(message);
+
+      if (!StartFtrace(categories)) {
+        LOG(ERROR) << "Failed to start ftrace";
+        Finish();
+        return;
+      }
+
+      if (!SendTracePipeToClient()) {
+        LOG(ERROR) << "Failed to send trace pipe";
+        Finish();
+        return;
+      }
+
+      LOG(INFO) << "Started tracing for categories: "
+                << base::JoinString(categories, ",");
+
+      state_ = State::TRACING;
+    } else if (state_ == State::TRACING) {
+      WriteFtraceTimeSyncMarker();
+      StopFtrace();
+      base::ScopedFD trace_data = GetFtraceData();
+      if (!trace_data.is_valid()) {
+        LOG(ERROR) << "Failed to get trace data";
+        Finish();
+        return;
+      }
+
+      LOG(INFO) << "Tracing stopped";
+      trace_copy_task_ = std::make_unique<TraceCopyTask>(
+          std::move(trace_data), std::move(trace_pipe_),
+          base::BindOnce(&TraceConnection::OnFinishedCopying,
+                         weak_ptr_factory_.GetWeakPtr()));
+      trace_copy_task_->Start();
+      state_ = State::COPYING;
+    } else {
+      LOG(WARNING) << "Unexpected message";
+      Finish();
+      return;
+    }
+  }
+
+  void OnFinishedCopying(TraceCopyTask::Status status, size_t trace_data_size) {
+    if (status == TraceCopyTask::Status::SUCCESS) {
+      LOG(INFO) << "Finished tracing (" << trace_data_size << " bytes copied)";
+      state_ = State::COPY_COMPLETE;
+    } else {
+      LOG(INFO) << "I/O error copying trace data";
+    }
+
+    Finish();
+  }
+
+  bool SendTracePipeToClient() {
+    int pipefd[2] = {-1, -1};
+    if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK)) {
+      PLOG(ERROR) << "pipe2";
+      return false;
+    }
+    base::ScopedFD read_end(pipefd[0]);
+    base::ScopedFD write_end(pipefd[1]);
+
+    const char response[] = {0};
+    std::vector<int> send_fds;
+    send_fds.push_back(read_end.get());
+    if (!base::UnixDomainSocket::SendMsg(connection_fd_.get(), response,
+                                         sizeof(response), send_fds)) {
+      PLOG(ERROR) << "sendmsg";
+      return false;
+    }
+
+    trace_pipe_ = std::move(write_end);
+    return true;
+  }
+
+  void Finish() {
+    if (state_ != State::COPY_COMPLETE)
+      LOG(WARNING) << "Ending tracing without sending data";
+    trace_copy_task_.reset();
+    state_ = State::FINISHED;
+    recv_buffer_.reset();
+    connection_watcher_.StopWatchingFileDescriptor();
+    connection_fd_.reset();
+    StopFtrace();
+    ClearFtrace();
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback_)));
+  }
+
+  // Tracing state.
+  State state_ = State::INITIAL;
+
+  // Buffer for incoming messages.
+  std::unique_ptr<char[]> recv_buffer_;
+
+  // Client connection.
+  base::ScopedFD connection_fd_;
+  base::MessagePumpLibevent::FileDescriptorWatcher connection_watcher_;
+
+  // Pipe for trace output.
+  base::ScopedFD trace_pipe_;
+
+  // Task to send all trace output to client via a pipe.
+  std::unique_ptr<TraceCopyTask> trace_copy_task_;
+
+  // Callback for when connection closes.
+  base::OnceClosure callback_;
+
+  base::WeakPtrFactory<TraceConnection> weak_ptr_factory_;
+};
+
+class TracingService : public base::MessagePumpLibevent::Watcher {
+ public:
+  TracingService()
+      : server_socket_watcher_(FROM_HERE), weak_ptr_factory_(this) {}
+  virtual ~TracingService() {}
+
+  bool Init() {
+    server_socket_ = CreateServerSocket();
+    if (!server_socket_.is_valid())
+      return false;
+
+    base::MessageLoopForIO::current()->WatchFileDescriptor(
+        server_socket_.get(), true /* persistent */,
+        base::MessageLoopForIO::WATCH_READ, &server_socket_watcher_, this);
+
+    return true;
+  }
+
+  // base::MessagePumpLibevent::Watcher:
+  void OnFileCanReadWithoutBlocking(int fd) override {
+    DCHECK_EQ(server_socket_.get(), fd);
+    AcceptConnection();
+  }
+  void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
+
+ private:
+  void AcceptConnection() {
+    base::ScopedFD connection_fd(accept4(server_socket_.get(), nullptr, nullptr,
+                                         SOCK_NONBLOCK | SOCK_CLOEXEC));
+    if (!connection_fd.is_valid()) {
+      PLOG(ERROR) << "accept: ";
+      return;
+    }
+
+    trace_connection_ = std::make_unique<TraceConnection>(
+        std::move(connection_fd),
+        base::BindOnce(&TracingService::OnConnectionClosed,
+                       weak_ptr_factory_.GetWeakPtr()));
+    trace_connection_->Init();
+  }
+
+  void OnConnectionClosed() { trace_connection_.reset(); }
+
+  // Socket and watcher for listening socket.
+  base::ScopedFD server_socket_;
+  base::MessagePumpLibevent::FileDescriptorWatcher server_socket_watcher_;
+
+  // Currently active tracing connection.
+  // There can only be one; ftrace affects the whole system.
+  std::unique_ptr<TraceConnection> trace_connection_;
+
+  base::WeakPtrFactory<TracingService> weak_ptr_factory_;
+};
+
+}  // namespace
+}  // namespace tracing
+}  // namespace chromecast
+
+int main(int argc, char** argv) {
+  base::AtExitManager exit_manager;
+  base::CommandLine::Init(argc, argv);
+  logging::InitLogging(logging::LoggingSettings());
+
+  signal(SIGPIPE, SIG_IGN);
+
+  LOG(INFO) << "Starting system tracing service...";
+
+  base::MessageLoopForIO message_loop;
+  chromecast::tracing::TracingService service;
+
+  if (!service.Init())
+    return EXIT_FAILURE;
+
+  base::RunLoop run_loop;
+  run_loop.Run();
+
+  return EXIT_SUCCESS;
+}
diff --git a/chromeos/dbus/cros_disks_client.cc b/chromeos/dbus/cros_disks_client.cc
index 3b0c08d8..44b40c7 100644
--- a/chromeos/dbus/cros_disks_client.cc
+++ b/chromeos/dbus/cros_disks_client.cc
@@ -141,15 +141,24 @@
 
   // CrosDisksClient override.
   void EnumerateAutoMountableDevices(
-      const EnumerateAutoMountableDevicesCallback& callback,
+      const EnumerateDevicesCallback& callback,
       const base::Closure& error_callback) override {
     dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
                                  cros_disks::kEnumerateAutoMountableDevices);
-    proxy_->CallMethod(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&CrosDisksClientImpl::OnEnumerateAutoMountableDevices,
-                       weak_ptr_factory_.GetWeakPtr(), callback,
-                       error_callback));
+    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+                       base::BindOnce(&CrosDisksClientImpl::OnEnumerateDevices,
+                                      weak_ptr_factory_.GetWeakPtr(), callback,
+                                      error_callback));
+  }
+
+  void EnumerateDevices(const EnumerateDevicesCallback& callback,
+                        const base::Closure& error_callback) override {
+    dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
+                                 cros_disks::kEnumerateDevices);
+    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+                       base::BindOnce(&CrosDisksClientImpl::OnEnumerateDevices,
+                                      weak_ptr_factory_.GetWeakPtr(), callback,
+                                      error_callback));
   }
 
   // CrosDisksClient override.
@@ -331,12 +340,11 @@
     callback.Run();
   }
 
-  // Handles the result of EnumerateAutoMountableDevices and calls |callback| or
-  // |error_callback|.
-  void OnEnumerateAutoMountableDevices(
-      const EnumerateAutoMountableDevicesCallback& callback,
-      const base::Closure& error_callback,
-      dbus::Response* response) {
+  // Handles the result of EnumerateDevices and EnumarateAutoMountableDevices.
+  // Calls |callback| or |error_callback|.
+  void OnEnumerateDevices(const EnumerateDevicesCallback& callback,
+                          const base::Closure& error_callback,
+                          dbus::Response* response) {
     if (!response) {
       error_callback.Run();
       return;
@@ -497,10 +505,11 @@
       has_media_(false),
       on_boot_device_(false),
       on_removable_device_(false),
-      device_type_(DEVICE_TYPE_UNKNOWN),
-      total_size_in_bytes_(0),
       is_read_only_(false),
-      is_hidden_(true) {
+      is_hidden_(true),
+      is_virtual_(false),
+      device_type_(DEVICE_TYPE_UNKNOWN),
+      total_size_in_bytes_(0) {
   InitializeFromResponse(response);
 }
 
@@ -627,6 +636,8 @@
       cros_disks::kDeviceIsOnBootDevice, &on_boot_device_);
   properties->GetBooleanWithoutPathExpansion(
       cros_disks::kDeviceIsOnRemovableDevice, &on_removable_device_);
+  properties->GetBooleanWithoutPathExpansion(cros_disks::kDeviceIsVirtual,
+                                             &is_virtual_);
   properties->GetStringWithoutPathExpansion(
       cros_disks::kNativePath, &system_path_);
   properties->GetStringWithoutPathExpansion(
diff --git a/chromeos/dbus/cros_disks_client.h b/chromeos/dbus/cros_disks_client.h
index 8b80ec6..d571986 100644
--- a/chromeos/dbus/cros_disks_client.h
+++ b/chromeos/dbus/cros_disks_client.h
@@ -162,6 +162,15 @@
   // Is the disk on a removable device.
   bool on_removable_device() const { return on_removable_device_; }
 
+  // Is the device read-only.
+  bool is_read_only() const { return is_read_only_; }
+
+  // Returns true if the device should be hidden from the file browser.
+  bool is_hidden() const { return is_hidden_; }
+
+  // Is the disk virtual.
+  bool is_virtual() const { return is_virtual_; }
+
   // Disk file path (e.g. /dev/sdb).
   const std::string& file_path() const { return file_path_; }
 
@@ -189,12 +198,6 @@
   // Total size of the disk in bytes.
   uint64_t total_size_in_bytes() const { return total_size_in_bytes_; }
 
-  // Is the device read-only.
-  bool is_read_only() const { return is_read_only_; }
-
-  // Returns true if the device should be hidden from the file browser.
-  bool is_hidden() const { return is_hidden_; }
-
   // Returns file system uuid.
   const std::string& uuid() const { return uuid_; }
 
@@ -211,6 +214,9 @@
   bool has_media_;
   bool on_boot_device_;
   bool on_removable_device_;
+  bool is_read_only_;
+  bool is_hidden_;
+  bool is_virtual_;
 
   std::string file_path_;
   std::string label_;
@@ -221,8 +227,6 @@
   std::string drive_model_;
   DeviceType device_type_;
   uint64_t total_size_in_bytes_;
-  bool is_read_only_;
-  bool is_hidden_;
   std::string uuid_;
   std::string file_system_type_;
 };
@@ -261,10 +265,10 @@
 // by callbacks.
 class CHROMEOS_EXPORT CrosDisksClient : public DBusClient {
  public:
-  // A callback to handle the result of EnumerateAutoMountableDevices.
-  // The argument is the enumerated device paths.
+  // A callback to handle the result of EnumerateAutoMountableDevices and
+  // EnumerateDevices. The argument is the enumerated device paths.
   typedef base::Callback<void(const std::vector<std::string>& device_paths)>
-      EnumerateAutoMountableDevicesCallback;
+      EnumerateDevicesCallback;
 
   // A callback to handle the result of EnumerateMountEntries.
   // The argument is the enumerated mount entries.
@@ -329,9 +333,14 @@
   // Calls EnumerateAutoMountableDevices method.  |callback| is called after the
   // method call succeeds, otherwise, |error_callback| is called.
   virtual void EnumerateAutoMountableDevices(
-      const EnumerateAutoMountableDevicesCallback& callback,
+      const EnumerateDevicesCallback& callback,
       const base::Closure& error_callback) = 0;
 
+  // Calls EnumerateDevices method.  |callback| is called after the
+  // method call succeeds, otherwise, |error_callback| is called.
+  virtual void EnumerateDevices(const EnumerateDevicesCallback& callback,
+                                const base::Closure& error_callback) = 0;
+
   // Calls EnumerateMountEntries.  |callback| is called after the
   // method call succeeds, otherwise, |error_callback| is called.
   virtual void EnumerateMountEntries(
diff --git a/chromeos/dbus/fake_cros_disks_client.cc b/chromeos/dbus/fake_cros_disks_client.cc
index aa1490f..9de0892 100644
--- a/chromeos/dbus/fake_cros_disks_client.cc
+++ b/chromeos/dbus/fake_cros_disks_client.cc
@@ -141,9 +141,12 @@
 }
 
 void FakeCrosDisksClient::EnumerateAutoMountableDevices(
-    const EnumerateAutoMountableDevicesCallback& callback,
-    const base::Closure& error_callback) {
-}
+    const EnumerateDevicesCallback& callback,
+    const base::Closure& error_callback) {}
+
+void FakeCrosDisksClient::EnumerateDevices(
+    const EnumerateDevicesCallback& callback,
+    const base::Closure& error_callback) {}
 
 void FakeCrosDisksClient::EnumerateMountEntries(
     const EnumerateMountEntriesCallback& callback,
diff --git a/chromeos/dbus/fake_cros_disks_client.h b/chromeos/dbus/fake_cros_disks_client.h
index 10e3fed1..495d97f 100644
--- a/chromeos/dbus/fake_cros_disks_client.h
+++ b/chromeos/dbus/fake_cros_disks_client.h
@@ -41,8 +41,10 @@
                const base::Closure& callback,
                const base::Closure& error_callback) override;
   void EnumerateAutoMountableDevices(
-      const EnumerateAutoMountableDevicesCallback& callback,
+      const EnumerateDevicesCallback& callback,
       const base::Closure& error_callback) override;
+  void EnumerateDevices(const EnumerateDevicesCallback& callback,
+                        const base::Closure& error_callback) override;
   void EnumerateMountEntries(const EnumerateMountEntriesCallback& callback,
                              const base::Closure& error_callback) override;
   void Format(const std::string& device_path,
diff --git a/chromeos/disks/disk_mount_manager.cc b/chromeos/disks/disk_mount_manager.cc
index 5b9853c..0f4856f 100644
--- a/chromeos/disks/disk_mount_manager.cc
+++ b/chromeos/disks/disk_mount_manager.cc
@@ -268,7 +268,7 @@
     refresh_callbacks_.push_back(callback);
     if (refresh_callbacks_.size() == 1) {
       // If there's no in-flight refreshing task, start it.
-      cros_disks_client_->EnumerateAutoMountableDevices(
+      cros_disks_client_->EnumerateDevices(
           base::Bind(&DiskMountManagerImpl::RefreshAfterEnumerateDevices,
                      weak_ptr_factory_.GetWeakPtr()),
           base::Bind(&DiskMountManagerImpl::RefreshCompleted,
@@ -626,10 +626,7 @@
 
   // Callback for GetDeviceProperties.
   void OnGetDeviceProperties(const DiskInfo& disk_info) {
-    // TODO(zelidrag): Find a better way to filter these out before we
-    // fetch the properties:
-    // Ignore disks coming from the device we booted the system from.
-    if (disk_info.on_boot_device())
+    if (disk_info.is_virtual())
       return;
 
     LOG(WARNING) << "Found disk " << disk_info.device_path();
@@ -650,6 +647,8 @@
     auto access_mode = access_modes_.find(disk_info.device_path());
     bool write_disabled_by_policy = access_mode != access_modes_.end()
         && access_mode->second == chromeos::MOUNT_ACCESS_MODE_READ_ONLY;
+    // TODO(agawronska): Add constructor for Disk from DiskInfo. Introduce Disk
+    // builder class for tests.
     Disk* disk = new Disk(
         disk_info.device_path(), disk_info.mount_path(),
         write_disabled_by_policy, disk_info.system_path(),
@@ -663,7 +662,7 @@
         disk_info.is_hidden(), disk_info.file_system_type(), base_mount_path);
     disks_.insert(
         std::make_pair(disk_info.device_path(), base::WrapUnique(disk)));
-    NotifyDiskStatusUpdate(is_new ? DISK_ADDED : DISK_CHANGED, disk);
+    NotifyDiskStatusUpdate(is_new ? DISK_ADDED : DISK_CHANGED, *disk);
   }
 
   // Part of EnsureMountInfoRefreshed(). Called after the list of devices are
@@ -743,7 +742,7 @@
         DiskMountManager::DiskMap::iterator iter = disks_.find(device_path);
         if (iter != disks_.end()) {
           Disk* disk = iter->second.get();
-          NotifyDiskStatusUpdate(DISK_REMOVED, disk);
+          NotifyDiskStatusUpdate(DISK_REMOVED, *disk);
           disks_.erase(iter);
         }
         break;
@@ -769,10 +768,11 @@
   }
 
   // Notifies all observers about disk status update.
-  void NotifyDiskStatusUpdate(DiskEvent event,
-                              const Disk* disk) {
-    for (auto& observer : observers_)
-      observer.OnDiskEvent(event, disk);
+  void NotifyDiskStatusUpdate(DiskEvent event, const Disk& disk) {
+    for (auto& observer : observers_) {
+      disk.IsAutoMountable() ? observer.OnAutoMountableDiskEvent(event, disk)
+                             : observer.OnBootDeviceDiskEvent(event, disk);
+    }
   }
 
   // Notifies all observers about device status update.
@@ -906,6 +906,15 @@
     base_mount_path_ = mount_path;
 }
 
+bool DiskMountManager::Disk::IsAutoMountable() const {
+  // Disks are considered auto-mountable if they are:
+  // 1. Non-virtual
+  // 2. Not on boot device
+  // Only the second condition is checked here, because Disks are created from
+  // non-virtual mount devices only.
+  return !on_boot_device_;
+};
+
 bool DiskMountManager::AddDiskForTest(std::unique_ptr<Disk> disk) {
   return false;
 }
diff --git a/chromeos/disks/disk_mount_manager.h b/chromeos/disks/disk_mount_manager.h
index cb8a927..fa8ff4b 100644
--- a/chromeos/disks/disk_mount_manager.h
+++ b/chromeos/disks/disk_mount_manager.h
@@ -54,7 +54,8 @@
 
   enum RenameEvent { RENAME_STARTED, RENAME_COMPLETED };
 
-  // Used to house an instance of each found mount device.
+  // Used to house an instance of each found mount device. Created from
+  // non-virtual mount devices only - see IsAutoMountable().
   class Disk {
    public:
     Disk(const std::string& device_path,
@@ -182,6 +183,8 @@
 
     void SetMountPath(const std::string& mount_path);
 
+    bool IsAutoMountable() const;
+
    private:
     std::string device_path_;
     std::string mount_path_;
@@ -245,12 +248,17 @@
   typedef base::Callback<void(bool success)> EnsureMountInfoRefreshedCallback;
 
   // Implement this interface to be notified about disk/mount related events.
+  // TODO(agawronska): Make observer methods non-pure virtual, because
+  // subclasses only use small subset of them.
   class Observer {
    public:
     virtual ~Observer() {}
 
-    // Called when disk mount status is changed.
-    virtual void OnDiskEvent(DiskEvent event, const Disk* disk) = 0;
+    // Called when auto-mountable disk mount status is changed.
+    virtual void OnAutoMountableDiskEvent(DiskEvent event,
+                                          const Disk& disk) = 0;
+    // Called when fixed storage disk status is changed.
+    virtual void OnBootDeviceDiskEvent(DiskEvent event, const Disk& disk) = 0;
     // Called when device status is changed.
     virtual void OnDeviceEvent(DeviceEvent event,
                                const std::string& device_path) = 0;
diff --git a/chromeos/disks/disk_mount_manager_unittest.cc b/chromeos/disks/disk_mount_manager_unittest.cc
index 8c94e15..7d4fad3 100644
--- a/chromeos/disks/disk_mount_manager_unittest.cc
+++ b/chromeos/disks/disk_mount_manager_unittest.cc
@@ -172,11 +172,12 @@
 
 // Represents which function in |DiskMountManager::Observer| was invoked.
 enum ObserverEventType {
-  DEVICE_EVENT,  // OnDeviceEvent()
-  DISK_EVENT,    // OnDiskEvent()
-  FORMAT_EVENT,  // OnFormatEvent()
-  MOUNT_EVENT,   // OnMountEvent()
-  RENAME_EVENT   // OnRenameEvent()
+  DEVICE_EVENT,               // OnDeviceEvent()
+  AUTO_MOUNTABLE_DISK_EVENT,  // OnAutoMountableDiskEvent()
+  BOOT_DEVICE_DISK_EVENT,     // OnBootDeviceDiskEvent()
+  FORMAT_EVENT,               // OnFormatEvent()
+  MOUNT_EVENT,                // OnMountEvent()
+  RENAME_EVENT                // OnRenameEvent()
 };
 
 // Represents every event notified to |DiskMountManager::Observer|.
@@ -208,30 +209,56 @@
   }
 };
 
-// Represents an invocation of |DiskMountManager::Observer::OnDiskEvent()|.
-struct DiskEvent : public ObserverEvent {
+// Represents an invocation of
+// DiskMountManager::Observer::OnAutoMountableDiskEvent().
+struct AutoMountableDiskEvent : public ObserverEvent {
   DiskMountManager::DiskEvent event;
   std::unique_ptr<DiskMountManager::Disk> disk;
 
-  DiskEvent(DiskMountManager::DiskEvent event,
-            const DiskMountManager::Disk& disk)
-      : event(event),
-        disk(std::unique_ptr<DiskMountManager::Disk>(
-            new DiskMountManager::Disk(disk))) {}
+  AutoMountableDiskEvent(DiskMountManager::DiskEvent event,
+                         const DiskMountManager::Disk& disk)
+      : event(event), disk(std::make_unique<DiskMountManager::Disk>(disk)) {}
 
-  DiskEvent(DiskEvent&& other)
+  AutoMountableDiskEvent(AutoMountableDiskEvent&& other)
       : event(other.event), disk(std::move(other.disk)) {}
 
-  ObserverEventType type() const override { return DISK_EVENT; }
+  ObserverEventType type() const override { return AUTO_MOUNTABLE_DISK_EVENT; }
 
-  bool operator==(const DiskEvent& other) const {
+  bool operator==(const AutoMountableDiskEvent& other) const {
     return event == other.event && disk == other.disk;
   }
 
   std::string DebugString() const {
-    return StringPrintf("OnDiskEvent(event=%d, device_path=%s, mount_path=%s",
-                        event, disk->device_path().c_str(),
-                        disk->mount_path().c_str());
+    return StringPrintf(
+        "OnAutoMountableDiskEvent(event=%d, device_path=%s, mount_path=%s",
+        event, disk->device_path().c_str(), disk->mount_path().c_str());
+  }
+};
+
+// Represents an invocation of
+// DiskMountManager::Observer::OnBootDeviceDiskEvent().
+// TODO(agawronska): Add tests for disks events.
+struct BootDeviceDiskEvent : public ObserverEvent {
+  DiskMountManager::DiskEvent event;
+  std::unique_ptr<DiskMountManager::Disk> disk;
+
+  BootDeviceDiskEvent(DiskMountManager::DiskEvent event,
+                      const DiskMountManager::Disk& disk)
+      : event(event), disk(std::make_unique<DiskMountManager::Disk>(disk)) {}
+
+  BootDeviceDiskEvent(BootDeviceDiskEvent&& other)
+      : event(other.event), disk(std::move(other.disk)) {}
+
+  ObserverEventType type() const override { return BOOT_DEVICE_DISK_EVENT; }
+
+  bool operator==(const BootDeviceDiskEvent& other) const {
+    return event == other.event && disk == other.disk;
+  }
+
+  std::string DebugString() const {
+    return StringPrintf(
+        "OnBootDeviceDiskEvent(event=%d, device_path=%s, mount_path=%s", event,
+        disk->device_path().c_str(), disk->mount_path().c_str());
   }
 };
 
@@ -334,11 +361,18 @@
     events_.push_back(std::make_unique<DeviceEvent>(event, device_path));
   }
 
-  void OnDiskEvent(DiskMountManager::DiskEvent event,
-                   const DiskMountManager::Disk* disk) override {
+  void OnBootDeviceDiskEvent(DiskMountManager::DiskEvent event,
+                             const DiskMountManager::Disk& disk) override {
     // Take a snapshot (copy) of the Disk object at the time of invocation for
     // later verification.
-    events_.push_back(std::make_unique<DiskEvent>(event, *disk));
+    events_.push_back(std::make_unique<BootDeviceDiskEvent>(event, disk));
+  }
+
+  void OnAutoMountableDiskEvent(DiskMountManager::DiskEvent event,
+                                const DiskMountManager::Disk& disk) override {
+    // Take a snapshot (copy) of the Disk object at the time of invocation for
+    // later verification.
+    events_.push_back(std::make_unique<AutoMountableDiskEvent>(event, disk));
   }
 
   void OnFormatEvent(DiskMountManager::FormatEvent event,
@@ -375,11 +409,20 @@
     return static_cast<const DeviceEvent&>(*events_[index]);
   }
 
-  // Verifies if the |index|th invocation is OnDiskEvent() and returns details.
-  const DiskEvent& GetDiskEvent(size_t index) {
+  // Verifies if the |index|th invocation is OnAutoMountableDiskEvent() and
+  // returns details.
+  const AutoMountableDiskEvent& GetAutoMountableDiskEvent(size_t index) {
     DCHECK_GT(events_.size(), index);
-    DCHECK_EQ(DISK_EVENT, events_[index]->type());
-    return static_cast<const DiskEvent&>(*events_[index]);
+    DCHECK_EQ(AUTO_MOUNTABLE_DISK_EVENT, events_[index]->type());
+    return static_cast<const AutoMountableDiskEvent&>(*events_[index]);
+  }
+
+  // Verifies if the |index|th invocation is OnBootDeviceDiskEvent() and returns
+  // details.
+  const BootDeviceDiskEvent& GetBootDeviceDiskEvent(size_t index) {
+    DCHECK_GT(events_.size(), index);
+    DCHECK_EQ(BOOT_DEVICE_DISK_EVENT, events_[index]->type());
+    return static_cast<const BootDeviceDiskEvent&>(*events_[index]);
   }
 
   // Verifies if the |index|th invocation is OnFormatEvent() and returns
diff --git a/chromeos/disks/mock_disk_mount_manager.cc b/chromeos/disks/mock_disk_mount_manager.cc
index d2d4c4866..36939aa3 100644
--- a/chromeos/disks/mock_disk_mount_manager.cc
+++ b/chromeos/disks/mock_disk_mount_manager.cc
@@ -227,8 +227,10 @@
 void MockDiskMountManager::NotifyDiskChanged(
     DiskEvent event,
     const DiskMountManager::Disk* disk) {
-  for (auto& observer : observers_)
-    observer.OnDiskEvent(event, disk);
+  for (auto& observer : observers_) {
+    disk->IsAutoMountable() ? observer.OnAutoMountableDiskEvent(event, *disk)
+                            : observer.OnBootDeviceDiskEvent(event, *disk);
+  }
 }
 
 void MockDiskMountManager::NotifyDeviceChanged(DeviceEvent event,
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index a4550c6f..9d5d168 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -160,6 +160,7 @@
 static_library("arc_test_support") {
   testonly = true
   sources = [
+    "test/connection_holder_util.h",
     "test/fake_app_instance.cc",
     "test/fake_app_instance.h",
     "test/fake_arc_bridge_host.cc",
diff --git a/components/arc/power/arc_power_bridge_unittest.cc b/components/arc/power/arc_power_bridge_unittest.cc
index e592c28..e9f02f9 100644
--- a/components/arc/power/arc_power_bridge_unittest.cc
+++ b/components/arc/power/arc_power_bridge_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/arc/power/arc_power_bridge.h"
 
+#include <utility>
+
 #include "base/run_loop.h"
 #include "base/test/scoped_task_environment.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -11,6 +13,7 @@
 #include "chromeos/dbus/power_manager/suspend.pb.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/common/power.mojom.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_power_instance.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/device/public/cpp/test/test_wake_lock_provider.h"
@@ -59,8 +62,8 @@
   // ArcPowerBridge::OnInstanceReady() being called.
   void CreatePowerInstance() {
     power_instance_ = std::make_unique<FakePowerInstance>();
-    bridge_service_->power()->SetInstance(power_instance_.get(),
-                                          mojom::PowerInstance::Version_);
+    bridge_service_->power()->SetInstance(power_instance_.get());
+    WaitForInstanceReady(bridge_service_->power());
   }
 
   // Destroys the FakePowerInstance. This results in
diff --git a/components/arc/test/connection_holder_util.h b/components/arc/test/connection_holder_util.h
new file mode 100644
index 0000000..53fa7a6
--- /dev/null
+++ b/components/arc/test/connection_holder_util.h
@@ -0,0 +1,60 @@
+// 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 COMPONENTS_ARC_TEST_CONNECTION_HOLDER_UTIL_H_
+#define COMPONENTS_ARC_TEST_CONNECTION_HOLDER_UTIL_H_
+
+#include <utility>
+
+#include "base/callback_helpers.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "components/arc/connection_holder.h"
+
+namespace arc {
+
+namespace internal {
+
+// An observer that runs a closure when the connection is ready.
+template <typename InstanceType, typename HostType>
+class ReadinessObserver
+    : public ConnectionHolder<InstanceType, HostType>::Observer {
+ public:
+  explicit ReadinessObserver(ConnectionHolder<InstanceType, HostType>* holder,
+                             base::OnceClosure closure)
+      : holder_(holder), closure_(std::move(closure)) {
+    holder_->AddObserver(this);
+  }
+  ~ReadinessObserver() override { holder_->RemoveObserver(this); }
+
+ private:
+  void OnConnectionReady() override {
+    if (!closure_)
+      return;
+    std::move(closure_).Run();
+  }
+
+  ConnectionHolder<InstanceType, HostType>* const holder_;  // Owned by caller
+  base::OnceClosure closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReadinessObserver);
+};
+
+}  // namespace internal
+
+// Waits for the instance to be ready.
+template <typename InstanceType, typename HostType>
+void WaitForInstanceReady(ConnectionHolder<InstanceType, HostType>* holder) {
+  if (holder->IsConnected())
+    return;
+
+  base::RunLoop run_loop;
+  internal::ReadinessObserver<InstanceType, HostType> readiness_observer(
+      holder, run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+}  // namespace arc
+
+#endif  // COMPONENTS_ARC_TEST_CONNECTION_HOLDER_UTIL_H_
diff --git a/components/arc/test/fake_app_instance.cc b/components/arc/test/fake_app_instance.cc
index 7fbf1e3..25ca126 100644
--- a/components/arc/test/fake_app_instance.cc
+++ b/components/arc/test/fake_app_instance.cc
@@ -52,6 +52,7 @@
   // ARC app instance calls RefreshAppList after Init() successfully. Call
   // RefreshAppList() here to keep the same behavior.
   RefreshAppList();
+  host_ = std::move(host_ptr);
   std::move(callback).Run();
 }
 
diff --git a/components/arc/test/fake_app_instance.h b/components/arc/test/fake_app_instance.h
index 316ef82..4f9f9790 100644
--- a/components/arc/test/fake_app_instance.h
+++ b/components/arc/test/fake_app_instance.h
@@ -200,6 +200,10 @@
   // Keeps information for running tasks.
   TaskIdToInfo task_id_to_info_;
 
+  // Keeps the binding alive so that calls to this class can be correctly
+  // routed.
+  mojom::AppHostPtr host_;
+
   bool GetFakeIcon(mojom::ScaleFactor scale_factor,
                    std::string* png_data_as_string);
 
diff --git a/components/arc/test/fake_bluetooth_instance.cc b/components/arc/test/fake_bluetooth_instance.cc
index 2641807..660cc48 100644
--- a/components/arc/test/fake_bluetooth_instance.cc
+++ b/components/arc/test/fake_bluetooth_instance.cc
@@ -34,6 +34,7 @@
 
 void FakeBluetoothInstance::Init(mojom::BluetoothHostPtr host_ptr,
                                  InitCallback callback) {
+  host_ = std::move(host_ptr);
   std::move(callback).Run();
 }
 
diff --git a/components/arc/test/fake_bluetooth_instance.h b/components/arc/test/fake_bluetooth_instance.h
index 893db67..abe5cac 100644
--- a/components/arc/test/fake_bluetooth_instance.h
+++ b/components/arc/test/fake_bluetooth_instance.h
@@ -149,6 +149,10 @@
   std::vector<std::unique_ptr<LEDeviceFoundData>> le_device_found_data_;
   std::vector<std::unique_ptr<GattDBResult>> gatt_db_result_;
 
+  // Keeps the binding alive so that calls to this class can be correctly
+  // routed.
+  mojom::BluetoothHostPtr host_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeBluetoothInstance);
 };
 
diff --git a/components/arc/test/fake_intent_helper_instance.cc b/components/arc/test/fake_intent_helper_instance.cc
index 9a25bd98..aacb8d1 100644
--- a/components/arc/test/fake_intent_helper_instance.cc
+++ b/components/arc/test/fake_intent_helper_instance.cc
@@ -77,6 +77,7 @@
 
 void FakeIntentHelperInstance::Init(mojom::IntentHelperHostPtr host_ptr,
                                     InitCallback callback) {
+  host_ = std::move(host_ptr);
   std::move(callback).Run();
 }
 
diff --git a/components/arc/test/fake_intent_helper_instance.h b/components/arc/test/fake_intent_helper_instance.h
index 8451ba3e..e70cca65 100644
--- a/components/arc/test/fake_intent_helper_instance.h
+++ b/components/arc/test/fake_intent_helper_instance.h
@@ -119,6 +119,10 @@
   std::map<std::string, std::vector<mojom::IntentHandlerInfoPtr>>
       intent_handlers_;
 
+  // Keeps the binding alive so that calls to this class can be correctly
+  // routed.
+  mojom::IntentHelperHostPtr host_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeIntentHelperInstance);
 };
 
diff --git a/components/arc/test/fake_wallpaper_instance.cc b/components/arc/test/fake_wallpaper_instance.cc
index 0a40514..a5b180e 100644
--- a/components/arc/test/fake_wallpaper_instance.cc
+++ b/components/arc/test/fake_wallpaper_instance.cc
@@ -21,6 +21,7 @@
 
 void FakeWallpaperInstance::Init(mojom::WallpaperHostPtr host_ptr,
                                  InitCallback callback) {
+  host_ = std::move(host_ptr);
   std::move(callback).Run();
 }
 
diff --git a/components/arc/test/fake_wallpaper_instance.h b/components/arc/test/fake_wallpaper_instance.h
index 6af83bd..d0f18f9 100644
--- a/components/arc/test/fake_wallpaper_instance.h
+++ b/components/arc/test/fake_wallpaper_instance.h
@@ -28,6 +28,10 @@
  private:
   std::vector<int32_t> changed_ids_;
 
+  // Keeps the binding alive so that calls to this class can be correctly
+  // routed.
+  mojom::WallpaperHostPtr host_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeWallpaperInstance);
 };
 
diff --git a/components/arc/volume_mounter/arc_volume_mounter_bridge.cc b/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
index 9412567..7cf9eb0 100644
--- a/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
+++ b/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
@@ -74,12 +74,18 @@
                            base::BindOnce(&SendAllMountEvents, this));
 }
 
-void ArcVolumeMounterBridge::OnDiskEvent(
+void ArcVolumeMounterBridge::OnAutoMountableDiskEvent(
     chromeos::disks::DiskMountManager::DiskEvent event,
-    const chromeos::disks::DiskMountManager::Disk* disk) {
+    const chromeos::disks::DiskMountManager::Disk& disk) {
   // Ignored. DiskEvents will be maintained in Vold during MountEvents.
 }
 
+void ArcVolumeMounterBridge::OnBootDeviceDiskEvent(
+    chromeos::disks::DiskMountManager::DiskEvent event,
+    const chromeos::disks::DiskMountManager::Disk& disk) {
+  // Ignored. ARC doesn't care about boot device disk events.
+}
+
 void ArcVolumeMounterBridge::OnDeviceEvent(
     chromeos::disks::DiskMountManager::DeviceEvent event,
     const std::string& device_path) {
diff --git a/components/arc/volume_mounter/arc_volume_mounter_bridge.h b/components/arc/volume_mounter/arc_volume_mounter_bridge.h
index af51cf38..5c47f2cf 100644
--- a/components/arc/volume_mounter/arc_volume_mounter_bridge.h
+++ b/components/arc/volume_mounter/arc_volume_mounter_bridge.h
@@ -42,9 +42,12 @@
   void OnConnectionReady() override;
 
   // chromeos::disks::DiskMountManager::Observer overrides:
-  void OnDiskEvent(
+  void OnAutoMountableDiskEvent(
       chromeos::disks::DiskMountManager::DiskEvent event,
-      const chromeos::disks::DiskMountManager::Disk* disk) override;
+      const chromeos::disks::DiskMountManager::Disk& disk) override;
+  void OnBootDeviceDiskEvent(
+      chromeos::disks::DiskMountManager::DiskEvent event,
+      const chromeos::disks::DiskMountManager::Disk& disk) override;
   void OnDeviceEvent(chromeos::disks::DiskMountManager::DeviceEvent event,
                      const std::string& device_path) override;
   void OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,
diff --git a/components/autofill/content/renderer/BUILD.gn b/components/autofill/content/renderer/BUILD.gn
index be934a5..b7acdc30 100644
--- a/components/autofill/content/renderer/BUILD.gn
+++ b/components/autofill/content/renderer/BUILD.gn
@@ -14,6 +14,8 @@
     "form_classifier.h",
     "html_based_username_detector.cc",
     "html_based_username_detector.h",
+    "html_based_username_detector_vocabulary.cc",
+    "html_based_username_detector_vocabulary.h",
     "page_form_analyser_logger.cc",
     "page_form_analyser_logger.h",
     "page_passwords_analyser.cc",
diff --git a/components/autofill/content/renderer/html_based_username_detector.cc b/components/autofill/content/renderer/html_based_username_detector.cc
index ea7a502..046932cc 100644
--- a/components/autofill/content/renderer/html_based_username_detector.cc
+++ b/components/autofill/content/renderer/html_based_username_detector.cc
@@ -5,12 +5,15 @@
 #include "components/autofill/content/renderer/html_based_username_detector.h"
 
 #include <algorithm>
-#include <map>
+#include <tuple>
 
+#include "base/containers/flat_set.h"
 #include "base/i18n/case_conversion.h"
+#include "base/macros.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/content/renderer/html_based_username_detector_vocabulary.h"
 #include "third_party/WebKit/public/web/WebFormElement.h"
 
 using blink::WebFormControlElement;
@@ -21,248 +24,38 @@
 
 namespace {
 
-// For each input element that can be username, we compute and save developer
-// and user group, along with associated short tokens lists (to handle finding
-// less than |kMinimumWordLength| letters long words).
+// List of separators that can appear in HTML attribute values.
+constexpr char kDelimiters[] = "$\"\'?%*@!\\/&^#:+~`;,>|<.[](){}-_ 0123456789";
+
+// Minimum length of a word, in order not to be considered short word. Short
+// words will not be searched in attribute values (especially after delimiters
+// removing), because a short word may be a part of another word. A short word
+// should be enclosed between delimiters, otherwise an occurrence doesn't count.
+constexpr int kMinimumWordLength = 4;
+
+// For each input element that can be a username, developer and user group
+// values are computed. The user group value includes what a user sees: label,
+// placeholder, aria-label (all are stored in FormFieldData.label). The
+// developer group value consists of name and id attribute values.
+// For each group the set of short tokens (tokens shorter than
+// |kMinimumWordLength|) is computed as well.
 struct UsernameFieldData {
   WebInputElement input_element;
   base::string16 developer_value;
-  std::vector<base::string16> developer_short_tokens;
+  base::flat_set<base::string16> developer_short_tokens;
   base::string16 user_value;
-  std::vector<base::string16> user_short_tokens;
+  base::flat_set<base::string16> user_short_tokens;
 };
 
+// Words that the algorithm looks for are split into multiple categories based
+// on feature reliability.
+// A category may contain a latin dictionary and a non-latin dictionary. It is
+// mandatory that it has a latin one, but a non-latin might be missing.
 // "Latin" translations are the translations of the words for which the
 // original translation is similar to the romanized translation (translation of
 // the word only using ISO basic Latin alphabet).
 // "Non-latin" translations are the translations of the words that have custom,
 // country specific characters.
-const char* const kNegativeLatin[] = {
-    "pin",    "parola",   "wagwoord",   "wachtwoord",
-    "fake",   "parole",   "givenname",  "achinsinsi",
-    "token",  "parool",   "firstname",  "facalfaire",
-    "fname",  "lozinka",  "pasahitza",  "focalfaire",
-    "lname",  "passord",  "pasiwedhi",  "iphasiwedi",
-    "geslo",  "huahuna",  "passwuert",  "katalaluan",
-    "heslo",  "fullname", "phasewete",  "adgangskode",
-    "parol",  "optional", "wachtwurd",  "contrasenya",
-    "sandi",  "lastname", "cyfrinair",  "contrasinal",
-    "senha",  "kupuhipa", "katasandi",  "kalmarsirri",
-    "hidden", "password", "loluszais",  "tenimiafina",
-    "second", "passwort", "middlename", "paroladordine",
-    "codice", "pasvorto", "familyname", "inomboloyokuvula",
-    "modpas", "salasana", "motdepasse", "numeraeleiloaesesi"};
-constexpr int kNegativeLatinSize = arraysize(kNegativeLatin);
-
-const char* const kNegativeNonLatin[] = {"fjalëkalim",
-                                         "የይለፍቃል",
-                                         "كلمهالسر",
-                                         "գաղտնաբառ",
-                                         "пароль",
-                                         "পাসওয়ার্ড",
-                                         "парола",
-                                         "密码",
-                                         "密碼",
-                                         "დაგავიწყდათ",
-                                         "κωδικόςπρόσβασης",
-                                         "પાસવર્ડ",
-                                         "סיסמה",
-                                         "पासवर्ड",
-                                         "jelszó",
-                                         "lykilorð",
-                                         "paswọọdụ",
-                                         "パスワード",
-                                         "ಪಾಸ್ವರ್ಡ್",
-                                         "пароль",
-                                         "ការពាក្យសម្ងាត់",
-                                         "암호",
-                                         "şîfre",
-                                         "купуясөз",
-                                         "ລະຫັດຜ່ານ",
-                                         "slaptažodis",
-                                         "лозинка",
-                                         "पासवर्ड",
-                                         "нууцүг",
-                                         "စကားဝှက်ကို",
-                                         "पासवर्ड",
-                                         "رمز",
-                                         "کلمهعبور",
-                                         "hasło",
-                                         "пароль",
-                                         "лозинка",
-                                         "پاسورڊ",
-                                         "මුරපදය",
-                                         "contraseña",
-                                         "lösenord",
-                                         "гузарвожа",
-                                         "கடவுச்சொல்",
-                                         "పాస్వర్డ్",
-                                         "รหัสผ่าน",
-                                         "пароль",
-                                         "پاسورڈ",
-                                         "mậtkhẩu",
-                                         "פּאַראָל",
-                                         "ọrọigbaniwọle"};
-constexpr int kNegativeNonLatinSize = arraysize(kNegativeNonLatin);
-
-const char* const kUsernameLatin[] = {
-    "gatti",      "uzantonomo",   "solonanarana",    "nombredeusuario",
-    "olumulo",    "nomenusoris",  "enwdefnyddiwr",   "nomdutilisateur",
-    "lolowera",   "notandanafn",  "nomedeusuario",   "vartotojovardas",
-    "username",   "ahanjirimara", "gebruikersnaam",  "numedeutilizator",
-    "brugernavn", "benotzernumm", "jinalamtumiaji",  "erabiltzaileizena",
-    "brukernavn", "benutzername", "sunanmaiamfani",  "foydalanuvchinomi",
-    "mosebedisi", "kasutajanimi", "ainmcleachdaidh", "igamalomsebenzisi",
-    "nomdusuari", "lomsebenzisi", "jenengpanganggo", "ingoakaiwhakamahi",
-    "nomeutente", "namapengguna"};
-constexpr int kUsernameLatinSize = arraysize(kUsernameLatin);
-
-const char* const kUsernameNonLatin[] = {"用户名",
-                                         "کاتيجونالو",
-                                         "用戶名",
-                                         "የተጠቃሚስም",
-                                         "логин",
-                                         "اسمالمستخدم",
-                                         "נאמען",
-                                         "کاصارفکانام",
-                                         "ユーザ名",
-                                         "όνομα χρήστη",
-                                         "brûkersnamme",
-                                         "корисничкоиме",
-                                         "nonitilizatè",
-                                         "корисничкоиме",
-                                         "ngaranpamaké",
-                                         "ຊື່ຜູ້ໃຊ້",
-                                         "användarnamn",
-                                         "యూజర్పేరు",
-                                         "korisničkoime",
-                                         "пайдаланушыаты",
-                                         "שםמשתמש",
-                                         "ім'якористувача",
-                                         "کارننوم",
-                                         "хэрэглэгчийннэр",
-                                         "nomedeusuário",
-                                         "имяпользователя",
-                                         "têntruynhập",
-                                         "பயனர்பெயர்",
-                                         "ainmúsáideora",
-                                         "ชื่อผู้ใช้",
-                                         "사용자이름",
-                                         "імякарыстальніка",
-                                         "lietotājvārds",
-                                         "потребителскоиме",
-                                         "uporabniškoime",
-                                         "колдонуучунунаты",
-                                         "kullanıcıadı",
-                                         "පරිශීලකනාමය",
-                                         "istifadəçiadı",
-                                         "օգտագործողիանունը",
-                                         "navêbikarhêner",
-                                         "ಬಳಕೆದಾರಹೆಸರು",
-                                         "emriipërdoruesit",
-                                         "वापरकर्तानाव",
-                                         "käyttäjätunnus",
-                                         "વપરાશકર્તાનામ",
-                                         "felhasználónév",
-                                         "उपयोगकर्तानाम",
-                                         "nazwaużytkownika",
-                                         "ഉപയോക്തൃനാമം",
-                                         "სახელი",
-                                         "အသုံးပြုသူအမည်",
-                                         "نامکاربری",
-                                         "प्रयोगकर्तानाम",
-                                         "uživatelskéjméno",
-                                         "ব্যবহারকারীরনাম",
-                                         "užívateľskémeno",
-                                         "ឈ្មោះអ្នកប្រើប្រាស់"};
-constexpr int kUsernameNonLatinSize = arraysize(kUsernameNonLatin);
-
-const char* const kUserLatin[] = {
-    "user",   "wosuta",   "gebruiker",  "utilizator",
-    "usor",   "notandi",  "gumagamit",  "vartotojas",
-    "fammi",  "olumulo",  "maiamfani",  "cleachdaidh",
-    "utent",  "pemakai",  "mpampiasa",  "umsebenzisi",
-    "bruger", "usuario",  "panganggo",  "utilisateur",
-    "bruker", "benotzer", "uporabnik",  "doutilizador",
-    "numake", "benutzer", "covneegsiv", "erabiltzaile",
-    "usuari", "kasutaja", "defnyddiwr", "kaiwhakamahi",
-    "utente", "korisnik", "mosebedisi", "foydalanuvchi",
-    "uzanto", "pengguna", "mushandisi"};
-constexpr int kUserLatinSize = arraysize(kUserLatin);
-
-const char* const kUserNonLatin[] = {"用户",
-                                     "użytkownik",
-                                     "tagatafaʻaaogā",
-                                     "دکارونکيعکس",
-                                     "用戶",
-                                     "užívateľ",
-                                     "корисник",
-                                     "карыстальнік",
-                                     "brûker",
-                                     "kullanıcı",
-                                     "истифода",
-                                     "អ្នកប្រើ",
-                                     "ọrụ",
-                                     "ተጠቃሚ",
-                                     "באַניצער",
-                                     "хэрэглэгчийн",
-                                     "يوزر",
-                                     "istifadəçi",
-                                     "ຜູ້ໃຊ້",
-                                     "пользователь",
-                                     "صارف",
-                                     "meahoʻohana",
-                                     "потребител",
-                                     "वापरकर्ता",
-                                     "uživatel",
-                                     "ユーザー",
-                                     "מִשׁתַמֵשׁ",
-                                     "ผู้ใช้งาน",
-                                     "사용자",
-                                     "bikaranîvan",
-                                     "колдонуучу",
-                                     "વપરાશકર્તા",
-                                     "përdorues",
-                                     "ngườidùng",
-                                     "корисникот",
-                                     "उपयोगकर्ता",
-                                     "itilizatè",
-                                     "χρήστης",
-                                     "користувач",
-                                     "օգտվողիանձնագիրը",
-                                     "használó",
-                                     "faoiúsáideoir",
-                                     "შესახებ",
-                                     "ব্যবহারকারী",
-                                     "lietotājs",
-                                     "பயனர்",
-                                     "ಬಳಕೆದಾರ",
-                                     "ഉപയോക്താവ്",
-                                     "کاربر",
-                                     "యూజర్",
-                                     "පරිශීලක",
-                                     "प्रयोगकर्ता",
-                                     "användare",
-                                     "المستعمل",
-                                     "пайдаланушы",
-                                     "အသုံးပြုသူကို",
-                                     "käyttäjä"};
-constexpr int kUserNonLatinSize = arraysize(kUserNonLatin);
-
-const char* const kTechnicalWords[] = {
-    "uid",         "newtel",     "uaccount",   "regaccount",  "ureg",
-    "loginid",     "laddress",   "accountreg", "regid",       "regname",
-    "loginname",   "membername", "uname",      "ucreate",     "loginmail",
-    "accountname", "umail",      "loginreg",   "accountid",   "loginaccount",
-    "ulogin",      "regemail",   "newmobile",  "accountlogin"};
-constexpr int kTechnicalWordsSize = arraysize(kTechnicalWords);
-
-const char* const kWeakWords[] = {"id", "login", "mail"};
-constexpr int kWeakWordsSize = arraysize(kWeakWords);
-
-// Words that the algorithm looks for are split into multiple categories.
-// A category may contain latin dictionary and non-latin dictionary. It is
-// mandatory that it has latin one, but non-latin might be missing.
 struct CategoryOfWords {
   const char* const* const latin_dictionary;
   const size_t latin_dictionary_size;
@@ -270,53 +63,65 @@
   const size_t non_latin_dictionary_size;
 };
 
-// Minimum length of a word, in order not to be considered short word.
-// Short words will have different treatment than the others.
-constexpr int kMinimumWordLength = 4;
-
-void BuildValueAndShortTokens(
+// 1. Removes delimiters from |raw_value| and appends it to |*field_data_value|.
+// A sentinel symbol is added first if |*field_data_value| is not empty.
+// 2. Tokenizes and appends short tokens (shorter than |kMinimumWordLength|)
+// from |raw_value| to |*field_data_short_tokens|, if any.
+void AppendValueAndShortTokens(
     const base::string16& raw_value,
     base::string16* field_data_value,
-    std::vector<base::string16>* field_data_short_tokens) {
-  // List of separators that can appear in HTML attribute values.
-  static const std::string kDelimiters =
-      "\"\'?%*@!\\/&^#:+~`;,>|<.[](){}-_ 0123456789";
+    base::flat_set<base::string16>* field_data_short_tokens) {
   base::string16 lowercase_value = base::i18n::ToLower(raw_value);
+  const base::string16 delimiters = base::ASCIIToUTF16(kDelimiters);
   std::vector<base::StringPiece16> tokens =
-      base::SplitStringPiece(lowercase_value, base::ASCIIToUTF16(kDelimiters),
-                             base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  *field_data_value = base::JoinString(tokens, base::string16());
+      base::SplitStringPiece(lowercase_value, delimiters, base::TRIM_WHITESPACE,
+                             base::SPLIT_WANT_NONEMPTY);
+  // Modify |lowercase_value| only when |tokens| has been processed.
 
-  std::vector<base::StringPiece16> short_tokens;
-  std::copy_if(tokens.begin(), tokens.end(), std::back_inserter(short_tokens),
-               [](const base::StringPiece16& token) {
-                 return token.size() < kMinimumWordLength;
-               });
+  std::vector<base::string16> short_tokens;
+  std::transform(
+      std::find_if(tokens.begin(), tokens.end(),
+                   [](const base::StringPiece16& token) {
+                     return token.size() < kMinimumWordLength;
+                   }),
+      tokens.end(), std::back_inserter(short_tokens),
+      [](const base::StringPiece16& token) { return token.as_string(); });
+  // It is better to insert elements to a |flat_map| in one operation.
+  field_data_short_tokens->insert(short_tokens.begin(), short_tokens.end());
 
-  for (const base::StringPiece16& token : short_tokens) {
-    field_data_short_tokens->push_back(token.as_string());
-  }
-}
+  // Now that tokens are processed, squeeze delimiters out of |lowercase_value|.
+  lowercase_value.erase(std::remove_if(
+      lowercase_value.begin(), lowercase_value.end(),
+      [delimiters](char c) { return delimiters.find(c) != delimiters.npos; }));
 
-// For a given input element, compute developer and user value, along with
-// developer and user short tokens.
-UsernameFieldData ComputeFieldData(const blink::WebInputElement& input_element,
-                                   const FormFieldData& field) {
-  UsernameFieldData field_data;
-  field_data.input_element = input_element;
   // When computing the developer value, '$' safety guard is being added
   // between field name and id, so that forming of accidental words is
   // prevented.
-  BuildValueAndShortTokens(field.name + base::ASCIIToUTF16("$") + field.id,
-                           &field_data.developer_value,
-                           &field_data.developer_short_tokens);
-  BuildValueAndShortTokens(field.label, &field_data.user_value,
-                           &field_data.user_short_tokens);
+  if (!field_data_value->empty())
+    field_data_value->push_back('$');
+  *field_data_value += lowercase_value;
+}
+
+// For the given |input_element|, compute developer and user value, along with
+// sets of short tokens, and returns it.
+UsernameFieldData ComputeUsernameFieldData(
+    const blink::WebInputElement& input_element,
+    const FormFieldData& field) {
+  UsernameFieldData field_data;
+  field_data.input_element = input_element;
+
+  AppendValueAndShortTokens(field.name, &field_data.developer_value,
+                            &field_data.developer_short_tokens);
+  AppendValueAndShortTokens(field.id, &field_data.developer_value,
+                            &field_data.developer_short_tokens);
+  AppendValueAndShortTokens(field.label, &field_data.user_value,
+                            &field_data.user_short_tokens);
   return field_data;
 }
 
-// For the fields of the given form that can be username fields, compute data
-// needed by the detector.
+// For the fields of the given form that can be username fields
+// (all_possible_usernames), computes |UsernameFieldData| needed by the
+// detector.
 void InferUsernameFieldData(
     const std::vector<blink::WebInputElement>& all_possible_usernames,
     const FormData& form_data,
@@ -324,41 +129,43 @@
   // |all_possible_usernames| and |form_data.fields| may have different set of
   // fields. Match them based on |WebInputElement.NameForAutofill| and
   // |FormFieldData.name|.
-  size_t current_index = 0;
+  size_t next_element_range_begin = 0;
 
   for (const blink::WebInputElement& input_element : all_possible_usernames) {
-    for (size_t i = current_index; i < form_data.fields.size(); ++i) {
-      const FormFieldData& field = form_data.fields[i];
+    const base::string16 element_name = input_element.NameForAutofill().Utf16();
+    for (size_t i = next_element_range_begin; i < form_data.fields.size();
+         ++i) {
+      const FormFieldData& field_data = form_data.fields[i];
       if (input_element.NameForAutofill().IsEmpty())
         continue;
 
-      // Find matching form data and web input element.
-      if (field.name == input_element.NameForAutofill().Utf16()) {
-        current_index = i + 1;
+      // Find matching field data and web input element.
+      if (field_data.name == element_name) {
+        next_element_range_begin = i + 1;
         possible_usernames_data->push_back(
-            ComputeFieldData(input_element, field));
+            ComputeUsernameFieldData(input_element, field_data));
         break;
       }
     }
   }
 }
 
-// Check if any word from the dictionary is encountered in computed field
-// information.
-bool SearchFieldInDictionary(const base::string16& value,
-                             const std::vector<base::string16>& tokens,
-                             const char* const* dictionary,
-                             const size_t& dictionary_size) {
+// Check if any word from |dictionary| is encountered in computed field
+// information (i.e. |value|, |tokens|).
+bool CheckFieldWithDictionary(
+    const base::string16& value,
+    const base::flat_set<base::string16>& short_tokens,
+    const char* const* dictionary,
+    const size_t& dictionary_size) {
   for (size_t i = 0; i < dictionary_size; ++i) {
-    if (strlen(dictionary[i]) < kMinimumWordLength) {
-      // Treat short words by looking up for them in the tokens list.
-      for (const base::string16& token : tokens) {
-        if (token == base::UTF8ToUTF16(dictionary[i]))
-          return true;
-      }
+    const base::string16 word = base::UTF8ToUTF16(dictionary[i]);
+    if (word.length() < kMinimumWordLength) {
+      // Treat short words by looking them up in the tokens set.
+      if (short_tokens.find(word) != short_tokens.end())
+        return true;
     } else {
-      // Treat long words by looking for them as a substring in |value|.
-      if (value.find(base::UTF8ToUTF16(dictionary[i])) != std::string::npos)
+      // Treat long words by looking them up as a substring in |value|.
+      if (value.find(word) != std::string::npos)
         return true;
     }
   }
@@ -366,33 +173,30 @@
 }
 
 // Check if any word from |category| is encountered in computed field
-// information.
+// information (|possible_username|).
 bool ContainsWordFromCategory(const UsernameFieldData& possible_username,
                               const CategoryOfWords& category) {
   // For user value, search in latin and non-latin dictionaries, because this
-  // value is user visible.
-  return SearchFieldInDictionary(
+  // value is user visible. For developer value, only look up in latin
+  /// dictionaries.
+  return CheckFieldWithDictionary(
              possible_username.user_value, possible_username.user_short_tokens,
              category.latin_dictionary, category.latin_dictionary_size) ||
-         SearchFieldInDictionary(possible_username.user_value,
-                                 possible_username.user_short_tokens,
-                                 category.non_latin_dictionary,
-                                 category.non_latin_dictionary_size) ||
-         // For developer value, only look up in latin dictionaries.
-         SearchFieldInDictionary(possible_username.developer_value,
-                                 possible_username.developer_short_tokens,
-                                 category.latin_dictionary,
-                                 category.latin_dictionary_size);
+         CheckFieldWithDictionary(possible_username.user_value,
+                                  possible_username.user_short_tokens,
+                                  category.non_latin_dictionary,
+                                  category.non_latin_dictionary_size) ||
+         CheckFieldWithDictionary(possible_username.developer_value,
+                                  possible_username.developer_short_tokens,
+                                  category.latin_dictionary,
+                                  category.latin_dictionary_size);
 }
 
 // Remove from |possible_usernames_data| the elements that definitely cannot be
 // usernames, because their computed values contain at least one negative word.
 void RemoveFieldsWithNegativeWords(
     std::vector<UsernameFieldData>* possible_usernames_data) {
-  // Words that certainly point to a non-username field.
-  // If field values contain at least one negative word, then the field is
-  // excluded from the list of possible usernames.
-  static const CategoryOfWords kNegativeCategory{
+  static const CategoryOfWords kNegativeCategory = {
       kNegativeLatin, kNegativeLatinSize, kNegativeNonLatin,
       kNegativeNonLatinSize};
 
@@ -406,11 +210,11 @@
       possible_usernames_data->end());
 }
 
-// Check if any word from the given category appears in fields from the form.
-// If a word appears in more than 2 fields, we do not make a decision, because
-// it may just be a prefix.
-// If a word appears in 1 or 2 fields, we return the first field in which we
-// found the substring as |username_element|.
+// Check if any word from the given category (|category|) appears in fields from
+// the form (|possible_usernames_data|). If the category words appear in more
+// than 2 fields, do not make a decision, because it may just be a prefix. If
+// the words appears in 1 or 2 fields, the first field is saved to
+// |*username_element|.
 bool FormContainsWordFromCategory(
     const std::vector<UsernameFieldData>& possible_usernames_data,
     const CategoryOfWords& category,
@@ -419,20 +223,21 @@
   // the form) in which a substring is encountered.
   WebInputElement chosen_field;
 
-  size_t count = 0;
+  size_t fields_found = 0;
   for (const UsernameFieldData& field_data : possible_usernames_data) {
     if (ContainsWordFromCategory(field_data, category)) {
-      if (count == 0)
+      if (fields_found == 0)
         chosen_field = field_data.input_element;
-      count++;
+      fields_found++;
     }
   }
 
-  if (count && count <= 2) {
+  if (fields_found > 0 && fields_found <= 2) {
     *username_element = chosen_field;
     return true;
+  } else {
+    return false;
   }
-  return false;
 }
 
 // Find username element if there is no cached result for the given form.
@@ -442,28 +247,18 @@
     WebInputElement* username_element) {
   DCHECK(username_element);
 
-  // Translations of "username".
-  static const CategoryOfWords kUsernameCategory{
+  static const CategoryOfWords kUsernameCategory = {
       kUsernameLatin, kUsernameLatinSize, kUsernameNonLatin,
       kUsernameNonLatinSize};
-
-  // Translations of "user".
-  static const CategoryOfWords kUserCategory{kUserLatin, kUserLatinSize,
-                                             kUserNonLatin, kUserNonLatinSize};
-
-  // Words that certainly point to a username field, if they appear in developer
-  // value. They are technical words, because they can only be used as variable
-  // names, and not as stand-alone words.
-  static const CategoryOfWords kTechnicalCategory{
+  static const CategoryOfWords kUserCategory = {
+      kUserLatin, kUserLatinSize, kUserNonLatin, kUserNonLatinSize};
+  static const CategoryOfWords kTechnicalCategory = {
       kTechnicalWords, kTechnicalWordsSize, nullptr, 0};
-
-  // Words that might point to a username field.They have the smallest priority
-  // in the heuristic, because there are also field attribute values that
-  // contain them, but are not username fields.
-  static const CategoryOfWords kWeakCategory{kWeakWords, kWeakWordsSize,
-                                             nullptr, 0};
-
+  static const CategoryOfWords kWeakCategory = {kWeakWords, kWeakWordsSize,
+                                                nullptr, 0};
   // These categories contain words that point to username field.
+  // Order of categories is vital: the detector searches for words in descending
+  // order of probability to point to a username field.
   static const CategoryOfWords kPositiveCategories[] = {
       kUsernameCategory, kUserCategory, kTechnicalCategory, kWeakCategory};
 
@@ -473,8 +268,6 @@
   RemoveFieldsWithNegativeWords(&possible_usernames_data);
 
   // These are the searches performed by the username detector.
-  // Order of categories is vital: the detector searches for words in descending
-  // order of probability to point to a username field.
   for (const CategoryOfWords& category : kPositiveCategories) {
     if (FormContainsWordFromCategory(possible_usernames_data, category,
                                      username_element)) {
@@ -496,20 +289,34 @@
   if (all_possible_usernames.empty())
     return false;
 
+  // All elements in |all_possible_usernames| should have the same |Form()|.
+  DCHECK(
+      std::adjacent_find(
+          all_possible_usernames.begin(), all_possible_usernames.end(),
+          [](const blink::WebInputElement& a, const blink::WebInputElement& b) {
+            return a.Form() != b.Form();
+          }) == all_possible_usernames.end());
   const blink::WebFormElement form = all_possible_usernames[0].Form();
-  if (!username_detector_cache ||
-      username_detector_cache->find(form) == username_detector_cache->end()) {
+
+  // True if the cache has no entry for |form|.
+  bool cache_miss = true;
+  // Iterator pointing to the entry for |form| if the entry for |form| is found.
+  UsernameDetectorCache::iterator form_position;
+  if (username_detector_cache) {
+    std::tie(form_position, cache_miss) = username_detector_cache->insert(
+        std::make_pair(form, blink::WebInputElement()));
+  }
+
+  if (!username_detector_cache || cache_miss) {
     bool username_found = FindUsernameFieldInternal(
         all_possible_usernames, form_data, username_element);
-    if (username_detector_cache) {
-      (*username_detector_cache)[form] =
-          username_found ? *username_element : blink::WebInputElement();
-    }
+    if (username_detector_cache && username_found)
+      form_position->second = *username_element;
     return username_found;
+  } else {
+    *username_element = form_position->second;
+    return !username_element->IsNull();
   }
-  // Use the cached value for |form|.
-  *username_element = (*username_detector_cache)[form];
-  return !username_element->IsNull();
 }
 
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/html_based_username_detector.h b/components/autofill/content/renderer/html_based_username_detector.h
index 11cf121..a7089fd 100644
--- a/components/autofill/content/renderer/html_based_username_detector.h
+++ b/components/autofill/content/renderer/html_based_username_detector.h
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <map>
+
 #include "components/autofill/core/common/password_form.h"
 #include "third_party/WebKit/public/web/WebFormControlElement.h"
 #include "third_party/WebKit/public/web/WebInputElement.h"
diff --git a/components/autofill/content/renderer/html_based_username_detector_vocabulary.cc b/components/autofill/content/renderer/html_based_username_detector_vocabulary.cc
new file mode 100644
index 0000000..abf4e4bf
--- /dev/null
+++ b/components/autofill/content/renderer/html_based_username_detector_vocabulary.cc
@@ -0,0 +1,234 @@
+// 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 "components/autofill/content/renderer/html_based_username_detector_vocabulary.h"
+
+#include "base/macros.h"
+
+namespace autofill {
+
+const char* const kNegativeLatin[] = {
+    "pin",    "parola",   "wagwoord",   "wachtwoord",
+    "fake",   "parole",   "givenname",  "achinsinsi",
+    "token",  "parool",   "firstname",  "facalfaire",
+    "fname",  "lozinka",  "pasahitza",  "focalfaire",
+    "lname",  "passord",  "pasiwedhi",  "iphasiwedi",
+    "geslo",  "huahuna",  "passwuert",  "katalaluan",
+    "heslo",  "fullname", "phasewete",  "adgangskode",
+    "parol",  "optional", "wachtwurd",  "contrasenya",
+    "sandi",  "lastname", "cyfrinair",  "contrasinal",
+    "senha",  "kupuhipa", "katasandi",  "kalmarsirri",
+    "hidden", "password", "loluszais",  "tenimiafina",
+    "second", "passwort", "middlename", "paroladordine",
+    "codice", "pasvorto", "familyname", "inomboloyokuvula",
+    "modpas", "salasana", "motdepasse", "numeraeleiloaesesi"};
+const int kNegativeLatinSize = arraysize(kNegativeLatin);
+
+const char* const kNegativeNonLatin[] = {"fjalëkalim",
+                                         "የይለፍቃል",
+                                         "كلمهالسر",
+                                         "գաղտնաբառ",
+                                         "пароль",
+                                         "পাসওয়ার্ড",
+                                         "парола",
+                                         "密码",
+                                         "密碼",
+                                         "დაგავიწყდათ",
+                                         "κωδικόςπρόσβασης",
+                                         "પાસવર્ડ",
+                                         "סיסמה",
+                                         "पासवर्ड",
+                                         "jelszó",
+                                         "lykilorð",
+                                         "paswọọdụ",
+                                         "パスワード",
+                                         "ಪಾಸ್ವರ್ಡ್",
+                                         "пароль",
+                                         "ការពាក្យសម្ងាត់",
+                                         "암호",
+                                         "şîfre",
+                                         "купуясөз",
+                                         "ລະຫັດຜ່ານ",
+                                         "slaptažodis",
+                                         "лозинка",
+                                         "पासवर्ड",
+                                         "нууцүг",
+                                         "စကားဝှက်ကို",
+                                         "पासवर्ड",
+                                         "رمز",
+                                         "کلمهعبور",
+                                         "hasło",
+                                         "пароль",
+                                         "лозинка",
+                                         "پاسورڊ",
+                                         "මුරපදය",
+                                         "contraseña",
+                                         "lösenord",
+                                         "гузарвожа",
+                                         "கடவுச்சொல்",
+                                         "పాస్వర్డ్",
+                                         "รหัสผ่าน",
+                                         "пароль",
+                                         "پاسورڈ",
+                                         "mậtkhẩu",
+                                         "פּאַראָל",
+                                         "ọrọigbaniwọle"};
+const int kNegativeNonLatinSize = arraysize(kNegativeNonLatin);
+
+const char* const kUsernameLatin[] = {
+    "gatti",      "uzantonomo",   "solonanarana",    "nombredeusuario",
+    "olumulo",    "nomenusoris",  "enwdefnyddiwr",   "nomdutilisateur",
+    "lolowera",   "notandanafn",  "nomedeusuario",   "vartotojovardas",
+    "username",   "ahanjirimara", "gebruikersnaam",  "numedeutilizator",
+    "brugernavn", "benotzernumm", "jinalamtumiaji",  "erabiltzaileizena",
+    "brukernavn", "benutzername", "sunanmaiamfani",  "foydalanuvchinomi",
+    "mosebedisi", "kasutajanimi", "ainmcleachdaidh", "igamalomsebenzisi",
+    "nomdusuari", "lomsebenzisi", "jenengpanganggo", "ingoakaiwhakamahi",
+    "nomeutente", "namapengguna"};
+const int kUsernameLatinSize = arraysize(kUsernameLatin);
+
+const char* const kUsernameNonLatin[] = {"用户名",
+                                         "کاتيجونالو",
+                                         "用戶名",
+                                         "የተጠቃሚስም",
+                                         "логин",
+                                         "اسمالمستخدم",
+                                         "נאמען",
+                                         "کاصارفکانام",
+                                         "ユーザ名",
+                                         "όνομα χρήστη",
+                                         "brûkersnamme",
+                                         "корисничкоиме",
+                                         "nonitilizatè",
+                                         "корисничкоиме",
+                                         "ngaranpamaké",
+                                         "ຊື່ຜູ້ໃຊ້",
+                                         "användarnamn",
+                                         "యూజర్పేరు",
+                                         "korisničkoime",
+                                         "пайдаланушыаты",
+                                         "שםמשתמש",
+                                         "ім'якористувача",
+                                         "کارننوم",
+                                         "хэрэглэгчийннэр",
+                                         "nomedeusuário",
+                                         "имяпользователя",
+                                         "têntruynhập",
+                                         "பயனர்பெயர்",
+                                         "ainmúsáideora",
+                                         "ชื่อผู้ใช้",
+                                         "사용자이름",
+                                         "імякарыстальніка",
+                                         "lietotājvārds",
+                                         "потребителскоиме",
+                                         "uporabniškoime",
+                                         "колдонуучунунаты",
+                                         "kullanıcıadı",
+                                         "පරිශීලකනාමය",
+                                         "istifadəçiadı",
+                                         "օգտագործողիանունը",
+                                         "navêbikarhêner",
+                                         "ಬಳಕೆದಾರಹೆಸರು",
+                                         "emriipërdoruesit",
+                                         "वापरकर्तानाव",
+                                         "käyttäjätunnus",
+                                         "વપરાશકર્તાનામ",
+                                         "felhasználónév",
+                                         "उपयोगकर्तानाम",
+                                         "nazwaużytkownika",
+                                         "ഉപയോക്തൃനാമം",
+                                         "სახელი",
+                                         "အသုံးပြုသူအမည်",
+                                         "نامکاربری",
+                                         "प्रयोगकर्तानाम",
+                                         "uživatelskéjméno",
+                                         "ব্যবহারকারীরনাম",
+                                         "užívateľskémeno",
+                                         "ឈ្មោះអ្នកប្រើប្រាស់"};
+const int kUsernameNonLatinSize = arraysize(kUsernameNonLatin);
+
+const char* const kUserLatin[] = {
+    "user",   "wosuta",   "gebruiker",  "utilizator",
+    "usor",   "notandi",  "gumagamit",  "vartotojas",
+    "fammi",  "olumulo",  "maiamfani",  "cleachdaidh",
+    "utent",  "pemakai",  "mpampiasa",  "umsebenzisi",
+    "bruger", "usuario",  "panganggo",  "utilisateur",
+    "bruker", "benotzer", "uporabnik",  "doutilizador",
+    "numake", "benutzer", "covneegsiv", "erabiltzaile",
+    "usuari", "kasutaja", "defnyddiwr", "kaiwhakamahi",
+    "utente", "korisnik", "mosebedisi", "foydalanuvchi",
+    "uzanto", "pengguna", "mushandisi"};
+const int kUserLatinSize = arraysize(kUserLatin);
+
+const char* const kUserNonLatin[] = {"用户",
+                                     "użytkownik",
+                                     "tagatafaʻaaogā",
+                                     "دکارونکيعکس",
+                                     "用戶",
+                                     "užívateľ",
+                                     "корисник",
+                                     "карыстальнік",
+                                     "brûker",
+                                     "kullanıcı",
+                                     "истифода",
+                                     "អ្នកប្រើ",
+                                     "ọrụ",
+                                     "ተጠቃሚ",
+                                     "באַניצער",
+                                     "хэрэглэгчийн",
+                                     "يوزر",
+                                     "istifadəçi",
+                                     "ຜູ້ໃຊ້",
+                                     "пользователь",
+                                     "صارف",
+                                     "meahoʻohana",
+                                     "потребител",
+                                     "वापरकर्ता",
+                                     "uživatel",
+                                     "ユーザー",
+                                     "מִשׁתַמֵשׁ",
+                                     "ผู้ใช้งาน",
+                                     "사용자",
+                                     "bikaranîvan",
+                                     "колдонуучу",
+                                     "વપરાશકર્તા",
+                                     "përdorues",
+                                     "ngườidùng",
+                                     "корисникот",
+                                     "उपयोगकर्ता",
+                                     "itilizatè",
+                                     "χρήστης",
+                                     "користувач",
+                                     "օգտվողիանձնագիրը",
+                                     "használó",
+                                     "faoiúsáideoir",
+                                     "შესახებ",
+                                     "ব্যবহারকারী",
+                                     "lietotājs",
+                                     "பயனர்",
+                                     "ಬಳಕೆದಾರ",
+                                     "ഉപയോക്താവ്",
+                                     "کاربر",
+                                     "యూజర్",
+                                     "පරිශීලක",
+                                     "प्रयोगकर्ता",
+                                     "användare",
+                                     "المستعمل",
+                                     "пайдаланушы",
+                                     "အသုံးပြုသူကို",
+                                     "käyttäjä"};
+const int kUserNonLatinSize = arraysize(kUserNonLatin);
+
+const char* const kTechnicalWords[] = {
+    "uid",         "newtel",     "uaccount",   "regaccount",  "ureg",
+    "loginid",     "laddress",   "accountreg", "regid",       "regname",
+    "loginname",   "membername", "uname",      "ucreate",     "loginmail",
+    "accountname", "umail",      "loginreg",   "accountid",   "loginaccount",
+    "ulogin",      "regemail",   "newmobile",  "accountlogin"};
+const int kTechnicalWordsSize = arraysize(kTechnicalWords);
+
+const char* const kWeakWords[] = {"id", "login", "mail"};
+const int kWeakWordsSize = arraysize(kWeakWords);
+
+}  // namespace autofill
diff --git a/components/autofill/content/renderer/html_based_username_detector_vocabulary.h b/components/autofill/content/renderer/html_based_username_detector_vocabulary.h
new file mode 100644
index 0000000..8164fa2
--- /dev/null
+++ b/components/autofill/content/renderer/html_based_username_detector_vocabulary.h
@@ -0,0 +1,39 @@
+// 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.
+
+namespace autofill {
+
+// Words that certainly point to a non-username field.
+// If field values contain at least one negative word, then the field is
+// excluded from the list of possible usernames.
+extern const char* const kNegativeLatin[];
+extern const int kNegativeLatinSize;
+extern const char* const kNegativeNonLatin[];
+extern const int kNegativeNonLatinSize;
+
+// Translations of "username".
+extern const char* const kUsernameLatin[];
+extern const int kUsernameLatinSize;
+extern const char* const kUsernameNonLatin[];
+extern const int kUsernameNonLatinSize;
+
+// Translations of "user".
+extern const char* const kUserLatin[];
+extern const int kUserLatinSize;
+extern const char* const kUserNonLatin[];
+extern const int kUserNonLatinSize;
+
+// Words that certainly point to a username field, if they appear in developer
+// value. They are technical words, because they can only be used as variable
+// names, and not as stand-alone words.
+extern const char* const kTechnicalWords[];
+extern const int kTechnicalWordsSize;
+
+// Words that might point to a username field.They have the smallest priority
+// in the heuristic, because there are also field attribute values that
+// contain them, but are not username fields.
+extern const char* const kWeakWords[];
+extern const int kWeakWordsSize;
+
+}  // namespace autofill
diff --git a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
index 915a853..b74ba55 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
@@ -221,7 +221,8 @@
     }
 
     return CreatePasswordFormFromWebForm(
-        form, with_user_input ? &user_input : nullptr, predictions, nullptr);
+        form, with_user_input ? &user_input : nullptr, predictions,
+        &username_detector_cache_);
   }
 
   // Iterates on the form generated by the |html| and adds the fields and type
@@ -270,6 +271,8 @@
     *form = forms[0];
   }
 
+  UsernameDetectorCache username_detector_cache_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(MAYBE_PasswordFormConversionUtilsTest);
 };
@@ -334,7 +337,7 @@
 }
 
 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
-       IdentifyingUsernameFieldsFromDeveloperGroupWithHTMLDetector) {
+       HTMLDetector_DeveloperGroupAttributes) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
       password_manager::features::kEnableHtmlBasedUsernameDetector);
@@ -408,17 +411,18 @@
        {"email", "", "js@google.com"},
        "email",
        "js@google.com"},
-      // If word matches in maximum 2 fields, it is accepted.
+      // If a word matches in maximum 2 fields, it is accepted.
       // First encounter is selected as username.
-      {{"loginusername", "", "johnsmith"},
-       {"loginemail", "", "js@google.com"},
-       "loginusername",
+      {{"username", "", "johnsmith"},
+       {"repeat_username", "", "johnsmith"},
+       "username",
        "johnsmith"},
-      // Check treatment for short dictionary words.
-      {{"identity_name", "", "johnsmith"},
-       {"email", "", "js@google.com"},
-       "email",
-       "js@google.com"}};
+      // A short word should be enclosed between delimiters. Otherwise, an
+      // Occurrence doesn't count.
+      {{"identity_name", "idn", "johnsmith"},
+       {"id", "id", "123"},
+       "id",
+       "123"}};
 
   for (size_t i = 0; i < arraysize(cases); ++i) {
     SCOPED_TRACE(testing::Message() << "Iteration " << i);
@@ -436,6 +440,7 @@
     builder.AddSubmitButton("submit");
     std::string html = builder.ProduceHTML();
 
+    username_detector_cache_.clear();
     std::unique_ptr<PasswordForm> password_form =
         LoadHTMLAndConvertForm(html, nullptr, false);
 
@@ -445,7 +450,19 @@
               password_form->username_element);
     EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
               password_form->username_value);
+    // Check that the username field was found by HTML detector.
+    ASSERT_EQ(1u, username_detector_cache_.size());
+    ASSERT_FALSE(username_detector_cache_.begin()->second.IsNull());
+    EXPECT_EQ(
+        cases[i].expected_username_element,
+        username_detector_cache_.begin()->second.NameForAutofill().Utf8());
   }
+}
+
+TEST_F(MAYBE_PasswordFormConversionUtilsTest, HTMLDetector_SeveralDetections) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      password_manager::features::kEnableHtmlBasedUsernameDetector);
 
   // If word matches in more than 2 fields, we don't match on it.
   // We search for match with another word.
@@ -459,6 +476,7 @@
   builder.AddSubmitButton("submit");
   std::string html = builder.ProduceHTML();
 
+  DCHECK(username_detector_cache_.empty());
   std::unique_ptr<PasswordForm> password_form =
       LoadHTMLAndConvertForm(html, nullptr, false);
 
@@ -466,10 +484,15 @@
 
   EXPECT_EQ(base::UTF8ToUTF16("loginid"), password_form->username_element);
   EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
+  // Check that the username field was found by HTML detector.
+  ASSERT_EQ(1u, username_detector_cache_.size());
+  ASSERT_FALSE(username_detector_cache_.begin()->second.IsNull());
+  EXPECT_EQ("loginid",
+            username_detector_cache_.begin()->second.NameForAutofill().Utf8());
 }
 
 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
-       IdentifyingUsernameFieldsFromUserGroupWithHTMLDetector) {
+       HTMLDetector_UserGroupAttributes) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
       password_manager::features::kEnableHtmlBasedUsernameDetector);
@@ -483,12 +506,12 @@
   struct TestCase {
     // Field parameters represent, in order of appearance, field name, field
     // id, field value and field label or placeholder.
+    // Field name and field id don't contain any significant information.
     const char* first_text_field_parameters[4];
     const char* second_text_field_parameters[4];
     const char* expected_username_element;
     const char* expected_username_value;
   } cases[] = {
-      // Developer group does not contain any significant information.
       // Label information will decide username.
       {{"name1", "id1", "johnsmith", "Username:"},
        {"name2", "id2", "js@google.com", "Email:"},
@@ -545,9 +568,10 @@
        {"username", "", "johnsmith", "Email:"},
        "email",
        "js@google.com"},
-      // Check treatment for short dictionary words.
+      // Check treatment for short dictionary words. "uid" has higher priority,
+      // but its occurrence is ignored because it is a part of another word.
       {{"name1", "", "johnsmith", "Insert your id:"},
-       {"name2", "", "js@google.com", "Insert something:"},
+       {"name2", "uidentical", "js@google.com", "Insert something:"},
        "name1",
        "johnsmith"}};
 
@@ -569,6 +593,7 @@
     builder.AddSubmitButton("submit");
     std::string html = builder.ProduceHTML();
 
+    username_detector_cache_.clear();
     std::unique_ptr<PasswordForm> password_form =
         LoadHTMLAndConvertForm(html, nullptr, false);
 
@@ -578,6 +603,12 @@
               password_form->username_element);
     EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
               password_form->username_value);
+    // Check that the username field was found by HTML detector.
+    ASSERT_EQ(1u, username_detector_cache_.size());
+    ASSERT_FALSE(username_detector_cache_.begin()->second.IsNull());
+    EXPECT_EQ(
+        cases[i].expected_username_element,
+        username_detector_cache_.begin()->second.NameForAutofill().Utf8());
   }
 }
 
@@ -613,7 +644,7 @@
   // will be the same because it was cached in |username_detector_cache|.
   WebVector<WebFormControlElement> control_elements;
   form.GetFormControlElements(control_elements);
-  control_elements[0].SetAttribute("name", "login");
+  control_elements[0].SetAttribute("name", "id");
   password_form = CreatePasswordFormFromWebForm(form, nullptr, nullptr,
                                                 &username_detector_cache);
   EXPECT_TRUE(password_form);
@@ -633,7 +664,7 @@
   ASSERT_EQ(1u, username_detector_cache.size());
   EXPECT_EQ(form, username_detector_cache.begin()->first);
   ASSERT_FALSE(username_detector_cache.begin()->second.IsNull());
-  EXPECT_EQ("login",
+  EXPECT_EQ("id",
             username_detector_cache.begin()->second.NameForAutofill().Utf8());
   EXPECT_THAT(
       histogram_tester.GetAllSamples("PasswordManager.UsernameDetectionMethod"),
@@ -650,7 +681,7 @@
   ASSERT_EQ(1u, username_detector_cache.size());
   EXPECT_EQ(form, username_detector_cache.begin()->first);
   ASSERT_FALSE(username_detector_cache.begin()->second.IsNull());
-  EXPECT_EQ("login",
+  EXPECT_EQ("id",
             username_detector_cache.begin()->second.NameForAutofill().Utf8());
   EXPECT_THAT(
       histogram_tester.GetAllSamples("PasswordManager.UsernameDetectionMethod"),
diff --git a/components/autofill/core/browser/credit_card_unittest.cc b/components/autofill/core/browser/credit_card_unittest.cc
index e45c40a..b110302 100644
--- a/components/autofill/core/browser/credit_card_unittest.cc
+++ b/components/autofill/core/browser/credit_card_unittest.cc
@@ -1271,7 +1271,12 @@
 
 // Test that credit card last used date suggestion can be generated correctly
 // in different variations.
-TEST(CreditCardTest, GetLastUsedDateForDisplay) {
+
+
+// TODO(scottmg): Disabling as sheriff. On Android, LastUsedDateForDisplay is
+// returning "Last used over a year ago", rather than "last used Nov 30" as of
+// today, Dec 1. https://crbug.com/791067.
+TEST(CreditCardTest, DISABLED_GetLastUsedDateForDisplay) {
   const base::Time::Exploded kTestDateTimeExploded = {
       2016, 12, 6, 10,  // Sat, Dec 10, 2016
       15,   42, 7, 0    // 15:42:07.000
diff --git a/components/os_crypt/key_storage_keyring.cc b/components/os_crypt/key_storage_keyring.cc
index 2061e194..af2f64b 100644
--- a/components/os_crypt/key_storage_keyring.cc
+++ b/components/os_crypt/key_storage_keyring.cc
@@ -9,8 +9,6 @@
 #include "base/rand_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
 #include "components/os_crypt/keyring_util_linux.h"
 
 namespace {
@@ -33,50 +31,34 @@
 
 KeyStorageKeyring::~KeyStorageKeyring() {}
 
+base::SequencedTaskRunner* KeyStorageKeyring::GetTaskRunner() {
+  return main_thread_runner_.get();
+}
+
 bool KeyStorageKeyring::Init() {
+  DCHECK(main_thread_runner_->RunsTasksInCurrentSequence());
   return GnomeKeyringLoader::LoadGnomeKeyring();
 }
 
 std::string KeyStorageKeyring::GetKeyImpl() {
+  DCHECK(main_thread_runner_->RunsTasksInCurrentSequence());
+
   std::string password;
-
-  // Ensure GetKeyDelegate() is executed on the main thread.
-  if (main_thread_runner_->BelongsToCurrentThread()) {
-    GetKeyDelegate(&password, nullptr);
-  } else {
-    base::WaitableEvent password_loaded(
-        base::WaitableEvent::ResetPolicy::MANUAL,
-        base::WaitableEvent::InitialState::NOT_SIGNALED);
-    main_thread_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&KeyStorageKeyring::GetKeyDelegate, base::Unretained(this),
-                   &password, &password_loaded));
-    password_loaded.Wait();
-  }
-
-  return password;
-}
-
-void KeyStorageKeyring::GetKeyDelegate(
-    std::string* password_ptr,
-    base::WaitableEvent* password_loaded_ptr) {
-  gchar* password = nullptr;
+  gchar* password_c = nullptr;
   GnomeKeyringResult result =
       GnomeKeyringLoader::gnome_keyring_find_password_sync_ptr(
-          &kSchema, &password, "application", kApplicationName, nullptr);
+          &kSchema, &password_c, "application", kApplicationName, nullptr);
   if (result == GNOME_KEYRING_RESULT_OK) {
-    *password_ptr = password;
-    GnomeKeyringLoader::gnome_keyring_free_password_ptr(password);
+    password = password_c;
+    GnomeKeyringLoader::gnome_keyring_free_password_ptr(password_c);
   } else if (result == GNOME_KEYRING_RESULT_NO_MATCH) {
-    *password_ptr = KeyStorageKeyring::AddRandomPasswordInKeyring();
+    password = KeyStorageKeyring::AddRandomPasswordInKeyring();
     VLOG(1) << "OSCrypt generated a new password";
   } else {
-    password_ptr->clear();
     VLOG(1) << "OSCrypt failed to use gnome-keyring";
   }
 
-  if (password_loaded_ptr)
-    password_loaded_ptr->Signal();
+  return password;
 }
 
 std::string KeyStorageKeyring::AddRandomPasswordInKeyring() {
diff --git a/components/os_crypt/key_storage_keyring.h b/components/os_crypt/key_storage_keyring.h
index e186ef1..fb41a708 100644
--- a/components/os_crypt/key_storage_keyring.h
+++ b/components/os_crypt/key_storage_keyring.h
@@ -13,7 +13,6 @@
 
 namespace base {
 class SingleThreadTaskRunner;
-class WaitableEvent;
 }  // namespace base
 
 // Specialisation of KeyStorageLinux that uses Libsecret.
@@ -25,17 +24,11 @@
 
  protected:
   // KeyStorageLinux
+  base::SequencedTaskRunner* GetTaskRunner() override;
   bool Init() override;
   std::string GetKeyImpl() override;
 
  private:
-  // Gnome keyring requires calls to originate from the main thread.
-  // This is the part of GetKey() that gets dispatched to the main thread.
-  // The password is stored in |password_ptr|. If |password_loaded_ptr| is not
-  // null, it will be signaled when |password_ptr| is safe to read.
-  void GetKeyDelegate(std::string* password_ptr,
-                      base::WaitableEvent* password_loaded_ptr);
-
   // Generate a random string and store it as OScrypt's new password.
   std::string AddRandomPasswordInKeyring();
 
diff --git a/components/password_manager/core/browser/export/destination.h b/components/password_manager/core/browser/export/destination.h
index 342601e..a0987a1 100644
--- a/components/password_manager/core/browser/export/destination.h
+++ b/components/password_manager/core/browser/export/destination.h
@@ -16,8 +16,9 @@
   Destination() = default;
   virtual ~Destination() = default;
 
-  // Send the data to the destination, synchronously.
-  virtual bool Write(const std::string& data) = 0;
+  // Send the data to the destination, synchronously. On failure, a
+  // human-readable error message will be returned.
+  virtual base::string16 Write(const std::string& data) = 0;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc b/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
index 48dec4b..2ab2407 100644
--- a/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
+++ b/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
@@ -59,7 +59,7 @@
   MockDestination() = default;
   ~MockDestination() override = default;
 
-  MOCK_METHOD1(Write, bool(const std::string& data));
+  MOCK_METHOD1(Write, base::string16(const std::string& data));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockDestination);
diff --git a/components/password_manager/core/browser/password_store_factory_util.cc b/components/password_manager/core/browser/password_store_factory_util.cc
index f7a9824..4e8cdb8 100644
--- a/components/password_manager/core/browser/password_store_factory_util.cc
+++ b/components/password_manager/core/browser/password_store_factory_util.cc
@@ -30,16 +30,21 @@
     PasswordStore* password_store,
     net::URLRequestContextGetter* request_context_getter,
     const base::FilePath& db_path) {
-  // The PasswordStore is so far the only consumer of the AffiliationService,
-  // therefore the service is owned by the AffiliatedMatchHelper, which in
-  // turn is owned by the PasswordStore.
+  // Subsequent instances of the AffiliationService must use the same sequenced
+  // task runner for their backends. This guarantees that the backend of the
+  // first instance will have closed the affiliation database before the second
+  // instance attempts to open it again. See: https://crbug.com/786157.
+  //
   // Task priority is USER_VISIBLE, because AffiliationService-related tasks
-  // block obtaining credentials from PasswordStore, which matches the
-  // USER_VISIBLE example: "Loading data that might be shown in the UI after a
-  // future user interaction."
+  // block obtaining credentials from PasswordStore, hence password autofill.
+  static auto backend_task_runner = base::CreateSequencedTaskRunnerWithTraits(
+      {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
+
+  // The PasswordStore is so far the only consumer of the AffiliationService,
+  // therefore the service is owned by the AffiliatedMatchHelper, which in turn
+  // is owned by the PasswordStore.
   std::unique_ptr<AffiliationService> affiliation_service(
-      new AffiliationService(base::CreateSequencedTaskRunnerWithTraits(
-          {base::MayBlock(), base::TaskPriority::USER_VISIBLE})));
+      new AffiliationService(backend_task_runner));
   affiliation_service->Initialize(request_context_getter, db_path);
   std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper(
       new AffiliatedMatchHelper(password_store,
diff --git a/components/password_manager_strings.grdp b/components/password_manager_strings.grdp
index cfc7a00..d0647b2 100644
--- a/components/password_manager_strings.grdp
+++ b/components/password_manager_strings.grdp
@@ -19,5 +19,8 @@
   <message name="IDS_PASSWORD_MANAGER_DEFAULT_EXPORT_FILENAME" desc="Chrome suggests this file name when user chooses to export their passwords saved with Chrome.">
     Chrome Passwords
   </message>
+  <message name="IDS_PASSWORD_MANAGER_EXPORT_TO_FILE_FAILED" desc="Chrome tried to export the passwords to a file and failed. This message will be the title of the error message that will be shown to the user.">
+    Can't export passwords to "<ph name="folder">$1<ex>Desktop</ex></ph>"
+  </message>
 
 </grit-part>
diff --git a/components/proxy_config/pref_proxy_config_tracker_impl.cc b/components/proxy_config/pref_proxy_config_tracker_impl.cc
index 33ddfac..c39d5e4 100644
--- a/components/proxy_config/pref_proxy_config_tracker_impl.cc
+++ b/components/proxy_config/pref_proxy_config_tracker_impl.cc
@@ -127,10 +127,11 @@
 
 PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl(
     PrefService* pref_service,
-    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+    scoped_refptr<base::SingleThreadTaskRunner>
+        proxy_config_service_task_runner)
     : pref_service_(pref_service),
       proxy_config_service_impl_(nullptr),
-      io_task_runner_(io_task_runner) {
+      proxy_config_service_task_runner_(proxy_config_service_task_runner) {
   pref_config_state_ = ReadPrefConfig(pref_service_, &pref_config_);
   active_config_state_ = pref_config_state_;
   active_config_ = pref_config_;
@@ -279,7 +280,19 @@
 
   if (!proxy_config_service_impl_)
     return;
-  io_task_runner_->PostTask(
+
+  // If the ProxyConfigService lives on the current thread, just synchronously
+  // tell it about the new configuration.
+  // TODO(mmenke): When/if iOS is migrated to using the NetworkService, get rid
+  // of |proxy_config_service_task_runner_|. Can also merge
+  // ProxyConfigServiceImpl into the tracker, and make the class talk over the
+  // Mojo pipe directly, at that point.
+  if (!proxy_config_service_task_runner_) {
+    proxy_config_service_impl_->UpdateProxyConfig(config_state, config);
+    return;
+  }
+
+  proxy_config_service_task_runner_->PostTask(
       FROM_HERE, base::Bind(&ProxyConfigServiceImpl::UpdateProxyConfig,
                             base::Unretained(proxy_config_service_impl_),
                             config_state, config));
diff --git a/components/proxy_config/pref_proxy_config_tracker_impl.h b/components/proxy_config/pref_proxy_config_tracker_impl.h
index b86fb327..4a5b7f5e 100644
--- a/components/proxy_config/pref_proxy_config_tracker_impl.h
+++ b/components/proxy_config/pref_proxy_config_tracker_impl.h
@@ -75,14 +75,16 @@
 };
 
 // A class that tracks proxy preferences. It translates the configuration
-// to net::ProxyConfig and pushes the result over to the IO thread for
-// ProxyConfigServiceImpl::UpdateProxyConfig to use.
+// to net::ProxyConfig and pushes the result over to
+// ProxyConfigServiceImpl::UpdateProxyConfig.
 class PROXY_CONFIG_EXPORT PrefProxyConfigTrackerImpl
     : public PrefProxyConfigTracker {
  public:
-  PrefProxyConfigTrackerImpl(
-      PrefService* pref_service,
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+  // |proxy_config_service_task_runner| is the thread the ProxyConfigService
+  // will live on. Use nullptr if it lives on the current thread.
+  PrefProxyConfigTrackerImpl(PrefService* pref_service,
+                             scoped_refptr<base::SingleThreadTaskRunner>
+                                 proxy_config_service_task_runner);
   ~PrefProxyConfigTrackerImpl() override;
 
   // PrefProxyConfigTracker implementation:
@@ -167,7 +169,7 @@
   // Active proxy configuration, last received from OnProxyConfigChanged.
   net::ProxyConfig active_config_;
 
-  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> proxy_config_service_task_runner_;
 
   base::ThreadChecker thread_checker_;
 
diff --git a/components/storage_monitor/storage_monitor_chromeos.cc b/components/storage_monitor/storage_monitor_chromeos.cc
index a31490e..82e0af2 100644
--- a/components/storage_monitor/storage_monitor_chromeos.cc
+++ b/components/storage_monitor/storage_monitor_chromeos.cc
@@ -80,9 +80,7 @@
 
 }  // namespace
 
-StorageMonitorCros::StorageMonitorCros()
-    : weak_ptr_factory_(this) {
-}
+StorageMonitorCros::StorageMonitorCros() : weak_ptr_factory_(this) {}
 
 StorageMonitorCros::~StorageMonitorCros() {
   DiskMountManager* manager = DiskMountManager::GetInstance();
@@ -134,8 +132,13 @@
                  weak_ptr_factory_.GetWeakPtr()));
 }
 
-void StorageMonitorCros::OnDiskEvent(DiskMountManager::DiskEvent event,
-                                     const DiskMountManager::Disk* disk) {}
+void StorageMonitorCros::OnAutoMountableDiskEvent(
+    DiskMountManager::DiskEvent event,
+    const DiskMountManager::Disk& disk) {}
+
+void StorageMonitorCros::OnBootDeviceDiskEvent(
+    DiskMountManager::DiskEvent event,
+    const DiskMountManager::Disk& disk) {}
 
 void StorageMonitorCros::OnDeviceEvent(DiskMountManager::DeviceEvent event,
                                        const std::string& device_path) {}
@@ -195,7 +198,6 @@
   media_transfer_protocol_manager_.reset(test_manager);
 }
 
-
 bool StorageMonitorCros::GetStorageInfoForPath(
     const base::FilePath& path,
     StorageInfo* device_info) const {
diff --git a/components/storage_monitor/storage_monitor_chromeos.h b/components/storage_monitor/storage_monitor_chromeos.h
index 30034971..4ad37c880 100644
--- a/components/storage_monitor/storage_monitor_chromeos.h
+++ b/components/storage_monitor/storage_monitor_chromeos.h
@@ -45,9 +45,12 @@
       device::MediaTransferProtocolManager* test_manager);
 
   // chromeos::disks::DiskMountManager::Observer implementation.
-  void OnDiskEvent(
+  void OnAutoMountableDiskEvent(
       chromeos::disks::DiskMountManager::DiskEvent event,
-      const chromeos::disks::DiskMountManager::Disk* disk) override;
+      const chromeos::disks::DiskMountManager::Disk& disk) override;
+  void OnBootDeviceDiskEvent(
+      chromeos::disks::DiskMountManager::DiskEvent event,
+      const chromeos::disks::DiskMountManager::Disk& disk) override;
   void OnDeviceEvent(chromeos::disks::DiskMountManager::DeviceEvent event,
                      const std::string& device_path) override;
   void OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,
diff --git a/components/viz/service/display_embedder/software_output_device_mac_unittest.mm b/components/viz/service/display_embedder/software_output_device_mac_unittest.mm
index 4485632e..ed0b8ad 100644
--- a/components/viz/service/display_embedder/software_output_device_mac_unittest.mm
+++ b/components/viz/service/display_embedder/software_output_device_mac_unittest.mm
@@ -13,7 +13,7 @@
 
 TEST(SoftwareOutputDeviceMacTest, Basics) {
   std::unique_ptr<SoftwareOutputDeviceMac> device(
-      new SoftwareOutputDeviceMac(nullptr));
+      new SoftwareOutputDeviceMac(gfx::kNullAcceleratedWidget));
   gfx::Size pixel_size(512, 512);
   float scale_factor = 1;
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4acf153..5029537c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1709,6 +1709,17 @@
     ]
   }
 
+  if (is_chromecast && is_linux) {
+    sources += [
+      "tracing/cast_tracing_agent.cc",
+      "tracing/cast_tracing_agent.h",
+    ]
+
+    deps += [ "//chromecast/tracing:system_tracer" ]
+
+    defines += [ "CAST_TRACING_AGENT=1" ]
+  }
+
   if (is_fuchsia) {
     sources += [
       "child_process_launcher_helper_fuchsia.cc",
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 33c5ec0..423902c 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -17,6 +17,7 @@
 #import "content/browser/accessibility/browser_accessibility_mac.h"
 #include "content/common/accessibility_messages.h"
 #include "content/public/browser/browser_thread.h"
+#include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
 #include "ui/accessibility/ax_role_properties.h"
 
 namespace {
@@ -486,7 +487,10 @@
 }
 
 NSView* BrowserAccessibilityManagerMac::GetParentView() {
-  return delegate() ? delegate()->AccessibilityGetAcceleratedWidget() : nullptr;
+  gfx::AcceleratedWidget accelerated_widget =
+      delegate() ? delegate()->AccessibilityGetAcceleratedWidget()
+                 : gfx::kNullAcceleratedWidget;
+  return ui::AcceleratedWidgetMac::GetNSView(accelerated_widget);
 }
 
 }  // namespace content
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc
index 56ae3820..7b20c5b 100644
--- a/content/browser/background_sync/background_sync_manager_unittest.cc
+++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -460,9 +460,8 @@
       sw_registration_1_->active_version()->provider_host();
   ASSERT_TRUE(provider_host);
 
-  // Remove the registration object host (it removes itself from |provider_host|
-  // upon destruction).
-  delete provider_host->registration_object_hosts_[sw_registration_1_->id()];
+  // Remove the registration object host.
+  provider_host->registration_object_hosts_.clear();
 
   // Ensure |sw_registration_1_| is the last reference to the registration.
   ASSERT_TRUE(sw_registration_1_->HasOneRef());
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.cc b/content/browser/devtools/devtools_url_interceptor_request_job.cc
index 5ddde72..c0eaf3c 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.cc
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.cc
@@ -754,16 +754,15 @@
     bool* defer_redirect) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(sub_request_);
-  // If we're not intercepting results then just exit.
-  if (stage_to_intercept_ == InterceptionStage::DONT_INTERCEPT) {
+
+  // If we're not intercepting results or are a response then cancel this
+  // redirect and tell the parent request it was redirected through |redirect_|.
+  if (stage_to_intercept_ == InterceptionStage::DONT_INTERCEPT ||
+      stage_to_intercept_ == InterceptionStage::RESPONSE) {
     *defer_redirect = false;
-    return;
-  }
-  // If we're a response interception only, we will pick it up on the followup
-  // request in DevToolsURLInterceptorRequestJob::Start().
-  if (stage_to_intercept_ == InterceptionStage::RESPONSE) {
-    interceptor_->ExpectRequestAfterRedirect(this->request(), interception_id_);
-    *defer_redirect = false;
+    ProcessRedirect(redirectinfo.status_code, redirectinfo.new_url.spec());
+    redirect_.reset();
+    sub_request_.reset();
     return;
   }
 
@@ -781,8 +780,6 @@
   }
 
   redirect_.reset(new net::RedirectInfo(redirectinfo));
-  sub_request_->Cancel();
-  sub_request_.reset();
 
   waiting_for_user_response_ = WaitingForUserResponse::WAITING_FOR_REQUEST_ACK;
 
@@ -793,6 +790,7 @@
   request_info->redirect_url = redirectinfo.new_url.spec();
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                           base::BindOnce(callback_, std::move(request_info)));
+  sub_request_.reset();
 }
 
 void DevToolsURLInterceptorRequestJob::OnInterceptedRequestResponseStarted(
@@ -966,6 +964,24 @@
   }
 }
 
+void DevToolsURLInterceptorRequestJob::ProcessRedirect(
+    int status_code,
+    const std::string& new_url) {
+  // NOTE we don't append the text form of the status code because
+  // net::HttpResponseHeaders doesn't need that.
+  std::string raw_headers = base::StringPrintf("HTTP/1.1 %d", status_code);
+  raw_headers.append(1, '\0');
+  raw_headers.append("Location: ");
+  raw_headers.append(new_url);
+  raw_headers.append(2, '\0');
+  mock_response_details_.reset(new MockResponseDetails(
+      base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers), "", 0,
+      base::TimeTicks::Now()));
+
+  interceptor_->ExpectRequestAfterRedirect(request(), interception_id_);
+  NotifyHeadersComplete();
+}
+
 void DevToolsURLInterceptorRequestJob::GetResponseBody(
     std::unique_ptr<GetResponseBodyForInterceptionCallback> callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -1050,22 +1066,10 @@
 
   if (redirect_) {
     DCHECK(!is_response_ack);
-    // NOTE we don't append the text form of the status code because
-    // net::HttpResponseHeaders doesn't need that.
-    std::string raw_headers =
-        base::StringPrintf("HTTP/1.1 %d", redirect_->status_code);
-    raw_headers.append(1, '\0');
-    raw_headers.append("Location: ");
-    raw_headers.append(
+    ProcessRedirect(
+        redirect_->status_code,
         modifications->modified_url.fromMaybe(redirect_->new_url.spec()));
-    raw_headers.append(2, '\0');
-    mock_response_details_.reset(new MockResponseDetails(
-        base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers), "", 0,
-        base::TimeTicks::Now()));
     redirect_.reset();
-
-    interceptor_->ExpectRequestAfterRedirect(request(), interception_id_);
-    NotifyHeadersComplete();
   } else if (is_response_ack) {
     DCHECK(sub_request_);
     // If we are continuing the request without change we fetch the body.
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.h b/content/browser/devtools/devtools_url_interceptor_request_job.h
index e438fb7..b8792cf 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.h
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.h
@@ -119,6 +119,7 @@
   // |mock_response_|.  In some cases (e.g. file access) this may be null.
   const net::HttpResponseHeaders* GetHttpResponseHeaders() const;
 
+  void ProcessRedirect(int status_code, const std::string& new_url);
   void ProcessInterceptionRespose(
       std::unique_ptr<DevToolsURLRequestInterceptor::Modifications>
           modification);
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 8cb9a50..3dd4390 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -2528,9 +2528,8 @@
 
   // Cancel the download and wait for download system quiesce.
   download->Cancel(true);
-  scoped_refptr<DownloadTestFlushObserver> flush_observer(
-      new DownloadTestFlushObserver(DownloadManagerForShell(shell())));
-  flush_observer->WaitForFlush();
+  DownloadTestFlushObserver flush_observer(DownloadManagerForShell(shell()));
+  flush_observer.WaitForFlush();
 
   // Get the important info from other threads and check it.
   EXPECT_TRUE(EnsureNoPendingDownloads());
@@ -2568,9 +2567,8 @@
 
   // Cancel the download and wait for download system quiesce.
   download->Cancel(true);
-  scoped_refptr<DownloadTestFlushObserver> flush_observer(
-      new DownloadTestFlushObserver(DownloadManagerForShell(shell())));
-  flush_observer->WaitForFlush();
+  DownloadTestFlushObserver flush_observer(DownloadManagerForShell(shell()));
+  flush_observer.WaitForFlush();
 
   // Get the important info from other threads and check it.
   EXPECT_TRUE(EnsureNoPendingDownloads());
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 53a8fa1..2a2fad9 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -40,10 +40,8 @@
 #include "content/browser/download/download_resource_handler.h"
 #include "content/browser/download/download_task_runner.h"
 #include "content/browser/download/parallel_download_utils.h"
-#include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/download_danger_type.h"
-#include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_paths.h"
@@ -99,17 +97,17 @@
 const std::string kOriginTwo = "two.example";
 const std::string kOriginThree = "example.com";
 
-class TestResourceDispatcherHostDelegate
-    : public ResourceDispatcherHostDelegate {
+// Implementation of TestContentBrowserClient that overrides
+// AllowRenderingMhtmlOverHttp() and allows consumers to set a value.
+class DownloadTestContentBrowserClient : public ContentBrowserClient {
  public:
-  TestResourceDispatcherHostDelegate() = default;
+  DownloadTestContentBrowserClient() = default;
 
-  bool AllowRenderingMhtmlOverHttp(net::URLRequest* request) const override {
+  bool AllowRenderingMhtmlOverHttp(
+      NavigationUIData* navigation_data) const override {
     return allowed_rendering_mhtml_over_http_;
   }
 
-  void SetDelegate() { ResourceDispatcherHost::Get()->SetDelegate(this); }
-
   void set_allowed_rendering_mhtml_over_http(bool allowed) {
     allowed_rendering_mhtml_over_http_ = allowed;
   }
@@ -117,7 +115,7 @@
  private:
   bool allowed_rendering_mhtml_over_http_ = false;
 
-  DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate);
+  DISALLOW_COPY_AND_ASSIGN(DownloadTestContentBrowserClient);
 };
 
 class MockDownloadItemObserver : public DownloadItem::Observer {
@@ -702,14 +700,6 @@
                              real_host);
     host_resolver()->AddRule(TestDownloadHttpResponse::kTestDownloadHostName,
                              real_host);
-
-    test_resource_dispatcher_host_delegate_ =
-        std::make_unique<TestResourceDispatcherHostDelegate>();
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::BindOnce(
-            &TestResourceDispatcherHostDelegate::SetDelegate,
-            base::Unretained(test_resource_dispatcher_host_delegate_.get())));
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -873,18 +863,12 @@
     return inject_error_callback_;
   }
 
-  TestResourceDispatcherHostDelegate* test_resource_dispatcher_host_delegate() {
-    return test_resource_dispatcher_host_delegate_.get();
-  }
-
  private:
   // Location of the downloads directory for these tests
   base::ScopedTempDir downloads_directory_;
   std::unique_ptr<TestShellDownloadManagerDelegate> test_delegate_;
   TestDownloadResponseHandler test_response_handler_;
   TestDownloadHttpResponse::InjectErrorCallback inject_error_callback_;
-  std::unique_ptr<TestResourceDispatcherHostDelegate>
-      test_resource_dispatcher_host_delegate_;
 };
 
 // Test fixture for parallel downloading.
@@ -912,7 +896,7 @@
 }  // namespace
 
 // Flaky. See https://crbug.com/754679.
-IN_PROC_BROWSER_TEST_F(DownloadContentTest, DISABLED_DownloadCancelled) {
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadCancelled) {
   SetupEnsureNoPendingDownloads();
 
   // Create a download, wait until it's started, and confirm
@@ -925,9 +909,8 @@
 
   // Cancel the download and wait for download system quiesce.
   download->Cancel(true);
-  scoped_refptr<DownloadTestFlushObserver> flush_observer(
-      new DownloadTestFlushObserver(DownloadManagerForShell(shell())));
-  flush_observer->WaitForFlush();
+  DownloadTestFlushObserver flush_observer(DownloadManagerForShell(shell()));
+  flush_observer.WaitForFlush();
 
   // Get the important info from other threads and check it.
   EXPECT_TRUE(EnsureNoPendingDownloads());
@@ -2891,8 +2874,14 @@
                download->GetTargetFilePath().BaseName().value().c_str());
 }
 
+#if defined(OS_ANDROID)
+// Flaky on android: https://crbug.com/786626
+#define MAYBE_ParallelDownloadComplete DISABLED_ParallelDownloadComplete
+#else
+#define MAYBE_ParallelDownloadComplete ParallelDownloadComplete
+#endif
 // Verify parallel download in normal case.
-IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, ParallelDownloadComplete) {
+IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, MAYBE_ParallelDownloadComplete) {
   EXPECT_TRUE(base::FeatureList::IsEnabled(features::kParallelDownloading));
 
   GURL url = TestDownloadHttpResponse::GetNextURLForDownload();
@@ -3115,18 +3104,21 @@
 
 IN_PROC_BROWSER_TEST_F(DownloadContentTest, ForceDownloadMhtml) {
   // Force downloading the MHTML.
-  test_resource_dispatcher_host_delegate()
-      ->set_allowed_rendering_mhtml_over_http(false);
+  DownloadTestContentBrowserClient new_client;
+  new_client.set_allowed_rendering_mhtml_over_http(false);
+  ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
 
   NavigateToURLAndWaitForDownload(
       shell(), embedded_test_server()->GetURL("/download/hello.mhtml"),
       DownloadItem::COMPLETE);
+  SetBrowserClientForTesting(old_client);
 }
 
 IN_PROC_BROWSER_TEST_F(DownloadContentTest, AllowRenderMhtml) {
   // Allows loading the MHTML, instead of downloading it.
-  test_resource_dispatcher_host_delegate()
-      ->set_allowed_rendering_mhtml_over_http(true);
+  DownloadTestContentBrowserClient new_client;
+  new_client.set_allowed_rendering_mhtml_over_http(true);
+  ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
 
   GURL url = embedded_test_server()->GetURL("/download/hello.mhtml");
   auto observer = std::make_unique<content::TestNavigationObserver>(url);
@@ -3136,6 +3128,7 @@
   NavigateToURL(shell(), url);
 
   observer->WaitForNavigationFinished();
+  SetBrowserClientForTesting(old_client);
 }
 
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index d563c284..b3bfdd5 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -135,13 +135,15 @@
 
 // TODO(clamy): This should match what's happening in
 // blink::FrameFetchContext::addAdditionalRequestHeaders.
-void AddAdditionalRequestHeaders(net::HttpRequestHeaders* headers,
-                                 const GURL& url,
-                                 FrameMsg_Navigate_Type::Value navigation_type,
-                                 BrowserContext* browser_context,
-                                 const std::string& method,
-                                 const std::string user_agent_override,
-                                 FrameTreeNode* frame_tree_node) {
+void AddAdditionalRequestHeaders(
+    net::HttpRequestHeaders* headers,
+    std::unique_ptr<net::HttpRequestHeaders> embedder_additional_headers,
+    const GURL& url,
+    FrameMsg_Navigate_Type::Value navigation_type,
+    BrowserContext* browser_context,
+    const std::string& method,
+    const std::string user_agent_override,
+    FrameTreeNode* frame_tree_node) {
   if (!url.SchemeIsHTTPOrHTTPS())
     return;
 
@@ -158,9 +160,6 @@
   }
 
   // Attach additional request headers specified by embedder.
-  std::unique_ptr<net::HttpRequestHeaders> embedder_additional_headers =
-      GetContentClient()->browser()->GetAdditionalNavigationRequestHeaders(
-          browser_context, url);
   if (embedder_additional_headers)
     headers->MergeFrom(*(embedder_additional_headers.get()));
 
@@ -394,10 +393,18 @@
         frame_tree_node_->navigator()->GetDelegate()->GetUserAgentOverride();
   }
 
+  std::unique_ptr<net::HttpRequestHeaders> embedder_additional_headers;
+  int additional_load_flags = 0;
+  GetContentClient()->browser()->NavigationRequestStarted(
+      frame_tree_node->frame_tree_node_id(), common_params_.url,
+      &embedder_additional_headers, &additional_load_flags);
+  begin_params_->load_flags |= additional_load_flags;
+
   net::HttpRequestHeaders headers;
   headers.AddHeadersFromString(begin_params_->headers);
   AddAdditionalRequestHeaders(
-      &headers, common_params_.url, common_params_.navigation_type,
+      &headers, std::move(embedder_additional_headers), common_params_.url,
+      common_params_.navigation_type,
       frame_tree_node_->navigator()->GetController()->GetBrowserContext(),
       common_params.method, user_agent_override, frame_tree_node);
 
diff --git a/content/browser/generic_sensor_browsertest.cc b/content/browser/generic_sensor_browsertest.cc
index cf0c4dc..af69ad6 100644
--- a/content/browser/generic_sensor_browsertest.cc
+++ b/content/browser/generic_sensor_browsertest.cc
@@ -233,7 +233,13 @@
   DISALLOW_COPY_AND_ASSIGN(GenericSensorBrowserTest);
 };
 
-IN_PROC_BROWSER_TEST_F(GenericSensorBrowserTest, AmbientLightSensorTest) {
+// Flakily crashes on Linux ASAN/TSAN bots.  https://crbug.com/789515
+#if defined(OS_LINUX)
+#define MAYBE_AmbientLightSensorTest DISABLED_AmbientLightSensorTest
+#else
+#define MAYBE_AmbientLightSensorTest AmbientLightSensorTest
+#endif
+IN_PROC_BROWSER_TEST_F(GenericSensorBrowserTest, MAYBE_AmbientLightSensorTest) {
   // The test page will create an AmbientLightSensor object in Javascript,
   // expects to get events with fake values then navigates to #pass.
   GURL test_url =
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index e9d16b4..f3963ef0 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -509,7 +509,7 @@
   if (g_gpu_process_hosts[kind_] == this)
     g_gpu_process_hosts[kind_] = nullptr;
 
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
+#if defined(OS_ANDROID)
   UMA_HISTOGRAM_COUNTS_100("GPU.AtExitSurfaceCount",
                            gpu::GpuSurfaceTracker::Get()->GetSurfaceCount());
 #endif
diff --git a/content/browser/loader/mime_sniffing_resource_handler.cc b/content/browser/loader/mime_sniffing_resource_handler.cc
index 130041fe..e04648f 100644
--- a/content/browser/loader/mime_sniffing_resource_handler.cc
+++ b/content/browser/loader/mime_sniffing_resource_handler.cc
@@ -527,11 +527,13 @@
   } else if (request()->url().SchemeIsHTTPOrHTTPS() &&
              // The MHTML mime type should be same as the one we check in
              // Blink's DocumentLoader.
-             response_->head.mime_type == "multipart/related" &&
-             !host_->delegate()->AllowRenderingMhtmlOverHttp(request())) {
-    // Force to download the MHTML page from the remote server, instead of
-    // loading it.
-    must_download_ = true;
+             response_->head.mime_type == "multipart/related") {
+    // It is OK to load the saved offline copy, in MHTML format.
+    const ResourceRequestInfo* info =
+        ResourceRequestInfo::ForRequest(request());
+    must_download_ =
+        !GetContentClient()->browser()->AllowRenderingMhtmlOverHttp(
+            info->GetNavigationUIData());
   } else {
     must_download_ = false;
   }
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index ad08aa9..d7ace9b4 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -538,6 +538,8 @@
   DISALLOW_COPY_AND_ASSIGN(URLLoaderRequestController);
 };
 
+// TODO(https://crbug.com/790734): pass |navigation_ui_data| along with the
+// request so that it could be modified.
 NavigationURLLoaderNetworkService::NavigationURLLoaderNetworkService(
     ResourceContext* resource_context,
     StoragePartition* storage_partition,
@@ -743,6 +745,11 @@
         net::HttpContentDisposition(disposition, std::string())
             .is_attachment()) {
       return true;
+    } else if (response_->head.mime_type == "multipart/related") {
+      // TODO(https://crbug.com/790734): retrieve the new NavigationUIData from
+      // the request and and pass it to AllowRenderingMhtmlOverHttp().
+      return !GetContentClient()->browser()->AllowRenderingMhtmlOverHttp(
+          nullptr);
     }
     // TODO(qinmin): Check whether this is special-case user script that needs
     // to be downloaded.
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 bf07eba..deb279e4 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1733,8 +1733,15 @@
   return gfx::Point(originInScreen.x, originInScreen.y);
 }
 
-NSView* RenderWidgetHostViewMac::AccessibilityGetAcceleratedWidget() {
-  return cocoa_view_;
+gfx::AcceleratedWidget
+RenderWidgetHostViewMac::AccessibilityGetAcceleratedWidget() {
+  if (browser_compositor_) {
+    ui::AcceleratedWidgetMac* accelerated_widget_mac =
+        browser_compositor_->GetAcceleratedWidgetMac();
+    if (accelerated_widget_mac)
+      return accelerated_widget_mac->accelerated_widget();
+  }
+  return gfx::kNullAcceleratedWidget;
 }
 
 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 70a8ce4..d264b6ce 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -362,9 +362,9 @@
   // Clear all service worker handles.
   dispatcher_host->Clear();
   EXPECT_EQ(0UL, dispatcher_host->handles().size());
-  // The service worker registration object host has been destroyed by the above
-  // unregistration. Then the only reference to the registration should be
-  // |registration|.
+  // The service worker registration object host has been destroyed together
+  // with |provider_host| by the above unregistration. Then the only reference
+  // to the registration should be |registration|.
   EXPECT_TRUE(registration->HasOneRef());
 
   registration = FindRegistrationForPattern(pattern,
@@ -435,7 +435,7 @@
   dispatcher_host->Clear();
   // Ensure that the registration's object host doesn't have the reference.
   EXPECT_EQ(1UL, provider_host->registration_object_hosts_.size());
-  delete provider_host->registration_object_hosts_[old_registration->id()];
+  provider_host->registration_object_hosts_.clear();
   EXPECT_EQ(0UL, provider_host->registration_object_hosts_.size());
   ASSERT_TRUE(old_registration->HasOneRef());
 
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index db1d532..ca80f936 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -1214,11 +1214,9 @@
   if (existing_host != registration_object_hosts_.end()) {
     return existing_host->second->CreateObjectInfo();
   }
-  // The newly created ServiceWorkerRegistrationObjectHost instance will destroy
-  // itself once it loses all its Mojo bindings.
   registration_object_hosts_[registration_id] =
-      new ServiceWorkerRegistrationObjectHost(context_, AsWeakPtr(),
-                                              std::move(registration));
+      std::make_unique<ServiceWorkerRegistrationObjectHost>(
+          context_, this, std::move(registration));
   return registration_object_hosts_[registration_id]->CreateObjectInfo();
 }
 
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 5c3522f..f4c69d0 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -509,7 +509,8 @@
   // the service worker registration JavaScript objects for the hosted execution
   // context (service worker global scope or service worker client) in the
   // renderer process.
-  std::map<int64_t /* registration_id */, ServiceWorkerRegistrationObjectHost*>
+  std::map<int64_t /* registration_id */,
+           std::unique_ptr<ServiceWorkerRegistrationObjectHost>>
       registration_object_hosts_;
 
   // The ready() promise is only allowed to be created once.
diff --git a/content/browser/service_worker/service_worker_registration_object_host.cc b/content/browser/service_worker/service_worker_registration_object_host.cc
index 56d91d3a..5f2606a 100644
--- a/content/browser/service_worker/service_worker_registration_object_host.cc
+++ b/content/browser/service_worker/service_worker_registration_object_host.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/service_worker/service_worker_registration_object_host.h"
 
+#include "base/memory/ptr_util.h"
 #include "content/browser/service_worker/service_worker_consts.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_handle.h"
@@ -17,13 +18,13 @@
 
 ServiceWorkerRegistrationObjectHost::ServiceWorkerRegistrationObjectHost(
     base::WeakPtr<ServiceWorkerContextCore> context,
-    base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+    ServiceWorkerProviderHost* provider_host,
     scoped_refptr<ServiceWorkerRegistration> registration)
     : provider_host_(provider_host),
       context_(context),
       registration_(registration),
       weak_ptr_factory_(this) {
-  DCHECK(registration_);
+  DCHECK(registration_.get());
   DCHECK(provider_host_);
   registration_->AddListener(this);
   bindings_.set_connection_error_handler(
@@ -32,16 +33,12 @@
 }
 
 ServiceWorkerRegistrationObjectHost::~ServiceWorkerRegistrationObjectHost() {
-  if (provider_host_) {
-    provider_host_->RemoveServiceWorkerRegistrationObjectHost(
-        registration_->id());
-  }
+  DCHECK(registration_.get());
   registration_->RemoveListener(this);
 }
 
 blink::mojom::ServiceWorkerRegistrationObjectInfoPtr
 ServiceWorkerRegistrationObjectHost::CreateObjectInfo() {
-  DCHECK(provider_host_);
   auto info = blink::mojom::ServiceWorkerRegistrationObjectInfo::New();
   info->options = blink::mojom::ServiceWorkerRegistrationOptions::New(
       registration_->pattern());
@@ -103,7 +100,7 @@
 
   context_->UpdateServiceWorker(
       registration_.get(), false /* force_bypass_cache */,
-      false /* skip_script_comparison */, provider_host_.get(),
+      false /* skip_script_comparison */, provider_host_,
       base::AdaptCallbackForRepeating(
           base::BindOnce(&ServiceWorkerRegistrationObjectHost::UpdateComplete,
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
@@ -184,13 +181,6 @@
   if (!net::HttpUtil::IsValidHeaderValue(value)) {
     bindings_.ReportBadMessage(
         ServiceWorkerConsts::kBadNavigationPreloadHeaderValue);
-    // The above ReportBadMessage() has erased the current dispatch binding from
-    // |bindings_|, but does not invoke OnConnectionError(), so we have to
-    // invoke it by ourselves, because it is necessary for us to determine
-    // whether |this| should destroy (whether |bindings_| is empty).
-    // TODO(leonhsl): Once crbug.com/789816 makes ReportBadMessage() internally
-    // invoke the connection error handler, we can remove this invocation.
-    OnConnectionError();
     return;
   }
 
@@ -280,8 +270,6 @@
     ServiceWorkerVersion* installing_version,
     ServiceWorkerVersion* waiting_version,
     ServiceWorkerVersion* active_version) {
-  if (!provider_host_)
-    return;
   if (!changed_mask.changed())
     return;
 
@@ -306,7 +294,9 @@
   // If there are still bindings, |this| is still being used.
   if (!bindings_.empty())
     return;
-  delete this;
+  // Will destroy |this|.
+  provider_host_->RemoveServiceWorkerRegistrationObjectHost(
+      registration()->id());
 }
 
 template <typename CallbackType, typename... Args>
@@ -314,7 +304,7 @@
     CallbackType* callback,
     const char* error_prefix,
     Args... args) {
-  if (!provider_host_ || !context_) {
+  if (!context_) {
     std::move(*callback).Run(
         blink::mojom::ServiceWorkerErrorType::kAbort,
         std::string(error_prefix) +
@@ -338,13 +328,6 @@
                             registration_->pattern()};
   if (!ServiceWorkerUtils::AllOriginsMatchAndCanAccessServiceWorkers(urls)) {
     bindings_.ReportBadMessage(ServiceWorkerConsts::kBadMessageImproperOrigins);
-    // The above ReportBadMessage() has erased the current dispatch binding from
-    // |bindings_|, but does not invoke OnConnectionError(), so we have to
-    // invoke it by ourselves, because it is necessary for us to determine
-    // whether |this| should destroy (whether |bindings_| is empty).
-    // TODO(leonhsl): Once crbug.com/789816 makes ReportBadMessage() internally
-    // invoke the connection error handler, we can remove this invocation.
-    OnConnectionError();
     return false;
   }
 
diff --git a/content/browser/service_worker/service_worker_registration_object_host.h b/content/browser/service_worker/service_worker_registration_object_host.h
index 771009a..be87d38c 100644
--- a/content/browser/service_worker/service_worker_registration_object_host.h
+++ b/content/browser/service_worker/service_worker_registration_object_host.h
@@ -23,10 +23,9 @@
 
 // ServiceWorkerRegistrationObjectHost has a 1:1 correspondence to
 // WebServiceWorkerRegistration in the renderer process.
-// The host manages its own lifetime. It stays alive while the
-// WebServiceWorkerRegistration is alive, and destroys itself when it detects
-// that it's no longer needed. It also initiates destruction of the
-// WebServiceWorkerRegistration. See the class documentation in
+// The host stays alive while the WebServiceWorkerRegistration is alive, and
+// also initiates destruction of the WebServiceWorkerRegistration once detected
+// that it's no longer needed. See the class documentation in
 // WebServiceWorkerRegistrationImpl for details.
 //
 // Has a reference to the corresponding ServiceWorkerRegistration in order to
@@ -37,7 +36,7 @@
  public:
   ServiceWorkerRegistrationObjectHost(
       base::WeakPtr<ServiceWorkerContextCore> context,
-      base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+      ServiceWorkerProviderHost* provider_host,
       scoped_refptr<ServiceWorkerRegistration> registration);
   ~ServiceWorkerRegistrationObjectHost() override;
 
@@ -47,10 +46,6 @@
   ServiceWorkerRegistration* registration() { return registration_.get(); }
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerJobTest, RegisterDuplicateScript);
-  FRIEND_TEST_ALL_PREFIXES(BackgroundSyncManagerTest,
-                           RegisterWithoutLiveSWRegistration);
-
   // ServiceWorkerRegistration::Listener overrides.
   void OnVersionAttributesChanged(
       ServiceWorkerRegistration* registration,
@@ -111,7 +106,9 @@
                                              const char* error_prefix,
                                              Args... args);
 
-  base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
+  // |provider_host_| is valid throughout lifetime of |this| because it owns
+  // |this|.
+  ServiceWorkerProviderHost* provider_host_;
   base::WeakPtr<ServiceWorkerContextCore> context_;
   mojo::AssociatedBindingSet<blink::mojom::ServiceWorkerRegistrationObjectHost>
       bindings_;
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index 4b5505f..2dc47b9 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -269,9 +269,9 @@
           helper_->mock_render_process_id(), 1 /* dummy provider_id */,
           context()->AsWeakPtr(), 1 /* route_id */, dispatcher_host.get(),
           &remote_endpoint);
-  // |registration_object_host| will destroy by itself.
-  auto* registration_object_host = new ServiceWorkerRegistrationObjectHost(
-      context()->AsWeakPtr(), provider_host->AsWeakPtr(), registration);
+  auto registration_object_host =
+      std::make_unique<ServiceWorkerRegistrationObjectHost>(
+          context()->AsWeakPtr(), provider_host.get(), registration);
   // To enable the caller end point
   // |registration_object_host->remote_registration_| to make calls safely with
   // no need to pass |object_info_->request| through a message pipe endpoint.
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index 8996b28..f844794 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/test/histogram_tester.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/loader/resource_request_info_impl.h"
@@ -422,6 +421,7 @@
   // ---------------------------------------------------------------------------
 
   TestBrowserThreadBundle thread_bundle_;
+  base::SimpleTestTickClock tick_clock_;
 
   std::unique_ptr<TestBrowserContext> browser_context_;
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
@@ -711,14 +711,12 @@
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
 
   // Set mock clock on version_ to check timeout behavior.
-  base::SimpleTestTickClock tick_clock;
-  tick_clock.SetNowTicks(base::TimeTicks::Now());
-  version_->SetTickClockForTesting(&tick_clock);
+  tick_clock_.SetNowTicks(base::TimeTicks::Now());
+  version_->SetTickClockForTesting(&tick_clock_);
 
   protocol_handler_->set_custom_timeout(base::TimeDelta::FromSeconds(5));
   TestRequest(200, "OK", std::string(), true /* expect_valid_ssl */);
   EXPECT_EQ(base::TimeDelta::FromSeconds(5), version_->remaining_timeout());
-  version_->SetTickClockForTesting(base::DefaultTickClock::GetInstance());
 }
 
 class ProviderDeleteHelper : public EmbeddedWorkerTestHelper {
diff --git a/content/browser/tracing/DEPS b/content/browser/tracing/DEPS
index c50f522..fddb5c8 100644
--- a/content/browser/tracing/DEPS
+++ b/content/browser/tracing/DEPS
@@ -7,5 +7,8 @@
 specific_include_rules = {
   "tracing_controller_impl\.cc": [
     "+v8"
+  ],
+  "cast_tracing_agent\.": [
+    "+chromecast/tracing"
   ]
 }
diff --git a/content/browser/tracing/cast_tracing_agent.cc b/content/browser/tracing/cast_tracing_agent.cc
new file mode 100644
index 0000000..5e045ffe
--- /dev/null
+++ b/content/browser/tracing/cast_tracing_agent.cc
@@ -0,0 +1,169 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/tracing/cast_tracing_agent.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/task_scheduler/task_scheduler.h"
+#include "base/trace_event/trace_config.h"
+#include "chromecast/tracing/system_tracing_common.h"
+#include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "services/resource_coordinator/public/interfaces/service_constants.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace content {
+namespace {
+
+std::string GetTracingCategories(
+    const base::trace_event::TraceConfig& trace_config) {
+  std::vector<base::StringPiece> categories;
+  for (size_t i = 0; i < chromecast::tracing::kCategoryCount; ++i) {
+    base::StringPiece category(chromecast::tracing::kCategories[i]);
+    if (trace_config.category_filter().IsCategoryGroupEnabled(category))
+      categories.push_back(category);
+  }
+  return base::JoinString(categories, ",");
+}
+
+}  // namespace
+
+CastTracingAgent::CastTracingAgent(service_manager::Connector* connector)
+    : binding_(this) {
+  // Connecto to the agent registry interface.
+  tracing::mojom::AgentRegistryPtr agent_registry;
+  connector->BindInterface(resource_coordinator::mojom::kServiceName,
+                           &agent_registry);
+
+  // Register this agent.
+  tracing::mojom::AgentPtr agent;
+  binding_.Bind(mojo::MakeRequest(&agent));
+  static const char kFtraceLabel[] = "systemTraceEvents";
+  agent_registry->RegisterAgent(std::move(agent), kFtraceLabel,
+                                tracing::mojom::TraceDataType::STRING,
+                                false /* supports_explicit_clock_sync */);
+
+  task_runner_ =
+      base::TaskScheduler::GetInstance()->CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BACKGROUND,
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+}
+
+CastTracingAgent::~CastTracingAgent() = default;
+
+// tracing::mojom::Agent. Called by Mojo internals on the UI thread.
+void CastTracingAgent::StartTracing(
+    const std::string& config,
+    base::TimeTicks coordinator_time,
+    const Agent::StartTracingCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  base::trace_event::TraceConfig trace_config(config);
+
+  if (!trace_config.IsSystraceEnabled()) {
+    callback.Run(false /* success */);
+    return;
+  }
+
+  start_tracing_callback_ = callback;
+
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&CastTracingAgent::StartTracingOnIO,
+                                        base::Unretained(this),
+                                        base::ThreadTaskRunnerHandle::Get(),
+                                        GetTracingCategories(trace_config)));
+}
+
+void CastTracingAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  recorder_ = std::move(recorder);
+
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&CastTracingAgent::StopAndFlushOnIO,
+                                        base::Unretained(this),
+                                        base::ThreadTaskRunnerHandle::Get()));
+}
+
+void CastTracingAgent::RequestClockSyncMarker(
+    const std::string& sync_id,
+    const Agent::RequestClockSyncMarkerCallback& callback) {
+  NOTREACHED();
+}
+
+void CastTracingAgent::GetCategories(
+    const Agent::GetCategoriesCallback& callback) {
+  callback.Run("" /* categories */);
+}
+
+void CastTracingAgent::RequestBufferStatus(
+    const Agent::RequestBufferStatusCallback& callback) {
+  callback.Run(0 /* capacity */, 0 /* count */);
+}
+
+void CastTracingAgent::StartTracingOnIO(
+    scoped_refptr<base::TaskRunner> reply_task_runner,
+    const std::string& categories) {
+  system_tracer_ = std::make_unique<chromecast::SystemTracer>();
+
+  system_tracer_->StartTracing(
+      categories, base::BindOnce(&CastTracingAgent::FinishStartOnIO,
+                                 base::Unretained(this), reply_task_runner));
+}
+
+void CastTracingAgent::FinishStartOnIO(
+    scoped_refptr<base::TaskRunner> reply_task_runner,
+    chromecast::SystemTracer::Status status) {
+  reply_task_runner->PostTask(FROM_HERE,
+                              base::BindOnce(&CastTracingAgent::FinishStart,
+                                             base::Unretained(this), status));
+  if (status != chromecast::SystemTracer::Status::OK) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&CastTracingAgent::CleanupOnIO, base::Unretained(this)));
+  }
+}
+
+void CastTracingAgent::FinishStart(chromecast::SystemTracer::Status status) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  start_tracing_callback_.Run(status == chromecast::SystemTracer::Status::OK);
+}
+
+void CastTracingAgent::StopAndFlushOnIO(
+    scoped_refptr<base::TaskRunner> reply_task_runner) {
+  system_tracer_->StopTracing(
+      base::BindRepeating(&CastTracingAgent::HandleTraceDataOnIO,
+                          base::Unretained(this), reply_task_runner));
+}
+
+void CastTracingAgent::HandleTraceDataOnIO(
+    scoped_refptr<base::TaskRunner> reply_task_runner,
+    chromecast::SystemTracer::Status status,
+    std::string trace_data) {
+  reply_task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CastTracingAgent::HandleTraceData, base::Unretained(this),
+                     status, std::move(trace_data)));
+  if (status != chromecast::SystemTracer::Status::KEEP_GOING) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&CastTracingAgent::CleanupOnIO, base::Unretained(this)));
+  }
+}
+
+void CastTracingAgent::HandleTraceData(chromecast::SystemTracer::Status status,
+                                       std::string trace_data) {
+  if (recorder_ && status != chromecast::SystemTracer::Status::FAIL)
+    recorder_->AddChunk(std::move(trace_data));
+  if (status != chromecast::SystemTracer::Status::KEEP_GOING)
+    recorder_.reset();
+}
+
+void CastTracingAgent::CleanupOnIO() {
+  system_tracer_.reset();
+}
+
+}  // namespace content
diff --git a/content/browser/tracing/cast_tracing_agent.h b/content/browser/tracing/cast_tracing_agent.h
new file mode 100644
index 0000000..7ad9d9a
--- /dev/null
+++ b/content/browser/tracing/cast_tracing_agent.h
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_TRACING_CAST_TRACING_AGENT_H_
+#define CONTENT_BROWSER_TRACING_CAST_TRACING_AGENT_H_
+
+#include <memory>
+#include <string>
+
+#include "chromecast/tracing/system_tracer.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+
+namespace service_manager {
+class Connector;
+}  // namespace service_manager
+
+namespace chromecast {
+class SystemTracer;
+}
+
+namespace content {
+
+class CastTracingAgent : public tracing::mojom::Agent {
+ public:
+  explicit CastTracingAgent(service_manager::Connector* connector);
+  ~CastTracingAgent() override;
+
+ private:
+  // tracing::mojom::Agent. Called by Mojo internals on the UI thread.
+  void StartTracing(const std::string& config,
+                    base::TimeTicks coordinator_time,
+                    const Agent::StartTracingCallback& callback) override;
+  void StopAndFlush(tracing::mojom::RecorderPtr recorder) override;
+  void RequestClockSyncMarker(
+      const std::string& sync_id,
+      const Agent::RequestClockSyncMarkerCallback& callback) override;
+  void GetCategories(const Agent::GetCategoriesCallback& callback) override;
+  void RequestBufferStatus(
+      const Agent::RequestBufferStatusCallback& callback) override;
+
+  void StartTracingOnIO(scoped_refptr<base::TaskRunner> reply_task_runner,
+                        const std::string& categories);
+  void FinishStartOnIO(scoped_refptr<base::TaskRunner> reply_task_runner,
+                       chromecast::SystemTracer::Status status);
+  void FinishStart(chromecast::SystemTracer::Status status);
+  void StopAndFlushOnIO(scoped_refptr<base::TaskRunner> reply_task_runner);
+  void HandleTraceDataOnIO(scoped_refptr<base::TaskRunner> reply_task_runner,
+                           chromecast::SystemTracer::Status,
+                           std::string trace_data);
+  void HandleTraceData(chromecast::SystemTracer::Status status,
+                       std::string trace_data);
+  void CleanupOnIO();
+
+  mojo::Binding<tracing::mojom::Agent> binding_;
+  Agent::StartTracingCallback start_tracing_callback_;
+  tracing::mojom::RecorderPtr recorder_;
+
+  // Task runner for collecting traces in a worker thread.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  std::unique_ptr<chromecast::SystemTracer> system_tracer_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastTracingAgent);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_TRACING_CAST_TRACING_AGENT_H_
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 1008cd71..fc8b15c 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -54,6 +54,10 @@
 #include "content/browser/tracing/cros_tracing_agent.h"
 #endif
 
+#if defined(CAST_TRACING_AGENT)
+#include "content/browser/tracing/cast_tracing_agent.h"
+#endif
+
 #if defined(OS_WIN)
 #include "content/browser/tracing/etw_tracing_agent_win.h"
 #endif
@@ -139,6 +143,8 @@
 #if defined(OS_CHROMEOS)
   agents_.push_back(std::make_unique<CrOSTracingAgent>(connector));
   agents_.push_back(std::make_unique<ArcTracingAgentImpl>(connector));
+#elif defined(CAST_TRACING_AGENT)
+  agents_.push_back(std::make_unique<CastTracingAgent>(connector));
 #elif defined(OS_WIN)
   agents_.push_back(std::make_unique<EtwTracingAgent>(connector));
 #endif
diff --git a/content/network/network_context.cc b/content/network/network_context.cc
index 5746ea0..1d5ab88 100644
--- a/content/network/network_context.cc
+++ b/content/network/network_context.cc
@@ -109,12 +109,8 @@
 }
 
 std::unique_ptr<NetworkContext> NetworkContext::CreateForTesting() {
-  mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New();
-  // Disable proxy configuration detection, to avoid dependencies on local
-  // network configuration.
-  params->initial_proxy_config = net::ProxyConfig::CreateDirect();
-
-  return base::WrapUnique(new NetworkContext(std::move(params)));
+  return base::WrapUnique(
+      new NetworkContext(mojom::NetworkContextParams::New()));
 }
 
 void NetworkContext::RegisterURLLoader(URLLoader* url_loader) {
@@ -223,20 +219,6 @@
   builder.set_accept_language("en-us,en");
   builder.set_user_agent(GetContentClient()->GetUserAgent());
 
-  // TODO(mmenke): Move this logic into content shell.
-  if (!network_context_params->initial_proxy_config &&
-      !network_context_params->proxy_config_client_request.is_pending()) {
-    if (command_line->HasSwitch(switches::kProxyServer)) {
-      net::ProxyConfig config;
-      config.proxy_rules().ParseFromString(
-          command_line->GetSwitchValueASCII(switches::kProxyServer));
-      network_context_params->initial_proxy_config = config;
-    } else {
-      network_context_params->initial_proxy_config =
-          net::ProxyConfig::CreateDirect();
-    }
-  }
-
   std::unique_ptr<net::CertVerifier> cert_verifier =
       net::CertVerifier::CreateDefault();
   builder.SetCertVerifier(
@@ -276,13 +258,15 @@
     builder->EnableHttpCache(cache_params);
   }
 
-  if (network_context_params->initial_proxy_config ||
-      network_context_params->proxy_config_client_request.is_pending()) {
-    builder->set_proxy_config_service(base::MakeUnique<ProxyConfigServiceMojo>(
-        std::move(network_context_params->proxy_config_client_request),
-        std::move(network_context_params->initial_proxy_config),
-        std::move(network_context_params->proxy_config_poller_client)));
+  if (!network_context_params->initial_proxy_config &&
+      !network_context_params->proxy_config_client_request.is_pending()) {
+    network_context_params->initial_proxy_config =
+        net::ProxyConfig::CreateDirect();
   }
+  builder->set_proxy_config_service(base::MakeUnique<ProxyConfigServiceMojo>(
+      std::move(network_context_params->proxy_config_client_request),
+      std::move(network_context_params->initial_proxy_config),
+      std::move(network_context_params->proxy_config_poller_client)));
 
   if (network_context_params->http_server_properties_path) {
     scoped_refptr<JsonPrefStore> json_pref_store(new JsonPrefStore(
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 797cc254..55d37fb9 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -232,13 +232,6 @@
   return false;
 }
 
-std::unique_ptr<net::HttpRequestHeaders>
-ContentBrowserClient::GetAdditionalNavigationRequestHeaders(
-    BrowserContext* context,
-    const GURL& url) const {
-  return nullptr;
-}
-
 bool ContentBrowserClient::AllowGetCookie(const GURL& url,
                                           const GURL& first_party,
                                           const net::CookieList& cookie_list,
@@ -596,4 +589,9 @@
 }
 #endif
 
+bool ContentBrowserClient::AllowRenderingMhtmlOverHttp(
+    NavigationUIData* navigation_ui_data) const {
+  return false;
+}
+
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index e56b3201..8ae1b96 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -416,10 +416,12 @@
   virtual bool IsDataSaverEnabled(BrowserContext* context);
 
   // Allow the embedder to return additional headers that should be sent when
-  // fetching |url|. May return a nullptr.
-  virtual std::unique_ptr<net::HttpRequestHeaders>
-  GetAdditionalNavigationRequestHeaders(BrowserContext* context,
-                                        const GURL& url) const;
+  // fetching |url| as well as add extra load flags.
+  virtual void NavigationRequestStarted(
+      int frame_tree_node_id,
+      const GURL& url,
+      std::unique_ptr<net::HttpRequestHeaders>* extra_headers,
+      int* extra_load_flags) {}
 
   // Allow the embedder to control if the given cookie can be read.
   // This is called on the IO thread.
@@ -975,6 +977,11 @@
                                         bool is_main_frame,
                                         ui::PageTransition transition);
 #endif
+
+  // Called on IO or UI thread to determine whether or not to allow load and
+  // render MHTML page from http/https URLs.
+  virtual bool AllowRenderingMhtmlOverHttp(
+      NavigationUIData* navigation_ui_data) const;
 };
 
 }  // namespace content
diff --git a/content/public/browser/resource_dispatcher_host_delegate.cc b/content/public/browser/resource_dispatcher_host_delegate.cc
index a96a467..fe0f8d87 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.cc
+++ b/content/public/browser/resource_dispatcher_host_delegate.cc
@@ -104,11 +104,6 @@
   return std::unique_ptr<net::ClientCertStore>();
 }
 
-bool ResourceDispatcherHostDelegate::AllowRenderingMhtmlOverHttp(
-    net::URLRequest* request) const {
-  return false;
-}
-
 ResourceDispatcherHostDelegate::~ResourceDispatcherHostDelegate() {
 }
 
diff --git a/content/public/browser/resource_dispatcher_host_delegate.h b/content/public/browser/resource_dispatcher_host_delegate.h
index 7dea653..ea004c5 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.h
+++ b/content/public/browser/resource_dispatcher_host_delegate.h
@@ -142,9 +142,6 @@
   virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
       ResourceContext* resource_context);
 
-  // Whether or not to allow load and render MHTML page from http/https URLs.
-  virtual bool AllowRenderingMhtmlOverHttp(net::URLRequest* request) const;
-
  protected:
   virtual ~ResourceDispatcherHostDelegate();
 };
diff --git a/content/public/common/network_service.mojom b/content/public/common/network_service.mojom
index 4ee8cae..b5333a9 100644
--- a/content/public/common/network_service.mojom
+++ b/content/public/common/network_service.mojom
@@ -69,11 +69,7 @@
   // proxy lookups will be deferred until a configuration is received via
   // |proxy_config_client_request|.
   //
-  // If both are null, the NetworkContext will attempt to automatically
-  // detect the system proxy configuration (Which defaults to no proxy on
-  // Linux and Android).
-  //
-  // TODO(mmenke): Use direct connections by default instead.
+  // If both are null, the NetworkContext will not use a proxy.
   ProxyConfig? initial_proxy_config;
   ProxyConfigClient&? proxy_config_client_request;
 
diff --git a/content/public/test/download_test_observer.cc b/content/public/test/download_test_observer.cc
index 76568bf..da7fc55 100644
--- a/content/public/test/download_test_observer.cc
+++ b/content/public/test/download_test_observer.cc
@@ -309,6 +309,28 @@
   return download->GetState() == DownloadItem::INTERRUPTED;
 }
 
+void PingIOThread(int cycle, base::OnceClosure callback);
+
+// Helper method to post a task to IO thread to ensure remaining operations on
+// the IO thread complete.
+void PingFileThread(int cycle, base::OnceClosure callback) {
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&PingIOThread, cycle, std::move(callback)));
+}
+
+// Post a task to file thread, and wait for it to be posted back on to the IO
+// thread if |cycle| is larger than 1. This ensures that all remaining
+// operations on the IO thread complete.
+void PingIOThread(int cycle, base::OnceClosure callback) {
+  if (--cycle) {
+    DownloadManager::GetTaskRunner()->PostTask(
+        FROM_HERE, base::BindOnce(&PingFileThread, cycle, std::move(callback)));
+  } else {
+    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(callback));
+  }
+}
+
 DownloadTestFlushObserver::DownloadTestFlushObserver(
     DownloadManager* download_manager)
     : download_manager_(download_manager),
@@ -319,7 +341,7 @@
   download_manager_->AddObserver(this);
   // The wait condition may have been met before WaitForFlush() was called.
   CheckDownloadsInProgress(true);
-  RunAllTasksUntilIdle();
+  run_loop_.Run();
 }
 
 void DownloadTestFlushObserver::OnDownloadCreated(
@@ -328,6 +350,10 @@
   CheckDownloadsInProgress(true);
 }
 
+void DownloadTestFlushObserver::ManagerGoingDown(DownloadManager* manager) {
+  download_manager_ = nullptr;
+}
+
 void DownloadTestFlushObserver::OnDownloadDestroyed(DownloadItem* download) {
   // Stop observing.  Do not do anything with it, as it is about to be gone.
   DownloadSet::iterator it = downloads_observed_.find(download);
@@ -342,6 +368,9 @@
 }
 
 DownloadTestFlushObserver::~DownloadTestFlushObserver() {
+  if (!download_manager_)
+    return;
+
   download_manager_->RemoveObserver(this);
   for (DownloadSet::iterator it = downloads_observed_.begin();
        it != downloads_observed_.end(); ++it) {
@@ -351,7 +380,7 @@
 
 // If we're waiting for that flush point, check the number
 // of downloads in the IN_PROGRESS state and take appropriate
-// action.  If requested, also observes all downloads while iterating.
+// action. If requested, also observes all downloads while iterating.
 void DownloadTestFlushObserver::CheckDownloadsInProgress(
     bool observe_downloads) {
   if (waiting_for_zero_inprogress_) {
@@ -389,29 +418,11 @@
       // there's a self-task posting in the IO thread cancel path.
       DownloadManager::GetTaskRunner()->PostTask(
           FROM_HERE,
-          base::BindOnce(&DownloadTestFlushObserver::PingFileThread, this, 2));
+          base::BindOnce(&PingFileThread, 2, run_loop_.QuitClosure()));
     }
   }
 }
 
-void DownloadTestFlushObserver::PingFileThread(int cycle) {
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&DownloadTestFlushObserver::PingIOThread, this, cycle));
-}
-
-void DownloadTestFlushObserver::PingIOThread(int cycle) {
-  if (--cycle) {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::BindOnce(&DownloadTestFlushObserver::PingFileThread, this,
-                       cycle));
-  } else {
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::MessageLoop::QuitWhenIdleClosure());
-  }
-}
-
 DownloadTestItemCreationObserver::DownloadTestItemCreationObserver()
     : download_id_(DownloadItem::kInvalidId),
       interrupt_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
diff --git a/content/public/test/download_test_observer.h b/content/public/test/download_test_observer.h
index 1e87631..3e4e1c1 100644
--- a/content/public/test/download_test_observer.h
+++ b/content/public/test/download_test_observer.h
@@ -13,7 +13,7 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
 #include "content/public/browser/download_interrupt_reasons.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
@@ -232,27 +232,22 @@
 //        IO threads.
 // This almost certainly means that a Download cancel has propagated through
 // the system.
-class DownloadTestFlushObserver
-    : public DownloadManager::Observer,
-      public DownloadItem::Observer,
-      public base::RefCountedThreadSafe<DownloadTestFlushObserver> {
+class DownloadTestFlushObserver : public DownloadManager::Observer,
+                                  public DownloadItem::Observer {
  public:
   explicit DownloadTestFlushObserver(DownloadManager* download_manager);
+  ~DownloadTestFlushObserver() override;
 
   void WaitForFlush();
 
   // DownloadsManager observer methods.
   void OnDownloadCreated(DownloadManager* manager, DownloadItem* item) override;
+  void ManagerGoingDown(DownloadManager* manager) override;
 
   // DownloadItem observer methods.
   void OnDownloadUpdated(DownloadItem* download) override;
   void OnDownloadDestroyed(DownloadItem* download) override;
 
- protected:
-  friend class base::RefCountedThreadSafe<DownloadTestFlushObserver>;
-
-  ~DownloadTestFlushObserver() override;
-
  private:
   typedef std::set<DownloadItem*> DownloadSet;
 
@@ -261,13 +256,10 @@
   // action.  If requested, also observes all downloads while iterating.
   void CheckDownloadsInProgress(bool observe_downloads);
 
-  void PingFileThread(int cycle);
-
-  void PingIOThread(int cycle);
-
   DownloadManager* download_manager_;
   DownloadSet downloads_observed_;
   bool waiting_for_zero_inprogress_;
+  base::RunLoop run_loop_;
 
   DISALLOW_COPY_AND_ASSIGN(DownloadTestFlushObserver);
 };
diff --git a/content/renderer/service_worker/web_service_worker_registration_impl.cc b/content/renderer/service_worker/web_service_worker_registration_impl.cc
index aa5f279..bf5efd7 100644
--- a/content/renderer/service_worker/web_service_worker_registration_impl.cc
+++ b/content/renderer/service_worker/web_service_worker_registration_impl.cc
@@ -65,9 +65,10 @@
   impl->host_for_global_scope_ =
       blink::mojom::ThreadSafeServiceWorkerRegistrationObjectHostAssociatedPtr::
           Create(std::move(impl->info_->host_ptr_info), io_task_runner);
-  // |impl|'s destruction happens only in OnConnectionError(), which cannot
-  // happen before BindRequest(), therefore using base::Unretained() here is
-  // safe.
+  // |impl|'s destruction needs both DetachAndMaybeDestroy() and
+  // OnConnectionError() to be called (see comments at LifecycleState enum), and
+  // OnConnectionError() cannot happen before BindRequest(), therefore using
+  // base::Unretained() here is safe.
   io_task_runner->PostTask(
       FROM_HERE, base::BindOnce(&WebServiceWorkerRegistrationImpl::BindRequest,
                                 base::Unretained(impl.get()),
@@ -190,6 +191,11 @@
   host_for_client_.reset();
   host_for_global_scope_ = nullptr;
   info_ = nullptr;
+  if (state_ == LifecycleState::kUnbound) {
+    state_ = LifecycleState::kDead;
+    delete this;
+    return;
+  }
   DCHECK_EQ(LifecycleState::kAttachedAndBound, state_);
   state_ = LifecycleState::kDetached;
   // We will continue in OnConnectionError() triggered by destruction of the
@@ -222,9 +228,15 @@
                        base::Unretained(this)));
     return;
   }
-  DCHECK_EQ(LifecycleState::kDetached, state_);
-  state_ = LifecycleState::kDead;
-  delete this;
+  if (state_ == LifecycleState::kDetached) {
+    state_ = LifecycleState::kDead;
+    delete this;
+    return;
+  }
+  DCHECK_EQ(LifecycleState::kAttachedAndBound, state_);
+  state_ = LifecycleState::kUnbound;
+  // We will continue in DetachAndMaybeDestroy() when all references of |this|
+  // have been released by Blink.
 }
 
 blink::WebServiceWorkerRegistrationProxy*
@@ -238,7 +250,8 @@
 
 void WebServiceWorkerRegistrationImpl::Update(
     std::unique_ptr<WebServiceWorkerUpdateCallbacks> callbacks) {
-  DCHECK_EQ(LifecycleState::kAttachedAndBound, state_);
+  DCHECK(state_ == LifecycleState::kAttachedAndBound ||
+         state_ == LifecycleState::kUnbound);
   GetRegistrationObjectHost()->Update(
       base::BindOnce(&WebServiceWorkerRegistrationImpl::OnUpdated,
                      base::Unretained(this), std::move(callbacks)));
@@ -246,7 +259,8 @@
 
 void WebServiceWorkerRegistrationImpl::Unregister(
     std::unique_ptr<WebServiceWorkerUnregistrationCallbacks> callbacks) {
-  DCHECK_EQ(LifecycleState::kAttachedAndBound, state_);
+  DCHECK(state_ == LifecycleState::kAttachedAndBound ||
+         state_ == LifecycleState::kUnbound);
   GetRegistrationObjectHost()->Unregister(
       base::BindOnce(&WebServiceWorkerRegistrationImpl::OnUnregistered,
                      base::Unretained(this), std::move(callbacks)));
@@ -255,7 +269,8 @@
 void WebServiceWorkerRegistrationImpl::EnableNavigationPreload(
     bool enable,
     std::unique_ptr<WebEnableNavigationPreloadCallbacks> callbacks) {
-  DCHECK_EQ(LifecycleState::kAttachedAndBound, state_);
+  DCHECK(state_ == LifecycleState::kAttachedAndBound ||
+         state_ == LifecycleState::kUnbound);
   GetRegistrationObjectHost()->EnableNavigationPreload(
       enable,
       base::BindOnce(
@@ -265,7 +280,8 @@
 
 void WebServiceWorkerRegistrationImpl::GetNavigationPreloadState(
     std::unique_ptr<WebGetNavigationPreloadStateCallbacks> callbacks) {
-  DCHECK_EQ(LifecycleState::kAttachedAndBound, state_);
+  DCHECK(state_ == LifecycleState::kAttachedAndBound ||
+         state_ == LifecycleState::kUnbound);
   GetRegistrationObjectHost()->GetNavigationPreloadState(base::BindOnce(
       &WebServiceWorkerRegistrationImpl::OnDidGetNavigationPreloadState,
       base::Unretained(this), std::move(callbacks)));
@@ -274,7 +290,8 @@
 void WebServiceWorkerRegistrationImpl::SetNavigationPreloadHeader(
     const blink::WebString& value,
     std::unique_ptr<WebSetNavigationPreloadHeaderCallbacks> callbacks) {
-  DCHECK_EQ(LifecycleState::kAttachedAndBound, state_);
+  DCHECK(state_ == LifecycleState::kAttachedAndBound ||
+         state_ == LifecycleState::kUnbound);
   GetRegistrationObjectHost()->SetNavigationPreloadHeader(
       value.Utf8(),
       base::BindOnce(
diff --git a/content/renderer/service_worker/web_service_worker_registration_impl.h b/content/renderer/service_worker/web_service_worker_registration_impl.h
index 05b370a3..2388b1d 100644
--- a/content/renderer/service_worker/web_service_worker_registration_impl.h
+++ b/content/renderer/service_worker/web_service_worker_registration_impl.h
@@ -65,6 +65,12 @@
 // is ready to be destroyed. If there was no ServiceWorkerRegistrationObjectInfo
 // inflight, the browser process destroys the Mojo connection to this instance,
 // which finally destroys it.
+//
+// Another destruction scenario is that the browser process destroys the
+// ServiceWorkerRegistrationObject Mojo connection while some
+// WebServiceWorkerRegistration::Handles are still held by Blink. In such a case
+// this instance will finally be destroyed after all Blink destroys all the
+// WebServiceWorkerRegistration::Handles.
 class CONTENT_EXPORT WebServiceWorkerRegistrationImpl
     : public blink::mojom::ServiceWorkerRegistrationObject,
       public blink::WebServiceWorkerRegistration,
@@ -150,9 +156,13 @@
   //   When all references to |this| have been released by Blink,
   //   DetachAndMaybeDestroy() will be triggered to change |state_| from
   //   |kAttachedAndBound| to |kDetached|.
-  // -     |kDetached| --> |kDead|
-  //   OnConnectionError() will set |state_| to |kDead| and delete |this|
-  //   immediately.
+  // -     |kAttachedAndBound| --> |kUnbound|
+  //   When |binding_| Mojo connection gets broken, OnConnectionError() will be
+  //   triggered to change |state_| from |kAttachedAndBound| to |kUnbound|.
+  // -     {|kUnbound|, |kDetached|} --> |kDead|
+  //   But if DetachAndMaybeDestroy() saw that |state_| is already |kUnbound| or
+  //   OnConnectionError() saw that |state_| is already |kDetached|, they will
+  //   just set |state_| to |kDead| and delete |this| immediately.
   // -     |kDetached| --> |kAttachedAndBound|
   //   When |this| is in |kDetached| state, if an inflight
   //   ServiceWorkerRegistrationObjectInfo for the same JavaScript registration
@@ -161,6 +171,7 @@
   enum class LifecycleState {
     kInitial,
     kAttachedAndBound,
+    kUnbound,
     kDetached,
     kDead
   };
diff --git a/gpu/gles2_conform_support/egl/display.cc b/gpu/gles2_conform_support/egl/display.cc
index 46a89fa..a7959ff 100644
--- a/gpu/gles2_conform_support/egl/display.cc
+++ b/gpu/gles2_conform_support/egl/display.cc
@@ -4,6 +4,7 @@
 
 #include "gpu/gles2_conform_support/egl/display.h"
 
+#include "build/build_config.h"
 #include "gpu/gles2_conform_support/egl/config.h"
 #include "gpu/gles2_conform_support/egl/context.h"
 #include "gpu/gles2_conform_support/egl/surface.h"
@@ -214,8 +215,12 @@
     return result;
   }
   scoped_refptr<gl::GLSurface> gl_surface;
-  gl_surface =
-      gl::init::CreateViewGLSurface(static_cast<gfx::AcceleratedWidget>(win));
+#if defined(OS_MACOSX)
+  gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget;
+#else
+  gfx::AcceleratedWidget widget = static_cast<gfx::AcceleratedWidget>(win);
+#endif
+  gl_surface = gl::init::CreateViewGLSurface(widget);
   if (!gl_surface)
     return ts->ReturnError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
   surfaces_.emplace_back(new Surface(gl_surface.get(), config));
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn
index 1562a0b..25b9d4cc 100644
--- a/gpu/ipc/common/BUILD.gn
+++ b/gpu/ipc/common/BUILD.gn
@@ -121,7 +121,7 @@
     ]
   }
 
-  if (is_mac || is_android) {
+  if (is_android) {
     sources += [
       "gpu_surface_lookup.cc",
       "gpu_surface_lookup.h",
diff --git a/gpu/ipc/common/surface_handle.h b/gpu/ipc/common/surface_handle.h
index ecebc066..cb6f279 100644
--- a/gpu/ipc/common/surface_handle.h
+++ b/gpu/ipc/common/surface_handle.h
@@ -9,7 +9,8 @@
 
 #include "build/build_config.h"
 
-#if (defined(OS_WIN) || defined(USE_X11) || defined(USE_OZONE)) && \
+#if (defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_X11) || \
+     defined(USE_OZONE)) &&                                       \
     !defined(OS_NACL)
 #include "ui/gfx/native_widget_types.h"
 #define GPU_SURFACE_HANDLE_IS_ACCELERATED_WINDOW
diff --git a/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.h b/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.h
index 27c5d8f..ae836f3e 100644
--- a/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.h
+++ b/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.h
@@ -11,6 +11,7 @@
 #include "ios/chrome/browser/passwords/ios_chrome_password_manager_infobar_delegate.h"
 
 @protocol ApplicationCommands;
+@class UIViewController;
 
 namespace password_manager {
 class PasswordFormManager;
@@ -25,12 +26,14 @@
     : public IOSChromePasswordManagerInfoBarDelegate {
  public:
   // Creates the infobar for |form_to_save| and adds it to |infobar_manager|.
-  // |is_smart_lock_enabled| controls the branding string. |dispatcher| is not
-  // retained.
+  // |is_smart_lock_enabled| controls the branding string. |baseViewController|
+  // is the base view controller from which to present UI, and is not retained.
+  // |dispatcher| is not retained.
   static void Create(
       bool is_smart_lock_branding_enabled,
       infobars::InfoBarManager* infobar_manager,
       std::unique_ptr<password_manager::PasswordFormManager> form_to_save,
+      UIViewController* baseViewController,
       id<ApplicationCommands> dispatcher);
 
   ~IOSChromeUpdatePasswordInfoBarDelegate() override;
diff --git a/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.mm b/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.mm
index 86f6ec8..3f15503b 100644
--- a/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.mm
@@ -31,6 +31,7 @@
     bool is_smart_lock_branding_enabled,
     infobars::InfoBarManager* infobar_manager,
     std::unique_ptr<PasswordFormManager> form_manager,
+    UIViewController* baseViewController,
     id<ApplicationCommands> dispatcher) {
   DCHECK(infobar_manager);
   auto delegate = base::WrapUnique(new IOSChromeUpdatePasswordInfoBarDelegate(
@@ -39,6 +40,7 @@
   std::unique_ptr<InfoBarIOS> infobar(new InfoBarIOS(std::move(delegate)));
   UpdatePasswordInfoBarController* controller =
       [[UpdatePasswordInfoBarController alloc] initWithDelegate:infobar.get()];
+  [controller setBaseViewController:baseViewController];
   infobar->SetController(controller);
   infobar_manager->AddInfoBar(std::move(infobar));
 }
diff --git a/ios/chrome/browser/passwords/password_controller.h b/ios/chrome/browser/passwords/password_controller.h
index 56444733..90a5da3 100644
--- a/ios/chrome/browser/passwords/password_controller.h
+++ b/ios/chrome/browser/passwords/password_controller.h
@@ -54,6 +54,9 @@
 // The PasswordFormFiller owned by this PasswordController.
 @property(nonatomic, readonly) id<PasswordFormFiller> passwordFormFiller;
 
+// The base view controller from which to present UI.
+@property(nonatomic, readwrite, weak) UIViewController* baseViewController;
+
 // The dispatcher used for the PasswordController. This property can return nil
 // even after being set to a non-nil object.
 @property(nonatomic, weak) id<ApplicationCommands> dispatcher;
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 146e139..1ac3016 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -296,6 +296,8 @@
 
 @synthesize isWebStateDestroyed = _isWebStateDestroyed;
 
+@synthesize baseViewController = _baseViewController;
+
 @synthesize dispatcher = _dispatcher;
 
 @synthesize delegate = _delegate;
@@ -1011,7 +1013,7 @@
     case PasswordInfoBarType::UPDATE:
       IOSChromeUpdatePasswordInfoBarDelegate::Create(
           isSmartLockBrandingEnabled, infoBarManager, std::move(form),
-          self.dispatcher);
+          self.baseViewController, self.dispatcher);
       break;
   }
 }
diff --git a/ios/chrome/browser/passwords/password_tab_helper.h b/ios/chrome/browser/passwords/password_tab_helper.h
index ca311f6..3891910 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.h
+++ b/ios/chrome/browser/passwords/password_tab_helper.h
@@ -15,6 +15,7 @@
 @protocol PasswordControllerDelegate;
 @protocol PasswordFormFiller;
 @protocol PasswordsUiDelegate;
+@class UIViewController;
 
 // Class binding a PasswordController to a WebState.
 class PasswordTabHelper : public web::WebStateObserver,
@@ -25,6 +26,9 @@
   // Creates a PasswordTabHelper and attaches it to the given |web_state|.
   static void CreateForWebState(web::WebState* web_state);
 
+  // Sets the BaseViewController from which to present UI.
+  void SetBaseViewController(UIViewController* baseViewController);
+
   // Sets the PasswordController dispatcher.
   void SetDispatcher(id<ApplicationCommands> dispatcher);
 
diff --git a/ios/chrome/browser/passwords/password_tab_helper.mm b/ios/chrome/browser/passwords/password_tab_helper.mm
index 58946ce..edba437 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.mm
+++ b/ios/chrome/browser/passwords/password_tab_helper.mm
@@ -25,6 +25,11 @@
   }
 }
 
+void PasswordTabHelper::SetBaseViewController(
+    UIViewController* baseViewController) {
+  controller_.baseViewController = baseViewController;
+}
+
 void PasswordTabHelper::SetDispatcher(id<ApplicationCommands> dispatcher) {
   controller_.dispatcher = dispatcher;
 }
diff --git a/ios/chrome/browser/passwords/update_password_infobar_controller.h b/ios/chrome/browser/passwords/update_password_infobar_controller.h
index 7aac940..b08c988 100644
--- a/ios/chrome/browser/passwords/update_password_infobar_controller.h
+++ b/ios/chrome/browser/passwords/update_password_infobar_controller.h
@@ -13,6 +13,9 @@
 // the user whether they want to update their password.
 @interface UpdatePasswordInfoBarController : ConfirmInfoBarController
 
+// The base view controller from which to present UI.
+@property(nonatomic, readwrite, weak) UIViewController* baseViewController;
+
 - (InfoBarView*)viewForDelegate:
                     (IOSChromeUpdatePasswordInfoBarDelegate*)delegate
                           frame:(CGRect)frame;
diff --git a/ios/chrome/browser/passwords/update_password_infobar_controller.mm b/ios/chrome/browser/passwords/update_password_infobar_controller.mm
index 50a68d8c..e743fc6 100644
--- a/ios/chrome/browser/passwords/update_password_infobar_controller.mm
+++ b/ios/chrome/browser/passwords/update_password_infobar_controller.mm
@@ -28,7 +28,7 @@
 @end
 
 @implementation UpdatePasswordInfoBarController
-
+@synthesize baseViewController = _baseViewController;
 @synthesize selectorCoordinator = _selectorCoordinator;
 
 - (InfoBarView*)viewForDelegate:
@@ -65,10 +65,9 @@
   if (tag != kAccountTag)
     return;
 
-  UIViewController* baseViewController =
-      [[UIApplication sharedApplication] keyWindow].rootViewController;
+  DCHECK(self.baseViewController);
   self.selectorCoordinator = [[SelectorCoordinator alloc]
-      initWithBaseViewController:baseViewController];
+      initWithBaseViewController:self.baseViewController];
   self.selectorCoordinator.delegate = self;
   self.selectorCoordinator.options =
       [NSOrderedSet orderedSetWithArray:_delegate->GetAccounts()];
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 6e5acae..391b6876 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -652,6 +652,9 @@
 @property(nonatomic, strong)
     BubbleViewControllerPresenter* incognitoTabTipBubblePresenter;
 
+// Vertical offset for fullscreen toolbar.
+@property(nonatomic, strong) NSLayoutConstraint* toolbarOffsetConstraint;
+
 // BVC initialization:
 // If the BVC is initialized with a valid browser state & tab model immediately,
 // the path is straightforward: functionality is enabled, and the UI is built
@@ -917,6 +920,7 @@
 @synthesize recentTabsCoordinator = _recentTabsCoordinator;
 @synthesize tabStripCoordinator = _tabStripCoordinator;
 @synthesize tabStripView = _tabStripView;
+@synthesize toolbarOffsetConstraint = _toolbarOffsetConstraint;
 
 #pragma mark - Object lifecycle
 
@@ -1896,11 +1900,13 @@
 
   [_toolbarCoordinator adjustToolbarHeight];
 
+  self.toolbarOffsetConstraint =
+      [_toolbarCoordinator.toolbarViewController.view.topAnchor
+          constraintEqualToAnchor:topAnchor];
   [NSLayoutConstraint activateConstraints:@[
+    self.toolbarOffsetConstraint,
     [_toolbarCoordinator.toolbarViewController.view.leadingAnchor
         constraintEqualToAnchor:[self view].leadingAnchor],
-    [_toolbarCoordinator.toolbarViewController.view.topAnchor
-        constraintEqualToAnchor:topAnchor],
     [_toolbarCoordinator.toolbarViewController.view.trailingAnchor
         constraintEqualToAnchor:[self view].trailingAnchor],
   ]];
@@ -2449,6 +2455,7 @@
   // TODO(crbug.com/777557): do not pass the dispatcher to PasswordTabHelper.
   if (PasswordTabHelper* passwordTabHelper =
           PasswordTabHelper::FromWebState(tab.webState)) {
+    passwordTabHelper->SetBaseViewController(self);
     passwordTabHelper->SetDispatcher(self.dispatcher);
     passwordTabHelper->SetPasswordControllerDelegate(self);
   }
@@ -3185,8 +3192,18 @@
                    atOffset:(CGFloat)headerOffset {
   CGFloat height = [self headerOffset];
   for (HeaderDefinition* header in headers) {
+    CGFloat yOrigin = height - headerOffset - header.inset;
+    // Make sure the toolbarView's constraints are also updated.  Leaving the
+    // -setFrame call to minimize changes in this CL -- otherwise the way
+    // toolbar_view manages it's alpha changes would also need to be updated.
+    // TODO(crbug.com/778822): This can be cleaned up when the new fullscreen
+    // is enabled.
+    if (IsSafeAreaCompatibleToolbarEnabled() &&
+        header.view == _toolbarCoordinator.toolbarViewController.view) {
+      self.toolbarOffsetConstraint.constant = yOrigin;
+    }
     CGRect frame = [header.view frame];
-    frame.origin.y = height - headerOffset - header.inset;
+    frame.origin.y = yOrigin;
     [header.view setFrame:frame];
     if (header.behaviour != Overlap)
       height += CGRectGetHeight(frame);
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/ntp/recent_tabs/BUILD.gn
index e277c8c..e7f1ee23 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/BUILD.gn
@@ -40,6 +40,7 @@
     "//ios/chrome/browser/ui/ntp/recent_tabs/views",
     "//ios/chrome/browser/ui/settings/sync_utils",
     "//ios/chrome/browser/ui/signin_interaction/public",
+    "//ios/chrome/browser/ui/util",
     "//ios/web",
     "//ui/base",
     "//url",
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_view_controller.mm
index bc3a9a7..4b989d1 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_view_controller.mm
@@ -42,6 +42,7 @@
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 #import "ios/chrome/browser/ui/util/constraints_ui_util.h"
+#import "ios/chrome/browser/ui/util/top_view_controller.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/web/public/referrer.h"
@@ -629,9 +630,9 @@
     params.view.reset(self.tableView);
 
     // Present sheet/popover using controller that is added to view hierarchy.
-    UIViewController* topController = [params.view window].rootViewController;
-    while (topController.presentedViewController)
-      topController = topController.presentedViewController;
+    // TODO(crbug.com/754642): Remove TopPresentedViewController().
+    UIViewController* topController =
+        top_view_controller::TopPresentedViewController();
 
     _contextMenuCoordinator =
         [[ContextMenuCoordinator alloc] initWithBaseViewController:topController
diff --git a/ios/chrome/browser/ui/toolbar/clean/toolbar_button_factory.h b/ios/chrome/browser/ui/toolbar/clean/toolbar_button_factory.h
index 2bdefd0..0b0dab1 100644
--- a/ios/chrome/browser/ui/toolbar/clean/toolbar_button_factory.h
+++ b/ios/chrome/browser/ui/toolbar/clean/toolbar_button_factory.h
@@ -46,6 +46,8 @@
 - (ToolbarButton*)bookmarkToolbarButton;
 // VoiceSearch ToolbarButton.
 - (ToolbarButton*)voiceSearchButton;
+// ContractToolbar ToolbarButton.
+- (ToolbarButton*)contractToolbarButton;
 
 // Returns images for Voice Search in an array representing the NORMAL/PRESSED
 // state
diff --git a/ios/chrome/browser/ui/toolbar/clean/toolbar_button_factory.mm b/ios/chrome/browser/ui/toolbar/clean/toolbar_button_factory.mm
index 7395be31..f62dfdd 100644
--- a/ios/chrome/browser/ui/toolbar/clean/toolbar_button_factory.mm
+++ b/ios/chrome/browser/ui/toolbar/clean/toolbar_button_factory.mm
@@ -49,6 +49,8 @@
   return self;
 }
 
+#pragma mark - Buttons
+
 - (ToolbarButton*)backToolbarButton {
   int backButtonImages[styleCount][TOOLBAR_STATE_COUNT] =
       TOOLBAR_IDR_THREE_STATE(BACK);
@@ -223,6 +225,21 @@
   return voiceSearchButton;
 }
 
+- (ToolbarButton*)contractToolbarButton {
+  NSString* collapseName = _style ? @"collapse_incognito" : @"collapse";
+  NSString* collapsePressedName =
+      _style ? @"collapse_pressed_incognito" : @"collapse_pressed";
+  ToolbarButton* contractToolbarButton = [ToolbarButton
+      toolbarButtonWithImageForNormalState:[UIImage imageNamed:collapseName]
+                  imageForHighlightedState:[UIImage
+                                               imageNamed:collapsePressedName]
+                     imageForDisabledState:nil];
+  contractToolbarButton.accessibilityLabel = l10n_util::GetNSString(IDS_CANCEL);
+  return contractToolbarButton;
+}
+
+#pragma mark - Helpers
+
 - (NSArray<UIImage*>*)voiceSearchImages {
   // The voice search images can be overridden by the branded image provider.
   int imageID;
diff --git a/ios/chrome/browser/ui/toolbar/clean/toolbar_constants.h b/ios/chrome/browser/ui/toolbar/clean/toolbar_constants.h
index 63fc247..3f4f4c4 100644
--- a/ios/chrome/browser/ui/toolbar/clean/toolbar_constants.h
+++ b/ios/chrome/browser/ui/toolbar/clean/toolbar_constants.h
@@ -20,6 +20,7 @@
 extern const CGFloat kVerticalMargin;
 extern const CGFloat kHorizontalMargin;
 extern const CGFloat kStackViewSpacing;
+extern const CGFloat kTrailingMargin;
 
 // Location bar styling.
 extern const CGFloat kLocationBarBorderWidth;
diff --git a/ios/chrome/browser/ui/toolbar/clean/toolbar_constants.mm b/ios/chrome/browser/ui/toolbar/clean/toolbar_constants.mm
index 77272af..ad2dff32 100644
--- a/ios/chrome/browser/ui/toolbar/clean/toolbar_constants.mm
+++ b/ios/chrome/browser/ui/toolbar/clean/toolbar_constants.mm
@@ -14,6 +14,7 @@
 const CGFloat kVerticalMargin = 7.0f;
 const CGFloat kHorizontalMargin = 1.0f;
 const CGFloat kStackViewSpacing = 2.0f;
+const CGFloat kTrailingMargin = 10.0f;
 
 const CGFloat kLocationBarBorderWidth = 1.0f;
 const CGFloat kLocationBarBorderColor = 0xD0D0D0;
diff --git a/ios/chrome/browser/ui/toolbar/clean/toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/clean/toolbar_view_controller.mm
index 3ab2948..f9003b38 100644
--- a/ios/chrome/browser/ui/toolbar/clean/toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/clean/toolbar_view_controller.mm
@@ -42,6 +42,7 @@
 @property(nonatomic, strong) ToolbarButton* stopButton;
 @property(nonatomic, strong) ToolbarButton* voiceSearchButton;
 @property(nonatomic, strong) ToolbarButton* bookmarkButton;
+@property(nonatomic, strong) ToolbarButton* contractButton;
 @property(nonatomic, assign) BOOL voiceSearchEnabled;
 @property(nonatomic, strong) MDCProgressView* progressBar;
 // Background view, used to display the incognito NTP background color on the
@@ -49,7 +50,9 @@
 @property(nonatomic, strong) UIView* backgroundView;
 // Whether a page is loading.
 @property(nonatomic, assign, getter=isLoading) BOOL loading;
-// Constraints used for the regular/contracted Toolbar state.
+// Constraints used for the regular/contracted Toolbar state that will be
+// deactivated and replaced by |_expandedToolbarConstraints| when animating the
+// toolbar expansion.
 @property(nonatomic, strong) NSMutableArray* regularToolbarConstraints;
 // Constraints used to layout the Toolbar to its expanded state. If these are
 // active the locationBarContainer will expand to the size of this VC's view.
@@ -78,6 +81,7 @@
 @synthesize stopButton = _stopButton;
 @synthesize voiceSearchButton = _voiceSearchButton;
 @synthesize bookmarkButton = _bookmarkButton;
+@synthesize contractButton = _contractButton;
 @synthesize voiceSearchEnabled = _voiceSearchEnabled;
 @synthesize progressBar = _progressBar;
 @synthesize expandedToolbarConstraints = _expandedToolbarConstraints;
@@ -118,18 +122,26 @@
 }
 
 - (void)addToolbarExpansionAnimations:(UIViewPropertyAnimator*)animator {
+  // iPad should never try to animate.
+  DCHECK(!IsIPadIdiom());
   [NSLayoutConstraint deactivateConstraints:self.regularToolbarConstraints];
   [NSLayoutConstraint activateConstraints:self.expandedToolbarConstraints];
   [animator addAnimations:^{
     [self.view layoutIfNeeded];
+    self.contractButton.hidden = NO;
+    self.contractButton.alpha = 1;
   }];
 }
 
 - (void)addToolbarContractionAnimations:(UIViewPropertyAnimator*)animator {
+  // iPad should never try to animate.
+  DCHECK(!IsIPadIdiom());
   [NSLayoutConstraint deactivateConstraints:self.expandedToolbarConstraints];
   [NSLayoutConstraint activateConstraints:self.regularToolbarConstraints];
   [animator addAnimations:^{
     [self.view layoutIfNeeded];
+    self.contractButton.hidden = YES;
+    self.contractButton.alpha = 0;
   }];
 }
 
@@ -172,26 +184,7 @@
       [self.buttonFactory.toolbarConfiguration backgroundColor];
 
   [self setUpToolbarStackView];
-  if (self.locationBarView) {
-    NSLayoutConstraint* locationBarTopAnchorConstraint =
-        [self.locationBarView.topAnchor
-            constraintEqualToAnchor:self.locationBarContainer.topAnchor];
-    [self.locationBarContainer addSubview:self.locationBarView];
-    NSArray* locationBarViewConstraints = @[
-      [self.locationBarView.leadingAnchor
-          constraintEqualToAnchor:self.locationBarContainer.leadingAnchor],
-      [self.locationBarView.trailingAnchor
-          constraintEqualToAnchor:self.locationBarContainer.trailingAnchor],
-      [self.locationBarView.bottomAnchor
-          constraintEqualToAnchor:self.locationBarContainer.bottomAnchor],
-      locationBarTopAnchorConstraint,
-    ];
-    // Adds
-    [self.regularToolbarConstraints addObject:locationBarTopAnchorConstraint];
-    [NSLayoutConstraint activateConstraints:locationBarViewConstraints];
-  }
-  [self.locationBarContainer addSubview:self.bookmarkButton];
-  [self.locationBarContainer addSubview:self.voiceSearchButton];
+  [self setUpToolbarContainerView];
   [self.view addSubview:self.stackView];
   [self.view addSubview:self.progressBar];
   [self setConstraints];
@@ -216,12 +209,51 @@
   self.stackView.distribution = UIStackViewDistributionFill;
 }
 
+// Sets up the LocationContainerView. This contains the locationBarView, and
+// other buttons like Voice Search, Bookmarks and Contract Toolbar.
+- (void)setUpToolbarContainerView {
+  if (self.locationBarView) {
+    [self.locationBarContainer addSubview:self.locationBarView];
+
+    // Bookmarks and Voice Search buttons will only be part of the Toolbar on
+    // iPad. On the other hand the contract button is only needed on non iPad
+    // devices, since iPad doesn't animate, thus it doesn't need to contract.
+    if (IsIPadIdiom()) {
+      [self.locationBarContainer addSubview:self.bookmarkButton];
+      [self.locationBarContainer addSubview:self.voiceSearchButton];
+    } else {
+      [self.locationBarContainer addSubview:self.contractButton];
+    }
+
+    // LocationBarView constraints.
+    NSLayoutConstraint* locationBarViewTrailingConstraint =
+        [self.locationBarView.trailingAnchor
+            constraintEqualToAnchor:self.locationBarContainer.trailingAnchor];
+    NSLayoutConstraint* locationBarViewTopConstraint =
+        [self.locationBarView.topAnchor
+            constraintEqualToAnchor:self.locationBarContainer.topAnchor];
+    [self.regularToolbarConstraints addObjectsFromArray:@[
+      locationBarViewTopConstraint, locationBarViewTrailingConstraint
+    ]];
+    NSArray* locationBarViewConstraints = @[
+      [self.locationBarView.leadingAnchor
+          constraintEqualToAnchor:self.locationBarContainer.leadingAnchor],
+      [self.locationBarView.bottomAnchor
+          constraintEqualToAnchor:self.locationBarContainer.bottomAnchor],
+      locationBarViewTrailingConstraint,
+      locationBarViewTopConstraint,
+    ];
+    [NSLayoutConstraint activateConstraints:locationBarViewConstraints];
+  }
+}
+
 - (void)setConstraints {
   self.view.translatesAutoresizingMaskIntoConstraints = NO;
   [self.view.bottomAnchor constraintEqualToAnchor:self.topSafeAnchor
                                          constant:kToolbarHeight]
       .active = YES;
 
+  // ProgressBar constraints.
   NSArray* progressBarConstraints = @[
     [self.progressBar.leadingAnchor
         constraintEqualToAnchor:self.view.leadingAnchor],
@@ -234,7 +266,8 @@
   ];
   [NSLayoutConstraint activateConstraints:progressBarConstraints];
 
-  NSArray* constraints = @[
+  // StackView constraints.
+  NSArray* stackViewConstraints = @[
     [self.stackView.heightAnchor
         constraintEqualToConstant:kToolbarHeight - 2 * kVerticalMargin],
     [self.stackView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor
@@ -245,17 +278,38 @@
     [self.stackView.trailingAnchor
         constraintEqualToAnchor:self.view.trailingAnchor
                        constant:-kHorizontalMargin],
-    [self.bookmarkButton.centerYAnchor
-        constraintEqualToAnchor:self.locationBarContainer.centerYAnchor],
-    [self.voiceSearchButton.centerYAnchor
-        constraintEqualToAnchor:self.locationBarContainer.centerYAnchor],
-    [self.voiceSearchButton.trailingAnchor
-        constraintEqualToAnchor:self.locationBarContainer.trailingAnchor],
-    [self.bookmarkButton.trailingAnchor
-        constraintEqualToAnchor:self.voiceSearchButton.leadingAnchor],
   ];
-  [self.regularToolbarConstraints addObjectsFromArray:constraints];
-  [NSLayoutConstraint activateConstraints:constraints];
+  [self.regularToolbarConstraints addObjectsFromArray:stackViewConstraints];
+  [NSLayoutConstraint activateConstraints:stackViewConstraints];
+
+  // LocationBarContainer Buttons constraints.
+  NSArray* locationBarButtonConstraints;
+  if (IsIPadIdiom()) {
+    locationBarButtonConstraints = @[
+      [self.bookmarkButton.centerYAnchor
+          constraintEqualToAnchor:self.locationBarContainer.centerYAnchor],
+      [self.voiceSearchButton.centerYAnchor
+          constraintEqualToAnchor:self.locationBarContainer.centerYAnchor],
+      [self.voiceSearchButton.trailingAnchor
+          constraintEqualToAnchor:self.locationBarContainer.trailingAnchor],
+      [self.bookmarkButton.trailingAnchor
+          constraintEqualToAnchor:self.voiceSearchButton.leadingAnchor],
+    ];
+  } else {
+    NSLayoutConstraint* contractButtonTrailingConstraint =
+        [self.contractButton.trailingAnchor
+            constraintEqualToAnchor:self.locationBarContainer.trailingAnchor
+                           constant:-2 * kTrailingMargin];
+    [self.regularToolbarConstraints addObject:contractButtonTrailingConstraint];
+    locationBarButtonConstraints = @[
+      [self.contractButton.topAnchor constraintEqualToAnchor:self.topSafeAnchor
+                                                    constant:kVerticalMargin],
+      [self.contractButton.bottomAnchor
+          constraintEqualToAnchor:self.locationBarContainer.bottomAnchor],
+      contractButtonTrailingConstraint,
+    ];
+  }
+  [NSLayoutConstraint activateConstraints:locationBarButtonConstraints];
 }
 
 #pragma mark - Components Setup
@@ -370,6 +424,16 @@
                           action:@selector(bookmarkPage)
                 forControlEvents:UIControlEventTouchUpInside];
 
+  // Contract button.
+  self.contractButton = [self.buttonFactory contractToolbarButton];
+  self.contractButton.visibilityMask = ToolbarComponentVisibilityCompactWidth |
+                                       ToolbarComponentVisibilityRegularWidth;
+  self.contractButton.alpha = 0;
+  self.contractButton.hidden = YES;
+  [buttonConstraints
+      addObject:[self.contractButton.widthAnchor
+                    constraintEqualToConstant:kToolbarButtonWidth]];
+
   // Add buttons to button updater.
   self.buttonUpdater.backButton = self.backButton;
   self.buttonUpdater.forwardButton = self.forwardButton;
@@ -612,6 +676,12 @@
           constraintEqualToAnchor:self.view.trailingAnchor],
       [self.locationBarView.topAnchor constraintEqualToAnchor:self.topSafeAnchor
                                                      constant:kVerticalMargin],
+      [self.locationBarView.trailingAnchor
+          constraintEqualToAnchor:self.contractButton.leadingAnchor
+                         constant:-kTrailingMargin],
+      [self.contractButton.trailingAnchor
+          constraintEqualToAnchor:self.locationBarContainer.trailingAnchor
+                         constant:-kTrailingMargin],
     ];
   }
   return _expandedToolbarConstraints;
diff --git a/ios/third_party/motion_animator_objc/BUILD.gn b/ios/third_party/motion_animator_objc/BUILD.gn
new file mode 100644
index 0000000..d4013998
--- /dev/null
+++ b/ios/third_party/motion_animator_objc/BUILD.gn
@@ -0,0 +1,61 @@
+# 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.
+
+import("//build/config/ios/ios_sdk.gni")
+import("//build/config/ios/rules.gni")
+import("//ios/build/chrome_build.gni")
+
+config("config") {
+  include_dirs = [ "src/src" ]
+  visibility = [ ":motion_animator_objc" ]
+  defines = [ "IS_BAZEL_BUILD" ]
+}
+
+source_set("motion_animator_objc") {
+  sources = [
+    "src/src/CATransaction+MotionAnimator.h",
+    "src/src/CATransaction+MotionAnimator.m",
+    "src/src/MDMAnimatableKeyPaths.h",
+    "src/src/MDMAnimatableKeyPaths.m",
+    "src/src/MDMCoreAnimationTraceable.h",
+    "src/src/MDMMotionAnimator.h",
+    "src/src/MDMMotionAnimator.m",
+    "src/src/MotionAnimator.h",
+    "src/src/private/CABasicAnimation+MotionAnimator.h",
+    "src/src/private/CABasicAnimation+MotionAnimator.m",
+    "src/src/private/CAMediaTimingFunction+MotionAnimator.h",
+    "src/src/private/CAMediaTimingFunction+MotionAnimator.m",
+    "src/src/private/MDMBlockAnimations.h",
+    "src/src/private/MDMBlockAnimations.m",
+    "src/src/private/MDMDragCoefficient.h",
+    "src/src/private/MDMDragCoefficient.m",
+    "src/src/private/MDMUIKitValueCoercion.h",
+    "src/src/private/MDMUIKitValueCoercion.m",
+  ]
+  public = [
+    "src/src/CATransaction+MotionAnimator.h",
+    "src/src/MDMAnimatableKeyPaths.h",
+    "src/src/MDMCoreAnimationTraceable.h",
+    "src/src/MDMMotionAnimator.h",
+    "src/src/MotionAnimator.h",
+  ]
+
+  libs = [
+    "CoreGraphics.framework",
+    "Foundation.framework",
+    "QuartzCore.framework",
+    "UIKit.framework",
+  ]
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [
+    ":config",
+    "//build/config/compiler:enable_arc",
+    "//build/config/compiler:no_chromium_code",
+  ]
+  public_configs = [ ":config" ]
+
+  public_deps = [
+    "//ios/third_party/motion_interchange_objc",
+  ]
+}
diff --git a/ios/third_party/motion_animator_objc/LICENSE b/ios/third_party/motion_animator_objc/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/ios/third_party/motion_animator_objc/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/ios/third_party/motion_animator_objc/OWNERS b/ios/third_party/motion_animator_objc/OWNERS
new file mode 100644
index 0000000..e9a3f8b3
--- /dev/null
+++ b/ios/third_party/motion_animator_objc/OWNERS
@@ -0,0 +1,9 @@
+sdefresne@chromium.org
+rohitrao@chromium.org
+
+# These are for the common case of adding or renaming files. If you're doing
+# structural changes, please get a review from an OWNER.
+per-file BUILD.gn=*
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/motion_animator_objc/README.chromium b/ios/third_party/motion_animator_objc/README.chromium
new file mode 100644
index 0000000..20d2547
--- /dev/null
+++ b/ios/third_party/motion_animator_objc/README.chromium
@@ -0,0 +1,14 @@
+Name: Motion Animator for Objective-C
+URL: https://github.com/material-motion/motion-animator-objc
+Version: 0
+Revision: 213bfb3a75018c49a223bf1269f6a48fbbfc3a12
+License: Apache 2.0
+License File: LICENSE
+Security Critical: yes
+
+Description:
+This library provides APIs that turn Motion Interchange motion specifications
+into animations.
+
+Local Modifications:
+None
diff --git a/mojo/public/cpp/bindings/interface_ptr_set.h b/mojo/public/cpp/bindings/interface_ptr_set.h
index c7e7c16..02bfc4beb 100644
--- a/mojo/public/cpp/bindings/interface_ptr_set.h
+++ b/mojo/public/cpp/bindings/interface_ptr_set.h
@@ -51,11 +51,14 @@
 
   bool empty() const { return ptrs_.empty(); }
 
+  // Calls FlushForTesting on all Ptrs sequentially. Since each call is a
+  // blocking operation, may be very slow as the number of pointers increases.
   void FlushForTesting() {
     for (const auto& it : ptrs_) {
       if (it)
         it->FlushForTesting();
     }
+    ClearNullPtrs();
   }
 
  private:
diff --git a/net/http2/decoder/decode_buffer_test.cc b/net/http2/decoder/decode_buffer_test.cc
index b982d4c9..2f1abdd 100644
--- a/net/http2/decoder/decode_buffer_test.cc
+++ b/net/http2/decoder/decode_buffer_test.cc
@@ -43,7 +43,7 @@
 
 class DecodeBufferTest : public ::testing::Test {
  public:
-  DecodeBufferTest() {}
+  DecodeBufferTest() = default;
 
  protected:
   Http2Random random_;
diff --git a/net/http2/decoder/http2_frame_decoder_listener_test_util.cc b/net/http2/decoder/http2_frame_decoder_listener_test_util.cc
index 9159b6cd..74c59ee 100644
--- a/net/http2/decoder/http2_frame_decoder_listener_test_util.cc
+++ b/net/http2/decoder/http2_frame_decoder_listener_test_util.cc
@@ -9,8 +9,8 @@
 
 namespace net {
 
-FailingHttp2FrameDecoderListener::FailingHttp2FrameDecoderListener() {}
-FailingHttp2FrameDecoderListener::~FailingHttp2FrameDecoderListener() {}
+FailingHttp2FrameDecoderListener::FailingHttp2FrameDecoderListener() = default;
+FailingHttp2FrameDecoderListener::~FailingHttp2FrameDecoderListener() = default;
 
 bool FailingHttp2FrameDecoderListener::OnFrameHeader(
     const Http2FrameHeader& header) {
@@ -196,7 +196,7 @@
 LoggingHttp2FrameDecoderListener::LoggingHttp2FrameDecoderListener(
     Http2FrameDecoderListener* wrapped)
     : wrapped_(wrapped) {}
-LoggingHttp2FrameDecoderListener::~LoggingHttp2FrameDecoderListener() {}
+LoggingHttp2FrameDecoderListener::~LoggingHttp2FrameDecoderListener() = default;
 
 bool LoggingHttp2FrameDecoderListener::OnFrameHeader(
     const Http2FrameHeader& header) {
diff --git a/net/http2/hpack/decoder/hpack_block_collector.cc b/net/http2/hpack/decoder/hpack_block_collector.cc
index 4e433e0e..2bb90b72 100644
--- a/net/http2/hpack/decoder/hpack_block_collector.cc
+++ b/net/http2/hpack/decoder/hpack_block_collector.cc
@@ -17,10 +17,10 @@
 namespace net {
 namespace test {
 
-HpackBlockCollector::HpackBlockCollector() {}
+HpackBlockCollector::HpackBlockCollector() = default;
 HpackBlockCollector::HpackBlockCollector(const HpackBlockCollector& other)
     : pending_entry_(other.pending_entry_), entries_(other.entries_) {}
-HpackBlockCollector::~HpackBlockCollector() {}
+HpackBlockCollector::~HpackBlockCollector() = default;
 
 void HpackBlockCollector::OnIndexedHeader(size_t index) {
   pending_entry_.OnIndexedHeader(index);
diff --git a/net/http2/hpack/decoder/hpack_decoder.cc b/net/http2/hpack/decoder/hpack_decoder.cc
index 91a0f03..c3ce241 100644
--- a/net/http2/hpack/decoder/hpack_decoder.cc
+++ b/net/http2/hpack/decoder/hpack_decoder.cc
@@ -17,7 +17,7 @@
       block_decoder_(&entry_buffer_),
       error_detected_(false) {}
 
-HpackDecoder::~HpackDecoder() {}
+HpackDecoder::~HpackDecoder() = default;
 
 void HpackDecoder::set_tables_debug_listener(
     HpackDecoderTablesDebugListener* debug_listener) {
diff --git a/net/http2/hpack/decoder/hpack_decoder_listener.cc b/net/http2/hpack/decoder/hpack_decoder_listener.cc
index 8f0e0a0..f85b903 100644
--- a/net/http2/hpack/decoder/hpack_decoder_listener.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_listener.cc
@@ -6,11 +6,11 @@
 
 namespace net {
 
-HpackDecoderListener::HpackDecoderListener() {}
-HpackDecoderListener::~HpackDecoderListener() {}
+HpackDecoderListener::HpackDecoderListener() = default;
+HpackDecoderListener::~HpackDecoderListener() = default;
 
-HpackDecoderNoOpListener::HpackDecoderNoOpListener() {}
-HpackDecoderNoOpListener::~HpackDecoderNoOpListener() {}
+HpackDecoderNoOpListener::HpackDecoderNoOpListener() = default;
+HpackDecoderNoOpListener::~HpackDecoderNoOpListener() = default;
 
 void HpackDecoderNoOpListener::OnHeaderListStart() {}
 void HpackDecoderNoOpListener::OnHeader(HpackEntryType entry_type,
diff --git a/net/http2/hpack/decoder/hpack_decoder_state.cc b/net/http2/hpack/decoder/hpack_decoder_state.cc
index 0e39994..60e7207 100644
--- a/net/http2/hpack/decoder/hpack_decoder_state.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_state.cc
@@ -33,7 +33,7 @@
       error_detected_(false) {
   CHECK(listener);
 }
-HpackDecoderState::~HpackDecoderState() {}
+HpackDecoderState::~HpackDecoderState() = default;
 
 void HpackDecoderState::set_tables_debug_listener(
     HpackDecoderTablesDebugListener* debug_listener) {
diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
index 3e4451c..9874320 100644
--- a/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
@@ -53,7 +53,7 @@
       is_huffman_encoded_(false),
       state_(State::RESET),
       backing_(Backing::RESET) {}
-HpackDecoderStringBuffer::~HpackDecoderStringBuffer() {}
+HpackDecoderStringBuffer::~HpackDecoderStringBuffer() = default;
 
 void HpackDecoderStringBuffer::Reset() {
   DVLOG(3) << "HpackDecoderStringBuffer::Reset";
diff --git a/net/http2/hpack/decoder/hpack_decoder_tables.cc b/net/http2/hpack/decoder/hpack_decoder_tables.cc
index 61e7970..c185dfc 100644
--- a/net/http2/hpack/decoder/hpack_decoder_tables.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_tables.cc
@@ -33,8 +33,8 @@
 
 }  // namespace
 
-HpackDecoderTablesDebugListener::HpackDecoderTablesDebugListener() {}
-HpackDecoderTablesDebugListener::~HpackDecoderTablesDebugListener() {}
+HpackDecoderTablesDebugListener::HpackDecoderTablesDebugListener() = default;
+HpackDecoderTablesDebugListener::~HpackDecoderTablesDebugListener() = default;
 
 HpackDecoderStaticTable::HpackDecoderStaticTable(
     const std::vector<HpackStringPair>* table)
@@ -56,7 +56,7 @@
 
 HpackDecoderDynamicTable::HpackDecoderDynamicTable()
     : insert_count_(kFirstDynamicTableIndex - 1), debug_listener_(nullptr) {}
-HpackDecoderDynamicTable::~HpackDecoderDynamicTable() {}
+HpackDecoderDynamicTable::~HpackDecoderDynamicTable() = default;
 
 void HpackDecoderDynamicTable::DynamicTableSizeUpdate(size_t size_limit) {
   DVLOG(3) << "HpackDecoderDynamicTable::DynamicTableSizeUpdate " << size_limit;
@@ -133,8 +133,8 @@
   }
 }
 
-HpackDecoderTables::HpackDecoderTables() {}
-HpackDecoderTables::~HpackDecoderTables() {}
+HpackDecoderTables::HpackDecoderTables() = default;
+HpackDecoderTables::~HpackDecoderTables() = default;
 
 void HpackDecoderTables::set_debug_listener(
     HpackDecoderTablesDebugListener* debug_listener) {
diff --git a/net/http2/hpack/decoder/hpack_decoder_tables_test.cc b/net/http2/hpack/decoder/hpack_decoder_tables_test.cc
index e9e6c54..77c2413 100644
--- a/net/http2/hpack/decoder/hpack_decoder_tables_test.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_tables_test.cc
@@ -56,7 +56,7 @@
 
 class HpackDecoderStaticTableTest : public ::testing::Test {
  protected:
-  HpackDecoderStaticTableTest() {}
+  HpackDecoderStaticTableTest() = default;
 
   std::vector<StaticEntry> shuffled_static_entries() {
     std::vector<StaticEntry> entries = MakeSpecStaticEntries();
diff --git a/net/http2/hpack/decoder/hpack_decoder_test.cc b/net/http2/hpack/decoder/hpack_decoder_test.cc
index 61e05ab..7a89e821 100644
--- a/net/http2/hpack/decoder/hpack_decoder_test.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_test.cc
@@ -83,7 +83,7 @@
   HpackDecoderTest() : decoder_(this, 4096) {
     fragment_the_hpack_block_ = GetParam();
   }
-  ~HpackDecoderTest() override {}
+  ~HpackDecoderTest() override = default;
 
   void OnHeaderListStart() override {
     ASSERT_FALSE(saw_start_);
diff --git a/net/http2/hpack/decoder/hpack_entry_collector.cc b/net/http2/hpack/decoder/hpack_entry_collector.cc
index d4d39d4c..0ea55dc 100644
--- a/net/http2/hpack/decoder/hpack_entry_collector.cc
+++ b/net/http2/hpack/decoder/hpack_entry_collector.cc
@@ -60,7 +60,7 @@
       started_(true),
       ended_(true) {}
 
-HpackEntryCollector::~HpackEntryCollector() {}
+HpackEntryCollector::~HpackEntryCollector() = default;
 
 void HpackEntryCollector::OnIndexedHeader(size_t index) {
   ASSERT_FALSE(started_);
diff --git a/net/http2/hpack/decoder/hpack_whole_entry_buffer.cc b/net/http2/hpack/decoder/hpack_whole_entry_buffer.cc
index 7131551..4799a6c8 100644
--- a/net/http2/hpack/decoder/hpack_whole_entry_buffer.cc
+++ b/net/http2/hpack/decoder/hpack_whole_entry_buffer.cc
@@ -15,7 +15,7 @@
     : max_string_size_bytes_(max_string_size_bytes) {
   set_listener(listener);
 }
-HpackWholeEntryBuffer::~HpackWholeEntryBuffer() {}
+HpackWholeEntryBuffer::~HpackWholeEntryBuffer() = default;
 
 void HpackWholeEntryBuffer::set_listener(HpackWholeEntryListener* listener) {
   CHECK(listener);
diff --git a/net/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc b/net/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc
index b7260e29..6626bc8 100644
--- a/net/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc
+++ b/net/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc
@@ -37,7 +37,7 @@
 
 class MockHpackWholeEntryListener : public HpackWholeEntryListener {
  public:
-  ~MockHpackWholeEntryListener() override {}
+  ~MockHpackWholeEntryListener() override = default;
 
   MOCK_METHOD1(OnIndexedHeader, void(size_t index));
   MOCK_METHOD3(OnNameIndexAndLiteralValue,
@@ -55,7 +55,7 @@
 class HpackWholeEntryBufferTest : public ::testing::Test {
  protected:
   HpackWholeEntryBufferTest() : entry_buffer_(&listener_, kMaxStringSize) {}
-  ~HpackWholeEntryBufferTest() override {}
+  ~HpackWholeEntryBufferTest() override = default;
 
   StrictMock<MockHpackWholeEntryListener> listener_;
   HpackWholeEntryBuffer entry_buffer_;
diff --git a/net/http2/hpack/decoder/hpack_whole_entry_listener.cc b/net/http2/hpack/decoder/hpack_whole_entry_listener.cc
index ed18ff1..419c3356 100644
--- a/net/http2/hpack/decoder/hpack_whole_entry_listener.cc
+++ b/net/http2/hpack/decoder/hpack_whole_entry_listener.cc
@@ -6,9 +6,9 @@
 
 namespace net {
 
-HpackWholeEntryListener::~HpackWholeEntryListener() {}
+HpackWholeEntryListener::~HpackWholeEntryListener() = default;
 
-HpackWholeEntryNoOpListener::~HpackWholeEntryNoOpListener() {}
+HpackWholeEntryNoOpListener::~HpackWholeEntryNoOpListener() = default;
 
 void HpackWholeEntryNoOpListener::OnIndexedHeader(size_t index) {}
 void HpackWholeEntryNoOpListener::OnNameIndexAndLiteralValue(
diff --git a/net/http2/hpack/hpack_string.cc b/net/http2/hpack/hpack_string.cc
index 4db9f48a..570a6cdb 100644
--- a/net/http2/hpack/hpack_string.cc
+++ b/net/http2/hpack/hpack_string.cc
@@ -14,8 +14,8 @@
 HpackString::HpackString(const char* data) : str_(data) {}
 HpackString::HpackString(Http2StringPiece str) : str_(str.as_string()) {}
 HpackString::HpackString(Http2String str) : str_(std::move(str)) {}
-HpackString::HpackString(const HpackString& other) : str_(other.str_) {}
-HpackString::~HpackString() {}
+HpackString::HpackString(const HpackString& other) = default;
+HpackString::~HpackString() = default;
 
 Http2StringPiece HpackString::ToStringPiece() const {
   return str_;
diff --git a/net/http2/hpack/huffman/hpack_huffman_decoder.cc b/net/http2/hpack/huffman/hpack_huffman_decoder.cc
index 3621ca1..68246fd 100644
--- a/net/http2/hpack/huffman/hpack_huffman_decoder.cc
+++ b/net/http2/hpack/huffman/hpack_huffman_decoder.cc
@@ -410,9 +410,9 @@
   return ss.str();
 }
 
-HpackHuffmanDecoder::HpackHuffmanDecoder() {}
+HpackHuffmanDecoder::HpackHuffmanDecoder() = default;
 
-HpackHuffmanDecoder::~HpackHuffmanDecoder() {}
+HpackHuffmanDecoder::~HpackHuffmanDecoder() = default;
 
 bool HpackHuffmanDecoder::Decode(Http2StringPiece input, Http2String* output) {
   return DecodeShortCodesFirst(input, output);
diff --git a/net/http2/test_tools/frame_parts.cc b/net/http2/test_tools/frame_parts.cc
index db0ee356..6d08f677 100644
--- a/net/http2/test_tools/frame_parts.cc
+++ b/net/http2/test_tools/frame_parts.cc
@@ -66,7 +66,7 @@
 
 FrameParts::FrameParts(const FrameParts& header) = default;
 
-FrameParts::~FrameParts() {}
+FrameParts::~FrameParts() = default;
 
 AssertionResult FrameParts::VerifyEquals(const FrameParts& that) const {
 #define COMMON_MESSAGE "\n  this: " << *this << "\n  that: " << that
diff --git a/net/http2/test_tools/frame_parts_collector.cc b/net/http2/test_tools/frame_parts_collector.cc
index 18978d7..7128edf0 100644
--- a/net/http2/test_tools/frame_parts_collector.cc
+++ b/net/http2/test_tools/frame_parts_collector.cc
@@ -13,8 +13,8 @@
 namespace net {
 namespace test {
 
-FramePartsCollector::FramePartsCollector() {}
-FramePartsCollector::~FramePartsCollector() {}
+FramePartsCollector::FramePartsCollector() = default;
+FramePartsCollector::~FramePartsCollector() = default;
 
 void FramePartsCollector::Reset() {
   current_frame_.reset();
diff --git a/net/http2/tools/random_decoder_test.cc b/net/http2/tools/random_decoder_test.cc
index 47769fc3..be5aaa1b 100644
--- a/net/http2/tools/random_decoder_test.cc
+++ b/net/http2/tools/random_decoder_test.cc
@@ -28,7 +28,7 @@
 namespace net {
 namespace test {
 
-RandomDecoderTest::RandomDecoderTest() {}
+RandomDecoderTest::RandomDecoderTest() = default;
 
 bool RandomDecoderTest::StopDecodeOnDone() {
   return stop_decode_on_done_;
diff --git a/net/spdy/chromium/bidirectional_stream_spdy_impl_unittest.cc b/net/spdy/chromium/bidirectional_stream_spdy_impl_unittest.cc
index c6f5d42..a92c304 100644
--- a/net/spdy/chromium/bidirectional_stream_spdy_impl_unittest.cc
+++ b/net/spdy/chromium/bidirectional_stream_spdy_impl_unittest.cc
@@ -79,7 +79,7 @@
         not_expect_callback_(false),
         on_failed_called_(false) {}
 
-  ~TestDelegateBase() override {}
+  ~TestDelegateBase() override = default;
 
   void OnStreamReady(bool request_headers_sent) override {
     CHECK(!on_failed_called_);
diff --git a/net/spdy/chromium/buffered_spdy_framer.cc b/net/spdy/chromium/buffered_spdy_framer.cc
index 8da2d10..3448a38e 100644
--- a/net/spdy/chromium/buffered_spdy_framer.cc
+++ b/net/spdy/chromium/buffered_spdy_framer.cc
@@ -32,8 +32,7 @@
       max_header_list_size_);
 }
 
-BufferedSpdyFramer::~BufferedSpdyFramer() {
-}
+BufferedSpdyFramer::~BufferedSpdyFramer() = default;
 
 void BufferedSpdyFramer::set_visitor(
     BufferedSpdyFramerVisitorInterface* visitor) {
diff --git a/net/spdy/chromium/http2_priority_dependencies.cc b/net/spdy/chromium/http2_priority_dependencies.cc
index 991924e..97c4b39 100644
--- a/net/spdy/chromium/http2_priority_dependencies.cc
+++ b/net/spdy/chromium/http2_priority_dependencies.cc
@@ -7,9 +7,9 @@
 
 namespace net {
 
-Http2PriorityDependencies::Http2PriorityDependencies() {}
+Http2PriorityDependencies::Http2PriorityDependencies() = default;
 
-Http2PriorityDependencies::~Http2PriorityDependencies() {}
+Http2PriorityDependencies::~Http2PriorityDependencies() = default;
 
 void Http2PriorityDependencies::OnStreamCreation(
     SpdyStreamId id,
diff --git a/net/spdy/chromium/http2_push_promise_index.cc b/net/spdy/chromium/http2_push_promise_index.cc
index 876b1ca9..4f5f7d1 100644
--- a/net/spdy/chromium/http2_push_promise_index.cc
+++ b/net/spdy/chromium/http2_push_promise_index.cc
@@ -10,7 +10,7 @@
 
 namespace net {
 
-Http2PushPromiseIndex::Http2PushPromiseIndex() {}
+Http2PushPromiseIndex::Http2PushPromiseIndex() = default;
 
 Http2PushPromiseIndex::~Http2PushPromiseIndex() {
   DCHECK(unclaimed_pushed_streams_.empty());
diff --git a/net/spdy/chromium/multiplexed_http_stream.cc b/net/spdy/chromium/multiplexed_http_stream.cc
index 3148fed..0a68594 100644
--- a/net/spdy/chromium/multiplexed_http_stream.cc
+++ b/net/spdy/chromium/multiplexed_http_stream.cc
@@ -14,7 +14,7 @@
     std::unique_ptr<MultiplexedSessionHandle> session)
     : session_(std::move(session)) {}
 
-MultiplexedHttpStream::~MultiplexedHttpStream() {}
+MultiplexedHttpStream::~MultiplexedHttpStream() = default;
 
 bool MultiplexedHttpStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
   return session_->GetRemoteEndpoint(endpoint);
diff --git a/net/spdy/chromium/multiplexed_session.cc b/net/spdy/chromium/multiplexed_session.cc
index e49e69b..8b380265 100644
--- a/net/spdy/chromium/multiplexed_session.cc
+++ b/net/spdy/chromium/multiplexed_session.cc
@@ -12,7 +12,7 @@
   SaveSSLInfo();
 }
 
-MultiplexedSessionHandle::~MultiplexedSessionHandle() {}
+MultiplexedSessionHandle::~MultiplexedSessionHandle() = default;
 
 bool MultiplexedSessionHandle::GetRemoteEndpoint(IPEndPoint* endpoint) {
   if (!session_)
diff --git a/net/spdy/chromium/spdy_buffer_producer.cc b/net/spdy/chromium/spdy_buffer_producer.cc
index 08847ec..294d826c 100644
--- a/net/spdy/chromium/spdy_buffer_producer.cc
+++ b/net/spdy/chromium/spdy_buffer_producer.cc
@@ -13,14 +13,14 @@
 
 namespace net {
 
-SpdyBufferProducer::SpdyBufferProducer() {}
+SpdyBufferProducer::SpdyBufferProducer() = default;
 
-SpdyBufferProducer::~SpdyBufferProducer() {}
+SpdyBufferProducer::~SpdyBufferProducer() = default;
 
 SimpleBufferProducer::SimpleBufferProducer(std::unique_ptr<SpdyBuffer> buffer)
     : buffer_(std::move(buffer)) {}
 
-SimpleBufferProducer::~SimpleBufferProducer() {}
+SimpleBufferProducer::~SimpleBufferProducer() = default;
 
 std::unique_ptr<SpdyBuffer> SimpleBufferProducer::ProduceBuffer() {
   DCHECK(buffer_);
diff --git a/net/spdy/chromium/spdy_http_stream_unittest.cc b/net/spdy/chromium/spdy_http_stream_unittest.cc
index 1db24232..d6d2778 100644
--- a/net/spdy/chromium/spdy_http_stream_unittest.cc
+++ b/net/spdy/chromium/spdy_http_stream_unittest.cc
@@ -130,7 +130,7 @@
     session_deps_.net_log = &net_log_;
   }
 
-  ~SpdyHttpStreamTest() override {}
+  ~SpdyHttpStreamTest() override = default;
 
  protected:
   void TearDown() override {
diff --git a/net/spdy/chromium/spdy_network_transaction_unittest.cc b/net/spdy/chromium/spdy_network_transaction_unittest.cc
index 6d4e16f..108151f 100644
--- a/net/spdy/chromium/spdy_network_transaction_unittest.cc
+++ b/net/spdy/chromium/spdy_network_transaction_unittest.cc
@@ -1237,7 +1237,7 @@
                              base::Unretained(this))) {
   }
 
-  ~KillerCallback() override {}
+  ~KillerCallback() override = default;
 
   const CompletionCallback& callback() const { return callback_; }
 
diff --git a/net/spdy/chromium/spdy_proxy_client_socket_unittest.cc b/net/spdy/chromium/spdy_proxy_client_socket_unittest.cc
index df25f48c..63c8d10 100644
--- a/net/spdy/chromium/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/chromium/spdy_proxy_client_socket_unittest.cc
@@ -1299,7 +1299,7 @@
         callback_(base::Bind(&DeleteSockCallback::OnComplete,
                              base::Unretained(this))) {}
 
-  ~DeleteSockCallback() override {}
+  ~DeleteSockCallback() override = default;
 
   const CompletionCallback& callback() const { return callback_; }
 
diff --git a/net/spdy/chromium/spdy_session.cc b/net/spdy/chromium/spdy_session.cc
index 420ec31..4b248c89 100644
--- a/net/spdy/chromium/spdy_session.cc
+++ b/net/spdy/chromium/spdy_session.cc
@@ -1379,8 +1379,8 @@
 SpdySession::UnclaimedPushedStreamContainer::UnclaimedPushedStreamContainer(
     SpdySession* spdy_session)
     : spdy_session_(spdy_session) {}
-SpdySession::UnclaimedPushedStreamContainer::~UnclaimedPushedStreamContainer() {
-}
+SpdySession::UnclaimedPushedStreamContainer::~UnclaimedPushedStreamContainer() =
+    default;
 
 bool SpdySession::UnclaimedPushedStreamContainer::erase(const GURL& url) {
   const_iterator it = find(url);
diff --git a/net/spdy/chromium/spdy_session_key.cc b/net/spdy/chromium/spdy_session_key.cc
index 42e260c..41e7afb 100644
--- a/net/spdy/chromium/spdy_session_key.cc
+++ b/net/spdy/chromium/spdy_session_key.cc
@@ -36,7 +36,7 @@
 
 SpdySessionKey::SpdySessionKey(const SpdySessionKey& other) = default;
 
-SpdySessionKey::~SpdySessionKey() {}
+SpdySessionKey::~SpdySessionKey() = default;
 
 bool SpdySessionKey::operator<(const SpdySessionKey& other) const {
   return std::tie(privacy_mode_, host_port_proxy_pair_.first,
diff --git a/net/spdy/chromium/spdy_session_pool_unittest.cc b/net/spdy/chromium/spdy_session_pool_unittest.cc
index 0d9cc49f..83614a4 100644
--- a/net/spdy/chromium/spdy_session_pool_unittest.cc
+++ b/net/spdy/chromium/spdy_session_pool_unittest.cc
@@ -80,7 +80,7 @@
       : spdy_session_pool_(spdy_session_pool),
         key_(key) {}
 
-  ~SessionOpeningDelegate() override {}
+  ~SessionOpeningDelegate() override = default;
 
   void OnHeadersSent() override {}
 
diff --git a/net/spdy/chromium/spdy_session_unittest.cc b/net/spdy/chromium/spdy_session_unittest.cc
index c4fb3dc9..f45ebc6 100644
--- a/net/spdy/chromium/spdy_session_unittest.cc
+++ b/net/spdy/chromium/spdy_session_unittest.cc
@@ -238,9 +238,9 @@
 // given SpdyStreamRequest.
 class StreamRequestDestroyingCallback : public TestCompletionCallbackBase {
  public:
-  StreamRequestDestroyingCallback() {}
+  StreamRequestDestroyingCallback() = default;
 
-  ~StreamRequestDestroyingCallback() override {}
+  ~StreamRequestDestroyingCallback() override = default;
 
   void SetRequestToDestroy(std::unique_ptr<SpdyStreamRequest> request) {
     request_ = std::move(request);
@@ -2481,7 +2481,7 @@
       : StreamDelegateDoNothing(stream),
         session_to_close_(session_to_close) {}
 
-  ~SessionClosingDelegate() override {}
+  ~SessionClosingDelegate() override = default;
 
   void OnClose(int status) override {
     session_to_close_->CloseSessionOnError(ERR_SPDY_PROTOCOL_ERROR, "Error");
@@ -3545,7 +3545,7 @@
       : StreamDelegateDoNothing(stream),
         session_(session) {}
 
-  ~StreamCreatingDelegate() override {}
+  ~StreamCreatingDelegate() override = default;
 
   void OnClose(int status) override {
     GURL url(kDefaultUrl);
@@ -4046,7 +4046,7 @@
                            SpdyStringPiece data)
       : StreamDelegateSendImmediate(stream, data) {}
 
-  ~DropReceivedDataDelegate() override {}
+  ~DropReceivedDataDelegate() override = default;
 
   // Drop any received data.
   void OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) override {}
@@ -4656,7 +4656,7 @@
                         SpdyStringPiece data)
       : StreamDelegateWithBody(stream, data) {}
 
-  ~StreamClosingDelegate() override {}
+  ~StreamClosingDelegate() override = default;
 
   void set_stream_to_close(const base::WeakPtr<SpdyStream>& stream_to_close) {
     stream_to_close_ = stream_to_close;
diff --git a/net/spdy/chromium/spdy_stream.cc b/net/spdy/chromium/spdy_stream.cc
index 685bc838..decec4b2 100644
--- a/net/spdy/chromium/spdy_stream.cc
+++ b/net/spdy/chromium/spdy_stream.cc
@@ -65,7 +65,7 @@
     DCHECK(stream_.get());
   }
 
-  ~HeadersBufferProducer() override {}
+  ~HeadersBufferProducer() override = default;
 
   std::unique_ptr<SpdyBuffer> ProduceBuffer() override {
     if (!stream_.get()) {
diff --git a/net/spdy/chromium/spdy_stream_test_util.cc b/net/spdy/chromium/spdy_stream_test_util.cc
index 00b9ab9..b891cd2 100644
--- a/net/spdy/chromium/spdy_stream_test_util.cc
+++ b/net/spdy/chromium/spdy_stream_test_util.cc
@@ -21,7 +21,7 @@
   DCHECK(stream_);
 }
 
-ClosingDelegate::~ClosingDelegate() {}
+ClosingDelegate::~ClosingDelegate() = default;
 
 void ClosingDelegate::OnHeadersSent() {}
 
@@ -51,8 +51,7 @@
       send_headers_completed_(false) {
 }
 
-StreamDelegateBase::~StreamDelegateBase() {
-}
+StreamDelegateBase::~StreamDelegateBase() = default;
 
 void StreamDelegateBase::OnHeadersSent() {
   stream_id_ = stream_->stream_id();
@@ -114,16 +113,14 @@
     const base::WeakPtr<SpdyStream>& stream)
     : StreamDelegateBase(stream) {}
 
-StreamDelegateDoNothing::~StreamDelegateDoNothing() {
-}
+StreamDelegateDoNothing::~StreamDelegateDoNothing() = default;
 
 StreamDelegateSendImmediate::StreamDelegateSendImmediate(
     const base::WeakPtr<SpdyStream>& stream,
     SpdyStringPiece data)
     : StreamDelegateBase(stream), data_(data) {}
 
-StreamDelegateSendImmediate::~StreamDelegateSendImmediate() {
-}
+StreamDelegateSendImmediate::~StreamDelegateSendImmediate() = default;
 
 void StreamDelegateSendImmediate::OnHeadersReceived(
     const SpdyHeaderBlock& response_headers) {
@@ -139,8 +136,7 @@
     SpdyStringPiece data)
     : StreamDelegateBase(stream), buf_(new StringIOBuffer(data.as_string())) {}
 
-StreamDelegateWithBody::~StreamDelegateWithBody() {
-}
+StreamDelegateWithBody::~StreamDelegateWithBody() = default;
 
 void StreamDelegateWithBody::OnHeadersSent() {
   StreamDelegateBase::OnHeadersSent();
@@ -152,8 +148,7 @@
     : StreamDelegateBase(stream) {
 }
 
-StreamDelegateCloseOnHeaders::~StreamDelegateCloseOnHeaders() {
-}
+StreamDelegateCloseOnHeaders::~StreamDelegateCloseOnHeaders() = default;
 
 void StreamDelegateCloseOnHeaders::OnHeadersReceived(
     const SpdyHeaderBlock& response_headers) {
diff --git a/net/spdy/chromium/spdy_stream_unittest.cc b/net/spdy/chromium/spdy_stream_unittest.cc
index 911a183..8f546f81 100644
--- a/net/spdy/chromium/spdy_stream_unittest.cc
+++ b/net/spdy/chromium/spdy_stream_unittest.cc
@@ -69,7 +69,7 @@
         offset_(0),
         ssl_(SYNCHRONOUS, OK) {}
 
-  ~SpdyStreamTest() override {}
+  ~SpdyStreamTest() override = default;
 
   base::WeakPtr<SpdySession> CreateDefaultSpdySession() {
     SpdySessionKey key(HostPortPair::FromURL(url_), ProxyServer::Direct(),
@@ -208,7 +208,7 @@
                              SpdyStringPiece data)
       : StreamDelegateWithBody(stream, data) {}
 
-  ~StreamDelegateWithTrailers() override {}
+  ~StreamDelegateWithTrailers() override = default;
 
   void OnTrailers(const SpdyHeaderBlock& trailers) override {
     trailers_ = trailers.Clone();
diff --git a/net/spdy/chromium/spdy_test_util_common.cc b/net/spdy/chromium/spdy_test_util_common.cc
index 3a0dc7a..0c49f71 100644
--- a/net/spdy/chromium/spdy_test_util_common.cc
+++ b/net/spdy/chromium/spdy_test_util_common.cc
@@ -156,7 +156,7 @@
 class PriorityGetter : public BufferedSpdyFramerVisitorInterface {
  public:
   PriorityGetter() : priority_(0) {}
-  ~PriorityGetter() override {}
+  ~PriorityGetter() override = default;
 
   SpdyPriority priority() const {
     return priority_;
@@ -238,9 +238,9 @@
       (rv == OK) ? stream_request.ReleaseStream() : base::WeakPtr<SpdyStream>();
 }
 
-StreamReleaserCallback::StreamReleaserCallback() {}
+StreamReleaserCallback::StreamReleaserCallback() = default;
 
-StreamReleaserCallback::~StreamReleaserCallback() {}
+StreamReleaserCallback::~StreamReleaserCallback() = default;
 
 CompletionCallback StreamReleaserCallback::MakeCallback(
     SpdyStreamRequest* request) {
@@ -335,7 +335,7 @@
   http2_settings[SETTINGS_INITIAL_WINDOW_SIZE] = kDefaultInitialWindowSize;
 }
 
-SpdySessionDependencies::~SpdySessionDependencies() {}
+SpdySessionDependencies::~SpdySessionDependencies() = default;
 
 // static
 std::unique_ptr<HttpNetworkSession> SpdySessionDependencies::SpdyCreateSession(
@@ -405,7 +405,7 @@
 
 class AllowAnyCertCTPolicyEnforcer : public CTPolicyEnforcer {
  public:
-  AllowAnyCertCTPolicyEnforcer() {}
+  AllowAnyCertCTPolicyEnforcer() = default;
   ~AllowAnyCertCTPolicyEnforcer() override = default;
 
   ct::CTPolicyCompliance CheckCompliance(
@@ -529,7 +529,7 @@
   explicit FakeSpdySessionClientSocket(int read_result)
       : MockClientSocket(NetLogWithSource()), read_result_(read_result) {}
 
-  ~FakeSpdySessionClientSocket() override {}
+  ~FakeSpdySessionClientSocket() override = default;
 
   int Read(IOBuffer* buf,
            int buf_len,
@@ -627,7 +627,7 @@
       response_spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
       default_url_(GURL(kDefaultUrl)) {}
 
-SpdyTestUtil::~SpdyTestUtil() {}
+SpdyTestUtil::~SpdyTestUtil() = default;
 
 void SpdyTestUtil::AddUrlToHeaderBlock(SpdyStringPiece url,
                                        SpdyHeaderBlock* headers) const {
diff --git a/net/spdy/chromium/spdy_write_queue.cc b/net/spdy/chromium/spdy_write_queue.cc
index 246c4d9..2d874e4 100644
--- a/net/spdy/chromium/spdy_write_queue.cc
+++ b/net/spdy/chromium/spdy_write_queue.cc
@@ -17,7 +17,7 @@
 
 namespace net {
 
-SpdyWriteQueue::PendingWrite::PendingWrite() {}
+SpdyWriteQueue::PendingWrite::PendingWrite() = default;
 
 SpdyWriteQueue::PendingWrite::PendingWrite(
     SpdyFrameType frame_type,
@@ -28,7 +28,7 @@
       stream(stream),
       has_stream(stream.get() != nullptr) {}
 
-SpdyWriteQueue::PendingWrite::~PendingWrite() {}
+SpdyWriteQueue::PendingWrite::~PendingWrite() = default;
 
 SpdyWriteQueue::PendingWrite::PendingWrite(PendingWrite&& other) = default;
 SpdyWriteQueue::PendingWrite& SpdyWriteQueue::PendingWrite::operator=(
diff --git a/net/spdy/core/fuzzing/hpack_fuzz_util.cc b/net/spdy/core/fuzzing/hpack_fuzz_util.cc
index 3d7ec4e..35604013 100644
--- a/net/spdy/core/fuzzing/hpack_fuzz_util.cc
+++ b/net/spdy/core/fuzzing/hpack_fuzz_util.cc
@@ -35,14 +35,14 @@
 using base::RandBytesAsString;
 using std::map;
 
-HpackFuzzUtil::GeneratorContext::GeneratorContext() {}
-HpackFuzzUtil::GeneratorContext::~GeneratorContext() {}
+HpackFuzzUtil::GeneratorContext::GeneratorContext() = default;
+HpackFuzzUtil::GeneratorContext::~GeneratorContext() = default;
 
 HpackFuzzUtil::Input::Input() : offset(0) {}
-HpackFuzzUtil::Input::~Input() {}
+HpackFuzzUtil::Input::~Input() = default;
 
-HpackFuzzUtil::FuzzerContext::FuzzerContext() {}
-HpackFuzzUtil::FuzzerContext::~FuzzerContext() {}
+HpackFuzzUtil::FuzzerContext::FuzzerContext() = default;
+HpackFuzzUtil::FuzzerContext::~FuzzerContext() = default;
 
 // static
 void HpackFuzzUtil::InitializeGeneratorContext(GeneratorContext* context) {
diff --git a/net/spdy/core/hpack/hpack_decoder_adapter.cc b/net/spdy/core/hpack/hpack_decoder_adapter.cc
index c3197005..4a11a32b 100644
--- a/net/spdy/core/hpack/hpack_decoder_adapter.cc
+++ b/net/spdy/core/hpack/hpack_decoder_adapter.cc
@@ -21,7 +21,7 @@
       max_decode_buffer_size_bytes_(kMaxDecodeBufferSizeBytes),
       header_block_started_(false) {}
 
-HpackDecoderAdapter::~HpackDecoderAdapter() {}
+HpackDecoderAdapter::~HpackDecoderAdapter() = default;
 
 void HpackDecoderAdapter::ApplyHeaderTableSizeSetting(size_t size_setting) {
   DVLOG(2) << "HpackDecoderAdapter::ApplyHeaderTableSizeSetting";
@@ -111,7 +111,7 @@
 }
 
 HpackDecoderAdapter::ListenerAdapter::ListenerAdapter() : handler_(nullptr) {}
-HpackDecoderAdapter::ListenerAdapter::~ListenerAdapter() {}
+HpackDecoderAdapter::ListenerAdapter::~ListenerAdapter() = default;
 
 void HpackDecoderAdapter::ListenerAdapter::set_handler(
     SpdyHeadersHandlerInterface* handler) {
diff --git a/net/spdy/core/hpack/hpack_encoder.cc b/net/spdy/core/hpack/hpack_encoder.cc
index 1ae47c6..824b0a3e 100644
--- a/net/spdy/core/hpack/hpack_encoder.cc
+++ b/net/spdy/core/hpack/hpack_encoder.cc
@@ -83,7 +83,7 @@
       enable_compression_(true),
       should_emit_table_size_(false) {}
 
-HpackEncoder::~HpackEncoder() {}
+HpackEncoder::~HpackEncoder() = default;
 
 void HpackEncoder::EncodeHeaderSet(const Representations& representations,
                                    SpdyString* output) {
diff --git a/net/spdy/core/hpack/hpack_entry.cc b/net/spdy/core/hpack/hpack_entry.cc
index 86d120bb..a948877 100644
--- a/net/spdy/core/hpack/hpack_entry.cc
+++ b/net/spdy/core/hpack/hpack_entry.cc
@@ -63,7 +63,7 @@
   return *this;
 }
 
-HpackEntry::~HpackEntry() {}
+HpackEntry::~HpackEntry() = default;
 
 // static
 size_t HpackEntry::Size(SpdyStringPiece name, SpdyStringPiece value) {
diff --git a/net/spdy/core/hpack/hpack_header_table.cc b/net/spdy/core/hpack/hpack_header_table.cc
index dc29b5c..e59a5db 100644
--- a/net/spdy/core/hpack/hpack_header_table.cc
+++ b/net/spdy/core/hpack/hpack_header_table.cc
@@ -39,7 +39,7 @@
       max_size_(kDefaultHeaderTableSizeSetting),
       total_insertions_(static_entries_.size()) {}
 
-HpackHeaderTable::~HpackHeaderTable() {}
+HpackHeaderTable::~HpackHeaderTable() = default;
 
 const HpackEntry* HpackHeaderTable::GetByIndex(size_t index) {
   if (index == 0) {
diff --git a/net/spdy/core/hpack/hpack_huffman_table.cc b/net/spdy/core/hpack/hpack_huffman_table.cc
index dbfa8e5..842acf2d 100644
--- a/net/spdy/core/hpack/hpack_huffman_table.cc
+++ b/net/spdy/core/hpack/hpack_huffman_table.cc
@@ -32,7 +32,7 @@
 
 HpackHuffmanTable::HpackHuffmanTable() : pad_bits_(0), failed_symbol_id_(0) {}
 
-HpackHuffmanTable::~HpackHuffmanTable() {}
+HpackHuffmanTable::~HpackHuffmanTable() = default;
 
 bool HpackHuffmanTable::Initialize(const HpackHuffmanSymbol* input_symbols,
                                    size_t symbol_count) {
diff --git a/net/spdy/core/hpack/hpack_output_stream.cc b/net/spdy/core/hpack/hpack_output_stream.cc
index 6f01f64..49b2d60 100644
--- a/net/spdy/core/hpack/hpack_output_stream.cc
+++ b/net/spdy/core/hpack/hpack_output_stream.cc
@@ -13,7 +13,7 @@
 
 HpackOutputStream::HpackOutputStream() : bit_offset_(0) {}
 
-HpackOutputStream::~HpackOutputStream() {}
+HpackOutputStream::~HpackOutputStream() = default;
 
 void HpackOutputStream::AppendBits(uint8_t bits, size_t bit_size) {
   DCHECK_GT(bit_size, 0u);
diff --git a/net/spdy/core/hpack/hpack_static_table.cc b/net/spdy/core/hpack/hpack_static_table.cc
index 2eb1720..0b3c0dc 100644
--- a/net/spdy/core/hpack/hpack_static_table.cc
+++ b/net/spdy/core/hpack/hpack_static_table.cc
@@ -12,9 +12,9 @@
 
 namespace net {
 
-HpackStaticTable::HpackStaticTable() {}
+HpackStaticTable::HpackStaticTable() = default;
 
-HpackStaticTable::~HpackStaticTable() {}
+HpackStaticTable::~HpackStaticTable() = default;
 
 void HpackStaticTable::Initialize(const HpackStaticEntry* static_entry_table,
                                   size_t static_entry_count) {
diff --git a/net/spdy/core/http2_frame_decoder_adapter.cc b/net/spdy/core/http2_frame_decoder_adapter.cc
index ad71405..fcdf468 100644
--- a/net/spdy/core/http2_frame_decoder_adapter.cc
+++ b/net/spdy/core/http2_frame_decoder_adapter.cc
@@ -165,7 +165,7 @@
   ResetInternal();
 }
 
-Http2DecoderAdapter::~Http2DecoderAdapter() {}
+Http2DecoderAdapter::~Http2DecoderAdapter() = default;
 
 void Http2DecoderAdapter::set_visitor(SpdyFramerVisitorInterface* visitor) {
   visitor_ = visitor;
diff --git a/net/spdy/core/mock_spdy_framer_visitor.cc b/net/spdy/core/mock_spdy_framer_visitor.cc
index 437dc57..f9f3f407 100644
--- a/net/spdy/core/mock_spdy_framer_visitor.cc
+++ b/net/spdy/core/mock_spdy_framer_visitor.cc
@@ -12,7 +12,7 @@
   DelegateHeaderHandling();
 }
 
-MockSpdyFramerVisitor::~MockSpdyFramerVisitor() {}
+MockSpdyFramerVisitor::~MockSpdyFramerVisitor() = default;
 
 }  // namespace test
 
diff --git a/net/spdy/core/spdy_alt_svc_wire_format.cc b/net/spdy/core/spdy_alt_svc_wire_format.cc
index a0a3783..df8b7d8 100644
--- a/net/spdy/core/spdy_alt_svc_wire_format.cc
+++ b/net/spdy/core/spdy_alt_svc_wire_format.cc
@@ -35,7 +35,7 @@
 
 }  // namespace
 
-SpdyAltSvcWireFormat::AlternativeService::AlternativeService() {}
+SpdyAltSvcWireFormat::AlternativeService::AlternativeService() = default;
 
 SpdyAltSvcWireFormat::AlternativeService::AlternativeService(
     const SpdyString& protocol_id,
@@ -49,7 +49,7 @@
       max_age(max_age),
       version(version) {}
 
-SpdyAltSvcWireFormat::AlternativeService::~AlternativeService() {}
+SpdyAltSvcWireFormat::AlternativeService::~AlternativeService() = default;
 
 SpdyAltSvcWireFormat::AlternativeService::AlternativeService(
     const AlternativeService& other) = default;
diff --git a/net/spdy/core/spdy_deframer_visitor.cc b/net/spdy/core/spdy_deframer_visitor.cc
index 20489d7..eadc070 100644
--- a/net/spdy/core/spdy_deframer_visitor.cc
+++ b/net/spdy/core/spdy_deframer_visitor.cc
@@ -126,7 +126,7 @@
       : listener_(std::move(listener)) {
     CHECK(listener_);
   }
-  ~SpdyTestDeframerImpl() override {}
+  ~SpdyTestDeframerImpl() override = default;
 
   bool AtFrameEnd() override;
 
@@ -765,7 +765,7 @@
       wrapped_ = SpdyMakeUnique<SpdyDeframerVisitorInterface>();
     }
   }
-  ~LoggingSpdyDeframerDelegate() override {}
+  ~LoggingSpdyDeframerDelegate() override = default;
 
   void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame) override {
     DVLOG(1) << "LoggingSpdyDeframerDelegate::OnAltSvc";
@@ -857,7 +857,7 @@
       std::move(wrapped_listener));
 }
 
-CollectedFrame::CollectedFrame() {}
+CollectedFrame::CollectedFrame() = default;
 
 CollectedFrame::CollectedFrame(CollectedFrame&& other)
     : frame_ir(std::move(other.frame_ir)),
@@ -865,7 +865,7 @@
       settings(std::move(other.settings)),
       error_reported(other.error_reported) {}
 
-CollectedFrame::~CollectedFrame() {}
+CollectedFrame::~CollectedFrame() = default;
 
 CollectedFrame& CollectedFrame::operator=(CollectedFrame&& other) {
   frame_ir = std::move(other.frame_ir);
diff --git a/net/spdy/core/spdy_frame_builder.cc b/net/spdy/core/spdy_frame_builder.cc
index 23eff89..2e865e40 100644
--- a/net/spdy/core/spdy_frame_builder.cc
+++ b/net/spdy/core/spdy_frame_builder.cc
@@ -26,7 +26,7 @@
       length_(0),
       offset_(0) {}
 
-SpdyFrameBuilder::~SpdyFrameBuilder() {}
+SpdyFrameBuilder::~SpdyFrameBuilder() = default;
 
 char* SpdyFrameBuilder::GetWritableBuffer(size_t length) {
   if (!CanWrite(length)) {
diff --git a/net/spdy/core/spdy_framer.cc b/net/spdy/core/spdy_framer.cc
index 4e90c05..581c2229 100644
--- a/net/spdy/core/spdy_framer.cc
+++ b/net/spdy/core/spdy_framer.cc
@@ -290,7 +290,7 @@
                 "Our send limit should be at most our receive limit.");
 }
 
-SpdyFramer::~SpdyFramer() {}
+SpdyFramer::~SpdyFramer() = default;
 
 void SpdyFramer::set_debug_visitor(
     SpdyFramerDebugVisitorInterface* debug_visitor) {
@@ -300,7 +300,7 @@
 SpdyFramer::SpdyFrameIterator::SpdyFrameIterator(SpdyFramer* framer)
     : framer_(framer), is_first_frame_(true), has_next_frame_(true) {}
 
-SpdyFramer::SpdyFrameIterator::~SpdyFrameIterator() {}
+SpdyFramer::SpdyFrameIterator::~SpdyFrameIterator() = default;
 
 size_t SpdyFramer::SpdyFrameIterator::NextFrame(ZeroCopyOutputBuffer* output) {
   const SpdyFrameIR& frame_ir = GetIR();
@@ -352,7 +352,7 @@
   SetEncoder(headers_ir_.get());
 }
 
-SpdyFramer::SpdyHeaderFrameIterator::~SpdyHeaderFrameIterator() {}
+SpdyFramer::SpdyHeaderFrameIterator::~SpdyHeaderFrameIterator() = default;
 
 const SpdyFrameIR& SpdyFramer::SpdyHeaderFrameIterator::GetIR() const {
   return *(headers_ir_.get());
@@ -376,7 +376,8 @@
   SetEncoder(push_promise_ir_.get());
 }
 
-SpdyFramer::SpdyPushPromiseFrameIterator::~SpdyPushPromiseFrameIterator() {}
+SpdyFramer::SpdyPushPromiseFrameIterator::~SpdyPushPromiseFrameIterator() =
+    default;
 
 const SpdyFrameIR& SpdyFramer::SpdyPushPromiseFrameIterator::GetIR() const {
   return *(push_promise_ir_.get());
@@ -398,7 +399,7 @@
     std::unique_ptr<const SpdyFrameIR> frame_ir)
     : framer_(framer), frame_ir_(std::move(frame_ir)) {}
 
-SpdyFramer::SpdyControlFrameIterator::~SpdyControlFrameIterator() {}
+SpdyFramer::SpdyControlFrameIterator::~SpdyControlFrameIterator() = default;
 
 size_t SpdyFramer::SpdyControlFrameIterator::NextFrame(
     ZeroCopyOutputBuffer* output) {
@@ -790,7 +791,7 @@
  public:
   explicit FrameSerializationVisitor(SpdyFramer* framer)
       : framer_(framer), frame_() {}
-  ~FrameSerializationVisitor() override {}
+  ~FrameSerializationVisitor() override = default;
 
   SpdySerializedFrame ReleaseSerializedFrame() { return std::move(frame_); }
 
@@ -1214,7 +1215,7 @@
   explicit FrameSerializationVisitorWithOutput(SpdyFramer* framer,
                                                ZeroCopyOutputBuffer* output)
       : framer_(framer), output_(output), result_(false) {}
-  ~FrameSerializationVisitorWithOutput() override {}
+  ~FrameSerializationVisitorWithOutput() override = default;
 
   size_t Result() { return result_; }
 
diff --git a/net/spdy/core/spdy_header_block.cc b/net/spdy/core/spdy_header_block.cc
index 9fda908..b9468f58 100644
--- a/net/spdy/core/spdy_header_block.cc
+++ b/net/spdy/core/spdy_header_block.cc
@@ -121,7 +121,7 @@
   return *this;
 }
 
-SpdyHeaderBlock::HeaderValue::~HeaderValue() {}
+SpdyHeaderBlock::HeaderValue::~HeaderValue() = default;
 
 SpdyStringPiece SpdyHeaderBlock::HeaderValue::ConsolidatedValue() const {
   if (fragments_.empty()) {
@@ -146,9 +146,9 @@
 
 SpdyHeaderBlock::iterator::iterator(MapType::const_iterator it) : it_(it) {}
 
-SpdyHeaderBlock::iterator::iterator(const iterator& other) : it_(other.it_) {}
+SpdyHeaderBlock::iterator::iterator(const iterator& other) = default;
 
-SpdyHeaderBlock::iterator::~iterator() {}
+SpdyHeaderBlock::iterator::~iterator() = default;
 
 SpdyHeaderBlock::ValueProxy::ValueProxy(
     SpdyHeaderBlock::MapType* block,
@@ -220,7 +220,7 @@
 
 SpdyHeaderBlock::SpdyHeaderBlock(SpdyHeaderBlock&& other) = default;
 
-SpdyHeaderBlock::~SpdyHeaderBlock() {}
+SpdyHeaderBlock::~SpdyHeaderBlock() = default;
 
 SpdyHeaderBlock& SpdyHeaderBlock::operator=(SpdyHeaderBlock&& other) {
   block_.swap(other.block_);
diff --git a/net/spdy/core/spdy_no_op_visitor.cc b/net/spdy/core/spdy_no_op_visitor.cc
index b82e547a..a196069c 100644
--- a/net/spdy/core/spdy_no_op_visitor.cc
+++ b/net/spdy/core/spdy_no_op_visitor.cc
@@ -13,7 +13,7 @@
   static_assert(std::is_abstract<SpdyNoOpVisitor>::value == false,
                 "Need to update SpdyNoOpVisitor.");
 }
-SpdyNoOpVisitor::~SpdyNoOpVisitor() {}
+SpdyNoOpVisitor::~SpdyNoOpVisitor() = default;
 
 SpdyHeadersHandlerInterface* SpdyNoOpVisitor::OnHeaderFrameStart(
     SpdyStreamId stream_id) {
diff --git a/net/spdy/core/spdy_pinnable_buffer_piece.cc b/net/spdy/core/spdy_pinnable_buffer_piece.cc
index a277060..588a17b 100644
--- a/net/spdy/core/spdy_pinnable_buffer_piece.cc
+++ b/net/spdy/core/spdy_pinnable_buffer_piece.cc
@@ -11,7 +11,7 @@
 SpdyPinnableBufferPiece::SpdyPinnableBufferPiece()
     : buffer_(nullptr), length_(0) {}
 
-SpdyPinnableBufferPiece::~SpdyPinnableBufferPiece() {}
+SpdyPinnableBufferPiece::~SpdyPinnableBufferPiece() = default;
 
 void SpdyPinnableBufferPiece::Pin() {
   if (!storage_ && buffer_ != nullptr && length_ != 0) {
diff --git a/net/spdy/core/spdy_protocol.cc b/net/spdy/core/spdy_protocol.cc
index e13761b5..6beb2fa 100644
--- a/net/spdy/core/spdy_protocol.cc
+++ b/net/spdy/core/spdy_protocol.cc
@@ -232,7 +232,7 @@
     SpdyHeaderBlock header_block)
     : SpdyFrameWithFinIR(stream_id), header_block_(std::move(header_block)) {}
 
-SpdyFrameWithHeaderBlockIR::~SpdyFrameWithHeaderBlockIR() {}
+SpdyFrameWithHeaderBlockIR::~SpdyFrameWithHeaderBlockIR() = default;
 
 SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, SpdyStringPiece data)
     : SpdyFrameWithFinIR(stream_id),
@@ -261,7 +261,7 @@
       padded_(false),
       padding_payload_len_(0) {}
 
-SpdyDataIR::~SpdyDataIR() {}
+SpdyDataIR::~SpdyDataIR() = default;
 
 void SpdyDataIR::Visit(SpdyFrameVisitor* visitor) const {
   return visitor->VisitData(*this);
@@ -281,7 +281,7 @@
   set_error_code(error_code);
 }
 
-SpdyRstStreamIR::~SpdyRstStreamIR() {}
+SpdyRstStreamIR::~SpdyRstStreamIR() = default;
 
 void SpdyRstStreamIR::Visit(SpdyFrameVisitor* visitor) const {
   return visitor->VisitRstStream(*this);
@@ -293,7 +293,7 @@
 
 SpdySettingsIR::SpdySettingsIR() : is_ack_(false) {}
 
-SpdySettingsIR::~SpdySettingsIR() {}
+SpdySettingsIR::~SpdySettingsIR() = default;
 
 void SpdySettingsIR::Visit(SpdyFrameVisitor* visitor) const {
   return visitor->VisitSettings(*this);
@@ -335,7 +335,7 @@
   set_error_code(error_code);
 }
 
-SpdyGoAwayIR::~SpdyGoAwayIR() {}
+SpdyGoAwayIR::~SpdyGoAwayIR() = default;
 
 void SpdyGoAwayIR::Visit(SpdyFrameVisitor* visitor) const {
   return visitor->VisitGoAway(*this);
@@ -350,7 +350,7 @@
   encoding_ = SpdyMakeUnique<SpdyString>();
 }
 
-SpdyContinuationIR::~SpdyContinuationIR() {}
+SpdyContinuationIR::~SpdyContinuationIR() = default;
 
 void SpdyContinuationIR::Visit(SpdyFrameVisitor* visitor) const {
   return visitor->VisitContinuation(*this);
@@ -386,7 +386,7 @@
 
 SpdyAltSvcIR::SpdyAltSvcIR(SpdyStreamId stream_id) : SpdyFrameIR(stream_id) {}
 
-SpdyAltSvcIR::~SpdyAltSvcIR() {}
+SpdyAltSvcIR::~SpdyAltSvcIR() = default;
 
 void SpdyAltSvcIR::Visit(SpdyFrameVisitor* visitor) const {
   return visitor->VisitAltSvc(*this);
diff --git a/net/spdy/core/spdy_test_utils.cc b/net/spdy/core/spdy_test_utils.cc
index 1717236..e8e29f7 100644
--- a/net/spdy/core/spdy_test_utils.cc
+++ b/net/spdy/core/spdy_test_utils.cc
@@ -149,9 +149,9 @@
   compressed_header_bytes_parsed_ = compressed_header_bytes_parsed;
 }
 
-TestServerPushDelegate::TestServerPushDelegate() {}
+TestServerPushDelegate::TestServerPushDelegate() = default;
 
-TestServerPushDelegate::~TestServerPushDelegate() {}
+TestServerPushDelegate::~TestServerPushDelegate() = default;
 
 void TestServerPushDelegate::OnPush(
     std::unique_ptr<ServerPushHelper> push_helper,
diff --git a/sandbox/linux/syscall_broker/broker_process_unittest.cc b/sandbox/linux/syscall_broker/broker_process_unittest.cc
index df49a50..ff7ff2b 100644
--- a/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -33,7 +33,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace sandbox {
-
 namespace syscall_broker {
 
 class BrokerProcessTestHelper {
@@ -48,6 +47,8 @@
 
 namespace {
 
+const int kFakeErrnoSentinel = 99999;
+
 bool NoOpCallback() {
   return true;
 }
@@ -55,22 +56,20 @@
 }  // namespace
 
 TEST(BrokerProcess, CreateAndDestroy) {
-  std::vector<BrokerFilePermission> permissions;
-  permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
-
-  std::unique_ptr<BrokerProcess> open_broker(
-      new BrokerProcess(EPERM, permissions));
-  ASSERT_TRUE(open_broker->Init(base::BindRepeating(&NoOpCallback)));
-
-  ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+  {
+    std::vector<BrokerFilePermission> permissions;
+    permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions);
+    ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+    ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+  }
   // Destroy the broker and check it has exited properly.
-  open_broker.reset();
   ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
 }
 
 TEST(BrokerProcess, TestOpenAccessNull) {
   std::vector<BrokerFilePermission> empty;
-  BrokerProcess open_broker(EPERM, empty);
+  BrokerProcess open_broker(kFakeErrnoSentinel, empty);
   ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
   int fd = open_broker.Open(NULL, O_RDONLY);
@@ -250,43 +249,43 @@
   const char kTrailingSlash[] = "/proc/";
 
   std::vector<BrokerFilePermission> permissions;
-
   permissions.push_back(BrokerFilePermission::ReadOnlyRecursive("/proc/"));
-  std::unique_ptr<BrokerProcess> open_broker(
-      new BrokerProcess(EPERM, permissions, fast_check_in_client));
-  ASSERT_TRUE(open_broker->Init(base::BindRepeating(&NoOpCallback)));
+  BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                            fast_check_in_client);
+  ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+
   // Open cpuinfo via the broker.
-  int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+  int cpuinfo_fd = open_broker.Open(kFileCpuInfo, O_RDONLY);
   base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
   ASSERT_GE(cpuinfo_fd, 0);
 
   int fd = -1;
   int can_access;
 
-  can_access = open_broker->Access(kNotAbsPath, R_OK);
-  ASSERT_EQ(can_access, -EPERM);
-  fd = open_broker->Open(kNotAbsPath, O_RDONLY);
-  ASSERT_EQ(fd, -EPERM);
+  can_access = open_broker.Access(kNotAbsPath, R_OK);
+  ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+  fd = open_broker.Open(kNotAbsPath, O_RDONLY);
+  ASSERT_EQ(fd, -kFakeErrnoSentinel);
 
-  can_access = open_broker->Access(kDotDotStart, R_OK);
-  ASSERT_EQ(can_access, -EPERM);
-  fd = open_broker->Open(kDotDotStart, O_RDONLY);
-  ASSERT_EQ(fd, -EPERM);
+  can_access = open_broker.Access(kDotDotStart, R_OK);
+  ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+  fd = open_broker.Open(kDotDotStart, O_RDONLY);
+  ASSERT_EQ(fd, -kFakeErrnoSentinel);
 
-  can_access = open_broker->Access(kDotDotMiddle, R_OK);
-  ASSERT_EQ(can_access, -EPERM);
-  fd = open_broker->Open(kDotDotMiddle, O_RDONLY);
-  ASSERT_EQ(fd, -EPERM);
+  can_access = open_broker.Access(kDotDotMiddle, R_OK);
+  ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+  fd = open_broker.Open(kDotDotMiddle, O_RDONLY);
+  ASSERT_EQ(fd, -kFakeErrnoSentinel);
 
-  can_access = open_broker->Access(kDotDotEnd, R_OK);
-  ASSERT_EQ(can_access, -EPERM);
-  fd = open_broker->Open(kDotDotEnd, O_RDONLY);
-  ASSERT_EQ(fd, -EPERM);
+  can_access = open_broker.Access(kDotDotEnd, R_OK);
+  ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+  fd = open_broker.Open(kDotDotEnd, O_RDONLY);
+  ASSERT_EQ(fd, -kFakeErrnoSentinel);
 
-  can_access = open_broker->Access(kTrailingSlash, R_OK);
-  ASSERT_EQ(can_access, -EPERM);
-  fd = open_broker->Open(kTrailingSlash, O_RDONLY);
-  ASSERT_EQ(fd, -EPERM);
+  can_access = open_broker.Access(kTrailingSlash, R_OK);
+  ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+  fd = open_broker.Open(kTrailingSlash, O_RDONLY);
+  ASSERT_EQ(fd, -kFakeErrnoSentinel);
 }
 
 TEST(BrokerProcess, BadPathsClientCheck) {
@@ -305,54 +304,53 @@
   const char kFileCpuInfo[] = "/proc/cpuinfo";
   const char kDirProc[] = "/proc/";
 
-  std::vector<BrokerFilePermission> permissions;
-  if (recursive)
-    permissions.push_back(BrokerFilePermission::ReadOnlyRecursive(kDirProc));
-  else
-    permissions.push_back(BrokerFilePermission::ReadOnly(kFileCpuInfo));
+  {
+    std::vector<BrokerFilePermission> permissions;
+    permissions.push_back(
+        recursive ? BrokerFilePermission::ReadOnlyRecursive(kDirProc)
+                  : BrokerFilePermission::ReadOnly(kFileCpuInfo));
 
-  std::unique_ptr<BrokerProcess> open_broker(
-      new BrokerProcess(EPERM, permissions, fast_check_in_client));
-  ASSERT_TRUE(open_broker->Init(base::BindRepeating(&NoOpCallback)));
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
+    ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
-  int fd = -1;
-  fd = open_broker->Open(kFileCpuInfo, O_RDWR);
-  base::ScopedFD fd_closer(fd);
-  ASSERT_EQ(fd, -EPERM);
+    int fd = open_broker.Open(kFileCpuInfo, O_RDWR);
+    ASSERT_EQ(fd, -kFakeErrnoSentinel);
 
-  // Check we can read /proc/cpuinfo.
-  int can_access = open_broker->Access(kFileCpuInfo, R_OK);
-  ASSERT_EQ(can_access, 0);
-  can_access = open_broker->Access(kFileCpuInfo, W_OK);
-  ASSERT_EQ(can_access, -EPERM);
-  // Check we can not write /proc/cpuinfo.
+    // Check we can read /proc/cpuinfo.
+    int can_access = open_broker.Access(kFileCpuInfo, R_OK);
+    EXPECT_EQ(can_access, 0);
+    can_access = open_broker.Access(kFileCpuInfo, W_OK);
+    EXPECT_EQ(can_access, -kFakeErrnoSentinel);
+    // Check we can not write /proc/cpuinfo.
 
-  // Open cpuinfo via the broker.
-  int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
-  base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
-  ASSERT_GE(cpuinfo_fd, 0);
-  char buf[3];
-  memset(buf, 0, sizeof(buf));
-  int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
-  ASSERT_GT(read_len1, 0);
+    // Open cpuinfo via the broker.
+    int cpuinfo_fd = open_broker.Open(kFileCpuInfo, O_RDONLY);
+    base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+    EXPECT_GE(cpuinfo_fd, 0);
+    char buf[3];
+    memset(buf, 0, sizeof(buf));
+    int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
+    EXPECT_GT(read_len1, 0);
 
-  // Open cpuinfo directly.
-  int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
-  base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
-  ASSERT_GE(cpuinfo_fd2, 0);
-  char buf2[3];
-  memset(buf2, 1, sizeof(buf2));
-  int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
-  ASSERT_GT(read_len1, 0);
+    // Open cpuinfo directly.
+    int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
+    base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
+    EXPECT_GE(cpuinfo_fd2, 0);
+    char buf2[3];
+    memset(buf2, 1, sizeof(buf2));
+    int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
+    EXPECT_GT(read_len1, 0);
 
-  // The following is not guaranteed true, but will be in practice.
-  ASSERT_EQ(read_len1, read_len2);
-  // Compare the cpuinfo as returned by the broker with the one we opened
-  // ourselves.
-  ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
+    // The following is not guaranteed true, but will be in practice.
+    EXPECT_EQ(read_len1, read_len2);
+    // Compare the cpuinfo as returned by the broker with the one we opened
+    // ourselves.
+    EXPECT_EQ(memcmp(buf, buf2, read_len1), 0);
 
-  ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
-  open_broker.reset();
+    ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+  }
+
   ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
 }
 
@@ -389,7 +387,7 @@
   std::vector<BrokerFilePermission> permissions;
   permissions.push_back(BrokerFilePermission::ReadWrite(tempfile_name));
 
-  BrokerProcess open_broker(EPERM, permissions);
+  BrokerProcess open_broker(kFakeErrnoSentinel, permissions);
   ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
   // Check we can access that file with read or write.
@@ -423,7 +421,8 @@
   std::vector<BrokerFilePermission> permissions;
   permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
 
-  BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+  BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                            true /* fast_check_in_client */,
                             true /* quiet_failures_for_tests */);
   SANDBOX_ASSERT(open_broker.Init(base::BindRepeating(&NoOpCallback)));
   const pid_t broker_pid = open_broker.broker_pid();
@@ -448,7 +447,8 @@
   std::vector<BrokerFilePermission> permissions;
   permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
 
-  BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+  BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                            fast_check_in_client);
   ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
   // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
   int fd = -1;
@@ -536,8 +536,7 @@
   static const char kCpuInfo[] = "/proc/cpuinfo";
   std::vector<BrokerFilePermission> permissions;
   permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
-
-  BrokerProcess open_broker(EPERM, permissions);
+  BrokerProcess open_broker(kFakeErrnoSentinel, permissions);
   SANDBOX_ASSERT(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
   const int ipc_fd = BrokerProcessTestHelper::GetIPCDescriptor(&open_broker);
@@ -586,7 +585,8 @@
   int lifeline_fds[2];
   PCHECK(0 == pipe(lifeline_fds));
 
-  BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+  BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                            true /* fast_check_in_client */,
                             false /* quiet_failures_for_tests */);
   ASSERT_TRUE(open_broker.Init(base::BindRepeating(&CloseFD, lifeline_fds[0])));
   // Make sure the writing end only exists in the broker process.
@@ -622,15 +622,14 @@
 
   std::vector<BrokerFilePermission> permissions;
   permissions.push_back(BrokerFilePermission::ReadWriteCreate(tempfile_name));
-
-  BrokerProcess open_broker(EPERM, permissions);
+  BrokerProcess open_broker(kFakeErrnoSentinel, permissions);
   ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
   int fd = -1;
 
   // Try without O_EXCL
   fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT);
-  ASSERT_EQ(fd, -EPERM);
+  ASSERT_EQ(fd, -kFakeErrnoSentinel);
 
   const char kTestText[] = "TESTTESTTEST";
   // Create a file
@@ -673,26 +672,29 @@
   {
     // Nonexistent file with no permissions to see file.
     std::vector<BrokerFilePermission> permissions;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
-    EXPECT_EQ(-EPERM, open_broker.Stat(nonesuch_name, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(nonesuch_name, &sb));
   }
   {
     // Actual file with no permission to see file.
     std::vector<BrokerFilePermission> permissions;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
-    EXPECT_EQ(-EPERM, open_broker.Stat(tempfile_name, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(tempfile_name, &sb));
   }
   {
     // Nonexistent file with permissions to see file.
     std::vector<BrokerFilePermission> permissions;
     permissions.push_back(BrokerFilePermission::ReadOnly(nonesuch_name));
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
@@ -702,7 +704,8 @@
     // Actual file with permissions to see file.
     std::vector<BrokerFilePermission> permissions;
     permissions.push_back(BrokerFilePermission::ReadOnly(tempfile_name));
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
@@ -753,9 +756,11 @@
     permissions.push_back(BrokerFilePermission::ReadWrite(oldpath));
 
     bool fast_check_in_client = false;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
-    EXPECT_EQ(-EPERM, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Rename(oldpath.c_str(), newpath.c_str()));
 
     // ... and no files moved around.
     EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -767,9 +772,11 @@
     permissions.push_back(BrokerFilePermission::ReadWrite(newpath));
 
     bool fast_check_in_client = false;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
-    EXPECT_EQ(-EPERM, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Rename(oldpath.c_str(), newpath.c_str()));
 
     // ... and no files moved around.
     EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -782,9 +789,11 @@
     permissions.push_back(BrokerFilePermission::ReadWrite(newpath));
 
     bool fast_check_in_client = false;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
-    EXPECT_EQ(-EPERM, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Rename(oldpath.c_str(), newpath.c_str()));
 
     // ... and no files moved around.
     EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -797,9 +806,11 @@
     permissions.push_back(BrokerFilePermission::ReadOnly(newpath));
 
     bool fast_check_in_client = false;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
-    EXPECT_EQ(-EPERM, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Rename(oldpath.c_str(), newpath.c_str()));
 
     // ... and no files moved around.
     EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -812,7 +823,8 @@
     permissions.push_back(BrokerFilePermission::ReadWrite(newpath));
 
     bool fast_check_in_client = false;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
     EXPECT_EQ(0, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
 
@@ -846,22 +858,27 @@
   {
     // Nonexistent file with no permissions to see file.
     std::vector<BrokerFilePermission> permissions;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
-    EXPECT_EQ(-EPERM, open_broker.Readlink(nonesuch_name, buf, sizeof(buf)));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Readlink(nonesuch_name, buf, sizeof(buf)));
   }
   {
     // Actual file with no permissions to see file.
     std::vector<BrokerFilePermission> permissions;
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
-    EXPECT_EQ(-EPERM, open_broker.Readlink(newpath_name, buf, sizeof(buf)));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Readlink(newpath_name, buf, sizeof(buf)));
   }
   {
     // Nonexistent file with permissions to see file.
     std::vector<BrokerFilePermission> permissions;
     permissions.push_back(BrokerFilePermission::ReadOnly(nonesuch_name));
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
     EXPECT_EQ(-ENOENT, open_broker.Readlink(nonesuch_name, buf, sizeof(buf)));
   }
@@ -869,7 +886,8 @@
     // Actual file with permissions to see file.
     std::vector<BrokerFilePermission> permissions;
     permissions.push_back(BrokerFilePermission::ReadOnly(newpath_name));
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
     ssize_t retlen = open_broker.Readlink(newpath_name, buf, sizeof(buf));
     EXPECT_TRUE(retlen == static_cast<ssize_t>(strlen(oldpath_name)));
@@ -879,7 +897,8 @@
     // Actual file with permissions to see file, but too small a buffer.
     std::vector<BrokerFilePermission> permissions;
     permissions.push_back(BrokerFilePermission::ReadOnly(newpath_name));
-    BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+    BrokerProcess open_broker(kFakeErrnoSentinel, permissions,
+                              fast_check_in_client);
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
     EXPECT_EQ(-ENAMETOOLONG, open_broker.Readlink(newpath_name, buf, 4));
   }
diff --git a/services/service_manager/public/cpp/connector.cc b/services/service_manager/public/cpp/connector.cc
index 850b940..6f0b1bc7 100644
--- a/services/service_manager/public/cpp/connector.cc
+++ b/services/service_manager/public/cpp/connector.cc
@@ -95,6 +95,10 @@
   return std::make_unique<Connector>(connector.PassInterface());
 }
 
+bool Connector::IsBound() const {
+  return connector_.is_bound();
+}
+
 void Connector::FilterInterfaces(const std::string& spec,
                                  const Identity& source_identity,
                                  mojom::InterfaceProviderRequest request,
diff --git a/services/service_manager/public/cpp/connector.h b/services/service_manager/public/cpp/connector.h
index 88c34882..932409079 100644
--- a/services/service_manager/public/cpp/connector.h
+++ b/services/service_manager/public/cpp/connector.h
@@ -131,6 +131,9 @@
   // to pass again.
   std::unique_ptr<Connector> Clone();
 
+  // Returns true if this Connector instance is already bound to a thread.
+  bool IsBound() const;
+
   void FilterInterfaces(const std::string& spec,
                         const Identity& source_identity,
                         mojom::InterfaceProviderRequest request,
diff --git a/services/ui/service.cc b/services/ui/service.cc
index 246360a..574db166 100644
--- a/services/ui/service.cc
+++ b/services/ui/service.cc
@@ -243,8 +243,13 @@
   // Because GL libraries need to be initialized before entering the sandbox,
   // in MUS, |InitializeForUI| will load the GL libraries.
   ui::OzonePlatform::InitParams params;
-  params.connector = context()->connector();
-  params.single_process = true;
+  if (should_host_viz_) {
+    // If mus is hosting viz, then it needs to set up ozone so that it can
+    // connect to the gpu service through the connector.
+    params.connector = context()->connector();
+    // TODO(crbug.com/620927): This should be false once ozone-mojo is done.
+    params.single_process = true;
+  }
   ui::OzonePlatform::InitializeForUI(params);
 
   // Assume a client will change the layout to an appropriate configuration.
@@ -265,9 +270,6 @@
   input_device_controller_->AddInterface(&registry_);
 #endif
 
-// TODO(rjkroege): Enter sandbox here before we start threads in GpuState
-// http://crbug.com/584532
-
 #if !defined(OS_ANDROID)
   event_source_ = ui::PlatformEventSource::CreateDefault();
 #endif
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index 6bce066..f150633 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -492,6 +492,14 @@
 -RepostFormWarningTest.TestLoginAfterRepost
 -WebSocketBrowserTest.ReuseMainPageBasicAuthCredentialsForWebSocket
 
+# These rely on proxy configuration and PAC execution being configured on the
+# legacy in-process URLRequestContext. They should be removed or updated to
+# use Mojo APIs instead.
+-HangingPacRequestProxyScriptBrowserTest.Shutdown
+-IOThreadBrowserTestWithHangingPacRequest.Shutdown
+-IOThreadBrowserTestWithPacFileURL.FilePac
+-ProxySettingsApiTest.ProxyEventsParseError
+
 # http://crbug.com/783996
 # Add support for TLS client auth.
 -PrerenderBrowserTest.PrerenderSSLClientCertIframe
@@ -893,7 +901,3 @@
 # TODO(jam): switch the tests to hook in via URLLoaderFactory to check load flags.
 -NoStatePrefetchBrowserTest/NoStatePrefetchBrowserTest.IssuesIdlePriorityRequests/0
 -NoStatePrefetchBrowserTest/NoStatePrefetchBrowserTest.IssuesIdlePriorityRequests/1
--NoStatePrefetchBrowserTest/NoStatePrefetchBrowserTest.Prefetch301LoadFlags/0
--NoStatePrefetchBrowserTest/NoStatePrefetchBrowserTest.Prefetch301LoadFlags/1
--NoStatePrefetchBrowserTest/NoStatePrefetchBrowserTest.PrefetchLoadFlag/0
--NoStatePrefetchBrowserTest/NoStatePrefetchBrowserTest.PrefetchLoadFlag/1
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index 0556cad2..e799263 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -15,10 +15,6 @@
 # https://crbug.com/756312
 -ServiceWorkerVersionBrowserV8CacheTest.Restart
 
-# http://crbug.com/715630
-# This started failing when the test got added in r514724.
--DownloadContentTest.ForceDownloadMhtml
-
 # http://crbug.com/721398
 -ClearSiteDataThrottleBrowserTest.CookiesIntegrationTest
 -ClearSiteDataThrottleBrowserTest.Credentials
diff --git a/testing/buildbot/filters/mojo.fyi.viz.content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.viz.content_browsertests.filter
index 340d045..7b6707b 100644
--- a/testing/buildbot/filters/mojo.fyi.viz.content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.viz.content_browsertests.filter
@@ -28,6 +28,7 @@
 -PointerLockBrowserTest.*
 -SitePerProcessGestureBrowserTest.*
 -SitePerProcessHighDPIBrowserTest.*
+-SitePerProcessInternalsBrowserTest.*
 -SitePerProcessMouseWheelBrowserTest.*
 -TouchSelectionForCrossProcessFramesTests/TouchSelectionControllerClientAuraSiteIsolationTest.*
 
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index f784d89..e4b3aa9 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -6279,7 +6279,7 @@
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-node-scaled.js [ Failure ]
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-node-scroll.js [ Failure ]
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-node.js [ Failure ]
-crbug.com/591099 http/tests/devtools/elements/styles-1/color-aware-property-value-edit.html [ Failure ]
+crbug.com/591099 http/tests/devtools/elements/styles-1/color-aware-property-value-edit.js [ Failure ]
 crbug.com/591099 http/tests/devtools/elements/styles-2/add-import-rule.html [ Failure ]
 crbug.com/591099 http/tests/devtools/elements/styles-3/style-rule-from-imported-stylesheet.html [ Failure ]
 crbug.com/591099 http/tests/devtools/elements/styles-3/styles-add-new-rule-tab.js [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index 391f8baa..53ac85b 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -192,7 +192,7 @@
 # DevTools crbug.com/783982
 crbug.com/783982 http/tests/devtools/startup/console/console-uncaught-promise-no-inspector.html [ Failure ]
 crbug.com/783982 http/tests/devtools/elements/styles-2/property-ui-location.html [ Failure ]
-crbug.com/783982 http/tests/devtools/elements/styles-1/empty-background-url.html [ Failure ]
+crbug.com/783982 http/tests/devtools/elements/styles-1/empty-background-url.js [ Failure ]
 
 # http/tests/fetch
 crbug.com/778721 http/tests/fetch/serviceworker-proxied/thorough/auth-base-https-other-https.html [ Pass Failure Timeout ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls b/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls
index 7627acd..c546c81 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/root-layer-scrolls
@@ -1,6 +1,5 @@
 crbug.com/417782 compositing/iframes/become-composited-nested-iframes.html [ Failure ]
 crbug.com/417782 compositing/layer-creation/iframe-background-attachment-fixed.html [ Failure ]
-crbug.com/417782 compositing/squashing/dont-squash-into-videos.html [ Failure ]
 crbug.com/417782 compositing/squashing/squash-above-fixed-1.html [ Failure ]
 crbug.com/417782 compositing/squashing/squash-above-fixed-3.html [ Failure ]
 crbug.com/417782 compositing/visibility/visibility-image-layers-dynamic.html [ Failure ]
@@ -48,7 +47,6 @@
 crbug.com/417782 inspector-protocol/layers/paint-profiler.js [ Crash ]
 crbug.com/417782 inspector-protocol/page/get-layout-metrics.js [ Failure ]
 crbug.com/417782 paint/invalidation/window-resize/window-resize-vertical-writing-mode.html [ Crash ]
-crbug.com/417782 [ Mac Win ] paint/overflow/fixed-background-scroll-window.html [ Failure ]
 crbug.com/417782 plugins/webview-plugin-nested-iframe-scroll.html [ Failure ]
 crbug.com/417782 plugins/webview-plugin-scroll.html [ Failure ]
 crbug.com/417782 transforms/selection-bounds-in-transformed-view.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index ea4eee8..2ddd271 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -148,8 +148,8 @@
 crbug.com/451577 virtual/mojo-loading/http/tests/devtools/resource-tree/resource-tree-crafted-frame-add.js [ Slow ]
 crbug.com/451577 http/tests/devtools/resource-tree/resource-tree-frame-in-crafted-frame.js [ Slow ]
 crbug.com/451577 virtual/mojo-loading/http/tests/devtools/resource-tree/resource-tree-frame-in-crafted-frame.js [ Slow ]
-crbug.com/510337 http/tests/devtools/elements/styles-1/edit-value-url-with-color.html [ Slow ]
-crbug.com/667560 virtual/mojo-loading/http/tests/devtools/elements/styles-1/edit-value-url-with-color.html [ Slow ]
+crbug.com/510337 http/tests/devtools/elements/styles-1/edit-value-url-with-color.js [ Slow ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/elements/styles-1/edit-value-url-with-color.js [ Slow ]
 crbug.com/451577 [ Mac ] http/tests/devtools/extensions/extensions-reload.html [ Slow ]
 crbug.com/451577 [ Mac ] http/tests/devtools/extensions/extensions-resources.html [ Slow ]
 crbug.com/451577 [ Win10 ] http/tests/devtools/extensions/extensions-sidebar.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 0c68300..89fdb4a 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2185,12 +2185,12 @@
 crbug.com/626703 external/wpt/mediacapture-streams/MediaStreamTrack-end-manual.https.html [ Timeout Failure ]
 crbug.com/626703 external/wpt/media-source/mediasource-avtracks.html [ Failure Crash ]
 crbug.com/626703 external/wpt/media-source/mediasource-getvideoplaybackquality.html [ Timeout Failure ]
-crbug.com/626703 external/wpt/notifications/constructor-basic.html [ Timeout ]
-crbug.com/626703 external/wpt/notifications/constructor-invalid.html [ Timeout ]
-crbug.com/626703 external/wpt/notifications/event-onclose.html [ Timeout ]
-crbug.com/626703 external/wpt/notifications/event-onshow.html [ Timeout ]
-crbug.com/626703 external/wpt/notifications/instance.html [ Timeout ]
-crbug.com/626703 external/wpt/notifications/lang.html [ Timeout ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/notifications/constructor-basic.html [ Timeout ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/notifications/constructor-invalid.html [ Timeout ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/notifications/event-onclose.html [ Timeout ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/notifications/event-onshow.html [ Timeout ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/notifications/instance.html [ Timeout ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/notifications/lang.html [ Timeout ]
 crbug.com/626703 external/wpt/orientation-event/deviceorientationabsoluteevent.html [ Timeout ]
 crbug.com/626703 external/wpt/payment-request/payment-request-show-method.https.html [ Pass Failure ]
 crbug.com/626703 external/wpt/performance-timeline/po-observe.html [ Timeout ]
@@ -2731,8 +2731,8 @@
 
 crbug.com/619427 [ Mac Linux ] fast/overflow/overflow-height-float-not-removed-crash3.html [ Pass Failure ]
 
-crbug.com/667371 http/tests/devtools/elements/styles-1/color-aware-property-value-edit.html [ Pass Failure ]
-crbug.com/667560 virtual/mojo-loading/http/tests/devtools/elements/styles-1/color-aware-property-value-edit.html [ Pass Failure ]
+crbug.com/667371 http/tests/devtools/elements/styles-1/color-aware-property-value-edit.js [ Pass Failure ]
+crbug.com/667560 virtual/mojo-loading/http/tests/devtools/elements/styles-1/color-aware-property-value-edit.js [ Pass Failure ]
 
 # [css-ui] Imported tests from W3C suite.
 crbug.com/669473 external/wpt/css/css-ui/box-sizing-014.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index c1f82744..3ca95b8 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -42190,7 +42190,7 @@
      "/css/css-multicol/multicol-br-inside-avoidcolumn-001.xht",
      [
       [
-       "/css/css-multicol/multicol-br-inside-avoidcolumn-ref.xht",
+       "/css/reference/ref-filled-green-200px-square.html",
        "=="
       ]
      ],
@@ -92426,6 +92426,11 @@
      {}
     ]
    ],
+   "content-security-policy/script-src/nonce-enforce-blocked-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "content-security-policy/script-src/script-src-multiple-policies-multiple-hashing-algorithms.html.sub.headers": [
     [
      {}
@@ -108881,11 +108886,6 @@
      {}
     ]
    ],
-   "css/css-multicol/multicol-br-inside-avoidcolumn-ref.xht": [
-    [
-     {}
-    ]
-   ],
    "css/css-multicol/multicol-break-000-ref.xht": [
     [
      {}
@@ -123571,6 +123571,11 @@
      {}
     ]
    ],
+   "domxpath/001-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "domxpath/002-expected.txt": [
     [
      {}
@@ -140366,6 +140371,36 @@
      {}
     ]
    ],
+   "notifications/constructor-basic-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "notifications/constructor-invalid-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "notifications/event-onclose-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "notifications/event-onshow-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "notifications/instance-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "notifications/lang-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "notifications/resources/icon.png": [
     [
      {}
@@ -162177,6 +162212,12 @@
      {}
     ]
    ],
+   "content-security-policy/inheritance/inherited-csp-list-modifications-are-local.html": [
+    [
+     "/content-security-policy/inheritance/inherited-csp-list-modifications-are-local.html",
+     {}
+    ]
+   ],
    "content-security-policy/inheritance/window.html": [
     [
      "/content-security-policy/inheritance/window.html",
@@ -222195,7 +222236,7 @@
    "support"
   ],
   "./lint.whitelist": [
-   "d92bc2e8adfdf40cd2023883e69e42fe59b5f069",
+   "c7da7debb6b2bcc32be61b6f0f89d4c4c1ba3d4d",
    "support"
   ],
   "./update-built-tests.sh": [
@@ -232126,6 +232167,10 @@
    "3121f2277196e721af7d8cd522be148c875c79bd",
    "testharness"
   ],
+  "content-security-policy/inheritance/inherited-csp-list-modifications-are-local.html": [
+   "b11ead62cbc23529449cdfd5dceba1058c1127b8",
+   "testharness"
+  ],
   "content-security-policy/inheritance/window.html": [
    "5a59b9b239186ad49aa1928f0beb9cf4234b4a6e",
    "testharness"
@@ -232462,6 +232507,10 @@
    "e8583f7292cd90aa13e0e997aedf41763eb18928",
    "support"
   ],
+  "content-security-policy/script-src/nonce-enforce-blocked-expected.txt": [
+   "8aaf7baa550f1fe8460868f3960def0a6a9bf8df",
+   "support"
+  ],
   "content-security-policy/script-src/nonce-enforce-blocked.html": [
    "79bb6635e0964eef935cc2c9e93ff68034353f55",
    "testharness"
@@ -233167,7 +233216,7 @@
    "support"
   ],
   "content-security-policy/svg/including.sub.svg": [
-   "4a7db45f8feeee77456ef0f837210b6f2f734a47",
+   "6e6534212bf7ac65d317b06ad7d9b719e6123b09",
    "support"
   ],
   "content-security-policy/svg/including.sub.svg.sub.headers": [
@@ -264319,13 +264368,9 @@
    "reftest"
   ],
   "css/css-multicol/multicol-br-inside-avoidcolumn-001.xht": [
-   "f2fe604ad53155fe3ba1eaaaadad7d4842aef239",
+   "934a25d72288a1400325662a7b3b9c47ceadd88d",
    "reftest"
   ],
-  "css/css-multicol/multicol-br-inside-avoidcolumn-ref.xht": [
-   "d6c09e048d1a938cbf814a9e88b9ba0877b0222f",
-   "support"
-  ],
   "css/css-multicol/multicol-break-000-ref.xht": [
    "213ce08f5d1294ce5ed571eca674886e20a10bde",
    "support"
@@ -294198,6 +294243,10 @@
    "b62d4cf898f819ccaf02769de3af12cdc80cea7e",
    "testharness"
   ],
+  "domxpath/001-expected.txt": [
+   "940467b338503727c4bfae21fba04eeb23047931",
+   "support"
+  ],
   "domxpath/001.html": [
    "f7161655a8955dd5a028b2e7dd5ada945176a930",
    "testharness"
@@ -320186,10 +320235,18 @@
    "8e1817d9460c31d4a761491d77a0651469fb9195",
    "support"
   ],
+  "notifications/constructor-basic-expected.txt": [
+   "d9bf190aed1a7792e43cdb90d32a366863860e8c",
+   "support"
+  ],
   "notifications/constructor-basic.html": [
    "f44faf981ecf76d2545709a510972c4f7687d75a",
    "testharness"
   ],
+  "notifications/constructor-invalid-expected.txt": [
+   "bc1d38db6ebc265bcf98be8d1d203f1055b8f695",
+   "support"
+  ],
   "notifications/constructor-invalid.html": [
    "9eb3e60038febe0953444e7032778a555fb5a98c",
    "testharness"
@@ -320198,6 +320255,10 @@
    "bd6af979456cb4f635ebd9db9c7db0ea9b719eba",
    "manual"
   ],
+  "notifications/event-onclose-expected.txt": [
+   "a1ff7d2129b7b58b1c83bf1029fd39bfde338353",
+   "support"
+  ],
   "notifications/event-onclose.html": [
    "746b480ea172babfb53352640479195eab5b1a75",
    "testharness"
@@ -320210,6 +320271,10 @@
    "1e82f951bc9c173dad643bed6f29d4db4f72c35a",
    "manual"
   ],
+  "notifications/event-onshow-expected.txt": [
+   "4651931f3f1deee063e483581c7720a5bea82858",
+   "support"
+  ],
   "notifications/event-onshow.html": [
    "6d222c489c530f03b8cc876150a1f12275454e9d",
    "testharness"
@@ -320222,6 +320287,10 @@
    "444f20a7ce7348764efe1b7eeb33e9611c4857ac",
    "manual"
   ],
+  "notifications/instance-expected.txt": [
+   "09b69a66ddfc87cacfa2fb2cf68eb147d6c7cba7",
+   "support"
+  ],
   "notifications/instance.html": [
    "0d1c3d540163dc596080f7c09291d41a41f5c9b9",
    "testharness"
@@ -320230,6 +320299,10 @@
    "0a653fc93d0d01761fb642d426461130134d9cbf",
    "testharness"
   ],
+  "notifications/lang-expected.txt": [
+   "d7cdf784912c710774225f14e0d6b54cc7885e98",
+   "support"
+  ],
   "notifications/lang.html": [
    "2fd743cbeb5f46435eb4ca739ff0c70c97ff3323",
    "testharness"
@@ -343523,7 +343596,7 @@
    "support"
   ],
   "webrtc/RTCTrackEvent-constructor.html": [
-   "a73dde79625035cdd40d5abd8856cd44161dfdf9",
+   "4e9060c8c67de3a13f9d5727a8140a8ecfb8dfbf",
    "testharness"
   ],
   "webrtc/coverage/RTCDTMFSender.txt": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/svg/including.sub.svg b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/svg/including.sub.svg
index 99b416b5..51215d90 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/svg/including.sub.svg
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/svg/including.sub.svg
@@ -2,7 +2,8 @@
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
   "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
 <svg width="6cm" height="5cm" viewBox="0 0 600 500"
-     xmlns="http://www.w3.org/2000/svg" version="1.1">
+     xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
   <desc>using SVG as a resource doc should apply this doc's CSP</desc>
 
   <use xlink:href="scripted.svg#postmessagescript" />
diff --git a/third_party/WebKit/LayoutTests/external/wpt/lint.whitelist b/third_party/WebKit/LayoutTests/external/wpt/lint.whitelist
index be63695..1b35835a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/lint.whitelist
+++ b/third_party/WebKit/LayoutTests/external/wpt/lint.whitelist
@@ -119,9 +119,6 @@
 CONSOLE: annotation-model/scripts/showdown.min.js
 CR AT EOL: annotation-model/scripts/showdown.min.js
 
-# Lint doesn't know about sub.svg I guess
-PARSE-FAILED: content-security-policy/svg/including.sub.svg
-
 # Helper files that aren't valid XML
 PARSE-FAILED: acid/acid3/empty.xml
 PARSE-FAILED: dom/nodes/Document-createElement-namespace-tests/empty.svg
diff --git a/third_party/WebKit/LayoutTests/external/wpt/notifications/constructor-basic-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/notifications/constructor-basic-expected.txt
new file mode 100644
index 0000000..e619090f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/notifications/constructor-basic-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+NOTRUN Called the notification constructor with one argument. You must allow notifications for this origin before running this test.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/notifications/constructor-invalid-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/notifications/constructor-invalid-expected.txt
new file mode 100644
index 0000000..812d44e5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/notifications/constructor-invalid-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+NOTRUN Called the notification constructor with no arguments. You must allow notifications for this origin before running this test.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/notifications/event-onclose-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/notifications/event-onclose-expected.txt
new file mode 100644
index 0000000..7736a0b9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/notifications/event-onclose-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+NOTRUN Checked test prerequisites. You must allow notifications for thisorigin before running this test.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/notifications/event-onshow-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/notifications/event-onshow-expected.txt
new file mode 100644
index 0000000..f908574
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/notifications/event-onshow-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+NOTRUN Checked test prerequisites. You must allow notifications for this origin before running this test.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/notifications/instance-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/notifications/instance-expected.txt
new file mode 100644
index 0000000..a464abf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/notifications/instance-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+NOTRUN Notification instance basic tests You must allow notifications for this origin before running this test.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/notifications/lang-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/notifications/lang-expected.txt
new file mode 100644
index 0000000..ffb5334
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/notifications/lang-expected.txt
@@ -0,0 +1,40 @@
+This is a testharness.js-based test.
+NOTRUN Roundtripping lang "". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "en". Expecting "en". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "en-US-x-hixie". Expecting "en-US-x-hixie". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-DE". Expecting "de-DE". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-de". Expecting "de-de". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-De". Expecting "de-De". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-dE". Expecting "de-dE". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-DE-1996". Expecting "de-DE-1996". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-Latn-DE". Expecting "de-Latn-DE". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-Latf-DE". Expecting "de-Latf-DE". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-Latn-DE-1996". Expecting "de-Latn-DE-1996". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "de-CH". Expecting "de-CH". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "it-CH". Expecting "it-CH". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "fr-CH". Expecting "fr-CH". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "rm-CH". Expecting "rm-CH". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "es-CH". Expecting "es-CH". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "Latn-de". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "Latf-de". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "tic-tac-tac-toe". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "cocoa-1-bar". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "cocoa-a-bar". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "en-". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "en--". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "foo--bar". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "id---Java". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "fr-x". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "fr-xenomorph". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "fr-x-xenomorph". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "a". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "a-fr-lang". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "b-fr-lang". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "es1-KK-aa-bb-cc-dd". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "es2-KL-aa-bb-cc-dd". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "es3-KM-aa-bb-cc-dd". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "fooÉ". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "foöÉ-bÁr". Expecting "". You must allow notifications for this origin before running this test.
+NOTRUN Roundtripping lang "foöÉbÁr". Expecting "". You must allow notifications for this origin before running this test.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/testharness.js b/third_party/WebKit/LayoutTests/external/wpt/resources/testharness.js
index 2b478e9..0907b85 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/testharness.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/testharness.js
@@ -1569,11 +1569,6 @@
         this._add_cleanup(callback);
     };
 
-    Test.prototype.force_timeout = function() {
-        this.set_status(this.TIMEOUT);
-        this.phase = this.phases.HAS_RESULT;
-    };
-
     Test.prototype.set_timeout = function()
     {
         if (this.timeout_length !== null) {
@@ -1600,6 +1595,8 @@
         this.done();
     };
 
+    Test.prototype.force_timeout = Test.prototype.timeout;
+
     Test.prototype.done = function()
     {
         if (this.phase == this.phases.COMPLETE) {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCTrackEvent-constructor.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCTrackEvent-constructor.html
index d5c5f62..9579dd4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCTrackEvent-constructor.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCTrackEvent-constructor.html
@@ -41,6 +41,7 @@
     assert_equals(trackEvent.receiver, receiver);
     assert_equals(trackEvent.track, track);
     assert_array_equals(trackEvent.streams, []);
+    assert_equals(trackEvent.streams, trackEvent.streams); // [SameObject]
     assert_equals(trackEvent.transceiver, transceiver);
 
     assert_equals(trackEvent.type, 'track');
diff --git a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/compositing/squashing/dont-squash-into-videos-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/compositing/squashing/dont-squash-into-videos-expected.txt
index 7c33d99..6bda0c3 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/compositing/squashing/dont-squash-into-videos-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/compositing/squashing/dont-squash-into-videos-expected.txt
@@ -23,7 +23,7 @@
       "backgroundColor": "#ADD8E6"
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-pre-ready state-no-source'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-pre-ready state-no-source use-default-poster'",
       "bounds": [100, 100]
     },
     {
diff --git a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/paint/overflow/fixed-background-scroll-window-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/platform/linux/paint/overflow/fixed-background-scroll-window-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/paint/overflow/fixed-background-scroll-window-expected.txt
rename to third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/platform/linux/paint/overflow/fixed-background-scroll-window-expected.txt
diff --git a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/platform/mac/paint/overflow/fixed-background-scroll-window-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/platform/mac/paint/overflow/fixed-background-scroll-window-expected.txt
new file mode 100644
index 0000000..222722a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/platform/mac/paint/overflow/fixed-background-scroll-window-expected.txt
@@ -0,0 +1,10 @@
+layer at (0,0) size 800x600 scrollY 1000.00 scrollHeight 4016
+  LayoutView at (0,0) size 800x600
+layer at (0,-1000) size 800x4016 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x4016
+    LayoutBlockFlow {BODY} at (8,8) size 784x4000
+layer at (8,116) size 666x18
+  LayoutBlockFlow (positioned) {P} at (8,1116) size 665.63x18
+    LayoutText {#text} at (0,0) size 666x18
+      text run at (0,0) width 666: "Tests painting of fixed background content in scrolled container. Passes if the background doesn't scroll."
+scrolled to 0,1000
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/platform/win/paint/overflow/fixed-background-scroll-window-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/platform/win/paint/overflow/fixed-background-scroll-window-expected.txt
new file mode 100644
index 0000000..a6574ba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/platform/win/paint/overflow/fixed-background-scroll-window-expected.txt
@@ -0,0 +1,10 @@
+layer at (0,0) size 800x600 scrollY 1000.00 scrollHeight 4016
+  LayoutView at (0,0) size 800x600
+layer at (0,-1000) size 800x4016 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x4016
+    LayoutBlockFlow {BODY} at (8,8) size 784x4000
+layer at (8,116) size 622x20
+  LayoutBlockFlow (positioned) {P} at (8,1116) size 622x20
+    LayoutText {#text} at (0,0) size 622x19
+      text run at (0,0) width 622: "Tests painting of fixed background content in scrolled container. Passes if the background doesn't scroll."
+scrolled to 0,1000
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp-expected.txt
index e8f0aba..88b2abe 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp-expected.txt
@@ -1,6 +1,5 @@
 Tests that adding a new rule does not crash the renderer and modifying an inline style does not report errors when forbidden by Content-Security-Policy.
 
-Text
 
 Running: testSetUp
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp.js
similarity index 85%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp.js
index 2e9fe6f..0c5f2e83 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-inline-style-csp.js
@@ -1,11 +1,16 @@
-<html>
-<head>
-<meta http-equiv="Content-Security-Policy" content="style-src https://*:443 'unsafe-eval'">
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
+// 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.
 
-function test() {
+(async function() {
+  TestRunner.addResult(
+      `Tests that adding a new rule does not crash the renderer and modifying an inline style does not report errors when forbidden by Content-Security-Policy.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected">Text</div>
+    `);
+
   var nodeId;
   var rule;
   var matchedStyles;
@@ -106,16 +111,4 @@
     for (var i = 0; i < allProperties.length; ++i)
       TestRunner.addResult(allProperties[i].text);
   }
-}
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that adding a new rule does not crash the renderer and modifying an inline style does not report errors when forbidden by Content-Security-Policy.
-</p>
-
-<div id="inspected">Text</div>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector-expected.txt
index 3d22cbc8..bb0a5e1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector-expected.txt
@@ -1,6 +1,5 @@
 Tests that adding a new rule with invalid selector works as expected.
 
-Text
 
 Running: init
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector.html
deleted file mode 100644
index 66dbc11..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-
-function test() {
-  TestRunner.runTestSuite([
-    function init(next) {
-      ElementsTestRunner.selectNodeAndWaitForStyles('inspected', next);
-    },
-
-    function keyframesRuleSelector(next) {
-      ElementsTestRunner.addNewRule('@-webkit-keyframes shake', callback);
-
-      function callback() {
-        ElementsTestRunner.dumpSelectedElementStyles(true, false, true);
-        next();
-      }
-    }
-  ]);
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that adding a new rule with invalid selector works as expected.
-</p>
-
-<div id="inspected">Text</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector.js
new file mode 100644
index 0000000..81e898e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-invalid-selector.js
@@ -0,0 +1,27 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`Tests that adding a new rule with invalid selector works as expected.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected">Text</div>
+    `);
+
+  TestRunner.runTestSuite([
+    function init(next) {
+      ElementsTestRunner.selectNodeAndWaitForStyles('inspected', next);
+    },
+
+    function keyframesRuleSelector(next) {
+      ElementsTestRunner.addNewRule('@-webkit-keyframes shake', callback);
+
+      function callback() {
+        ElementsTestRunner.dumpSelectedElementStyles(true, false, true);
+        next();
+      }
+    }
+  ]);
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard-expected.txt
index b44bba4..67bac53 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard-expected.txt
@@ -1,6 +1,5 @@
 Tests that adding a new rule works properly with user input.
 
-Text
 Is editing? true
 [expanded] 
 element.style { ()
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard.html
deleted file mode 100644
index ad3c71d..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-
-function test() {
-  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', next);
-
-  async function next() {
-    await Elements.StylesSidebarPane._instance._createNewRuleInViaInspectorStyleSheet();
-    eventSender.keyDown("Tab");
-    await TestRunner.addSnifferPromise(Elements.StylePropertiesSection.prototype, "_editingSelectorCommittedForTest");
-
-    TestRunner.addResult("Is editing? " + UI.isEditing());
-    ElementsTestRunner.dumpSelectedElementStyles(true, false, true);
-
-
-    TestRunner.completeTest();
-  }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that adding a new rule works properly with user input.
-</p>
-
-<div id="inspected">Text</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard.js
new file mode 100644
index 0000000..68dd47f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-keyboard.js
@@ -0,0 +1,26 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`Tests that adding a new rule works properly with user input.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected">Text</div>
+    `);
+
+  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', next);
+
+  async function next() {
+    await Elements.StylesSidebarPane._instance._createNewRuleInViaInspectorStyleSheet();
+    eventSender.keyDown('Tab');
+    await TestRunner.addSnifferPromise(Elements.StylePropertiesSection.prototype, '_editingSelectorCommittedForTest');
+
+    TestRunner.addResult('Is editing? ' + UI.isEditing());
+    ElementsTestRunner.dumpSelectedElementStyles(true, false, true);
+
+
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body-expected.txt
index 56e9191..51a5349e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body-expected.txt
@@ -1,6 +1,5 @@
-Tests that adding a new rule works when there is a STYLE element after BODY. TIMEOUT SHOULD NOT OCCUR! Bug 111299
+Tests that adding a new rule works when there is a STYLE element after BODY. TIMEOUT SHOULD NOT OCCUR! Bug 111299 https://bugs.webkit.org/show_bug.cgi?id=111299
 
-Text
 After adding new rule:
 [expanded] 
 element.style { ()
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body.html
deleted file mode 100644
index 1f73899..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-function addStyle()
-{
-    var style = document.createElement("style");
-    document.documentElement.appendChild(style);
-    style.sheet.insertRule("foo {display: none;}", 0);
-}
-
-function test() {
-  TestRunner.cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, stylesheetAdded);
-  TestRunner.evaluateInPage('addStyle()');
-
-  function stylesheetAdded() {
-    TestRunner.cssModel.removeEventListener(SDK.CSSModel.Events.StyleSheetAdded, stylesheetAdded);
-    ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
-  }
-
-  var treeElement;
-  var hasResourceChanged;
-
-  function step1() {
-    ElementsTestRunner.addNewRule('inspected', step2);
-  }
-
-  function step2() {
-    var section = ElementsTestRunner.firstMatchedStyleSection();
-    var newProperty = section.addNewBlankProperty();
-    newProperty.startEditing();
-    newProperty.nameElement.textContent = 'color';
-    newProperty.nameElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
-    newProperty.valueElement.textContent = 'maroon';
-    newProperty.valueElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
-    ElementsTestRunner.waitForStyles('inspected', step3);
-  }
-
-  function step3() {
-    TestRunner.addResult('After adding new rule:');
-    ElementsTestRunner.dumpSelectedElementStyles(true, false, true);
-    TestRunner.completeTest();
-  }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that adding a new rule works when there is a STYLE element after BODY. TIMEOUT SHOULD NOT OCCUR! <a href="https://bugs.webkit.org/show_bug.cgi?id=111299">Bug 111299</a>
-</p>
-
-<div id="inspected" style="font-size: 12px">Text</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body.js
new file mode 100644
index 0000000..bdc89fb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body.js
@@ -0,0 +1,57 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that adding a new rule works when there is a STYLE element after BODY. TIMEOUT SHOULD NOT OCCUR! Bug 111299 https://bugs.webkit.org/show_bug.cgi?id=111299\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <p>
+      Tests that adding a new rule works when there is a STYLE element after BODY. TIMEOUT SHOULD NOT OCCUR! <a href="https://bugs.webkit.org/show_bug.cgi?id=111299">Bug 111299</a>
+      </p>
+
+      <div id="inspected" style="font-size: 12px">Text</div>
+    `);
+  await TestRunner.evaluateInPagePromise(`
+      function addStyle()
+      {
+          var style = document.createElement("style");
+          document.documentElement.appendChild(style);
+          style.sheet.insertRule("foo {display: none;}", 0);
+      }
+  `);
+
+  TestRunner.cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, stylesheetAdded);
+  TestRunner.evaluateInPage('addStyle()');
+
+  function stylesheetAdded() {
+    TestRunner.cssModel.removeEventListener(SDK.CSSModel.Events.StyleSheetAdded, stylesheetAdded);
+    ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
+  }
+
+  var treeElement;
+  var hasResourceChanged;
+
+  function step1() {
+    ElementsTestRunner.addNewRule('inspected', step2);
+  }
+
+  function step2() {
+    var section = ElementsTestRunner.firstMatchedStyleSection();
+    var newProperty = section.addNewBlankProperty();
+    newProperty.startEditing();
+    newProperty.nameElement.textContent = 'color';
+    newProperty.nameElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
+    newProperty.valueElement.textContent = 'maroon';
+    newProperty.valueElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
+    ElementsTestRunner.waitForStyles('inspected', step3);
+  }
+
+  function step3() {
+    TestRunner.addResult('After adding new rule:');
+    ElementsTestRunner.dumpSelectedElementStyles(true, false, true);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash-expected.txt
index 2a3d11d..2680e84 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash-expected.txt
@@ -1 +1,3 @@
 This test passes if it doesn't ASSERT.
+
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash.html
deleted file mode 100644
index 53bbb463..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<html>
-<head>
-<style>
-.absent {
-    background: #fff url(foo.png) no-repeat left 4px;
-}
-
-body {
-    background: #fff;
-}
-</style>
-<script src="../../../inspector/inspector-test.js"></script>
-<script>
-function test() {
-  TestRunner.completeTest();
-}
-</script>
-</head>
-<body onload="runTest()">
-This test passes if it doesn't ASSERT.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash.js
new file mode 100644
index 0000000..d2251047
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/background-parsing-crash.js
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`This test passes if it doesn't ASSERT.\n`);
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      .absent {
+          background: #fff url(foo.png) no-repeat left 4px;
+      }
+
+      body {
+          background: #fff;
+      }
+      </style>
+    `);
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles-expected.txt
index e5891ff..5e7be13e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles-expected.txt
@@ -1,6 +1,5 @@
 Tests that computed styles are cached across synchronous requests.
 
-Test
 # of backend calls sent [2 requests]: 1
 # of backend calls sent [style update + another request]: 2
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles.html
deleted file mode 100644
index 506d2c808..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<html>
-<head>
-<style id="style">
-#inspected {
-    background-color: green;
-}
-</style>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-
-function updateStyle()
-{
-    document.getElementById("style").textContent = "#inspected { color: red }";
-}
-
-function test() {
-  ElementsTestRunner.nodeWithId('inspected', step1);
-  var backendCallCount = 0;
-  var nodeId;
-
-  function onBackendCall(domain, method, params) {
-    if (method === 'CSS.getComputedStyleForNode' && params.nodeId === nodeId)
-      ++backendCallCount;
-  }
-
-  function step1(node) {
-    var callsLeft = 2;
-    nodeId = node.id;
-    TestRunner.addSniffer(Protocol.TargetBase.prototype, '_wrapCallbackAndSendMessageObject', onBackendCall, true);
-    TestRunner.cssModel.computedStylePromise(nodeId).then(styleCallback);
-    TestRunner.cssModel.computedStylePromise(nodeId).then(styleCallback);
-    function styleCallback() {
-      if (--callsLeft)
-        return;
-      TestRunner.addResult('# of backend calls sent [2 requests]: ' + backendCallCount);
-      TestRunner.evaluateInPage('updateStyle()', step2);
-    }
-  }
-
-  function step2() {
-    TestRunner.cssModel.computedStylePromise(nodeId).then(callback);
-    function callback() {
-      TestRunner.addResult('# of backend calls sent [style update + another request]: ' + backendCallCount);
-      TestRunner.completeTest();
-    }
-  }
-}
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that computed styles are cached across synchronous requests.
-</p>
-
-<div>
-  <div id="inspected">Test</div>
-</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles.js
new file mode 100644
index 0000000..b4c5435f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cached-sync-computed-styles.js
@@ -0,0 +1,56 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`Tests that computed styles are cached across synchronous requests.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      #inspected {
+          background-color: green;
+      }
+      </style>
+      <div>
+        <div id="inspected">Test</div>
+      </div>
+    `);
+  await TestRunner.evaluateInPagePromise(`
+      function updateStyle()
+      {
+          document.getElementById("style").textContent = "#inspected { color: red }";
+      }
+  `);
+
+  ElementsTestRunner.nodeWithId('inspected', step1);
+  var backendCallCount = 0;
+  var nodeId;
+
+  function onBackendCall(domain, method, params) {
+    if (method === 'CSS.getComputedStyleForNode' && params.nodeId === nodeId)
+      ++backendCallCount;
+  }
+
+  function step1(node) {
+    var callsLeft = 2;
+    nodeId = node.id;
+    TestRunner.addSniffer(Protocol.TargetBase.prototype, '_wrapCallbackAndSendMessageObject', onBackendCall, true);
+    TestRunner.cssModel.computedStylePromise(nodeId).then(styleCallback);
+    TestRunner.cssModel.computedStylePromise(nodeId).then(styleCallback);
+    function styleCallback() {
+      if (--callsLeft)
+        return;
+      TestRunner.addResult('# of backend calls sent [2 requests]: ' + backendCallCount);
+      TestRunner.evaluateInPage('updateStyle()', step2);
+    }
+  }
+
+  function step2() {
+    TestRunner.cssModel.computedStylePromise(nodeId).then(callback);
+    function callback() {
+      TestRunner.addResult('# of backend calls sent [style update + another request]: ' + backendCallCount);
+      TestRunner.completeTest();
+    }
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/case-sensitive-suggestions.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/case-sensitive-suggestions.js
similarity index 82%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/case-sensitive-suggestions.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/case-sensitive-suggestions.js
index 804c2a8..88f1c655 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/case-sensitive-suggestions.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/case-sensitive-suggestions.js
@@ -1,10 +1,12 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
+// 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.
 
-function test() {
+(async function() {
+  TestRunner.addResult(`Tests that text prompt suggestions' casing follows that of the user input.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+
   var prompt = new Elements.StylesSidebarPane.CSSPropertyPrompt(SDK.cssMetadata().allProperties(), [], null, true);
 
   TestRunner.runTestSuite([
@@ -59,13 +61,4 @@
       callback();
     }
   }
-}
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that text prompt suggestions' casing follows that of the user input.
-</p>
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit-expected.txt
index e46a3d5..51cd6954e1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit-expected.txt
@@ -1,7 +1,5 @@
 Tests that property value being edited uses the user-specified color format.
 
-inspected1
-inspected2
 
 Running: init
 
@@ -30,5 +28,5 @@
 rgb(255, 255, 238)
 
 Running: editNewProperty
-alicebluehsl(120, 100%, 25%)
+hsl(120, 100%, 25%)
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit.js
similarity index 81%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit.js
index 351a90e..8bce8a3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-aware-property-value-edit.js
@@ -1,10 +1,16 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
+// 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.
 
-function test() {
+(async function() {
+  TestRunner.addResult(`Tests that property value being edited uses the user-specified color format.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected1" style="border: 1px solid red">inspected1</div>
+      <div id="inspected2" style="color: #ffffee">inspected2</div>
+    `);
+
   TestRunner.runTestSuite([
     function init(next) {
       ElementsTestRunner.selectNodeAndWaitForStyles('inspected1', next);
@@ -81,18 +87,4 @@
       next();
     }
   }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that property value being edited uses the user-specified color format.
-</p>
-
-<div id="inspected1" style="border: 1px solid red">inspected1</div>
-<div id="inspected2" style="color: #ffffee">inspected2</div>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-nicknames-lowercase.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-nicknames-lowercase.html
deleted file mode 100644
index ff48435..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-nicknames-lowercase.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script>
-
-function test() {
-  var badNames = [];
-  for (var nickname in Common.Color.Nicknames) {
-    if (nickname.toLowerCase() !== nickname)
-      badNames.push(nickname);
-  }
-
-  if (badNames.length === 0)
-    TestRunner.addResult('PASSED');
-  else
-    TestRunner.addResult('Non-lowercase color nicknames: ' + badNames.join(', '));
-
-  TestRunner.completeTest();
-}
-
-</script>
-</head>
-<body onload="runTest()">
-<p>Tests that all color nicknames are lowercase to facilitate lookup</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-nicknames-lowercase.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-nicknames-lowercase.js
new file mode 100644
index 0000000..a819e6d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-nicknames-lowercase.js
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`Tests that all color nicknames are lowercase to facilitate lookup\n`);
+  await TestRunner.showPanel('elements');
+
+  var badNames = [];
+  for (var nickname in Common.Color.Nicknames) {
+    if (nickname.toLowerCase() !== nickname)
+      badNames.push(nickname);
+  }
+
+  if (badNames.length === 0)
+    TestRunner.addResult('PASSED');
+  else
+    TestRunner.addResult('Non-lowercase color nicknames: ' + badNames.join(', '));
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch-expected.txt
index 47015b12..8b14301 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch-expected.txt
@@ -1,6 +1,5 @@
 The patch verifies that color swatch functions properly in matched and computed styles. crbug.com/461363
 
-Inspected div
 
 Running: selectNode
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch.js
similarity index 77%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch.js
index 415934b..874eb52 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/color-swatch.js
@@ -1,15 +1,22 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<style>
-#inspected {
-    color: red;
-    --variable: red;
-}
-</style>
-<script>
-function test() {
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `The patch verifies that color swatch functions properly in matched and computed styles. crbug.com/461363\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      #inspected {
+          color: red;
+          --variable: red;
+      }
+      </style>
+      <div id="inspected">Inspected div</div>
+    `);
+
   TestRunner.runTestSuite([
     function selectNode(next) {
       ElementsTestRunner.selectNodeAndWaitForStylesWithComputed('inspected', next);
@@ -57,14 +64,4 @@
   function popoverVisible() {
     return !!document.body.querySelector('* /deep/ .spectrum-color');
   }
-}
-</script>
-</head>
-
-<body onload="runTest()">
-<p>The patch verifies that color swatch functions properly in matched and computed styles. crbug.com/461363</p>
-
-<div id="inspected">Inspected div</div>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-expected.txt
index 55aeab6..6c42192 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-expected.txt
@@ -1,13 +1,12 @@
-Tests that renaming a selector updates element styles. Bug 70018.
+Tests that renaming a selector updates element styles. Bug 70018. https://bugs.webkit.org/show_bug.cgi?id=70018
 
-Text
 === Before selector modification ===
 [expanded] 
 element.style { ()
     color: red;
 
 [expanded] 
-#inspected { (commit-selector.html:4 -> commit-selector.html:4:13)
+#inspected { (<style>…</style>)
 /-- overloaded --/     color: green;
 
 [expanded] 
@@ -20,7 +19,7 @@
     color: red;
 
 [expanded] 
-hr, #inspected { (commit-selector.html:4 -> commit-selector.html:4:17)
+hr, #inspected { (<style>…</style>)
 /-- overloaded --/     color: green;
 
 [expanded] 
@@ -33,7 +32,7 @@
     color: red;
 
 [expanded] [no-affect] 
-#inspectedChanged { (commit-selector.html:4 -> commit-selector.html:4:20)
+#inspectedChanged { (<style>…</style>)
     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-mark-matching.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-mark-matching.js
similarity index 69%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-mark-matching.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-mark-matching.js
index 2f1b431..182e97f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-mark-matching.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector-mark-matching.js
@@ -1,10 +1,16 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
+// 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.
 
-function test() {
+(async function() {
+  TestRunner.addResult(
+      `Tests that matching selectors are marked properly after new rule creation and selector change.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected"></div>
+    `);
+
   var nodeId;
   var stylesPane;
 
@@ -42,17 +48,4 @@
       }
     }
   ]);
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that matching selectors are marked properly after new rule creation and selector change.
-</p>
-
-<div id="inspected"></div>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector.html
deleted file mode 100644
index 563acaa9..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<html>
-<head>
-<style>
-#inspected {
-  color: green;
-}
-</style>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-
-function test() {
-  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
-
-  function step1() {
-    TestRunner.addResult('=== Before selector modification ===');
-    ElementsTestRunner.dumpSelectedElementStyles(true);
-    var section = ElementsTestRunner.firstMatchedStyleSection();
-    section.startEditingSelector();
-    section._selectorElement.textContent = 'hr, #inspected ';
-    ElementsTestRunner.waitForSelectorCommitted(step2);
-    section._selectorElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
-  }
-
-  function step2() {
-    TestRunner.addResult('=== After non-affecting selector modification ===');
-    ElementsTestRunner.dumpSelectedElementStyles(true);
-    var section = ElementsTestRunner.firstMatchedStyleSection();
-    section.startEditingSelector();
-    section._selectorElement.textContent = '#inspectedChanged';
-    ElementsTestRunner.waitForSelectorCommitted(step3);
-    section._selectorElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
-  }
-
-  function step3() {
-    TestRunner.addResult('=== After affecting selector modification ===');
-    ElementsTestRunner.dumpSelectedElementStyles(true);
-    TestRunner.completeTest();
-  }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that renaming a selector updates element styles. <a href="https://bugs.webkit.org/show_bug.cgi?id=70018">Bug 70018</a>.
-</p>
-
-<div id="inspected" style="color: red">Text</div>
-<div id="other"></div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector.js
new file mode 100644
index 0000000..4facc3f2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/commit-selector.js
@@ -0,0 +1,51 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that renaming a selector updates element styles. Bug 70018. https://bugs.webkit.org/show_bug.cgi?id=70018\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      #inspected {
+        color: green;
+      }
+      </style>
+      <p>
+      Tests that renaming a selector updates element styles. <a href="https://bugs.webkit.org/show_bug.cgi?id=70018">Bug 70018</a>.
+      </p>
+
+      <div id="inspected" style="color: red">Text</div>
+      <div id="other"></div>
+    `);
+
+  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
+
+  function step1() {
+    TestRunner.addResult('=== Before selector modification ===');
+    ElementsTestRunner.dumpSelectedElementStyles(true);
+    var section = ElementsTestRunner.firstMatchedStyleSection();
+    section.startEditingSelector();
+    section._selectorElement.textContent = 'hr, #inspected ';
+    ElementsTestRunner.waitForSelectorCommitted(step2);
+    section._selectorElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
+  }
+
+  function step2() {
+    TestRunner.addResult('=== After non-affecting selector modification ===');
+    ElementsTestRunner.dumpSelectedElementStyles(true);
+    var section = ElementsTestRunner.firstMatchedStyleSection();
+    section.startEditingSelector();
+    section._selectorElement.textContent = '#inspectedChanged';
+    ElementsTestRunner.waitForSelectorCommitted(step3);
+    section._selectorElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
+  }
+
+  function step3() {
+    TestRunner.addResult('=== After affecting selector modification ===');
+    ElementsTestRunner.dumpSelectedElementStyles(true);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-live-edit.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-live-edit.html
deleted file mode 100644
index 197aa6d..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-live-edit.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script src="../../../inspector/live-edit-test.js"></script>
-<script src="../../../inspector/debugger-test.js"></script>
-<link rel="stylesheet" href="../styles/resources/css-live-edit.css">
-<div id=foo></div>
-<script>
-
-function test() {
-  TestRunner.runTestSuite([function testLiveEdit(next) {
-    SourcesTestRunner.showScriptSource('css-live-edit.css', didShowResource);
-
-    function didShowResource(sourceFrame) {
-      TestRunner.addSniffer(SDK.CSSModel.prototype, '_fireStyleSheetChanged', didEditResource);
-      SourcesTestRunner.replaceInSource(sourceFrame, 'font-size: 12px;', 'font-size: 20px;');
-    }
-
-    function didEditResource() {
-      ElementsTestRunner.selectNodeAndWaitForStylesWithComputed('foo', didSelectElement);
-    }
-
-    function didSelectElement() {
-      ElementsTestRunner.dumpSelectedElementStyles(false, true);
-      next();
-    }
-  }]);
-};
-
-</script>
-
-</head>
-
-<body onload="runTest()">
-<p>Tests that styles are updated when live-editing css resource.</p>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-live-edit.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-live-edit.js
new file mode 100644
index 0000000..7202c7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-live-edit.js
@@ -0,0 +1,32 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`Tests that styles are updated when live-editing css resource.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.showPanel('sources');
+  await TestRunner.loadHTML(`
+      <div id="foo"></div>
+    `);
+  await TestRunner.addStylesheetTag('../styles/resources/css-live-edit.css');
+
+  TestRunner.runTestSuite([function testLiveEdit(next) {
+    SourcesTestRunner.showScriptSource('css-live-edit.css', didShowResource);
+
+    function didShowResource(sourceFrame) {
+      TestRunner.addSniffer(SDK.CSSModel.prototype, '_fireStyleSheetChanged', didEditResource);
+      SourcesTestRunner.replaceInSource(sourceFrame, 'font-size: 12px;', 'font-size: 20px;');
+    }
+
+    function didEditResource() {
+      ElementsTestRunner.selectNodeAndWaitForStylesWithComputed('foo', didSelectElement);
+    }
+
+    function didSelectElement() {
+      ElementsTestRunner.dumpSelectedElementStyles(false, true);
+      next();
+    }
+  }]);
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-outline.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-outline.js
similarity index 68%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-outline.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-outline.js
index cd350a8..0a75d8f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-outline.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/css-outline.js
@@ -1,6 +1,12 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
+// 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.
+
+(async function() {
+  TestRunner.addResult(`The test verifies the CSS outline functionality.\n`);
+  await TestRunner.loadModule('formatter');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
 <style id="styler">
 @import url("some-url-to-load-css.css") print;
 @charset "ISO-8859-15";
@@ -51,18 +57,18 @@
     }
 }
 </style>
-<script>
+    `);
+  await TestRunner.evaluateInPagePromise(`
+      function initialize_Formatter() {
+          InspectorTest.preloadModule('formatter');
+      }
 
-function initialize_Formatter() {
-    InspectorTest.preloadModule('formatter');
-}
+      function getCSS()
+      {
+          return document.querySelector("#styler").textContent;
+      }
+  `);
 
-function getCSS()
-{
-    return document.querySelector("#styler").textContent;
-}
-
-function test() {
   function onRulesParsed(isLastChunk, rules) {
     for (var i = 0; i < rules.length; ++i)
       TestRunner.addObject(rules[i]);
@@ -75,13 +81,4 @@
   }
 
   TestRunner.evaluateInPage('getCSS()', onStyleFetched);
-}
-
-</script>
-
-</head>
-
-<body onload="runTest()">
-<p>The test verifies the CSS outline functionality.</p>
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash-expected.txt
index 30ca49a4..507c282 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash-expected.txt
@@ -1,5 +1,5 @@
-Tests that the inspected page does not crash after inspecting element with CSSOM added rules. Bug 373508
-Inspecting this element crashes DevTools
+Tests that the inspected page does not crash after inspecting element with CSSOM added rules. Bug 373508 crbug.com/373508
+
 [expanded] 
 element.style { ()
 
@@ -9,7 +9,7 @@
     background: red;
 
 [expanded] 
-div { (cssom-media-ins…-crash.html:18 -> cssom-media-insert-crash.html:18:6)
+div { (<style>…</style>)
     border: 1px solid black;
 /-- overloaded --/     background-color: white;
     padding: 20px;
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash.html
deleted file mode 100644
index e066e85..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-
-function test() {
-  ElementsTestRunner.selectNodeAndWaitForStyles('box', step1);
-
-  function step1() {
-    ElementsTestRunner.dumpSelectedElementStyles(true, false, true, false);
-    TestRunner.completeTest();
-  }
-}
-
-</script>
-<style>
-div {
-    border: 1px solid black;
-    background-color: white;
-    padding: 20px;
-}
-</style>
-</head>
-
-<body onload="runTest()">
-Tests that the inspected page does not crash after inspecting element with CSSOM added rules. <a href="http://crbug.com/373508">Bug 373508</a>
-<div id="box">Inspecting this element crashes DevTools</div>
-<script>
-var lastSheet = document.styleSheets[document.styleSheets.length - 1];
-var mediaIndex = lastSheet.insertRule('@media all { }', lastSheet.cssRules.length);
-var mediaRule = lastSheet.cssRules[mediaIndex];
-mediaRule.insertRule('#box { background: red; color: white; }', mediaRule.cssRules.length);
-</script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash.js
new file mode 100644
index 0000000..ec861d3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/cssom-media-insert-crash.js
@@ -0,0 +1,34 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that the inspected page does not crash after inspecting element with CSSOM added rules. Bug 373508 crbug.com/373508\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      div {
+          border: 1px solid black;
+          background-color: white;
+          padding: 20px;
+      }
+      </style>
+      Tests that the inspected page does not crash after inspecting element with CSSOM added rules. <a href="http://crbug.com/373508">Bug 373508</a>
+      <div id="box">Inspecting this element crashes DevTools</div>
+    `);
+  await TestRunner.evaluateInPagePromise(`
+      var lastSheet = document.styleSheets[document.styleSheets.length - 1];
+      var mediaIndex = lastSheet.insertRule('@media all { }', lastSheet.cssRules.length);
+      var mediaRule = lastSheet.cssRules[mediaIndex];
+      mediaRule.insertRule('#box { background: red; color: white; }', mediaRule.cssRules.length);
+    `);
+
+  ElementsTestRunner.selectNodeAndWaitForStyles('box', step1);
+
+  function step1() {
+    ElementsTestRunner.dumpSelectedElementStyles(true, false, true, false);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/disable-property-workingcopy-update.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/disable-property-workingcopy-update.js
similarity index 72%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/disable-property-workingcopy-update.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/disable-property-workingcopy-update.js
index 24c14a1..11adf5c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/disable-property-workingcopy-update.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/disable-property-workingcopy-update.js
@@ -1,12 +1,19 @@
-<html>
-<head>
-<link rel="stylesheet" href="../styles/resources/disable-property-workingcopy-update.css">
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/debugger-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
+// 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.
 
-function test() {
+(async function() {
+  TestRunner.addResult(
+      `Tests that style property disablement is propagated into the stylesheet UISourceCode working copy.\n`);
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('sources');
+  await TestRunner.loadHTML(`
+      <div id="inspected">
+      </div>
+    `);
+  await TestRunner.addStylesheetTag('../styles/resources/disable-property-workingcopy-update.css');
+
   var cssSourceFrame;
   Bindings.StylesSourceMapping.MinorChangeUpdateTimeoutMs = 10;
 
@@ -65,17 +72,4 @@
         next();
     }
   }
-}
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that style property disablement is propagated into the stylesheet UISourceCode working copy.
-</p>
-
-<div id="inspected">
-</div>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag-expected.txt
index 4aa0b93..b18a0e4 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag-expected.txt
@@ -22,7 +22,7 @@
   - hasSourceURL: false
   - contents: 
 /* comment */.inline-style-added-by-parser {
-   color: red;
+    color: red;
 }
 
 Stylesheet added:
@@ -41,7 +41,7 @@
   - hasSourceURL: true
   - contents: 
 .inline-style-added-by-parser-with-source-url {
-   color: green;
+    color: green;
 }
 /*# sourceURL=inlineStyleAddedByParser.css*/
 
@@ -78,7 +78,7 @@
 /-- overloaded --/     color: green;
 
 [expanded] 
-.inline-style-added-by-parser { (dynamic-style-tag.html:6 -> dynamic-style-tag.html:6:45)
+.inline-style-added-by-parser { (dynamic-style-tag.html:3 -> dynamic-style-tag.html:3:45)
 /-- overloaded --/     color: red;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag.html
deleted file mode 100644
index a578d7524..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<style>
-/* comment */.inline-style-added-by-parser {
-   color: red;
-}
-</style>
-<style>
-.inline-style-added-by-parser-with-source-url {
-   color: green;
-}
-/*# sourceURL=inlineStyleAddedByParser.css*/
-</style>
-<script>
-document.write("<style>\n.inline-style-added-by-parser-in-document-write {\n   color: blue;\n}\n</style>");
-document.write("<style>\n.inline-style-added-by-document-write-with-source-url {\n   color: yellow;\n}\n/*# sourceURL=inlineStyleAddedByDocumentWrite.css*/\n</style>");
-addStyleElement(".inline-style-created-by-script {\n   color: orange;\n}");
-addStyleElement(".inline-style-created-by-script-with-source-url {\n   color: grey;\n}\n/*# sourceURL=inlineStyleCreatedByScript.css*/");
-
-function addStyleElement(styleContent)
-{
-    var styleElement = document.createElement("style");
-    styleElement.textContent = styleContent;
-    document.head.appendChild(styleElement);
-}
-
-function test() {
-  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
-
-  async function step1() {
-    var styleSheets = TestRunner.cssModel.allStyleSheets();
-    styleSheets.sort();
-    for (var header of styleSheets) {
-      var content = await TestRunner.CSSAgent.getStyleSheetText(header.id);
-
-      TestRunner.addResult('Stylesheet added:');
-      TestRunner.addResult('  - isInline: ' + header.isInline);
-      TestRunner.addResult('  - sourceURL: ' + header.sourceURL.substring(header.sourceURL.lastIndexOf('/') + 1));
-      TestRunner.addResult('  - hasSourceURL: ' + header.hasSourceURL);
-      TestRunner.addResult('  - contents: ' + content);
-    }
-    ElementsTestRunner.dumpSelectedElementStyles(true, false, true);
-    TestRunner.completeTest();
-  }
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>Tests that different types of inline styles are correctly disambiguated and their sourceURL is correct.
-<div id="inspected" style="color:red" class="inline-style-added-by-parser inline-style-added-by-parser-with-source-url inline-style-added-by-parser-in-document-write inline-style-added-by-document-write-with-source-url inline-style-created-by-script inline-style-created-by-script-with-source-url"></div>
-</body>
-</html>
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag.js
new file mode 100644
index 0000000..f79866c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/dynamic-style-tag.js
@@ -0,0 +1,29 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that different types of inline styles are correctly disambiguated and their sourceURL is correct.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.navigatePromise('resources/dynamic-style-tag.html');
+
+  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
+
+  async function step1() {
+    var styleSheets = TestRunner.cssModel.allStyleSheets();
+    styleSheets.sort();
+    for (var header of styleSheets) {
+      var content = await TestRunner.CSSAgent.getStyleSheetText(header.id);
+
+      TestRunner.addResult('Stylesheet added:');
+      TestRunner.addResult('  - isInline: ' + header.isInline);
+      TestRunner.addResult('  - sourceURL: ' + header.sourceURL.substring(header.sourceURL.lastIndexOf('/') + 1));
+      TestRunner.addResult('  - hasSourceURL: ' + header.hasSourceURL);
+      TestRunner.addResult('  - contents: ' + content);
+    }
+    ElementsTestRunner.dumpSelectedElementStyles(true, false, true);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet-expected.txt
index 0579f500..38eee3c0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet-expected.txt
@@ -1,6 +1,5 @@
 Tests that adding a new rule creates inspector stylesheet resource and allows its live editing.
 
-Text
 Inspector stylesheet URL: inspector-stylesheet
 Inspector stylesheet content:
 #inspected {}
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet.js
similarity index 73%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet.js
index 02f03e8..2cd347f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-inspector-stylesheet.js
@@ -1,11 +1,17 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script src="../../../inspector/debugger-test.js"></script>
-<script>
+// 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.
 
-function test() {
+(async function() {
+  TestRunner.addResult(
+      `Tests that adding a new rule creates inspector stylesheet resource and allows its live editing.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected">Text</div>
+    `);
+
   ElementsTestRunner.selectNodeAndWaitForStyles('inspected', onStylesSelected);
 
   function onStylesSelected(node) {
@@ -48,16 +54,4 @@
     }
     return result;
   }
-}
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that adding a new rule creates inspector stylesheet resource and allows its live editing.
-</p>
-
-<div id="inspected">Text</div>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text-expected.txt
index dfc4c355..46bb6c3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text-expected.txt
@@ -1,6 +1,5 @@
 Tests that editing media text updates element styles.
 
-Text
 === Before media text modification ===
 [expanded] 
 element.style { ()
@@ -8,12 +7,12 @@
 
 [expanded] 
 @media screen and (max-device-width: 100000px)
-#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:17)
+#inspected { (<style>…</style>)
 /-- overloaded --/     color: blue;
 
 [expanded] 
 @media screen and (max-device-width: 100000px)
-#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:17)
+#inspected { (<style>…</style>)
 /-- overloaded --/     color: green;
 
 [expanded] 
@@ -27,12 +26,12 @@
 
 [expanded] 
 @media screen and (max-device-width: 99999px)
-#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:17)
+#inspected { (<style>…</style>)
 /-- overloaded --/     color: blue;
 
 [expanded] 
 @media screen and (max-device-width: 99999px)
-#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:17)
+#inspected { (<style>…</style>)
 /-- overloaded --/     color: green;
 
 [expanded] 
@@ -45,11 +44,11 @@
     color: red;
 
 [expanded] 
-#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:17)
+#inspected { (<style>…</style>)
 /-- overloaded --/     color: blue;
 
 [expanded] 
-#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:17)
+#inspected { (<style>…</style>)
 /-- overloaded --/     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text.js
similarity index 61%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text.js
index 02a80f3..bc1b27a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-media-text.js
@@ -1,25 +1,31 @@
-<html>
-<head>
-<style>
-@media screen and (max-device-width: 100000px) {
-    #inspected {
-        color: green;
-    }
-    #inspected {
-        color: blue;
-    }
-}
-@media screen and (max-device-width: 200000px) {
-    #other {
-        color: green;
-    }
-}
-</style>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
+// 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.
 
-function test() {
+(async function() {
+  TestRunner.addResult(`Tests that editing media text updates element styles.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      @media screen and (max-device-width: 100000px) {
+          #inspected {
+              color: green;
+          }
+          #inspected {
+              color: blue;
+          }
+      }
+      @media screen and (max-device-width: 200000px) {
+          #other {
+              color: green;
+          }
+      }
+      </style>
+      <div id="inspected" style="color: red">Text</div>
+      <div id="other"></div>
+    `);
+
   ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
 
   function step1() {
@@ -49,18 +55,4 @@
     ElementsTestRunner.dumpSelectedElementStyles(true);
     TestRunner.completeTest();
   }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that editing media text updates element styles.
-</p>
-
-<div id="inspected" style="color: red">Text</div>
-<div id="other"></div>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-name-with-trimmed-value.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-name-with-trimmed-value.html
deleted file mode 100644
index 5ad7762e..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-name-with-trimmed-value.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-
-function test() {
-  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
-
-  function step1() {
-    var treeElement = ElementsTestRunner.getElementStylePropertyTreeItem('background');
-    TestRunner.addResult('Viewing \'background\' value in Styles:');
-    TestRunner.addResult(treeElement.valueElement.textContent);
-
-    treeElement.startEditing(treeElement.nameElement);
-    treeElement.nameElement.textContent = 'background-image';
-    treeElement.nameElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
-    ElementsTestRunner.waitForStyleApplied(step2);
-  }
-
-  function step2() {
-    var treeElement = ElementsTestRunner.getElementStylePropertyTreeItem('background-image');
-    TestRunner.addResult('Renamed \'background\' to \'background-image\' (edited value):');
-    TestRunner.addResult(treeElement.valueElement.textContent);
-    TestRunner.completeTest();
-  }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that editing a CSS property name in the Styles pane retains its original, non-trimmed value text.
-</p>
-
-<div id="inspected" style="background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC)" />
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-name-with-trimmed-value.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-name-with-trimmed-value.js
new file mode 100644
index 0000000..5eda2d91
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-name-with-trimmed-value.js
@@ -0,0 +1,37 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that editing a CSS property name in the Styles pane retains its original, non-trimmed value text.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected" style="background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC)">
+
+
+
+      </div>
+    `);
+
+  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
+
+  function step1() {
+    var treeElement = ElementsTestRunner.getElementStylePropertyTreeItem('background');
+    TestRunner.addResult('Viewing \'background\' value in Styles:');
+    TestRunner.addResult(treeElement.valueElement.textContent);
+
+    treeElement.startEditing(treeElement.nameElement);
+    treeElement.nameElement.textContent = 'background-image';
+    treeElement.nameElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
+    ElementsTestRunner.waitForStyleApplied(step2);
+  }
+
+  function step2() {
+    var treeElement = ElementsTestRunner.getElementStylePropertyTreeItem('background-image');
+    TestRunner.addResult('Renamed \'background\' to \'background-image\' (edited value):');
+    TestRunner.addResult(treeElement.valueElement.textContent);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
index 392ad45..bfe3e72 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
@@ -1,13 +1,11 @@
 Tests that editing sourcecode which is referred by multiple stylesheets (via sourceURL comment) updates all stylesheets.
 
-Inspected node
-
 Headers count: 3
 
 Running: Make edits with Sources Panel
 Both headers and uiSourceCode content:
 div{color:EDITED;}
-
+      
 
 Running: Make edits via css model
 Both headers and uiSourceCode content:
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags.js
similarity index 63%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags.js
index eda1c30..6c58765 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-resource-referred-by-multiple-styletags.js
@@ -1,18 +1,29 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script src="../../../inspector/debugger-test.js"></script>
-<script src="../../../inspector/live-edit-test.js"></script>
-<script src="../../../inspector/bindings/bindings-test.js"></script>
-<script>
+// 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.
 
-function prepareTest()
-{
-    runTest();
-}
+(async function() {
+  TestRunner.addResult(
+      `Tests that editing sourcecode which is referred by multiple stylesheets (via sourceURL comment) updates all stylesheets.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.loadModule('bindings_test_runner');
+  await TestRunner.showPanel('sources');
+  await TestRunner.loadHTML(`
+      <div id="inspected">Inspected node</div>
 
-async function test() {
+      <style>div{color:red;}
+      /*# sourceURL=stylesheet.css */
+      </style>
+
+      <template id="template">
+      <style>div{color:red;}
+      /*# sourceURL=stylesheet.css */
+      </style>
+      <p>Hi! I'm ShadowDOM v1!</p>
+      </template>
+    `);
+
   await BindingsTestRunner.attachShadowDOM('shadow1', '#template'),
       await BindingsTestRunner.attachFrame('frame', './resources/frame.html');
   var uiSourceCode = await TestRunner.waitForUISourceCode('stylesheet.css');
@@ -46,26 +57,4 @@
     TestRunner.addResult('Both headers and uiSourceCode content:');
     TestRunner.addResult(dedup.firstValue());
   }
-}
-</script>
-</head>
-<body onload="prepareTest()">
-<p>
-Tests that editing sourcecode which is referred by multiple stylesheets (via sourceURL comment) updates all stylesheets.
-</p>
-
-<div id="inspected">Inspected node</div>
-
-<style>div{color:red;}
-/*# sourceURL=stylesheet.css */
-</style>
-
-<template id='template'>
-<style>div{color:red;}
-/*# sourceURL=stylesheet.css */
-</style>
-<p>Hi! I'm ShadowDOM v1!</p>
-</template>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-inside-property.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-inside-property.js
similarity index 72%
rename from third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-inside-property.html
rename to third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-inside-property.js
index 56b63902..0a602cb 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-inside-property.html
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-inside-property.js
@@ -1,10 +1,15 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
+// 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.
 
-function test() {
+(async function() {
+  TestRunner.addResult(`Verifies that property value editing triggers style update in rendering engine.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected" style="font-size: 19px"></div>
+    `);
+
   ElementsTestRunner.selectNodeAndWaitForStyles('inspected', testEmulateKeypress);
 
   function testEmulateKeypress() {
@@ -38,17 +43,4 @@
     TestRunner.addResult('font-size: ' + inlineStyleResult.inlineStyle.getPropertyValue('font-size'));
     TestRunner.completeTest();
   }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Verifies that property value editing triggers style update in rendering engine.
-</p>
-
-<div id="inspected" style="font-size: 19px"></div>
-
-</body>
-</html>
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color-expected.txt
index ab08c0f22..c8968819 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color-expected.txt
@@ -1,16 +1,5 @@
 Tests that colors are not re-formatted inside url(...) when editing property values.
 
-"white" background
-"url( white )" background
-"url(white.png)" background
-"url(../foo/white.png)" background
-"green url(white)" background
-"url(white) green" background
-"url(white) green, url(green)" background
-"url(white), url(green)" background
-"hsl(0, 50%, 50%) url(white)" background
-"url(white) hsl(0, 50%, 50%)" background
-"url(../black/white.png)" background
 rgb(255, 255, 255)
 url( white )
 url(white.png)
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color.html
deleted file mode 100644
index 472b1de5..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-
-function test() {
-  var maxIndex = 11;
-  var idIndex = 1;
-
-  Common.Color.detectColorFormat = function() {
-    return Common.Color.Format.RGB;
-  };
-
-  selectDivAndEditValue();
-
-  function selectDivAndEditValue() {
-    ElementsTestRunner.selectNodeAndWaitForStyles('inspected' + idIndex++, editCallback);
-  }
-
-  function editCallback() {
-    var treeElement = ElementsTestRunner.getMatchedStylePropertyTreeItem('background');
-    treeElement.startEditing(treeElement.valueElement);
-    TestRunner.addResult(treeElement.valueElement.textContent);
-    if (idIndex <= maxIndex)
-      selectDivAndEditValue();
-    else
-      TestRunner.completeTest();
-  }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that colors are not re-formatted inside url(...) when editing property values.
-</p>
-
-<div id="inspected1" style="background: white">"white" background</div>
-<div id="inspected2" style="background: url( white )">"url( white )" background</div>
-<div id="inspected3" style="background: url(white.png)">"url(white.png)" background</div>
-<div id="inspected4" style="background: url(../foo/white.png)">"url(../foo/white.png)" background</div>
-<div id="inspected5" style="background: green url(white)">"green url(white)" background</div>
-<div id="inspected6" style="background: url(white) green">"url(white) green" background</div>
-<div id="inspected7" style="background: url(white) green, url(green)">"url(white) green, url(green)" background</div>
-<div id="inspected8" style="background: url(white), url(green)">"url(white), url(green)" background</div>
-<div id="inspected9" style="background: hsl(0, 50%, 50%) url(white)">"hsl(0, 50%, 50%) url(white)" background</div>
-<div id="inspected10" style="background: url(white) hsl(0, 50%, 50%)">"url(white) hsl(0, 50%, 50%)" background</div>
-<div id="inspected11" style="background: url(../black/white.png)">"url(../black/white.png)" background</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color.js
new file mode 100644
index 0000000..1740ebb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-url-with-color.js
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`Tests that colors are not re-formatted inside url(...) when editing property values.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected1" style="background: white">&quot;white&quot; background</div>
+      <div id="inspected2" style="background: url( white )">&quot;url( white )&quot; background</div>
+      <div id="inspected3" style="background: url(white.png)">&quot;url(white.png)&quot; background</div>
+      <div id="inspected4" style="background: url(../foo/white.png)">&quot;url(../foo/white.png)&quot; background</div>
+      <div id="inspected5" style="background: green url(white)">&quot;green url(white)&quot; background</div>
+      <div id="inspected6" style="background: url(white) green">&quot;url(white) green&quot; background</div>
+      <div id="inspected7" style="background: url(white) green, url(green)">&quot;url(white) green, url(green)&quot; background</div>
+      <div id="inspected8" style="background: url(white), url(green)">&quot;url(white), url(green)&quot; background</div>
+      <div id="inspected9" style="background: hsl(0, 50%, 50%) url(white)">&quot;hsl(0, 50%, 50%) url(white)&quot; background</div>
+      <div id="inspected10" style="background: url(white) hsl(0, 50%, 50%)">&quot;url(white) hsl(0, 50%, 50%)&quot; background</div>
+      <div id="inspected11" style="background: url(../black/white.png)">&quot;url(../black/white.png)&quot; background</div>
+    `);
+
+  var maxIndex = 11;
+  var idIndex = 1;
+
+  Common.Color.detectColorFormat = function() {
+    return Common.Color.Format.RGB;
+  };
+
+  selectDivAndEditValue();
+
+  function selectDivAndEditValue() {
+    ElementsTestRunner.selectNodeAndWaitForStyles('inspected' + idIndex++, editCallback);
+  }
+
+  function editCallback() {
+    var treeElement = ElementsTestRunner.getMatchedStylePropertyTreeItem('background');
+    treeElement.startEditing(treeElement.valueElement);
+    TestRunner.addResult(treeElement.valueElement.textContent);
+    if (idIndex <= maxIndex)
+      selectDivAndEditValue();
+    else
+      TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url-expected.txt
index 9d4a9e7f..23141aa 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url-expected.txt
@@ -1,4 +1,4 @@
-Tests that editing a CSS property value in the Styles pane restores the original, non-trimmed value text. Bug 107936.
+Tests that editing a CSS property value in the Styles pane restores the original, non-trimmed value text. Bug 107936. https://bugs.webkit.org/show_bug.cgi?id=107936
 
 Viewing 'background' value in Styles:
 transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFW…9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC) repeat-y 50% top
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url.html
deleted file mode 100644
index d96806897..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<script>
-
-function test() {
-  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
-
-  function step1() {
-    var treeElement = ElementsTestRunner.getElementStylePropertyTreeItem('background');
-    TestRunner.addResult('Viewing \'background\' value in Styles:');
-    TestRunner.addResult(treeElement.valueElement.textContent);
-
-    treeElement.startEditing(treeElement.valueElement);
-    TestRunner.addResult('Editing \'background\' value in Styles:');
-    TestRunner.addResult(treeElement.valueElement.textContent);
-    TestRunner.completeTest();
-  }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p>
-Tests that editing a CSS property value in the Styles pane restores the original, non-trimmed value text. <a href="https://bugs.webkit.org/show_bug.cgi?id=107936">Bug 107936</a>.
-</p>
-
-<div id="inspected" style="background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC) repeat-y 50% top" />
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url.js
new file mode 100644
index 0000000..9f549b71
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url.js
@@ -0,0 +1,34 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      `Tests that editing a CSS property value in the Styles pane restores the original, non-trimmed value text. Bug 107936. https://bugs.webkit.org/show_bug.cgi?id=107936\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <p>
+      Tests that editing a CSS property value in the Styles pane restores the original, non-trimmed value text. <a href="https://bugs.webkit.org/show_bug.cgi?id=107936">Bug 107936</a>.
+      </p>
+
+      <div id="inspected" style="background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC) repeat-y 50% top">
+
+
+
+      </div>
+    `);
+
+  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', step1);
+
+  function step1() {
+    var treeElement = ElementsTestRunner.getElementStylePropertyTreeItem('background');
+    TestRunner.addResult('Viewing \'background\' value in Styles:');
+    TestRunner.addResult(treeElement.valueElement.textContent);
+
+    treeElement.startEditing(treeElement.valueElement);
+    TestRunner.addResult('Editing \'background\' value in Styles:');
+    TestRunner.addResult(treeElement.valueElement.textContent);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url-expected.txt
index 22812eb..440b5d3a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url-expected.txt
@@ -4,7 +4,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (empty-background-url.css:1 -> empty-background-url.css:1:13)
+#inspected { (<style>…</style>)
     background-image: url();
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url.html
deleted file mode 100644
index 173bddc3..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<link rel="stylesheet" href="../styles/resources/empty-background-url.css">
-<script>
-function test() {
-  ElementsTestRunner.selectNodeAndWaitForStylesWithComputed('inspected', step1);
-
-  function step1() {
-    ElementsTestRunner.dumpSelectedElementStyles(true, false);
-    TestRunner.completeTest();
-  }
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>Tests that empty url in the property value does not break inspector.</p>
-<div id="inspected"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url.js
new file mode 100644
index 0000000..2711a26
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/empty-background-url.js
@@ -0,0 +1,24 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`Tests that empty url in the property value does not break inspector.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+        #inspected {
+            background-image: url();
+        }
+      </style>
+      <div id="inspected"></div>
+    `);
+
+  ElementsTestRunner.selectNodeAndWaitForStylesWithComputed('inspected', step1);
+
+  function step1() {
+    ElementsTestRunner.dumpSelectedElementStyles(true, false);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/filter-matched-styles.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/filter-matched-styles.html
deleted file mode 100644
index 2b78ae5..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/filter-matched-styles.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<html>
-<head>
-<script src="../../../inspector/inspector-test.js"></script>
-<script src="../../../inspector/elements-test.js"></script>
-<style>
-.mydiv {
-    border: 1px solid black;
-    padding: 10px 10px 10px 10px;
-}
-
-#inspected {
-    border-size: 2px;
-}
-
-</style>
-<script>
-function test() {
-  TestRunner.runTestSuite([
-    function selectInitialNode(next) {
-      ElementsTestRunner.selectNodeAndWaitForStyles('inspected', next);
-    },
-
-    function testSimpleFiltering(next) {
-      ElementsTestRunner.filterMatchedStyles('padding');
-      ElementsTestRunner.dumpRenderedMatchedStyles();
-      next();
-    },
-
-    function testLonghandsAreAutoExpanded(next) {
-      ElementsTestRunner.filterMatchedStyles('-top');
-      ElementsTestRunner.dumpRenderedMatchedStyles();
-      next();
-    },
-
-    function testAutoExpandedLonghandsAreCollapsed(next) {
-      ElementsTestRunner.filterMatchedStyles(null);
-      ElementsTestRunner.dumpRenderedMatchedStyles();
-      next();
-    }
-  ]);
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>Verifies that filtering in StylesSidebarPane works as expected.</p>
-<div style="margin: 1px;" class="mydiv" id="inspected"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/filter-matched-styles.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/filter-matched-styles.js
new file mode 100644
index 0000000..27223746
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/filter-matched-styles.js
@@ -0,0 +1,47 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`Verifies that filtering in StylesSidebarPane works as expected.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      .mydiv {
+          border: 1px solid black;
+          padding: 10px 10px 10px 10px;
+      }
+
+      #inspected {
+          border-size: 2px;
+      }
+
+      </style>
+      <div style="margin: 1px;" class="mydiv" id="inspected"></div>
+    `);
+
+  TestRunner.runTestSuite([
+    function selectInitialNode(next) {
+      ElementsTestRunner.selectNodeAndWaitForStyles('inspected', next);
+    },
+
+    function testSimpleFiltering(next) {
+      ElementsTestRunner.filterMatchedStyles('padding');
+      ElementsTestRunner.dumpRenderedMatchedStyles();
+      next();
+    },
+
+    function testLonghandsAreAutoExpanded(next) {
+      ElementsTestRunner.filterMatchedStyles('-top');
+      ElementsTestRunner.dumpRenderedMatchedStyles();
+      next();
+    },
+
+    function testAutoExpandedLonghandsAreCollapsed(next) {
+      ElementsTestRunner.filterMatchedStyles(null);
+      ElementsTestRunner.dumpRenderedMatchedStyles();
+      next();
+    }
+  ]);
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/resources/dynamic-style-tag.html b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/resources/dynamic-style-tag.html
new file mode 100644
index 0000000..4e47863
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/styles-1/resources/dynamic-style-tag.html
@@ -0,0 +1,27 @@
+<head>
+<style>
+/* comment */.inline-style-added-by-parser {
+    color: red;
+}
+</style>
+<style>
+.inline-style-added-by-parser-with-source-url {
+    color: green;
+}
+/*# sourceURL=inlineStyleAddedByParser.css*/
+</style>
+<script>
+    document.write("<style>\n.inline-style-added-by-parser-in-document-write {\n   color: blue;\n}\n</style>");
+    document.write("<style>\n.inline-style-added-by-document-write-with-source-url {\n   color: yellow;\n}\n/*# sourceURL=inlineStyleAddedByDocumentWrite.css*/\n</style>");
+    addStyleElement(".inline-style-created-by-script {\n   color: orange;\n}");
+    addStyleElement(".inline-style-created-by-script-with-source-url {\n   color: grey;\n}\n/*# sourceURL=inlineStyleCreatedByScript.css*/");
+
+    function addStyleElement(styleContent)
+    {
+        var styleElement = document.createElement("style");
+        styleElement.textContent = styleContent;
+        document.head.appendChild(styleElement);
+    }
+</script>
+</head>
+<div id="inspected" style="color:red" class="inline-style-added-by-parser inline-style-added-by-parser-with-source-url inline-style-added-by-parser-in-document-write inline-style-added-by-document-write-with-source-url inline-style-created-by-script inline-style-created-by-script-with-source-url"></div>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/network/resources/redirect-cross-origin-empty-html.php b/third_party/WebKit/LayoutTests/http/tests/devtools/network/resources/redirect-cross-origin-empty-html.php
new file mode 100644
index 0000000..1cffe015
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/network/resources/redirect-cross-origin-empty-html.php
@@ -0,0 +1,2 @@
+<?php
+	header("Location: http://localhost:8000/devtools/network/resources/empty.html");
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/response-interception-main-resource-cross-origin-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/response-interception-main-resource-cross-origin-expected.txt
new file mode 100644
index 0000000..48db5bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/response-interception-main-resource-cross-origin-expected.txt
@@ -0,0 +1,10 @@
+Tests to ensure response interception works with cross origin redirects.
+Network agent enabled
+Setting interception patterns to intercept: http://127.0.0.1:8000/devtools/network/resources/redirect-cross-origin-empty-html.php
+
+Navigating to: http://127.0.0.1:8000/devtools/network/resources/redirect-cross-origin-empty-html.php
+--- This url should redirect to: http://localhost:8000/devtools/network/resources/empty.html ---
+
+Request Intercepted: http://localhost:8000/devtools/network/resources/empty.html
+
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/response-interception-main-resource-cross-origin.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/response-interception-main-resource-cross-origin.js
new file mode 100644
index 0000000..b6ff167
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/response-interception-main-resource-cross-origin.js
@@ -0,0 +1,22 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startBlank(`Tests to ensure response interception works with cross origin redirects.`);
+  dp.Network.onRequestIntercepted(event => {
+      testRunner.log('Request Intercepted: ' + event.params.request.url);
+      testRunner.log('');
+      testRunner.completeTest();
+  });
+
+  await dp.Network.clearBrowserCookies();
+  await dp.Network.clearBrowserCache();
+  await dp.Network.setCacheDisabled({cacheDisabled: true});
+  await session.protocol.Network.enable();
+  testRunner.log('Network agent enabled');
+  testRunner.log('Setting interception patterns to intercept: http://127.0.0.1:8000/devtools/network/resources/redirect-cross-origin-empty-html.php');
+  await dp.Network.setRequestInterception({patterns: [{urlPattern: "http://localhost:8000/devtools/network/resources/empty.html", interceptionStage: 'HeadersReceived'}]});
+  testRunner.log('');
+
+  testRunner.log('Navigating to: http://127.0.0.1:8000/devtools/network/resources/redirect-cross-origin-empty-html.php');
+  testRunner.log('--- This url should redirect to: http://localhost:8000/devtools/network/resources/empty.html ---');
+  dp.Page.navigate({url: 'http://127.0.0.1:8000/devtools/network/resources/redirect-cross-origin-empty-html.php'});
+  testRunner.log('');
+})
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
index 50b12b0..adfb391 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.cpp
@@ -23,9 +23,6 @@
     url_.RemoveFragmentIdentifier();
 }
 
-ScriptSourceCode::ScriptSourceCode(ScriptResource* resource)
-    : ScriptSourceCode(nullptr, resource) {}
-
 ScriptSourceCode::ScriptSourceCode(ScriptStreamer* streamer,
                                    ScriptResource* resource)
     : source_(resource->SourceText()),
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h
index 3fd58b0c..52bb2bfd 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h
@@ -46,14 +46,17 @@
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
 
  public:
-  // We lose the encoding information from ScriptResource.
-  // Not sure if that matters.
-  explicit ScriptSourceCode(ScriptResource*);
+  // For inline scripts.
   ScriptSourceCode(
       const String& source,
       ScriptSourceLocationType = ScriptSourceLocationType::kUnknown,
       const KURL& = KURL(),
       const TextPosition& start_position = TextPosition::MinimumPosition());
+
+  // For external scripts.
+  //
+  // We lose the encoding information from ScriptResource.
+  // Not sure if that matters.
   ScriptSourceCode(ScriptStreamer*, ScriptResource*);
 
   ~ScriptSourceCode();
diff --git a/third_party/WebKit/Source/core/layout/LayoutInline.cpp b/third_party/WebKit/Source/core/layout/LayoutInline.cpp
index 7a4209fc..30d3fd5c 100644
--- a/third_party/WebKit/Source/core/layout/LayoutInline.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutInline.cpp
@@ -34,6 +34,7 @@
 #include "core/layout/LayoutView.h"
 #include "core/layout/api/LineLayoutBoxModel.h"
 #include "core/layout/line/InlineTextBox.h"
+#include "core/layout/ng/inline/ng_inline_fragment_iterator.h"
 #include "core/layout/ng/layout_ng_block_flow.h"
 #include "core/paint/BoxPainter.h"
 #include "core/paint/InlinePainter.h"
@@ -45,6 +46,19 @@
 
 namespace blink {
 
+namespace {
+
+// TODO(layout-dev): Once we generate fragment for all inline element, we should
+// use |LayoutObject::EnclosingBlockFlowFragment()|.
+const NGPhysicalBoxFragment* EnclosingBlockFlowFragmentOf(
+    const LayoutInline& node) {
+  if (!RuntimeEnabledFeatures::LayoutNGPaintFragmentsEnabled())
+    return nullptr;
+  return node.EnclosingBlockFlowFragment();
+}
+
+}  // anonymous namespace
+
 struct SameSizeAsLayoutInline : public LayoutBoxModelObject {
   ~SameSizeAsLayoutInline() override {}
   LayoutObjectChildList children_;
@@ -953,6 +967,19 @@
 }  // unnamed namespace
 
 LayoutRect LayoutInline::LinesBoundingBox() const {
+  if (const NGPhysicalBoxFragment* box_fragment =
+          EnclosingBlockFlowFragmentOf(*this)) {
+    LayoutRect result;
+    NGInlineFragmentIterator children(*box_fragment, this);
+    for (const auto& child : children) {
+      NGPhysicalOffset left_top =
+          child.fragment->Offset() + child.offset_to_container_box;
+      result.Unite(LayoutRect(LayoutPoint(left_top.left, left_top.top),
+                              child.fragment->Size().ToLayoutSize()));
+    }
+    return result;
+  }
+
   if (!AlwaysCreateLineBoxes()) {
     DCHECK(!FirstLineBox());
     FloatRect float_result;
diff --git a/third_party/WebKit/Source/core/layout/LayoutInlineTest.cpp b/third_party/WebKit/Source/core/layout/LayoutInlineTest.cpp
index ef11b4cd..8683a54 100644
--- a/third_party/WebKit/Source/core/layout/LayoutInlineTest.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutInlineTest.cpp
@@ -6,12 +6,63 @@
 
 #include "core/layout/LayoutBlockFlow.h"
 #include "core/layout/LayoutTestHelper.h"
+#include "platform/runtime_enabled_features.h"
+#include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
 
 class LayoutInlineTest : public RenderingTest {};
 
+// Helper class to run the same test code with and without LayoutNG
+class ParameterizedLayoutInlineTest
+    : public ::testing::WithParamInterface<bool>,
+      private ScopedLayoutNGForTest,
+      private ScopedLayoutNGPaintFragmentsForTest,
+      public LayoutInlineTest {
+ public:
+  ParameterizedLayoutInlineTest()
+      : ScopedLayoutNGForTest(GetParam()),
+        ScopedLayoutNGPaintFragmentsForTest(GetParam()) {}
+
+ protected:
+  bool LayoutNGEnabled() const { return GetParam(); }
+};
+
+INSTANTIATE_TEST_CASE_P(All, ParameterizedLayoutInlineTest, ::testing::Bool());
+
+TEST_P(ParameterizedLayoutInlineTest, LinesBoundingBox) {
+  LoadAhem();
+  SetBodyInnerHTML(
+      "<style>"
+      "* { font-family: Ahem; font-size: 13px; }"
+      ".vertical { writing-mode: vertical-rl; }"
+      "</style>"
+      "<p><span id=ltr1>abc<br>xyz</span></p>"
+      "<p><span id=ltr2>12 345 6789</span></p>"
+      "<p dir=rtl><span id=rtl1>abc<br>xyz</span></p>"
+      "<p dir=rtl><span id=rtl2>12 345 6789</span></p>"
+      "<p class=vertical><span id=vertical>abc<br>xyz</span></p>");
+  EXPECT_EQ(
+      LayoutRect(LayoutPoint(0, 0), LayoutSize(39, 26)),
+      ToLayoutInline(GetLayoutObjectByElementId("ltr1"))->LinesBoundingBox());
+  EXPECT_EQ(
+      LayoutRect(LayoutPoint(0, 0), LayoutSize(143, 13)),
+      ToLayoutInline(GetLayoutObjectByElementId("ltr2"))->LinesBoundingBox());
+  EXPECT_EQ(
+      LayoutRect(LayoutPoint(745, 0), LayoutSize(39, 26)),
+      ToLayoutInline(GetLayoutObjectByElementId("rtl1"))->LinesBoundingBox());
+  // TODO(layout-dev): LayoutNG should have same LayoutRect of legacy.
+  // See http://crbug.com/785687
+  EXPECT_EQ(
+      LayoutNGEnabled() ? LayoutRect(LayoutPoint(745, 0), LayoutSize(65, 13))
+                        : LayoutRect(LayoutPoint(641, 0), LayoutSize(143, 13)),
+      ToLayoutInline(GetLayoutObjectByElementId("rtl2"))->LinesBoundingBox());
+  EXPECT_EQ(LayoutRect(LayoutPoint(0, 0), LayoutSize(26, 39)),
+            ToLayoutInline(GetLayoutObjectByElementId("vertical"))
+                ->LinesBoundingBox());
+}
+
 TEST_F(LayoutInlineTest, SimpleContinuation) {
   SetBodyInnerHTML(
       "<span id='splitInline'><i id='before'></i><h1 id='blockChild'></h1><i "
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png b/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png
index 34beef0..0114d7e 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png
+++ b/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons_2x.png b/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons_2x.png
index 1964000b..d445127 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons_2x.png
+++ b/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons_2x.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/smallIcons.png b/third_party/WebKit/Source/devtools/front_end/Images/smallIcons.png
index ddd12a5..baec937a 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/smallIcons.png
+++ b/third_party/WebKit/Source/devtools/front_end/Images/smallIcons.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png b/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png
index 1d25e31..04372bb8 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png
+++ b/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/mediumIcons.svg b/third_party/WebKit/Source/devtools/front_end/Images/src/mediumIcons.svg
index 198e83df..0a2250d 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/mediumIcons.svg
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/mediumIcons.svg
@@ -12,7 +12,7 @@
    height="80"
    id="svg4775"
    version="1.1"
-   inkscape:version="0.48.0 r9654"
+   inkscape:version="0.48.4 r9939"
    sodipodi:docname="mediumIcons.svg"
    inkscape:export-filename="/Users/pfeldman/code/chromium/src/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png"
    inkscape:export-xdpi="90"
@@ -25,7 +25,7 @@
         <dc:format>image/svg+xml</dc:format>
         <dc:type
            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
+        <dc:title />
       </cc:Work>
     </rdf:RDF>
   </metadata>
@@ -715,4 +715,21 @@
        d="M 20,8 H 17.19 C 16.74,7.22 16.12,6.55 15.37,6.04 L 17,4.41 15.59,3 13.42,5.17 C 12.96,5.06 12.49,5 12,5 11.51,5 11.04,5.06 10.59,5.17 L 8.41,3 7,4.41 8.62,6.04 C 7.88,6.55 7.26,7.22 6.81,8 H 4 v 2 H 6.09 C 6.04,10.33 6,10.66 6,11 v 1 H 4 v 2 h 2 v 1 c 0,0.34 0.04,0.67 0.09,1 H 4 v 2 h 2.81 c 1.04,1.79 2.97,3 5.19,3 2.22,0 4.15,-1.21 5.19,-3 H 20 V 16 H 17.91 C 17.96,15.67 18,15.34 18,15 v -1 h 2 v -2 h -2 v -1 c 0,-0.34 -0.04,-0.67 -0.09,-1 H 20 V 8 z m -6,8 h -4 v -2 h 4 v 2 z m 0,-4 h -4 v -2 h 4 v 2 z"
        inkscape:connector-curvature="0" />
   </g>
+  <g
+     style="fill:none;stroke:none"
+     id="g6340"
+     transform="translate(48,0)">
+    <rect
+       style="opacity:0.2"
+       height="16"
+       width="16"
+       y="0"
+       x="0"
+       id="rect6342" />
+    <path
+       style="fill:#000000"
+       inkscape:connector-curvature="0"
+       id="path6344"
+       d="m 0.5,14 15,0 L 8,1 0.5,14 l 0,0 z m 8.5,-2 -2,0 0,-2 2,0 0,2 0,0 z M 9,9 7,9 7,6 9,6 9,9 9,9 z" />
+  </g>
 </svg>
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
index ddcd6d8..1e8a0aae 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
@@ -4,8 +4,8 @@
     "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28",
     "checkboxCheckmark.svg": "f039bf85cee42ad5c30ca3bfdce7912a",
     "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45",
-    "smallIcons.svg": "354f1686454c0605c0616f2ea45c426b",
-    "mediumIcons.svg": "06ad4148a1c881f25c07e27dc80aa1f6",
+    "smallIcons.svg": "beee6da075d6de8ef77fdf430ee798e5",
+    "mediumIcons.svg": "5ecf5731037057c627e6a222e9b24350",
     "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "treeoutlineTriangles.svg": "017d2f89437df0afc6b9cd5ff43735d9",
     "chevrons.svg": "79b4b527771e30b6388ce664077b3409"
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/smallIcons.svg b/third_party/WebKit/Source/devtools/front_end/Images/src/smallIcons.svg
index ede94e8..2139a32 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/smallIcons.svg
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/smallIcons.svg
@@ -12,7 +12,7 @@
    height="110"
    id="svg4185"
    version="1.1"
-   inkscape:version="0.48.0 r9654"
+   inkscape:version="0.48.4 r9939"
    sodipodi:docname="smallIcons.svg">
   <metadata
      id="metadata4459">
@@ -78,14 +78,13 @@
      inkscape:window-height="1006"
      id="namedview4455"
      showgrid="true"
-     inkscape:zoom="7.4167645"
-     inkscape:cx="64.063483"
-     inkscape:cy="73.026233"
-     inkscape:window-x="508"
+     inkscape:zoom="3.7083823"
+     inkscape:cx="29.009401"
+     inkscape:cy="135.34201"
+     inkscape:window-x="980"
      inkscape:window-y="0"
      inkscape:window-maximized="0"
-     inkscape:current-layer="svg4185"
-     inkscape:snap-global="true">
+     inkscape:current-layer="svg4185">
     <inkscape:grid
        type="xygrid"
        id="grid4461"
@@ -1026,4 +1025,19 @@
        inkscape:connector-curvature="0"
        style="fill:none" />
   </g>
+  <path
+     style="fill:#000000"
+     inkscape:connector-curvature="0"
+     d="M 105,22.528009 101,26.958771 101.94,28 105,24.617852 108.06,28 109,26.958771 z"
+     id="path3942" />
+  <path
+     style="fill:none"
+     inkscape:connector-curvature="0"
+     d="M 76.448271,14.990123 H 93.976446 V 34.405914 H 76.448271 z"
+     id="path3944" />
+  <path
+     style="fill:#000000"
+     inkscape:connector-curvature="0"
+     d="M 88.06,3.059999 85,6.113332 81.94,3.059999 l -0.94,0.94 4,4 4,-4 z"
+     id="path3974" />
 </svg>
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
index ddcd6d8..1e8a0aae 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
@@ -4,8 +4,8 @@
     "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28",
     "checkboxCheckmark.svg": "f039bf85cee42ad5c30ca3bfdce7912a",
     "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45",
-    "smallIcons.svg": "354f1686454c0605c0616f2ea45c426b",
-    "mediumIcons.svg": "06ad4148a1c881f25c07e27dc80aa1f6",
+    "smallIcons.svg": "beee6da075d6de8ef77fdf430ee798e5",
+    "mediumIcons.svg": "5ecf5731037057c627e6a222e9b24350",
     "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "treeoutlineTriangles.svg": "017d2f89437df0afc6b9cd5ff43735d9",
     "chevrons.svg": "79b4b527771e30b6388ce664077b3409"
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
index f71bddc..3c65e20 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
@@ -126,6 +126,8 @@
   'smallicon-cross': {position: 'b4', spritesheet: 'smallicons'},
   'smallicon-device': {position: 'c5', spritesheet: 'smallicons'},
   'smallicon-error': {position: 'c4', spritesheet: 'smallicons'},
+  'smallicon-expand-less': {position: 'f5', spritesheet: 'smallicons', isMask: true},
+  'smallicon-expand-more': {position: 'e6', spritesheet: 'smallicons', isMask: true},
   'smallicon-green-arrow': {position: 'a3', spritesheet: 'smallicons'},
   'smallicon-green-ball': {position: 'b3', spritesheet: 'smallicons'},
   'smallicon-info': {position: 'c3', spritesheet: 'smallicons'},
@@ -172,6 +174,7 @@
   'mediumicon-info-circle': {position: 'e2', spritesheet: 'mediumicons'},
   'mediumicon-bug': {position: 'd1', spritesheet: 'mediumicons'},
   'mediumicon-list': {position: 'e5', spritesheet: 'mediumicons'},
+  'mediumicon-warning': {position: 'd5', spritesheet: 'mediumicons', isMask: true},
 
   'badge-navigator-file-sync': {position: 'a9', spritesheet: 'largeicons'},
   'largeicon-activate-breakpoints': {position: 'b9', spritesheet: 'largeicons', isMask: true},
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
index 44fa0eb..f97edf6 100644
--- a/tools/gn/filesystem_utils.cc
+++ b/tools/gn/filesystem_utils.cc
@@ -412,6 +412,37 @@
 #endif
 }
 
+base::FilePath MakeAbsoluteFilePathRelativeIfPossible(
+    const base::FilePath& base,
+    const base::FilePath& target) {
+  DCHECK(base.IsAbsolute());
+  DCHECK(target.IsAbsolute());
+  std::vector<base::FilePath::StringType> base_components;
+  std::vector<base::FilePath::StringType> target_components;
+  base.GetComponents(&base_components);
+  target.GetComponents(&target_components);
+#if defined(OS_WIN)
+  // On Windows, it's impossible to have a relative path from C:\foo to D:\bar,
+  // so return the target as an aboslute path instead.
+  if (base_components[0] != target_components[0])
+    return target;
+#endif
+  size_t i;
+  for (i = 0; i < base_components.size() && i < target_components.size(); i++) {
+    if (base_components[i] != target_components[i])
+      break;
+  }
+  std::vector<base::FilePath::StringType> relative_components;
+  for (size_t j = i; j < base_components.size(); j++)
+    relative_components.push_back(base::FilePath::kParentDirectory);
+  for (size_t j = i; j < target_components.size(); j++)
+    relative_components.push_back(target_components[j]);
+  base::FilePath relative(base::FilePath::kCurrentDirectory);
+  for (const auto& component : relative_components)
+    relative = relative.Append(component);
+  return relative;
+}
+
 void NormalizePath(std::string* path, const base::StringPiece& source_root) {
   char* pathbuf = path->empty() ? nullptr : &(*path)[0];
 
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h
index bc14eb92..0396283 100644
--- a/tools/gn/filesystem_utils.h
+++ b/tools/gn/filesystem_utils.h
@@ -116,6 +116,16 @@
                                         const base::StringPiece& path,
                                         std::string* dest);
 
+// Given two absolute paths |base| and |target|, returns a relative path to
+// |target| as if the current directory was |base|.  The relative path returned
+// is minimal.  For example, if "../../a/b/" and "../b" are both valid, then the
+// latter will be returned.  On Windows, it's impossible to have a relative path
+// from C:\foo to D:\bar, so the absolute path |target| is returned instead for
+// this case.
+base::FilePath MakeAbsoluteFilePathRelativeIfPossible(
+    const base::FilePath& base,
+    const base::FilePath& target);
+
 // Collapses "." and sequential "/"s and evaluates "..". |path| may be
 // system-absolute, source-absolute, or relative. If |path| is source-absolute
 // and |source_root| is non-empty, |path| may be system absolute after this
diff --git a/tools/gn/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc
index f5724c43..03ec7809 100644
--- a/tools/gn/filesystem_utils_unittest.cc
+++ b/tools/gn/filesystem_utils_unittest.cc
@@ -173,6 +173,43 @@
 #endif
 }
 
+TEST(FilesystemUtils, MakeAbsoluteFilePathRelativeIfPossible) {
+#if defined(OS_WIN)
+  EXPECT_EQ(
+      base::FilePath(L"out\\Debug"),
+      MakeAbsoluteFilePathRelativeIfPossible(
+          base::FilePath(L"C:\\src"), base::FilePath(L"C:\\src\\out\\Debug")));
+  EXPECT_EQ(
+      base::FilePath(L"..\\.."),
+      MakeAbsoluteFilePathRelativeIfPossible(
+          base::FilePath(L"C:\\src\\out\\Debug"), base::FilePath(L"C:\\src")));
+  EXPECT_EQ(base::FilePath(L"."),
+            MakeAbsoluteFilePathRelativeIfPossible(base::FilePath(L"C:\\src"),
+                                                   base::FilePath(L"C:\\src")));
+  EXPECT_EQ(base::FilePath(L"..\\..\\..\\u\\v\\w"),
+            MakeAbsoluteFilePathRelativeIfPossible(
+                base::FilePath(L"C:\\a\\b\\c\\x\\y\\z"),
+                base::FilePath(L"C:\\a\\b\\c\\u\\v\\w")));
+  EXPECT_EQ(base::FilePath(L"D:\\bar"),
+            MakeAbsoluteFilePathRelativeIfPossible(base::FilePath(L"C:\\foo"),
+                                                   base::FilePath(L"D:\\bar")));
+#else
+  EXPECT_EQ(base::FilePath("out/Debug"),
+            MakeAbsoluteFilePathRelativeIfPossible(
+                base::FilePath("/src"), base::FilePath("/src/out/Debug")));
+  EXPECT_EQ(base::FilePath("../.."),
+            MakeAbsoluteFilePathRelativeIfPossible(
+                base::FilePath("/src/out/Debug"), base::FilePath("/src")));
+  EXPECT_EQ(base::FilePath("."),
+            MakeAbsoluteFilePathRelativeIfPossible(base::FilePath("/src"),
+                                                   base::FilePath("/src")));
+  EXPECT_EQ(
+      base::FilePath("../../../u/v/w"),
+      MakeAbsoluteFilePathRelativeIfPossible(base::FilePath("/a/b/c/x/y/z"),
+                                             base::FilePath("/a/b/c/u/v/w")));
+#endif
+}
+
 TEST(FilesystemUtils, NormalizePath) {
   std::string input;
 
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc
index 4ee3f6043..334071a6 100644
--- a/tools/gn/ninja_build_writer.cc
+++ b/tools/gn/ninja_build_writer.cc
@@ -48,10 +48,15 @@
 };
 
 std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
-  base::FilePath executable;
-  PathService::Get(base::FILE_EXE, &executable);
+  const base::FilePath build_path =
+      build_settings->build_dir().Resolve(build_settings->root_path());
 
-  base::CommandLine cmdline(executable.NormalizePathSeparatorsTo('/'));
+  base::FilePath exe_path;
+  PathService::Get(base::FILE_EXE, &exe_path);
+  if (build_path.IsAbsolute())
+    exe_path = MakeAbsoluteFilePathRelativeIfPossible(build_path, exe_path);
+
+  base::CommandLine cmdline(exe_path.NormalizePathSeparatorsTo('/'));
 
   // Use "." for the directory to generate. When Ninja runs the command it
   // will have the build directory as the current one. Coding it explicitly
@@ -59,8 +64,12 @@
   cmdline.AppendArg("gen");
   cmdline.AppendArg(".");
 
+  base::FilePath root_path = build_settings->root_path();
+  if (build_path.IsAbsolute())
+    root_path = MakeAbsoluteFilePathRelativeIfPossible(build_path, root_path);
+
   cmdline.AppendSwitchPath(std::string("--") + switches::kRoot,
-                           build_settings->root_path());
+                           root_path.NormalizePathSeparatorsTo('/'));
   // Successful automatic invocations shouldn't print output.
   cmdline.AppendSwitch(std::string("-") + switches::kQuiet);
 
@@ -267,8 +276,13 @@
   std::set<base::FilePath> fileset(input_files.begin(), input_files.end());
   fileset.insert(other_files.begin(), other_files.end());
 
-  for (const auto& other_file : fileset)
-    dep_out_ << " " << FilePathToUTF8(other_file);
+  const base::FilePath build_path =
+      build_settings_->build_dir().Resolve(build_settings_->root_path());
+  for (const auto& other_file : fileset) {
+    const base::FilePath file =
+        MakeAbsoluteFilePathRelativeIfPossible(build_path, other_file);
+    dep_out_ << " " << FilePathToUTF8(file.NormalizePathSeparatorsTo('/'));
+  }
 
   out_ << std::endl;
 }
diff --git a/tools/grit/grit/format/data_pack.py b/tools/grit/grit/format/data_pack.py
index 075899c..6037dc60 100755
--- a/tools/grit/grit/format/data_pack.py
+++ b/tools/grit/grit/format/data_pack.py
@@ -73,17 +73,19 @@
 
 def Format(root, lang='en', output_dir='.'):
   """Writes out the data pack file format (platform agnostic resource file)."""
+  id_map = root.GetIdMap()
   data = {}
   root.info = []
   for node in root.ActiveDescendants():
     with node:
       if isinstance(node, (include.IncludeNode, message.MessageNode,
                            structure.StructureNode)):
-        id, value = node.GetDataPackPair(lang, UTF8)
+        value = node.GetDataPackValue(lang, UTF8)
         if value is not None:
-          data[id] = value
-          root.info.append(
-              '{},{},{}'.format(node.attrs.get('name'), id, node.source))
+          resource_id = id_map[node.GetTextualIds()[0]]
+          data[resource_id] = value
+          root.info.append('{},{},{}'.format(
+              node.attrs.get('name'), resource_id, node.source))
   return WriteDataPackToString(data, UTF8)
 
 
diff --git a/tools/grit/grit/format/gzip_string_unittest.py b/tools/grit/grit/format/gzip_string_unittest.py
old mode 100644
new mode 100755
index 8df8b3b..a920693
--- a/tools/grit/grit/format/gzip_string_unittest.py
+++ b/tools/grit/grit/format/gzip_string_unittest.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 # Copyright (c) 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/rc.py b/tools/grit/grit/format/rc.py
index bd3de1a..5dfeb03 100755
--- a/tools/grit/grit/format/rc.py
+++ b/tools/grit/grit/format/rc.py
@@ -12,7 +12,6 @@
 from functools import partial
 
 from grit import util
-from grit.format import rc_header
 from grit.node import misc
 
 
@@ -442,7 +441,7 @@
     return ''
 
   name = item.attrs['name']
-  item_id = rc_header.GetIds(item.GetRoot())[name]
+  item_id = item.GetRoot().GetIdMap()[name]
   return '// ID: %d\n%-18s %-18s "%s"\n' % (item_id, name, type, filename)
 
 
diff --git a/tools/grit/grit/format/rc_header.py b/tools/grit/grit/format/rc_header.py
index 7b02122..cea0cad 100755
--- a/tools/grit/grit/format/rc_header.py
+++ b/tools/grit/grit/format/rc_header.py
@@ -6,9 +6,7 @@
 '''Item formatters for RC headers.
 '''
 
-from grit import exception
-from grit import util
-from grit.extern import FP
+from grit.node import message
 
 
 def Format(root, lang='en', output_dir='.'):
@@ -44,13 +42,11 @@
     output_all_resource_defines: If False, output only the symbols used in the
       current output configuration.
   '''
-  from grit.node import message
-  tids = GetIds(root)
-
   if output_all_resource_defines:
     items = root.Preorder()
   else:
     items = root.ActiveDescendants()
+  tids = root.GetIdMap()
 
   if not rc_header_format:
     rc_header_format = "#define {textual_id} {numeric_id}"
@@ -74,164 +70,3 @@
           if tid in tids and tid not in seen:
             seen.add(tid)
             yield rc_header_format.format(textual_id=tid,numeric_id=tids[tid])
-
-
-_cached_ids = {}
-
-
-_predetermined_tids = {}
-
-
-def SetPredeterminedIdsFile(predetermined_ids_file):
-  global _predetermined_tids
-  if predetermined_ids_file:
-    _predetermined_tids = _ReadIdsFromFile(predetermined_ids_file)
-  else:
-    _predetermined_tids = {}
-
-
-def _ReadIdsFromFile(path):
-  with open(path, "r") as f:
-    content = f.readlines()
-  tids = {}  # Maps textual id to numeric id
-  for line in content:
-    tid, id = line.split()
-    tids[tid] = int(id)
-  return tids
-
-
-def GetIds(root):
-  '''Return a dictionary mapping textual ids to numeric ids for the given tree.
-
-  Args:
-    root: A GritNode.
-  '''
-  global _cached_ids
-  global _predetermined_tids
-  # TODO(benrg): Since other formatters use this, it might make sense to move it
-  # and _ComputeIds to GritNode and store the cached ids as an attribute. On the
-  # other hand, GritNode has too much random stuff already.
-  if root not in _cached_ids:
-    _cached_ids[root] = _ComputeIds(root, _predetermined_tids)
-  return _cached_ids[root]
-
-
-def _ComputeIds(root, predetermined_tids):
-  from grit.node import empty, include, message, misc, structure
-
-  ids = {}  # Maps numeric id to textual id
-  tids = {}  # Maps textual id to numeric id
-  id_reasons = {}  # Maps numeric id to text id and a human-readable explanation
-  group = None
-  last_id = None
-  predetermined_ids = {value: key
-                       for key, value in predetermined_tids.iteritems()}
-
-  for item in root:
-    if isinstance(item, empty.GroupingNode):
-      # Note: this won't work if any GroupingNode can be contained inside
-      # another.
-      group = item
-      last_id = None
-      continue
-
-    assert not item.GetTextualIds() or isinstance(item,
-        (include.IncludeNode, message.MessageNode,
-         misc.IdentifierNode, structure.StructureNode))
-
-    # Resources that use the RES protocol don't need
-    # any numerical ids generated, so we skip them altogether.
-    # This is accomplished by setting the flag 'generateid' to false
-    # in the GRD file.
-    if item.attrs.get('generateid', 'true') == 'false':
-      continue
-
-    for tid in item.GetTextualIds():
-      if util.SYSTEM_IDENTIFIERS.match(tid):
-        # Don't emit a new ID for predefined IDs
-        continue
-
-      if tid in tids:
-        continue
-
-      if predetermined_tids and tid in predetermined_tids:
-        id = predetermined_tids[tid]
-        reason = "from predetermined_tids map"
-
-      # Some identifier nodes can provide their own id,
-      # and we use that id in the generated header in that case.
-      elif hasattr(item, 'GetId') and item.GetId():
-        id = long(item.GetId())
-        reason = 'returned by GetId() method'
-
-      elif ('offset' in item.attrs and group and
-            group.attrs.get('first_id', '') != ''):
-         offset_text = item.attrs['offset']
-         parent_text = group.attrs['first_id']
-
-         try:
-          offset_id = long(offset_text)
-         except ValueError:
-          offset_id = tids[offset_text]
-
-         try:
-          parent_id = long(parent_text)
-         except ValueError:
-          parent_id = tids[parent_text]
-
-         id = parent_id + offset_id
-         reason = 'first_id %d + offset %d' % (parent_id, offset_id)
-
-      # We try to allocate IDs sequentially for blocks of items that might
-      # be related, for instance strings in a stringtable (as their IDs might be
-      # used e.g. as IDs for some radio buttons, in which case the IDs must
-      # be sequential).
-      #
-      # We do this by having the first item in a section store its computed ID
-      # (computed from a fingerprint) in its parent object.  Subsequent children
-      # of the same parent will then try to get IDs that sequentially follow
-      # the currently stored ID (on the parent) and increment it.
-      elif last_id is None:
-        # First check if the starting ID is explicitly specified by the parent.
-        if group and group.attrs.get('first_id', '') != '':
-          id = long(group.attrs['first_id'])
-          reason = "from parent's first_id attribute"
-        else:
-          # Automatically generate the ID based on the first clique from the
-          # first child of the first child node of our parent (i.e. when we
-          # first get to this location in the code).
-
-          # According to
-          # http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx
-          # the safe usable range for resource IDs in Windows is from decimal
-          # 101 to 0x7FFF.
-
-          id = FP.UnsignedFingerPrint(tid)
-          id = id % (0x7FFF - 101) + 101
-          reason = 'chosen by random fingerprint -- use first_id to override'
-
-        last_id = id
-      else:
-        id = last_id = last_id + 1
-        reason = 'sequentially assigned'
-
-      reason = "%s (%s)" % (tid, reason)
-      # Don't fail when 'offset' is specified, as the base and the 0th
-      # offset will have the same ID.
-      if id in id_reasons and not 'offset' in item.attrs:
-        raise exception.IdRangeOverlap('ID %d was assigned to both %s and %s.'
-                                       % (id, id_reasons[id], reason))
-
-      if id < 101:
-        print ('WARNING: Numeric resource IDs should be greater than 100 to\n'
-               'avoid conflicts with system-defined resource IDs.')
-
-      if tid not in predetermined_tids and id in predetermined_ids:
-        raise exception.IdRangeOverlap('ID %d overlaps between %s and %s'
-                                       % (id, tid, predetermined_ids[tid]))
-
-      ids[id] = tid
-      tids[tid] = id
-      id_reasons[id] = reason
-
-  return tids
diff --git a/tools/grit/grit/format/rc_header_unittest.py b/tools/grit/grit/format/rc_header_unittest.py
index 22c5f38..3cba963 100755
--- a/tools/grit/grit/format/rc_header_unittest.py
+++ b/tools/grit/grit/format/rc_header_unittest.py
@@ -10,13 +10,11 @@
 
 import os
 import sys
-import tempfile
+import unittest
+
 if __name__ == '__main__':
   sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
 
-import StringIO
-import unittest
-
 from grit import exception
 from grit import grd_reader
 from grit import util
@@ -28,71 +26,55 @@
     output = rc_header.FormatDefines(grd, grd.ShouldOutputAllResourceDefines())
     return ''.join(output).replace(' ', '')
 
-  def _MakeTempPredeterminedIdsFile(self, content):
-    tmp_dir = tempfile.gettempdir()
-    predetermined_ids_file = tmp_dir + "/predetermined_ids.txt"
-    with open(predetermined_ids_file, 'w') as f:
-      f.write(content)
-    return predetermined_ids_file
-
   def testFormatter(self):
-    grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3" base_dir=".">
-        <release seq="3">
-          <includes first_id="300" comment="bingo">
-            <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-          </includes>
-          <messages first_id="10000">
-            <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-              Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-            </message>
-            <message name="IDS_BONGO">
-              Bongo!
-            </message>
-          </messages>
-          <structures>
-            <structure type="dialog" name="IDD_NARROW_DIALOG" file="rc_files/dialogs.rc" />
-            <structure type="version" name="VS_VERSION_INFO" file="rc_files/version.rc" />
-          </structures>
-        </release>
-      </grit>'''), '.')
+    grd = util.ParseGrdForUnittest('''
+        <includes first_id="300" comment="bingo">
+          <include type="gif" name="ID_LOGO" file="images/logo.gif" />
+        </includes>
+        <messages first_id="10000">
+          <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
+            Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
+          </message>
+          <message name="IDS_BONGO">
+            Bongo!
+          </message>
+        </messages>
+        <structures>
+          <structure type="dialog" name="IDD_NARROW_DIALOG" file="rc_files/dialogs.rc" />
+          <structure type="version" name="VS_VERSION_INFO" file="rc_files/version.rc" />
+        </structures>''')
     output = self.FormatAll(grd)
     self.failUnless(output.count('IDS_GREETING10000'))
     self.failUnless(output.count('ID_LOGO300'))
 
   def testOnlyDefineResourcesThatSatisfyOutputCondition(self):
-    grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3"
-            base_dir="." output_all_resource_defines="false">
-        <release seq="3">
-          <includes first_id="300" comment="bingo">
-            <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-          </includes>
-          <messages first_id="10000">
-            <message name="IDS_FIRSTPRESENTSTRING" desc="Present in .rc file.">
-              I will appear in the .rc file.
+    grd = util.ParseGrdForUnittest('''
+        <includes first_id="300" comment="bingo">
+          <include type="gif" name="ID_LOGO" file="images/logo.gif" />
+        </includes>
+        <messages first_id="10000">
+          <message name="IDS_FIRSTPRESENTSTRING" desc="Present in .rc file.">
+            I will appear in the .rc file.
+          </message>
+          <if expr="False"> <!--Do not include in the .rc files until used.-->
+            <message name="IDS_MISSINGSTRING" desc="Not present in .rc file.">
+              I will not appear in the .rc file.
             </message>
-            <if expr="False"> <!--Do not include in the .rc files until used.-->
-              <message name="IDS_MISSINGSTRING" desc="Not present in .rc file.">
-                I will not appear in the .rc file.
-              </message>
-            </if>
-            <if expr="lang != 'es'">
-              <message name="IDS_LANGUAGESPECIFICSTRING" desc="Present in .rc file.">
-                Hello.
-              </message>
-            </if>
-            <if expr="lang == 'es'">
-              <message name="IDS_LANGUAGESPECIFICSTRING" desc="Present in .rc file.">
-                Hola.
-              </message>
-            </if>
-            <message name="IDS_THIRDPRESENTSTRING" desc="Present in .rc file.">
-              I will also appear in the .rc file.
+          </if>
+          <if expr="lang != 'es'">
+            <message name="IDS_LANGUAGESPECIFICSTRING" desc="Present in .rc file.">
+              Hello.
             </message>
-         </messages>
-        </release>
-      </grit>'''), '.')
+          </if>
+          <if expr="lang == 'es'">
+            <message name="IDS_LANGUAGESPECIFICSTRING" desc="Present in .rc file.">
+              Hola.
+            </message>
+          </if>
+          <message name="IDS_THIRDPRESENTSTRING" desc="Present in .rc file.">
+            I will also appear in the .rc file.
+          </message>
+       </messages>''')
     output = self.FormatAll(grd)
     self.failUnless(output.count('IDS_FIRSTPRESENTSTRING10000'))
     self.failIf(output.count('IDS_MISSINGSTRING'))
@@ -100,47 +82,8 @@
     self.failUnless(output.count('IDS_LANGUAGESPECIFICSTRING10002'))
     self.failUnless(output.count('IDS_THIRDPRESENTSTRING10003'))
 
-  def testExplicitFirstIdOverlaps(self):
-    # second first_id will overlap preexisting range
-    grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3" base_dir=".">
-        <release seq="3">
-          <includes first_id="300" comment="bingo">
-            <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-            <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
-          </includes>
-          <messages first_id="301">
-            <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-              Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-            </message>
-            <message name="IDS_SMURFGEBURF">Frubegfrums</message>
-          </messages>
-        </release>
-      </grit>'''), '.')
-    self.assertRaises(exception.IdRangeOverlap, self.FormatAll, grd)
-
-  def testImplicitOverlapsPreexisting(self):
-    # second message in <messages> will overlap preexisting range
-    grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3" base_dir=".">
-        <release seq="3">
-          <includes first_id="301" comment="bingo">
-            <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-            <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
-          </includes>
-          <messages first_id="300">
-            <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-              Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-            </message>
-            <message name="IDS_SMURFGEBURF">Frubegfrums</message>
-          </messages>
-        </release>
-      </grit>'''), '.')
-    self.assertRaises(exception.IdRangeOverlap, self.FormatAll, grd)
-
   def testEmit(self):
-    grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3" base_dir=".">
+    grd = util.ParseGrdForUnittest('''
         <outputs>
           <output type="rc_all" filename="dummy">
             <emit emit_type="prepend">Wrong</emit>
@@ -156,29 +99,24 @@
           <output type="rc_header" filename="dummy">
             <emit emit_type="prepend">Bingo</emit>
           </output>
-        </outputs>
-      </grit>'''), '.')
+        </outputs>''')
     output = ''.join(rc_header.Format(grd, 'en', '.'))
     output = util.StripBlankLinesAndComments(output)
     self.assertEqual('#pragma once\nBingo', output)
 
   def testRcHeaderFormat(self):
-    grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3" base_dir=".">
-        <release seq="3">
-          <includes first_id="300" comment="bingo">
-            <include type="gif" name="IDR_LOGO" file="images/logo.gif" />
-          </includes>
-          <messages first_id="10000">
-            <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-              Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-            </message>
-            <message name="IDS_BONGO">
-              Bongo!
-            </message>
-          </messages>
-        </release>
-      </grit>'''), '.')
+    grd = util.ParseGrdForUnittest('''
+        <includes first_id="300" comment="bingo">
+          <include type="gif" name="IDR_LOGO" file="images/logo.gif" />
+        </includes>
+        <messages first_id="10000">
+          <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
+            Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
+          </message>
+          <message name="IDS_BONGO">
+            Bongo!
+          </message>
+        </messages>''')
 
     # Using the default rc_header format string.
     output = rc_header.FormatDefines(grd, grd.ShouldOutputAllResourceDefines(),
@@ -197,53 +135,5 @@
                       '#define IDS_BONGO _Pragma("IDS_BONGO") 10001\n'),
                      ''.join(output))
 
-  def testPredeterminedIds(self):
-    predetermined_ids_file = self._MakeTempPredeterminedIdsFile(
-        'IDS_BONGO 101\nID_LOGO 102\n')
-    grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3" base_dir=".">
-        <release seq="3">
-          <includes first_id="300" comment="bingo">
-            <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-          </includes>
-          <messages first_id="10000">
-            <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-              Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-            </message>
-            <message name="IDS_BONGO">
-              Bongo!
-            </message>
-          </messages>
-        </release>
-      </grit>'''), '.', predetermined_ids_file=predetermined_ids_file)
-    output = rc_header.FormatDefines(grd, grd.ShouldOutputAllResourceDefines(),
-                                     grd.GetRcHeaderFormat())
-    self.assertEqual(('#define ID_LOGO 102\n'
-                      '#define IDS_GREETING 10000\n'
-                      '#define IDS_BONGO 101\n'), ''.join(output))
-
-  def testPredeterminedIdsOverlap(self):
-    predetermined_ids_file = self._MakeTempPredeterminedIdsFile(
-        'ID_LOGO 10000\n')
-    grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3" base_dir=".">
-        <release seq="3">
-          <includes first_id="300" comment="bingo">
-            <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-          </includes>
-          <messages first_id="10000">
-            <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-              Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-            </message>
-            <message name="IDS_BONGO">
-              Bongo!
-            </message>
-          </messages>
-        </release>
-      </grit>'''), '.', predetermined_ids_file=predetermined_ids_file)
-    output = rc_header.FormatDefines(grd, grd.ShouldOutputAllResourceDefines(),
-                                     grd.GetRcHeaderFormat())
-    self.assertRaises(exception.IdRangeOverlap, self.FormatAll, grd)
-
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/grit/grit/format/resource_map.py b/tools/grit/grit/format/resource_map.py
index f1e3132..eb163ed 100755
--- a/tools/grit/grit/format/resource_map.py
+++ b/tools/grit/grit/format/resource_map.py
@@ -108,10 +108,9 @@
 
 
 def _FormatSource(get_key, root, lang, output_dir):
-  from grit.format import rc_header
   from grit.node import include, structure, message
+  id_map = root.GetIdMap()
   yield _FormatSourceHeader(root, output_dir)
-  tids = rc_header.GetIds(root)
   seen = set()
   active_descendants = [item for item in root.ActiveDescendants()]
   output_all_resource_defines = root.ShouldOutputAllResourceDefines()
@@ -120,7 +119,7 @@
       continue
     key = get_key(item)
     tid = item.attrs['name']
-    if tid not in tids or key in seen:
+    if tid not in id_map or key in seen:
       continue
     if item.GeneratesResourceMapEntry(output_all_resource_defines,
                                       item in active_descendants):
diff --git a/tools/grit/grit/format/resource_map_unittest.py b/tools/grit/grit/format/resource_map_unittest.py
index 31d481e..939341c 100755
--- a/tools/grit/grit/format/resource_map_unittest.py
+++ b/tools/grit/grit/format/resource_map_unittest.py
@@ -10,7 +10,6 @@
 if __name__ == '__main__':
   sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
 
-import StringIO
 import unittest
 
 from grit import grd_reader
@@ -20,10 +19,7 @@
 
 class FormatResourceMapUnittest(unittest.TestCase):
   def testFormatResourceMap(self):
-    grd = grd_reader.Parse(StringIO.StringIO(
-      '''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3"
-            base_dir=".">
+    grd = util.ParseGrdForUnittest('''
         <outputs>
           <output type="rc_header" filename="the_rc_header.h" />
           <output type="resource_map_header"
@@ -47,10 +43,7 @@
             </if>
             <include type="foo" file="mno" name="IDS_THIRDPRESENT" />
          </includes>
-        </release>
-      </grit>'''), util.PathFromRoot('.'))
-    grd.SetOutputLanguage('en')
-    grd.RunGatherers()
+       </release>''', run_gatherers=True)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
     self.assertEqual('''\
@@ -97,10 +90,7 @@
 const size_t kTheRcHeaderSize = arraysize(kTheRcHeader);''', output)
 
   def testFormatResourceMapWithOutputAllEqualsFalseForStructures(self):
-    grd = grd_reader.Parse(StringIO.StringIO(
-      '''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3"
-            base_dir="." output_all_resource_defines="false">
+    grd = util.ParseGrdForUnittest('''
         <outputs>
           <output type="rc_header" filename="the_rc_header.h" />
           <output type="resource_map_header"
@@ -139,10 +129,7 @@
                          file="xyz.png" />
             </if>
          </structures>
-        </release>
-      </grit>'''), util.PathFromRoot('.'))
-    grd.SetOutputLanguage('en')
-    grd.RunGatherers()
+        </release>''', run_gatherers=True, output_all_resource_defines=False)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
     self.assertEqual('''\
@@ -186,10 +173,7 @@
 const size_t kTheRcHeaderSize = arraysize(kTheRcHeader);''', output)
 
   def testFormatResourceMapWithOutputAllEqualsFalseForIncludes(self):
-    grd = grd_reader.Parse(StringIO.StringIO(
-      '''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3"
-            base_dir="." output_all_resource_defines="false">
+    grd = util.ParseGrdForUnittest('''
         <outputs>
           <output type="rc_header" filename="the_rc_header.h" />
           <output type="resource_map_header"
@@ -224,10 +208,7 @@
               <include type="foo" file="xyz" name="IDS_LAST" />
             </if>
          </includes>
-        </release>
-      </grit>'''), util.PathFromRoot('.'))
-    grd.SetOutputLanguage('en')
-    grd.RunGatherers()
+        </release>''', run_gatherers=True, output_all_resource_defines=False)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
     self.assertEqual('''\
@@ -275,10 +256,7 @@
 const size_t kTheRcHeaderSize = arraysize(kTheRcHeader);''', output)
 
   def testFormatStringResourceMap(self):
-    grd = grd_reader.Parse(StringIO.StringIO(
-      '''<?xml version="1.0" encoding="UTF-8"?>
-      <grit latest_public_release="2" source_lang_id="en" current_release="3"
-            base_dir=".">
+    grd = util.ParseGrdForUnittest('''
         <outputs>
           <output type="rc_header" filename="the_rc_header.h" />
           <output type="resource_map_header" filename="the_rc_map_header.h" />
@@ -302,10 +280,7 @@
               </message>
             </if>
           </messages>
-        </release>
-      </grit>'''), util.PathFromRoot('.'))
-    grd.SetOutputLanguage('en')
-    grd.RunGatherers()
+        </release>''', run_gatherers=True, output_all_resource_defines=False)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
     self.assertEqual('''\
diff --git a/tools/grit/grit/grd_reader.py b/tools/grit/grit/grd_reader.py
index fb33e9c7..733dc23 100755
--- a/tools/grit/grit/grd_reader.py
+++ b/tools/grit/grit/grd_reader.py
@@ -13,7 +13,6 @@
 
 from grit import exception
 from grit import util
-from grit.format import rc_header
 from grit.node import base
 from grit.node import mapping
 from grit.node import misc
@@ -192,7 +191,6 @@
   else:
     source = None
 
-  rc_header.SetPredeterminedIdsFile(predetermined_ids_file)
   handler = GrdContentHandler(stop_after=stop_after, debug=debug, dir=dir,
                               defines=defines, tags_to_ignore=tags_to_ignore,
                               target_platform=target_platform, source=source)
@@ -215,6 +213,7 @@
     handler.root.SetOwnDir(dir)
 
   if isinstance(handler.root, misc.GritNode):
+    handler.root.SetPredeterminedIdsFile(predetermined_ids_file)
     if first_ids_file:
       # Make the path to the first_ids_file relative to the grd file,
       # unless it begins with GRIT_DIR.
diff --git a/tools/grit/grit/node/include.py b/tools/grit/grit/node/include.py
index 5787e6e1..3ebdcf61 100755
--- a/tools/grit/grit/node/include.py
+++ b/tools/grit/grit/node/include.py
@@ -12,7 +12,6 @@
 from grit import util
 import grit.format.html_inline
 import grit.format.rc
-import grit.format.rc_header
 from grit.format import minifier
 from grit.node import base
 
@@ -77,15 +76,8 @@
 
     return self.ToRealPath(input_path)
 
-  def GetDataPackPair(self, lang, encoding):
-    """Returns a (id, string) pair that represents the resource id and raw
-    bytes of the data.  This is used to generate the data pack data file.
-    """
-    # TODO(benrg/joi): Move this and other implementations of GetDataPackPair
-    # to grit.format.data_pack?
-    from grit.format import rc_header
-    id_map = rc_header.GetIds(self.GetRoot())
-    id = id_map[self.GetTextualIds()[0]]
+  def GetDataPackValue(self, lang, encoding):
+    '''Returns a str represenation for a data_pack entry.'''
     filename = self.ToRealPath(self.GetInputPath())
     if self.attrs['flattenhtml'] == 'true':
       allow_external_script = self.attrs['allowexternalscript'] == 'true'
@@ -98,7 +90,7 @@
 
     # Include does not care about the encoding, because it only returns binary
     # data.
-    return id, self.CompressDataIfNeeded(data)
+    return self.CompressDataIfNeeded(data)
 
   def Process(self, output_dir):
     """Rewrite file references to be base64 encoded data URLs.  The new file
diff --git a/tools/grit/grit/node/include_unittest.py b/tools/grit/grit/node/include_unittest.py
index 031c1bfa..3bf1a6891 100755
--- a/tools/grit/grit/node/include_unittest.py
+++ b/tools/grit/grit/node/include_unittest.py
@@ -76,7 +76,7 @@
                    compress="gzip" type="BINDATA"/>
         </includes>''', base_dir = util.PathFromRoot('grit/testdata'))
     inc, = root.GetChildrenOfType(include.IncludeNode)
-    throwaway, compressed = inc.GetDataPackPair(lang='en', encoding=1)
+    compressed = inc.GetDataPackValue(lang='en', encoding=1)
 
     decompressed_data = zlib.decompress(compressed, 16 + zlib.MAX_WBITS)
     self.assertEqual(util.ReadFile(util.PathFromRoot('grit/testdata')
diff --git a/tools/grit/grit/node/message.py b/tools/grit/grit/node/message.py
index e3024fb..b8eca3e 100755
--- a/tools/grit/grit/node/message.py
+++ b/tools/grit/grit/node/message.py
@@ -223,17 +223,10 @@
     '''We always expand variables on Messages.'''
     return True
 
-  def GetDataPackPair(self, lang, encoding):
-    '''Returns a (id, string) pair that represents the string id and the string
-    in the specified encoding, where |encoding| is one of the encoding values
-    accepted by util.Encode.  This is used to generate the data pack data file.
-    '''
-    from grit.format import rc_header
-    id_map = rc_header.GetIds(self.GetRoot())
-    id = id_map[self.GetTextualIds()[0]]
-
+  def GetDataPackValue(self, lang, encoding):
+    '''Returns a str represenation for a data_pack entry.'''
     message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
-    return id, util.Encode(message, encoding)
+    return util.Encode(message, encoding)
 
   def IsResourceMapSource(self):
     return True
diff --git a/tools/grit/grit/node/misc.py b/tools/grit/grit/node/misc.py
index bd999709b..7cff4930 100755
--- a/tools/grit/grit/node/misc.py
+++ b/tools/grit/grit/node/misc.py
@@ -13,6 +13,7 @@
 from grit import constants
 from grit import exception
 from grit import util
+from grit.extern import FP
 import grit.format.rc_header
 from grit.node import base
 from grit.node import io
@@ -72,6 +73,138 @@
   return (src_root_dir, first_ids_dict)
 
 
+def _ComputeIds(root, predetermined_tids):
+  """Returns a dict of textual id -> numeric id for all nodes in root.
+
+  IDs are mostly assigned sequentially, but will vary based on:
+    * first_id node attribute (from first_ids_file)
+    * hash of textual id (if not first_id is defined)
+    * offset node attribute
+    * whether the textual id matches a system id
+    * whether the node generates its own ID via GetId()
+
+  Args:
+    predetermined_tids: Dict of textual id -> numeric id to use in return dict.
+  """
+  from grit.node import empty, include, message, misc, structure
+
+  ids = {}  # Maps numeric id to textual id
+  tids = {}  # Maps textual id to numeric id
+  id_reasons = {}  # Maps numeric id to text id and a human-readable explanation
+  group = None
+  last_id = None
+  predetermined_ids = {value: key
+                       for key, value in predetermined_tids.iteritems()}
+
+  for item in root:
+    if isinstance(item, empty.GroupingNode):
+      # Note: this won't work if any GroupingNode can be contained inside
+      # another.
+      group = item
+      last_id = None
+      continue
+
+    assert not item.GetTextualIds() or isinstance(item,
+        (include.IncludeNode, message.MessageNode,
+         misc.IdentifierNode, structure.StructureNode))
+
+    # Resources that use the RES protocol don't need
+    # any numerical ids generated, so we skip them altogether.
+    # This is accomplished by setting the flag 'generateid' to false
+    # in the GRD file.
+    if item.attrs.get('generateid', 'true') == 'false':
+      continue
+
+    for tid in item.GetTextualIds():
+      if util.SYSTEM_IDENTIFIERS.match(tid):
+        # Don't emit a new ID for predefined IDs
+        continue
+
+      if tid in tids:
+        continue
+
+      if predetermined_tids and tid in predetermined_tids:
+        id = predetermined_tids[tid]
+        reason = "from predetermined_tids map"
+
+      # Some identifier nodes can provide their own id,
+      # and we use that id in the generated header in that case.
+      elif hasattr(item, 'GetId') and item.GetId():
+        id = long(item.GetId())
+        reason = 'returned by GetId() method'
+
+      elif ('offset' in item.attrs and group and
+            group.attrs.get('first_id', '') != ''):
+         offset_text = item.attrs['offset']
+         parent_text = group.attrs['first_id']
+
+         try:
+           offset_id = long(offset_text)
+         except ValueError:
+           offset_id = tids[offset_text]
+
+         try:
+           parent_id = long(parent_text)
+         except ValueError:
+           parent_id = tids[parent_text]
+
+         id = parent_id + offset_id
+         reason = 'first_id %d + offset %d' % (parent_id, offset_id)
+
+      # We try to allocate IDs sequentially for blocks of items that might
+      # be related, for instance strings in a stringtable (as their IDs might be
+      # used e.g. as IDs for some radio buttons, in which case the IDs must
+      # be sequential).
+      #
+      # We do this by having the first item in a section store its computed ID
+      # (computed from a fingerprint) in its parent object.  Subsequent children
+      # of the same parent will then try to get IDs that sequentially follow
+      # the currently stored ID (on the parent) and increment it.
+      elif last_id is None:
+        # First check if the starting ID is explicitly specified by the parent.
+        if group and group.attrs.get('first_id', '') != '':
+          id = long(group.attrs['first_id'])
+          reason = "from parent's first_id attribute"
+        else:
+          # Automatically generate the ID based on the first clique from the
+          # first child of the first child node of our parent (i.e. when we
+          # first get to this location in the code).
+
+          # According to
+          # http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx
+          # the safe usable range for resource IDs in Windows is from decimal
+          # 101 to 0x7FFF.
+
+          id = FP.UnsignedFingerPrint(tid)
+          id = id % (0x7FFF - 101) + 101
+          reason = 'chosen by random fingerprint -- use first_id to override'
+
+        last_id = id
+      else:
+        id = last_id = last_id + 1
+        reason = 'sequentially assigned'
+
+      reason = "%s (%s)" % (tid, reason)
+      # Don't fail when 'offset' is specified, as the base and the 0th
+      # offset will have the same ID.
+      if id in id_reasons and not 'offset' in item.attrs:
+        raise exception.IdRangeOverlap('ID %d was assigned to both %s and %s.'
+                                       % (id, id_reasons[id], reason))
+
+      if id < 101:
+        print ('WARNING: Numeric resource IDs should be greater than 100 to\n'
+               'avoid conflicts with system-defined resource IDs.')
+
+      if tid not in predetermined_tids and id in predetermined_ids:
+        raise exception.IdRangeOverlap('ID %d overlaps between %s and %s'
+                                       % (id, tid, predetermined_ids[tid]))
+
+      ids[id] = tid
+      tids[tid] = id
+      id_reasons[id] = reason
+
+  return tids
+
 class SplicingNode(base.Node):
   """A node whose children should be considered to be at the same level as
   its siblings for most purposes. This includes <if> and <part> nodes.
@@ -172,6 +305,8 @@
     self.defines = {}
     self.substituter = None
     self.target_platform = sys.platform
+    self._predetermined_ids_file = None
+    self._id_map = None  # Dict of textual_id -> numeric_id.
 
   def _IsValidChild(self, child):
     from grit.node import empty
@@ -442,6 +577,7 @@
     """Assign first ids to each grouping node based on values from the
     first_ids file (if specified on the <grit> node).
     """
+    assert self._id_map is None, 'AssignFirstIds() after InitializeIds()'
     # If the input is a stream, then we're probably in a unit test and
     # should skip this step.
     if type(filename_or_stream) not in (str, unicode):
@@ -487,6 +623,25 @@
           raise Exception('Please update %s and add a first id for %s (%s).'
                           % (first_ids_filename, filename, node.name))
 
+  def GetIdMap(self):
+    '''Return a dictionary mapping textual ids to numeric ids.'''
+    return self._id_map
+
+  def SetPredeterminedIdsFile(self, predetermined_ids_file):
+    assert self._id_map is None, (
+        'SetPredeterminedIdsFile() after InitializeIds()')
+    self._predetermined_ids_file = predetermined_ids_file
+
+  def InitializeIds(self):
+    '''Initializes the text ID -> numeric ID mapping.'''
+    predetermined_id_map = {}
+    if self._predetermined_ids_file:
+      with open(self._predetermined_ids_file) as f:
+        for line in f:
+          tid, nid = line.split()
+          predetermined_id_map[tid] = int(nid)
+    self._id_map = _ComputeIds(self, predetermined_id_map)
+
   def RunGatherers(self, debug=False):
     '''Call RunPreSubstitutionGatherer() on every node of the tree, then apply
     substitutions, then call RunPostSubstitutionGatherer() on every node.
diff --git a/tools/grit/grit/node/misc_unittest.py b/tools/grit/grit/node/misc_unittest.py
index 005cbf6..0589631 100755
--- a/tools/grit/grit/node/misc_unittest.py
+++ b/tools/grit/grit/node/misc_unittest.py
@@ -6,21 +6,32 @@
 '''Unit tests for misc.GritNode'''
 
 
+import StringIO
+import contextlib
 import os
 import sys
+import tempfile
+import unittest
+
 if __name__ == '__main__':
   sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
 
-import unittest
-import StringIO
-
 from grit import grd_reader
 import grit.exception
 from grit import util
 from grit.format import rc
+from grit.format import rc_header
 from grit.node import misc
 
 
+@contextlib.contextmanager
+def _MakeTempPredeterminedIdsFile(content):
+  with tempfile.NamedTemporaryFile() as f:
+    f.write(content)
+    f.flush()
+    yield f.name
+
+
 class GritNodeUnittest(unittest.TestCase):
   def testUniqueNameAttribute(self):
     try:
@@ -106,6 +117,83 @@
     actual = [path.replace('\\', '/') for path in actual]
     self.assertEquals(expected, actual)
 
+  def testNonDefaultEntry(self):
+    grd = util.ParseGrdForUnittest('''
+      <messages>
+        <message name="IDS_A" desc="foo">bar</message>
+        <if expr="lang == 'fr'">
+          <message name="IDS_B" desc="foo">bar</message>
+        </if>
+      </messages>''')
+    grd.SetOutputLanguage('fr')
+    output = ''.join(rc_header.Format(grd, 'fr', '.'))
+    self.assertIn('#define IDS_A 2378\n#define IDS_B 2379', output)
+
+  def testExplicitFirstIdOverlaps(self):
+    # second first_id will overlap preexisting range
+    self.assertRaises(grit.exception.IdRangeOverlap,
+                      util.ParseGrdForUnittest, '''
+        <includes first_id="300" comment="bingo">
+          <include type="gif" name="ID_LOGO" file="images/logo.gif" />
+          <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
+        </includes>
+        <messages first_id="301">
+          <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
+            Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
+          </message>
+          <message name="IDS_SMURFGEBURF">Frubegfrums</message>
+        </messages>''')
+
+  def testImplicitOverlapsPreexisting(self):
+    # second message in <messages> will overlap preexisting range
+    self.assertRaises(grit.exception.IdRangeOverlap,
+                      util.ParseGrdForUnittest, '''
+        <includes first_id="301" comment="bingo">
+          <include type="gif" name="ID_LOGO" file="images/logo.gif" />
+          <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
+        </includes>
+        <messages first_id="300">
+          <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
+            Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
+          </message>
+          <message name="IDS_SMURFGEBURF">Frubegfrums</message>
+        </messages>''')
+
+  def testPredeterminedIds(self):
+    with _MakeTempPredeterminedIdsFile('IDS_A 101\nIDS_B 102') as ids_file:
+      grd = util.ParseGrdForUnittest('''
+          <includes first_id="300" comment="bingo">
+            <include type="gif" name="IDS_B" file="images/logo.gif" />
+          </includes>
+          <messages first_id="10000">
+            <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
+              Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
+            </message>
+            <message name="IDS_A">
+              Bongo!
+            </message>
+          </messages>''', predetermined_ids_file=ids_file)
+      output = rc_header.FormatDefines(grd, grd.GetRcHeaderFormat())
+      self.assertEqual(('#define IDS_B 102\n'
+                        '#define IDS_GREETING 10000\n'
+                        '#define IDS_A 101\n'), ''.join(output))
+
+  def testPredeterminedIdsOverlap(self):
+    with _MakeTempPredeterminedIdsFile('ID_LOGO 10000') as ids_file:
+      self.assertRaises(grit.exception.IdRangeOverlap,
+                        util.ParseGrdForUnittest, '''
+          <includes first_id="300" comment="bingo">
+            <include type="gif" name="ID_LOGO" file="images/logo.gif" />
+          </includes>
+          <messages first_id="10000">
+            <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
+              Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
+            </message>
+            <message name="IDS_BONGO">
+              Bongo!
+            </message>
+          </messages>''', predetermined_ids_file=ids_file)
+
 
 class IfNodeUnittest(unittest.TestCase):
   def testIffyness(self):
diff --git a/tools/grit/grit/node/structure.py b/tools/grit/grit/node/structure.py
index 4f0226f..cf7c1c7 100755
--- a/tools/grit/grit/node/structure.py
+++ b/tools/grit/grit/node/structure.py
@@ -26,7 +26,6 @@
 import grit.gather.txt
 
 import grit.format.rc
-import grit.format.rc_header
 
 # Type of the gatherer to use for each type attribute
 _GATHERERS = {
@@ -196,20 +195,14 @@
   def GetCliques(self):
     return self.gatherer.GetCliques()
 
-  def GetDataPackPair(self, lang, encoding):
-    """Returns a (id, string|None) pair that represents the resource id and raw
-    bytes of the data (or None if no resource is generated).  This is used to
-    generate the data pack data file.
-    """
-    from grit.format import rc_header
-    id_map = rc_header.GetIds(self.GetRoot())
-    id = id_map[self.GetTextualIds()[0]]
+  def GetDataPackValue(self, lang, encoding):
+    """Returns a str represenation for a data_pack entry."""
     if self.ExpandVariables():
       text = self.gatherer.GetText()
       data = util.Encode(self._Substitute(text), encoding)
     else:
       data = self.gatherer.GetData(lang, encoding)
-    return id, self.CompressDataIfNeeded(data)
+    return self.CompressDataIfNeeded(data)
 
   def GetHtmlResourceFilenames(self):
     """Returns a set of all filenames inlined by this node."""
diff --git a/tools/grit/grit/node/structure_unittest.py b/tools/grit/grit/node/structure_unittest.py
index 03d63baa..2da15ed9 100755
--- a/tools/grit/grit/node/structure_unittest.py
+++ b/tools/grit/grit/node/structure_unittest.py
@@ -74,7 +74,7 @@
         </structures>''', base_dir=test_data_root)
     struct, = root.GetChildrenOfType(structure.StructureNode)
     struct.RunPreSubstitutionGatherer()
-    _, compressed = struct.GetDataPackPair(lang='en', encoding=1)
+    compressed = struct.GetDataPackValue(lang='en', encoding=1)
 
     decompressed_data = zlib.decompress(compressed, 16 + zlib.MAX_WBITS)
     self.assertEqual(util.ReadFile(
@@ -89,7 +89,7 @@
         </structures>''', base_dir=test_data_root)
     struct, = root.GetChildrenOfType(structure.StructureNode)
     struct.RunPreSubstitutionGatherer()
-    _, data = struct.GetDataPackPair(lang='en', encoding=1)
+    data = struct.GetDataPackValue(lang='en', encoding=1)
 
     self.assertEqual(util.ReadFile(
         os.path.join(test_data_root, "test_text.txt"), util.BINARY), data)
diff --git a/tools/grit/grit/tool/build.py b/tools/grit/grit/tool/build.py
index f409c95..8b14854 100755
--- a/tools/grit/grit/tool/build.py
+++ b/tools/grit/grit/tool/build.py
@@ -387,6 +387,10 @@
       self.res.SetFallbackToDefaultLayout(output.GetFallbackToDefaultLayout())
       self.res.SetDefines(self.defines)
 
+      # Assign IDs only once to ensure that all outputs use the same IDs.
+      if self.res.GetIdMap() is None:
+        self.res.InitializeIds()
+
       # Make the output directory if it doesn't exist.
       self.MakeDirectoriesTo(output.GetOutputFilename())
 
diff --git a/tools/grit/grit/util.py b/tools/grit/grit/util.py
index 93dce261..4a710b7 100755
--- a/tools/grit/grit/util.py
+++ b/tools/grit/grit/util.py
@@ -319,7 +319,8 @@
   return os.path.normpath(os.path.join(_root_dir, path))
 
 
-def ParseGrdForUnittest(body, base_dir=None):
+def ParseGrdForUnittest(body, base_dir=None, predetermined_ids_file=None,
+                        run_gatherers=False, output_all_resource_defines=True):
   '''Parse a skeleton .grd file and return it, for use in unit tests.
 
   Args:
@@ -332,15 +333,26 @@
     body = body.encode('utf-8')
   if base_dir is None:
     base_dir = PathFromRoot('.')
-  body = '''<?xml version="1.0" encoding="UTF-8"?>
-<grit latest_public_release="2" current_release="3" source_lang_id="en" base_dir="%s">
-  <outputs>
-  </outputs>
-  <release seq="3">
-    %s
-  </release>
-</grit>''' % (base_dir, body)
-  return grd_reader.Parse(StringIO.StringIO(body), dir=".")
+  lines = ['<?xml version="1.0" encoding="UTF-8"?>']
+  lines.append(('<grit latest_public_release="2" current_release="3" '
+                'source_lang_id="en" base_dir="{}" '
+                'output_all_resource_defines="{}">').format(
+                base_dir, str(output_all_resource_defines).lower()))
+  if '<outputs>' in body:
+    lines.append(body)
+  else:
+    lines.append('  <outputs></outputs>')
+    lines.append('  <release seq="3">')
+    lines.append(body)
+    lines.append('  </release>')
+  lines.append('</grit>')
+  ret = grd_reader.Parse(StringIO.StringIO('\n'.join(lines)), dir=".")
+  ret.SetOutputLanguage('en')
+  if run_gatherers:
+    ret.RunGatherers()
+  ret.SetPredeterminedIdsFile(predetermined_ids_file)
+  ret.InitializeIds()
+  return ret
 
 
 def StripBlankLinesAndComments(text):
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.h b/ui/accelerated_widget_mac/accelerated_widget_mac.h
index 4e80950..cff0f38 100644
--- a/ui/accelerated_widget_mac/accelerated_widget_mac.h
+++ b/ui/accelerated_widget_mac/accelerated_widget_mac.h
@@ -54,6 +54,12 @@
       base::TimeTicks* timebase, base::TimeDelta* interval) const;
 
   static AcceleratedWidgetMac* Get(gfx::AcceleratedWidget widget);
+
+  // Translate from a gfx::AcceleratedWidget to the NSView in which it will
+  // appear. This may return nil if |widget| is invalid or is not currently
+  // attached to an NSView.
+  static NSView* GetNSView(gfx::AcceleratedWidget widget);
+
   void GotCALayerFrame(base::scoped_nsobject<CALayer> content_layer,
                        const gfx::Size& pixel_size,
                        float scale_factor);
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.mm b/ui/accelerated_widget_mac/accelerated_widget_mac.mm
index 754cbfd7..7f9d0b6 100644
--- a/ui/accelerated_widget_mac/accelerated_widget_mac.mm
+++ b/ui/accelerated_widget_mac/accelerated_widget_mac.mm
@@ -48,10 +48,8 @@
 
   // Use a sequence number as the accelerated widget handle that we can use
   // to look up the internals structure.
-  static intptr_t last_sequence_number = 0;
-  last_sequence_number += 1;
-  native_widget_ = reinterpret_cast<gfx::AcceleratedWidget>(
-      last_sequence_number);
+  static uint64_t last_sequence_number = 0;
+  native_widget_ = ++last_sequence_number;
   g_widget_to_helper_map.Pointer()->insert(
       std::make_pair(native_widget_, this));
 }
@@ -106,6 +104,7 @@
   }
 }
 
+// static
 AcceleratedWidgetMac* AcceleratedWidgetMac::Get(gfx::AcceleratedWidget widget) {
   WidgetToHelperMap::const_iterator found =
       g_widget_to_helper_map.Pointer()->find(widget);
@@ -117,6 +116,14 @@
   return found->second;
 }
 
+// static
+NSView* AcceleratedWidgetMac::GetNSView(gfx::AcceleratedWidget widget) {
+  AcceleratedWidgetMac* widget_mac = Get(widget);
+  if (!widget_mac || !widget_mac->view_)
+    return nil;
+  return widget_mac->view_->AcceleratedWidgetGetNSView();
+}
+
 void AcceleratedWidgetMac::GotCALayerFrame(
     base::scoped_nsobject<CALayer> content_layer,
     const gfx::Size& pixel_size,
diff --git a/ui/arc/notification/arc_notification_manager_unittest.cc b/ui/arc/notification/arc_notification_manager_unittest.cc
index dca1379..6a03147 100644
--- a/ui/arc/notification/arc_notification_manager_unittest.cc
+++ b/ui/arc/notification/arc_notification_manager_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/connection_holder.h"
+#include "components/arc/test/connection_holder_util.h"
 #include "components/arc/test/fake_notifications_instance.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/arc/notification/arc_notification_manager.h"
@@ -59,20 +60,6 @@
   DISALLOW_COPY_AND_ASSIGN(MockMessageCenter);
 };
 
-class NotificationsObserver
-    : public ConnectionObserver<mojom::NotificationsInstance> {
- public:
-  NotificationsObserver() = default;
-  void OnConnectionReady() override { ready_ = true; }
-
-  bool IsReady() { return ready_; }
-
- private:
-  bool ready_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(NotificationsObserver);
-};
-
 }  // anonymous namespace
 
 class ArcNotificationManagerTest : public testing::Test {
@@ -122,14 +109,8 @@
     arc_notification_manager_ = ArcNotificationManager::CreateForTesting(
         service_.get(), EmptyAccountId(), message_center_.get());
 
-    NotificationsObserver observer;
-    service_->notifications()->AddObserver(&observer);
     service_->notifications()->SetInstance(arc_notifications_instance_.get());
-
-    while (!observer.IsReady())
-      base::RunLoop().RunUntilIdle();
-
-    service_->notifications()->RemoveObserver(&observer);
+    WaitForInstanceReady(service_->notifications());
   }
 
   void TearDown() override {
diff --git a/ui/compositor/test/test_compositor_host_mac.mm b/ui/compositor/test/test_compositor_host_mac.mm
index 722edc6..376b1e0 100644
--- a/ui/compositor/test/test_compositor_host_mac.mm
+++ b/ui/compositor/test/test_compositor_host_mac.mm
@@ -44,6 +44,10 @@
   DCHECK(compositor_) << "Drawing with no compositor set.";
   compositor_->ScheduleFullRedraw();
 }
+
+- (gfx::AcceleratedWidget)widget {
+  return compositor_->widget();
+}
 @end
 
 namespace ui {
@@ -142,7 +146,7 @@
                               defer:NO];
   base::scoped_nsobject<AcceleratedTestView> view(
       [[AcceleratedTestView alloc] init]);
-  compositor_.SetAcceleratedWidget(view);
+  compositor_.SetAcceleratedWidget([view widget]);
   compositor_.SetScaleAndSize(1.0f, bounds_.size());
   [view setCompositor:&compositor_];
   [window_ setContentView:view];
diff --git a/ui/gfx/mojo/struct_traits_unittest.cc b/ui/gfx/mojo/struct_traits_unittest.cc
index 81b0b0c7..5841585d 100644
--- a/ui/gfx/mojo/struct_traits_unittest.cc
+++ b/ui/gfx/mojo/struct_traits_unittest.cc
@@ -5,6 +5,7 @@
 #include <utility>
 
 #include "base/message_loop/message_loop.h"
+#include "build/build_config.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/mojo/buffer_types_struct_traits.h"
@@ -18,7 +19,7 @@
 namespace {
 
 gfx::AcceleratedWidget castToAcceleratedWidget(int i) {
-#if defined(USE_OZONE) || defined(USE_X11)
+#if defined(USE_OZONE) || defined(USE_X11) || defined(OS_MACOSX)
   return static_cast<gfx::AcceleratedWidget>(i);
 #else
   return reinterpret_cast<gfx::AcceleratedWidget>(i);
diff --git a/ui/gfx/native_widget_types.h b/ui/gfx/native_widget_types.h
index 58c5e60..1252ad1 100644
--- a/ui/gfx/native_widget_types.h
+++ b/ui/gfx/native_widget_types.h
@@ -187,7 +187,7 @@
 typedef UIView* AcceleratedWidget;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
 #elif defined(OS_MACOSX)
-typedef NSView* AcceleratedWidget;
+typedef uint64_t AcceleratedWidget;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
 #elif defined(OS_ANDROID)
 typedef ANativeWindow* AcceleratedWidget;
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm
index c6384ed..520e006 100644
--- a/ui/views/cocoa/bridged_native_widget.mm
+++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -1063,7 +1063,7 @@
 
 ui::InputMethod* BridgedNativeWidget::GetInputMethod() {
   if (!input_method_) {
-    input_method_ = ui::CreateInputMethod(this, nil);
+    input_method_ = ui::CreateInputMethod(this, gfx::kNullAcceleratedWidget);
     // For now, use always-focused mode on Mac for the input method.
     // TODO(tapted): Move this to OnWindowKeyStatusChangedTo() and balance.
     input_method_->OnFocus();
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc
index 57e2b0a..d4c0c6f9 100644
--- a/ui/views/controls/button/label_button.cc
+++ b/ui/views/controls/button/label_button.cc
@@ -207,9 +207,7 @@
   size.set_height(std::max(preferred_label_size.height() + GetInsets().height(),
                            size.height()));
 
-  // Increase the minimum size monotonically with the preferred size.
   size.SetToMax(min_size_);
-  min_size_ = size;
 
   // Clamp size to max size (if valid).
   if (max_size_.width() > 0)
diff --git a/ui/views/controls/button/label_button.h b/ui/views/controls/button/label_button.h
index 259c355b..b2323da 100644
--- a/ui/views/controls/button/label_button.h
+++ b/ui/views/controls/button/label_button.h
@@ -69,7 +69,6 @@
   // optional image will lead the text, unless the button is right-aligned.
   void SetHorizontalAlignment(gfx::HorizontalAlignment alignment);
 
-  // Call SetMinSize(gfx::Size()) to clear the monotonically increasing size.
   void SetMinSize(const gfx::Size& min_size);
   void SetMaxSize(const gfx::Size& max_size);
 
@@ -83,9 +82,7 @@
   ButtonStyle style() const { return style_; }
   void SetStyleDeprecated(ButtonStyle style);
 
-  // Sets the spacing between the image and the text. Shrinking the spacing
-  // will not shrink the overall button size, as it is monotonically increasing.
-  // Call SetMinSize(gfx::Size()) to clear the size if needed.
+  // Sets the spacing between the image and the text.
   void SetImageLabelSpacing(int spacing);
 
   // Creates the default border for this button. This can be overridden by
@@ -214,9 +211,8 @@
   // Used to track whether SetTextColor() has been invoked.
   std::array<bool, STATE_COUNT> explicitly_set_colors_;
 
-  // |min_size_| increases monotonically with the preferred size.
-  mutable gfx::Size min_size_;
-  // |max_size_| may be set to clamp the preferred size.
+  // |min_size_| and |max_size_| may be set to clamp the preferred size.
+  gfx::Size min_size_;
   gfx::Size max_size_;
 
   // Cache the last computed preferred size.
diff --git a/ui/views/controls/button/label_button_unittest.cc b/ui/views/controls/button/label_button_unittest.cc
index 94acc4b3..466b9cc 100644
--- a/ui/views/controls/button/label_button_unittest.cc
+++ b/ui/views/controls/button/label_button_unittest.cc
@@ -154,7 +154,6 @@
   const int short_text_width = gfx::GetStringWidth(short_text, font_list);
   const int long_text_width = gfx::GetStringWidth(long_text, font_list);
 
-  // The width increases monotonically with string size (it does not shrink).
   EXPECT_LT(button_->GetPreferredSize().width(), short_text_width);
   button_->SetText(short_text);
   EXPECT_GT(button_->GetPreferredSize().height(), font_list.GetHeight());
@@ -163,16 +162,20 @@
   button_->SetText(long_text);
   EXPECT_GT(button_->GetPreferredSize().width(), long_text_width);
   button_->SetText(short_text);
-  EXPECT_GT(button_->GetPreferredSize().width(), long_text_width);
-
-  // Clamp the size to a maximum value.
-  button_->SetMaxSize(gfx::Size(long_text_width, 1));
-  EXPECT_EQ(button_->GetPreferredSize(), gfx::Size(long_text_width, 1));
-
-  // Clear the monotonically increasing minimum size.
-  button_->SetMinSize(gfx::Size());
   EXPECT_GT(button_->GetPreferredSize().width(), short_text_width);
   EXPECT_LT(button_->GetPreferredSize().width(), long_text_width);
+
+  // Clamp the size to a maximum value.
+  button_->SetText(long_text);
+  button_->SetMaxSize(gfx::Size(short_text_width, 1));
+  EXPECT_EQ(button_->GetPreferredSize(), gfx::Size(short_text_width, 1));
+
+  // Clamp the size to a minimum value.
+  button_->SetText(short_text);
+  button_->SetMaxSize(gfx::Size());
+  button_->SetMinSize(gfx::Size(long_text_width, font_list.GetHeight() * 2));
+  EXPECT_EQ(button_->GetPreferredSize(),
+            gfx::Size(long_text_width, font_list.GetHeight() * 2));
 }
 
 // Test behavior of View::GetAccessibleNodeData() for buttons when setting a
@@ -212,7 +215,6 @@
   const gfx::ImageSkia small_image = CreateTestImage(small_size, small_size);
   const gfx::ImageSkia large_image = CreateTestImage(large_size, large_size);
 
-  // The width increases monotonically with image size (it does not shrink).
   EXPECT_LT(button_->GetPreferredSize().width(), small_size);
   EXPECT_LT(button_->GetPreferredSize().height(), small_size);
   button_->SetImage(Button::STATE_NORMAL, small_image);
@@ -224,17 +226,21 @@
   EXPECT_GT(button_->GetPreferredSize().width(), large_size);
   EXPECT_GT(button_->GetPreferredSize().height(), large_size);
   button_->SetImage(Button::STATE_NORMAL, small_image);
-  EXPECT_GT(button_->GetPreferredSize().width(), large_size);
-  EXPECT_GT(button_->GetPreferredSize().height(), large_size);
+  EXPECT_GT(button_->GetPreferredSize().width(), small_size);
+  EXPECT_GT(button_->GetPreferredSize().height(), small_size);
+  EXPECT_LT(button_->GetPreferredSize().width(), large_size);
+  EXPECT_LT(button_->GetPreferredSize().height(), large_size);
 
   // Clamp the size to a maximum value.
+  button_->SetImage(Button::STATE_NORMAL, large_image);
   button_->SetMaxSize(gfx::Size(large_size, 1));
   EXPECT_EQ(button_->GetPreferredSize(), gfx::Size(large_size, 1));
 
-  // Clear the monotonically increasing minimum size.
-  button_->SetMinSize(gfx::Size());
-  EXPECT_GT(button_->GetPreferredSize().width(), small_size);
-  EXPECT_LT(button_->GetPreferredSize().width(), large_size);
+  // Clamp the size to a minimum value.
+  button_->SetImage(Button::STATE_NORMAL, small_image);
+  button_->SetMaxSize(gfx::Size());
+  button_->SetMinSize(gfx::Size(large_size, large_size));
+  EXPECT_EQ(button_->GetPreferredSize(), gfx::Size(large_size, large_size));
 }
 
 TEST_F(LabelButtonTest, LabelAndImage) {
@@ -246,7 +252,6 @@
   const gfx::ImageSkia image = CreateTestImage(image_size, image_size);
   ASSERT_LT(font_list.GetHeight(), image_size);
 
-  // The width increases monotonically with content size (it does not shrink).
   EXPECT_LT(button_->GetPreferredSize().width(), text_width);
   EXPECT_LT(button_->GetPreferredSize().width(), image_size);
   EXPECT_LT(button_->GetPreferredSize().height(), image_size);
@@ -279,21 +284,24 @@
   EXPECT_LT(button_->label()->bounds().right(), button_->image()->bounds().x());
 
   button_->SetText(base::string16());
-  EXPECT_GT(button_->GetPreferredSize().width(), text_width + image_size);
+  EXPECT_LT(button_->GetPreferredSize().width(), text_width + image_size);
+  EXPECT_GT(button_->GetPreferredSize().width(), image_size);
   EXPECT_GT(button_->GetPreferredSize().height(), image_size);
   button_->SetImage(Button::STATE_NORMAL, gfx::ImageSkia());
-  EXPECT_GT(button_->GetPreferredSize().width(), text_width + image_size);
-  EXPECT_GT(button_->GetPreferredSize().height(), image_size);
-
-  // Clamp the size to a maximum value.
-  button_->SetMaxSize(gfx::Size(image_size, 1));
-  EXPECT_EQ(button_->GetPreferredSize(), gfx::Size(image_size, 1));
-
-  // Clear the monotonically increasing minimum size.
-  button_->SetMinSize(gfx::Size());
-  EXPECT_LT(button_->GetPreferredSize().width(), text_width);
   EXPECT_LT(button_->GetPreferredSize().width(), image_size);
   EXPECT_LT(button_->GetPreferredSize().height(), image_size);
+
+  // Clamp the size to a minimum value.
+  button_->SetText(text);
+  button_->SetImage(Button::STATE_NORMAL, image);
+  button_->SetMinSize(gfx::Size((text_width + image_size) * 2, image_size * 2));
+  EXPECT_EQ(button_->GetPreferredSize().width(), (text_width + image_size) * 2);
+  EXPECT_EQ(button_->GetPreferredSize().height(), image_size * 2);
+
+  // Clamp the size to a maximum value.
+  button_->SetMinSize(gfx::Size());
+  button_->SetMaxSize(gfx::Size(1, 1));
+  EXPECT_EQ(button_->GetPreferredSize(), gfx::Size(1, 1));
 }
 
 // This test was added because GetHeightForWidth and GetPreferredSize were
@@ -326,10 +334,6 @@
     EXPECT_EQ(std::max(image_size, font_height) + button_->GetInsets().height(),
               preferred_button_size.height());
 
-    // Clear min size, this ensures that GetHeightForWidth() is consistent on
-    // its own and not because min_size_ is set to the preferred size.
-    button_->SetMinSize(gfx::Size());
-
     // Make sure this preferred height is consistent with GetHeightForWidth().
     EXPECT_EQ(preferred_button_size.height(),
               button_->GetHeightForWidth(preferred_button_size.width()));
@@ -399,7 +403,6 @@
 
   // The button and the label view return to its original size when the original
   // text is restored.
-  button_->SetMinSize(gfx::Size());
   button_->SetText(text);
   EXPECT_EQ(original_label_width, button_->label()->bounds().width());
   EXPECT_EQ(original_width, button_->GetPreferredSize().width());
@@ -418,7 +421,6 @@
   EXPECT_GT(button_->GetPreferredSize().width(), original_width);
 
   // The button shrinks if the original spacing is restored.
-  button_->SetMinSize(gfx::Size());
   button_->SetImageLabelSpacing(kOriginalSpacing);
   EXPECT_EQ(original_width, button_->GetPreferredSize().width());
 }
diff --git a/ui/views/examples/button_example.cc b/ui/views/examples/button_example.cc
index 5a0f969..048ef485 100644
--- a/ui/views/examples/button_example.cc
+++ b/ui/views/examples/button_example.cc
@@ -113,8 +113,6 @@
     }
   } else if (event.IsAltDown()) {
     label_button->SetIsDefault(!label_button->is_default());
-  } else {
-    label_button->SetMinSize(gfx::Size());
   }
   example_view()->GetLayoutManager()->Layout(example_view());
 }