diff --git a/DEPS b/DEPS
index 25fa1d1..698e765 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '8c8cb5bfc547229422a33db5c344fe1542bf00a7',
+  'skia_revision': 'f55ea6a1deb21120944d406124a2984b5009260a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'a818c327fe6bb770c3a3621a985c4afd698ad0ab',
+  'angle_revision': 'a66779fcf2aeed8588bcc1ba6a3668d81e65bf3b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '8d94b6687f27e1238f352939434704f75b330c1d',
+  'pdfium_revision': '591ed144f8dfe4b2915f01f1cc725f584d498a3f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'fa1926f937dcec01fa9ec6658da1a7c1b8321ec3',
+  'catapult_revision': 'f84aaa04d4d0bb5e902eaa8e224f2323a018e64c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -356,19 +356,19 @@
       Var('chromium_git') + '/external/github.com/swisspol/GCDWebServer.git' + '@' + '3d5fd0b8281a7224c057deb2d17709b5bea64836',
 
     'src/ios/third_party/material_components_ios/src':
-      Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '079c1c9a8b040f924de2919393b65c38239890f0',
+      Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'b1eb638d70792b7aa06118b128e3518c20e2aaae',
 
     'src/ios/third_party/material_font_disk_loader_ios/src':
-      Var('chromium_git') + '/external/github.com/material-foundation/material-font-disk-loader-ios.git' + '@' + '20c8fe37329cb18826f90159ce4ee445079e2e46',
+      Var('chromium_git') + '/external/github.com/material-foundation/material-font-disk-loader-ios.git' + '@' + '93acc021e3034898716028822cb802a3a816be7e',
 
     'src/ios/third_party/material_roboto_font_loader_ios/src':
       Var('chromium_git') + '/external/github.com/material-foundation/material-roboto-font-loader-ios.git' + '@' + 'e9b86479540ee3a200ade154f907fac889865c72',
 
     'src/ios/third_party/material_sprited_animation_view_ios/src':
-      Var('chromium_git') + '/external/github.com/material-foundation/material-sprited-animation-view-ios.git' + '@' + 'e240cdcd4538f0763ca5bd8c5afc2991eb482f1a',
+      Var('chromium_git') + '/external/github.com/material-foundation/material-sprited-animation-view-ios.git' + '@' + 'c6e16d06bdafd95540c62b3402d9414692fbca81',
 
     'src/ios/third_party/material_text_accessibility_ios/src':
-      Var('chromium_git') + '/external/github.com/material-foundation/material-text-accessibility-ios.git' + '@' + '96d2b0f13976a897bc7a41daf67f36d9548cff94',
+      Var('chromium_git') + '/external/github.com/material-foundation/material-text-accessibility-ios.git' + '@' + '318d5100f2976e59c94643e5dcab69e7a830ee43',
 
     'src/ios/third_party/ochamcrest/src':
       Var('chromium_git') + '/external/github.com/hamcrest/OCHamcrest.git' + '@' + 'd7ee4ecfb6bd13c3c8d364682b6228ccd86e1e1a',
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 7a9c62a1..9d51c8f 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -474,18 +474,16 @@
 void AwContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
       const base::CommandLine& command_line,
       int child_process_id,
-      content::FileDescriptorInfo* mappings,
-      std::map<int, base::MemoryMappedFile::Region>* regions) {
-  int fd = ui::GetMainAndroidPackFd(
-      &(*regions)[kAndroidWebViewMainPakDescriptor]);
-  mappings->Share(kAndroidWebViewMainPakDescriptor, fd);
+      content::FileDescriptorInfo* mappings) {
+  base::MemoryMappedFile::Region region;
+  int fd = ui::GetMainAndroidPackFd(&region);
+  mappings->ShareWithRegion(kAndroidWebViewMainPakDescriptor, fd, region);
 
-  fd = ui::GetCommonResourcesPackFd(
-      &(*regions)[kAndroidWebView100PercentPakDescriptor]);
-  mappings->Share(kAndroidWebView100PercentPakDescriptor, fd);
+  fd = ui::GetCommonResourcesPackFd(&region);
+  mappings->ShareWithRegion(kAndroidWebView100PercentPakDescriptor, fd, region);
 
-  fd = ui::GetLocalePackFd(&(*regions)[kAndroidWebViewLocalePakDescriptor]);
-  mappings->Share(kAndroidWebViewLocalePakDescriptor, fd);
+  fd = ui::GetLocalePackFd(&region);
+  mappings->ShareWithRegion(kAndroidWebViewLocalePakDescriptor, fd, region);
 
   base::ScopedFD crash_signal_file =
       breakpad::CrashMicroDumpManager::GetInstance()->CreateCrashInfoChannel(
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index e06c99b..1c4e28a 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -120,8 +120,7 @@
   void GetAdditionalMappedFilesForChildProcess(
       const base::CommandLine& command_line,
       int child_process_id,
-      content::FileDescriptorInfo* mappings,
-      std::map<int, base::MemoryMappedFile::Region>* regions) override;
+      content::FileDescriptorInfo* mappings) override;
   void OverrideWebkitPrefs(content::RenderViewHost* rvh,
                            content::WebPreferences* web_prefs) override;
   ScopedVector<content::NavigationThrottle> CreateThrottlesForNavigation(
diff --git a/ash/common/ash_constants.cc b/ash/common/ash_constants.cc
index 4d483be7..e8b1434 100644
--- a/ash/common/ash_constants.cc
+++ b/ash/common/ash_constants.cc
@@ -14,9 +14,7 @@
 const int kResizeOutsideBoundsScaleForTouch = 5;
 const int kResizeInsideBoundsSize = 1;
 
-#if defined(OS_CHROMEOS)
 const SkColor kChromeOsBootColor = SkColorSetRGB(0xfe, 0xfe, 0xfe);
-#endif
 
 const SkColor kFocusBorderColor = SkColorSetA(gfx::kGoogleBlue500, 0x99);
 const float kFocusBorderThickness = 2.f;
diff --git a/ash/common/ash_constants.h b/ash/common/ash_constants.h
index 1a42e28..9ff5092 100644
--- a/ash/common/ash_constants.h
+++ b/ash/common/ash_constants.h
@@ -23,10 +23,8 @@
 ASH_EXPORT extern const int kResizeOutsideBoundsScaleForTouch;
 ASH_EXPORT extern const int kResizeInsideBoundsSize;
 
-#if defined(OS_CHROMEOS)
 // Background color used for the Chrome OS boot splash screen.
 extern const SkColor kChromeOsBootColor;
-#endif
 
 // The border color of keyboard focus for launcher items and system tray.
 extern const SkColor kFocusBorderColor;
diff --git a/ash/common/ash_switches.cc b/ash/common/ash_switches.cc
index d3d9571..e9eb255 100644
--- a/ash/common/ash_switches.cc
+++ b/ash/common/ash_switches.cc
@@ -34,11 +34,9 @@
 const char kAshDisableMaximizeModeWindowBackdrop[] =
     "ash-disable-maximize-mode-window-backdrop";
 
-#if defined(OS_CHROMEOS)
 // Disable the support for WebContents to lock the screen orientation.
 const char kAshDisableScreenOrientationLock[] =
     "ash-disable-screen-orientation-lock";
-#endif
 
 // Disable the Touch Exploration Mode. Touch Exploration Mode will no longer be
 // turned on automatically when spoken feedback is enabled when this flag is
@@ -46,7 +44,6 @@
 const char kAshDisableTouchExplorationMode[] =
     "ash-disable-touch-exploration-mode";
 
-#if defined(OS_CHROMEOS)
 // Enables fullscreen app list if Ash is in maximize mode.
 const char kAshEnableFullscreenAppList[] = "ash-enable-fullscreen-app-list";
 
@@ -60,7 +57,6 @@
 // Enables the palette on every display, instead of only the internal one.
 const char kAshEnablePaletteOnAllDisplays[] =
     "ash-enable-palette-on-all-displays";
-#endif
 
 // Enables the observation of accelerometer events to enter touch-view mode.
 const char kAshEnableTouchView[] = "enable-touchview";
@@ -94,7 +90,6 @@
 // instead of displaying an interactive animation.
 const char kAuraLegacyPowerButton[] = "aura-legacy-power-button";
 
-#if defined(OS_CHROMEOS)
 // Constrains the pointer movement within a root window on desktop.
 bool ConstrainPointerToRoot() {
   const char kAshConstrainPointerToRoot[] = "ash-constrain-pointer-to-root";
@@ -104,7 +99,5 @@
              kAshConstrainPointerToRoot);
 }
 
-#endif
-
 }  // namespace switches
 }  // namespace ash
diff --git a/ash/common/ash_switches.h b/ash/common/ash_switches.h
index 8a145f6..08548f6 100644
--- a/ash/common/ash_switches.h
+++ b/ash/common/ash_switches.h
@@ -7,8 +7,6 @@
 
 #include "ash/ash_export.h"
 
-#include "build/build_config.h"
-
 namespace ash {
 namespace switches {
 
@@ -23,16 +21,12 @@
 ASH_EXPORT extern const char kAshDebugShortcuts[];
 ASH_EXPORT extern const char kAshDeveloperShortcuts[];
 ASH_EXPORT extern const char kAshDisableMaximizeModeWindowBackdrop[];
-#if defined(OS_CHROMEOS)
 ASH_EXPORT extern const char kAshDisableScreenOrientationLock[];
-#endif
 ASH_EXPORT extern const char kAshDisableTouchExplorationMode[];
-#if defined(OS_CHROMEOS)
 ASH_EXPORT extern const char kAshEnableFullscreenAppList[];
 ASH_EXPORT extern const char kAshEnableMagnifierKeyScroller[];
 ASH_EXPORT extern const char kAshEnablePalette[];
 ASH_EXPORT extern const char kAshEnablePaletteOnAllDisplays[];
-#endif
 ASH_EXPORT extern const char kAshEnableTouchView[];
 ASH_EXPORT extern const char kAshEnableMirroredScreen[];
 ASH_EXPORT extern const char kAshDisableStableOverviewOrder[];
@@ -45,10 +39,8 @@
 ASH_EXPORT extern const char kAshTouchHud[];
 ASH_EXPORT extern const char kAuraLegacyPowerButton[];
 
-#if defined(OS_CHROMEOS)
 // True if the pointer (cursor) position should be kept inside root windows.
 ASH_EXPORT bool ConstrainPointerToRoot();
-#endif
 
 }  // namespace switches
 }  // namespace ash
diff --git a/ash/common/devtools/ash_devtools_unittest.cc b/ash/common/devtools/ash_devtools_unittest.cc
index 4328f9b..9226051 100644
--- a/ash/common/devtools/ash_devtools_unittest.cc
+++ b/ash/common/devtools/ash_devtools_unittest.cc
@@ -400,14 +400,7 @@
   ExpectChildNodeInserted(target_node->getNodeId(), sibling_node->getNodeId());
 }
 
-#if defined(OS_WIN)
-#define MAYBE_WindowReorganizedChildNodeRemovedAndInserted \
-  DISABLED_WindowReorganizedChildNodeRemovedAndInserted
-#else
-#define MAYBE_WindowReorganizedChildNodeRemovedAndInserted \
-  WindowReorganizedChildNodeRemovedAndInserted
-#endif  // defined(OS_WIN)
-TEST_F(AshDevToolsTest, MAYBE_WindowReorganizedChildNodeRemovedAndInserted) {
+TEST_F(AshDevToolsTest, WindowReorganizedChildNodeRemovedAndInserted) {
   WmWindow* root_window = WmShell::Get()->GetPrimaryRootWindow();
   WmWindow* target_window = root_window->GetChildren()[1];
   WmWindow* parent_window = root_window->GetChildren()[0];
diff --git a/ash/common/mojo_interface_factory.cc b/ash/common/mojo_interface_factory.cc
index 038a2dc..501c006 100644
--- a/ash/common/mojo_interface_factory.cc
+++ b/ash/common/mojo_interface_factory.cc
@@ -14,6 +14,7 @@
 #include "ash/common/shelf/shelf_controller.h"
 #include "ash/common/shell_delegate.h"
 #include "ash/common/shutdown_controller.h"
+#include "ash/common/system/chromeos/network/vpn_list.h"
 #include "ash/common/system/locale/locale_notification_controller.h"
 #include "ash/common/system/tray/system_tray_controller.h"
 #include "ash/common/wallpaper/wallpaper_controller.h"
@@ -23,10 +24,6 @@
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "ui/app_list/presenter/app_list.h"
 
-#if defined(OS_CHROMEOS)
-#include "ash/common/system/chromeos/network/vpn_list.h"
-#endif
-
 namespace ash {
 
 namespace {
@@ -82,11 +79,9 @@
   WmShell::Get()->maximize_mode_controller()->BindRequest(std::move(request));
 }
 
-#if defined(OS_CHROMEOS)
 void BindVpnListRequestOnMainThread(mojom::VpnListRequest request) {
   WmShell::Get()->vpn_list()->BindRequest(std::move(request));
 }
-#endif
 
 void BindWallpaperRequestOnMainThread(
     mojom::WallpaperControllerRequest request) {
@@ -125,10 +120,8 @@
                          main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindTouchViewRequestOnMainThread),
                          main_thread_task_runner);
-#if defined(OS_CHROMEOS)
   registry->AddInterface(base::Bind(&BindVpnListRequestOnMainThread),
                          main_thread_task_runner);
-#endif
   registry->AddInterface(base::Bind(&BindWallpaperRequestOnMainThread),
                          main_thread_task_runner);
 }
diff --git a/ash/common/shelf/app_list_button.cc b/ash/common/shelf/app_list_button.cc
index 8847fb81..6ab78cb2 100644
--- a/ash/common/shelf/app_list_button.cc
+++ b/ash/common/shelf/app_list_button.cc
@@ -288,16 +288,23 @@
 }
 
 gfx::Point AppListButton::GetCenterPoint() const {
-  // During shelf hide/show animations, width and height may not be equal. Take
-  // the greater of the two as the one that represents the normal size of the
-  // button.
-  int center = std::max(width(), height()) / 2.f;
-  gfx::Point centroid(center, center);
-  // For the left shelf alignment, we need to right-justify. For other shelf
-  // alignments, left/top justification (i.e. no adjustments are necessary).
-  if (SHELF_ALIGNMENT_LEFT == wm_shelf_->GetAlignment())
-    centroid.set_x(width() - center);
-  return centroid;
+  // For a bottom-aligned shelf, the button bounds could have a larger height
+  // than width (in the case of touch-dragging the shelf updwards) or a larger
+  // width than height (in the case of a shelf hide/show animation), so adjust
+  // the y-position of the circle's center to ensure correct layout. Similarly
+  // adjust the x-position for a left- or right-aligned shelf.
+  const int x_mid = width() / 2.f;
+  const int y_mid = height() / 2.f;
+  ShelfAlignment alignment = wm_shelf_->GetAlignment();
+  if (alignment == SHELF_ALIGNMENT_BOTTOM ||
+      alignment == SHELF_ALIGNMENT_BOTTOM_LOCKED) {
+    return gfx::Point(x_mid, x_mid);
+  } else if (alignment == SHELF_ALIGNMENT_RIGHT) {
+    return gfx::Point(y_mid, y_mid);
+  } else {
+    DCHECK_EQ(alignment, SHELF_ALIGNMENT_LEFT);
+    return gfx::Point(width() - y_mid, y_mid);
+  }
 }
 
 }  // namespace ash
diff --git a/ash/common/shelf/overflow_button.cc b/ash/common/shelf/overflow_button.cc
index 7d63c2e2..2bf558a 100644
--- a/ash/common/shelf/overflow_button.cc
+++ b/ash/common/shelf/overflow_button.cc
@@ -172,18 +172,17 @@
 gfx::Rect OverflowButton::CalculateButtonBounds() const {
   ShelfAlignment alignment = wm_shelf_->GetAlignment();
   gfx::Rect bounds(GetContentsBounds());
-  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
   if (MaterialDesignController::IsShelfMaterial()) {
-    const int width_offset = (bounds.width() - kOverflowButtonSize) / 2;
-    const int height_offset = (bounds.height() - kOverflowButtonSize) / 2;
-    if (IsHorizontalAlignment(alignment)) {
-      bounds = gfx::Rect(bounds.x() + width_offset, bounds.y() + height_offset,
-                         kOverflowButtonSize, kOverflowButtonSize);
-    } else {
-      bounds = gfx::Rect(bounds.x() + height_offset, bounds.y() + width_offset,
-                         kOverflowButtonSize, kOverflowButtonSize);
-    }
+    // Align the button to the top of a bottom-aligned shelf, to the right edge
+    // a left-aligned shelf, and to the left edge of a right-aligned shelf.
+    const int inset = (GetShelfConstant(SHELF_SIZE) - kOverflowButtonSize) / 2;
+    const int x = alignment == SHELF_ALIGNMENT_LEFT
+                      ? bounds.right() - inset - kOverflowButtonSize
+                      : bounds.x() + inset;
+    bounds = gfx::Rect(x, bounds.y() + inset, kOverflowButtonSize,
+                       kOverflowButtonSize);
   } else {
+    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
     const gfx::ImageSkia* background =
         rb.GetImageNamed(NonMaterialBackgroundImageId()).ToImageSkia();
     if (alignment == SHELF_ALIGNMENT_LEFT) {
diff --git a/ash/common/shutdown_controller.cc b/ash/common/shutdown_controller.cc
index 7342eb2..b40fe86 100644
--- a/ash/common/shutdown_controller.cc
+++ b/ash/common/shutdown_controller.cc
@@ -6,12 +6,9 @@
 
 #include "ash/common/shell_delegate.h"
 #include "ash/common/wm_shell.h"
-
-#if defined(OS_CHROMEOS)
 #include "base/sys_info.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_manager_client.h"
-#endif
 
 namespace ash {
 
@@ -20,20 +17,18 @@
 ShutdownController::~ShutdownController() {}
 
 void ShutdownController::ShutDownOrReboot() {
-#if defined(OS_CHROMEOS)
-  if (base::SysInfo::IsRunningOnChromeOS()) {
-    // Power manager handles shutdown on Chrome OS hardware.
-    using chromeos::DBusThreadManager;
-    if (reboot_on_shutdown_)
-      DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
-    else
-      DBusThreadManager::Get()->GetPowerManagerClient()->RequestShutdown();
+  // For developers on Linux desktop just exit the app.
+  if (!base::SysInfo::IsRunningOnChromeOS()) {
+    WmShell::Get()->delegate()->Exit();
     return;
   }
-#endif  // defined(OS_CHROMEOS)
 
-  // On Windows and on Linux desktop just exit.
-  WmShell::Get()->delegate()->Exit();
+  // On real Chrome OS hardware the power manager handles shutdown.
+  using chromeos::DBusThreadManager;
+  if (reboot_on_shutdown_)
+    DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+  else
+    DBusThreadManager::Get()->GetPowerManagerClient()->RequestShutdown();
 }
 
 void ShutdownController::SetRebootOnShutdown(bool reboot_on_shutdown) {
diff --git a/ash/common/system/chromeos/palette/common_palette_tool.cc b/ash/common/system/chromeos/palette/common_palette_tool.cc
index b9f39df0..fa8624c 100644
--- a/ash/common/system/chromeos/palette/common_palette_tool.cc
+++ b/ash/common/system/chromeos/palette/common_palette_tool.cc
@@ -89,8 +89,7 @@
                                           kMenuIconSize, gfx::kGoogleGreen700);
 
   highlight_view_ = new HoverHighlightView(this);
-  highlight_view_->SetBorder(
-      views::CreateEmptyBorder(0, kMenuExtraMarginFromLeftEdge, 0, 0));
+  highlight_view_->SetBorder(views::CreateEmptyBorder(0, 0, 0, 0));
   const int interior_button_padding = (kMenuButtonSize - kMenuIconSize) / 2;
   highlight_view_->AddIconAndLabelCustomSize(icon, name, false, kMenuIconSize,
                                              interior_button_padding,
diff --git a/ash/common/system/chromeos/palette/palette_tray.cc b/ash/common/system/chromeos/palette/palette_tray.cc
index 179be69..0ffec05 100644
--- a/ash/common/system/chromeos/palette/palette_tray.cc
+++ b/ash/common/system/chromeos/palette/palette_tray.cc
@@ -45,31 +45,21 @@
 
 // Predefined padding for the icon used in this tray. These are to be set to the
 // border of the icon, depending on the current |shelf_alignment()|.
-const int kHorizontalShelfHorizontalPadding = 8;
-const int kHorizontalShelfVerticalPadding = 4;
-const int kVerticalShelfHorizontalPadding = 2;
-const int kVerticalShelfVerticalPadding = 5;
+constexpr int kHorizontalShelfHorizontalPadding = 8;
+constexpr int kHorizontalShelfVerticalPadding = 4;
+constexpr int kVerticalShelfHorizontalPadding = 2;
+constexpr int kVerticalShelfVerticalPadding = 5;
 
 // Width of the palette itself (dp).
-const int kPaletteWidth = 332;
+constexpr int kPaletteWidth = 332;
 
 // Padding at the top/bottom of the palette (dp).
-const int kPalettePaddingOnTop = 4;
-const int kPalettePaddingOnBottom = 4;
-
-// Size of icon in the shelf (dp).
-const int kShelfIconSize = 18;
-
-// Vertical margin around the title view elements so that the title view height
-// matches kMenuButtonSize.
-const int kVerticalMarginAroundTitleView = 1;
+constexpr int kPalettePaddingOnTop = 4;
+constexpr int kPalettePaddingOnBottom = 2;
 
 // Margins between the title view and the edges around it (dp).
-const int kPaddingBetweenTitleAndLeftEdge = 12;
-const int kPaddingBetweenTitleAndSeparator = 3;
-
-// The distance between the title, help, and settings button in the title (dp).
-const int kHorizontalPaddingBetweenTitleEntries = 2;
+constexpr int kPaddingBetweenTitleAndLeftEdge = 12;
+constexpr int kPaddingBetweenTitleAndSeparator = 3;
 
 // Color of the separator.
 const SkColor kPaletteSeparatorColor = SkColorSetARGB(0x1E, 0x00, 0x00, 0x00);
@@ -90,9 +80,8 @@
   explicit TitleView(PaletteTray* palette_tray) : palette_tray_(palette_tray) {
     // TODO(tdanderson|jdufault): Use TriView to handle the layout of the title.
     // See crbug.com/614453.
-    auto* box_layout = new views::BoxLayout(
-        views::BoxLayout::kHorizontal, 0, kVerticalMarginAroundTitleView,
-        kHorizontalPaddingBetweenTitleEntries);
+    auto* box_layout =
+        new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
     SetLayoutManager(box_layout);
 
     title_label_ =
@@ -431,7 +420,7 @@
   icon_->SetImage(CreateVectorIcon(
       palette_tool_manager_->GetActiveTrayIcon(
           palette_tool_manager_->GetActiveTool(ash::PaletteGroup::MODE)),
-      kShelfIconSize, kShelfIconColor));
+      kTrayIconSize, kShelfIconColor));
 }
 
 void PaletteTray::OnStylusStateChanged(ui::StylusState stylus_state) {
diff --git a/ash/common/wallpaper/wallpaper_controller_unittest.cc b/ash/common/wallpaper/wallpaper_controller_unittest.cc
index d24845d..72e5895 100644
--- a/ash/common/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/common/wallpaper/wallpaper_controller_unittest.cc
@@ -372,13 +372,7 @@
   EXPECT_TRUE(resized_image.BackedBySameObjectAs(controller_->GetWallpaper()));
 }
 
-#if defined(OS_WIN) && !defined(USE_ASH)
-// TODO(msw): Broken on Windows. http://crbug.com/584038
-#define MAYBE_GetMaxDisplaySize DISABLED_GetMaxDisplaySize
-#else
-#define MAYBE_GetMaxDisplaySize GetMaxDisplaySize
-#endif
-TEST_F(WallpaperControllerTest, MAYBE_GetMaxDisplaySize) {
+TEST_F(WallpaperControllerTest, GetMaxDisplaySize) {
   // Device scale factor shouldn't affect the native size.
   UpdateDisplay("1000x300*2");
   EXPECT_EQ("1000x300",
diff --git a/ash/common/wm/maximize_mode/maximize_mode_controller.cc b/ash/common/wm/maximize_mode/maximize_mode_controller.cc
index 6901481..ef07902 100644
--- a/ash/common/wm/maximize_mode/maximize_mode_controller.cc
+++ b/ash/common/wm/maximize_mode/maximize_mode_controller.cc
@@ -14,22 +14,18 @@
 #include "base/metrics/histogram.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
 #include "ui/base/accelerators/accelerator.h"
+#include "ui/chromeos/accelerometer/accelerometer_util.h"
 #include "ui/display/display.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/geometry/vector3d_f.h"
 
-#if defined(OS_CHROMEOS)
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "ui/chromeos/accelerometer/accelerometer_util.h"
-#endif  // OS_CHROMEOS
-
 namespace ash {
 
 namespace {
 
-#if defined(OS_CHROMEOS)
 // The hinge angle at which to enter maximize mode.
 const float kEnterMaximizeModeAngle = 200.0f;
 
@@ -43,7 +39,6 @@
 // vice versa).
 const float kMinStableAngle = 20.0f;
 const float kMaxStableAngle = 340.0f;
-#endif  // OS_CHROMEOS
 
 // The time duration to consider the lid to be recently opened.
 // This is used to prevent entering maximize mode if an erroneous accelerometer
@@ -51,7 +46,6 @@
 // lid from a closed position.
 const int kLidRecentlyOpenedDurationSeconds = 2;
 
-#if defined(OS_CHROMEOS)
 // When the device approaches vertical orientation (i.e. portrait orientation)
 // the accelerometers for the base and lid approach the same values (i.e.
 // gravity pointing in the direction of the hinge). When this happens abrupt
@@ -83,7 +77,6 @@
                  update.get(chromeos::ACCELEROMETER_SOURCE_SCREEN))
                  .Length()) <= kNoisyMagnitudeDeviation;
 }
-#endif  // OS_CHROMEOS
 
 bool IsEnabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -96,9 +89,7 @@
     : have_seen_accelerometer_data_(false),
       touchview_usage_interval_start_time_(base::Time::Now()),
       tick_clock_(new base::DefaultTickClock()),
-#if defined(OS_CHROMEOS)
       tablet_mode_switch_is_on_(false),
-#endif
       lid_is_closed_(false) {
   WmShell::Get()->AddShellObserver(this);
   WmShell::Get()->RecordUserMetricsAction(UMA_MAXIMIZE_MODE_INITIALLY_DISABLED);
@@ -111,12 +102,10 @@
   if (is_enabled)
     WmShell::Get()->AddDisplayObserver(this);
 
-#if defined(OS_CHROMEOS)
   if (is_enabled)
     chromeos::AccelerometerReader::GetInstance()->AddObserver(this);
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
       this);
-#endif  // OS_CHROMEOS
 }
 
 MaximizeModeController::~MaximizeModeController() {
@@ -125,12 +114,10 @@
   if (is_enabled)
     WmShell::Get()->RemoveDisplayObserver(this);
 
-#if defined(OS_CHROMEOS)
   if (is_enabled)
     chromeos::AccelerometerReader::GetInstance()->RemoveObserver(this);
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
       this);
-#endif  // OS_CHROMEOS
 }
 
 bool MaximizeModeController::CanEnterMaximizeMode() {
@@ -193,7 +180,6 @@
   bindings_.AddBinding(this, std::move(request));
 }
 
-#if defined(OS_CHROMEOS)
 void MaximizeModeController::OnAccelerometerUpdated(
     scoped_refptr<const chromeos::AccelerometerUpdate> update) {
   bool first_accelerometer_update = !have_seen_accelerometer_data_;
@@ -323,7 +309,6 @@
     EnterMaximizeMode();
   }
 }
-#endif  // OS_CHROMEOS
 
 void MaximizeModeController::EnterMaximizeMode() {
   // Always reset first to avoid creation before destruction of a previous
diff --git a/ash/common/wm/maximize_mode/maximize_mode_controller.h b/ash/common/wm/maximize_mode/maximize_mode_controller.h
index 37fd472..32650fe 100644
--- a/ash/common/wm/maximize_mode/maximize_mode_controller.h
+++ b/ash/common/wm/maximize_mode/maximize_mode_controller.h
@@ -14,15 +14,12 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "mojo/public/cpp/bindings/interface_ptr_set.h"
-#include "ui/gfx/geometry/vector3d_f.h"
-
-#if defined(OS_CHROMEOS)
 #include "chromeos/accelerometer/accelerometer_reader.h"
 #include "chromeos/accelerometer/accelerometer_types.h"
 #include "chromeos/dbus/power_manager_client.h"
-#endif  // OS_CHROMEOS
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "ui/gfx/geometry/vector3d_f.h"
 
 namespace base {
 class TickClock;
@@ -48,10 +45,8 @@
 // enters and exits maximize mode when the lid is opened beyond the triggering
 // angle and rotates the display to match the device when in maximize mode.
 class ASH_EXPORT MaximizeModeController :
-#if defined(OS_CHROMEOS)
     public chromeos::AccelerometerReader::Observer,
     public chromeos::PowerManagerClient::Observer,
-#endif  // OS_CHROMEOS
     NON_EXPORTED_BASE(public mojom::TouchViewManager),
     public ShellObserver,
     public WmDisplayObserver {
@@ -91,7 +86,6 @@
   // WmDisplayObserver:
   void OnDisplayConfigurationChanged() override;
 
-#if defined(OS_CHROMEOS)
   // chromeos::AccelerometerReader::Observer:
   void OnAccelerometerUpdated(
       scoped_refptr<const chromeos::AccelerometerUpdate> update) override;
@@ -101,7 +95,6 @@
   void TabletModeEventReceived(bool on, const base::TimeTicks& time) override;
   void SuspendImminent() override;
   void SuspendDone(const base::TimeDelta& sleep_duration) override;
-#endif  // OS_CHROMEOS
 
  private:
   friend class MaximizeModeControllerTest;
@@ -120,12 +113,10 @@
   // artificially and deterministically control the current time.
   void SetTickClockForTest(std::unique_ptr<base::TickClock> tick_clock);
 
-#if defined(OS_CHROMEOS)
   // Detect hinge rotation from base and lid accelerometers and automatically
   // start / stop maximize mode.
   void HandleHingeRotation(
       scoped_refptr<const chromeos::AccelerometerUpdate> update);
-#endif
 
   // Returns true if the lid was recently opened.
   bool WasLidOpenedRecently() const;
@@ -175,10 +166,8 @@
   // Source for the current time in base::TimeTicks.
   std::unique_ptr<base::TickClock> tick_clock_;
 
-#if defined(OS_CHROMEOS)
   // Set when tablet mode switch is on. This is used to force maximize mode.
   bool tablet_mode_switch_is_on_;
-#endif
 
   // Tracks when the lid is closed. Used to prevent entering maximize mode.
   bool lid_is_closed_;
diff --git a/ash/common/wm/workspace/workspace_layout_manager_unittest.cc b/ash/common/wm/workspace/workspace_layout_manager_unittest.cc
index df850df..7cd55d8a 100644
--- a/ash/common/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/common/wm/workspace/workspace_layout_manager_unittest.cc
@@ -684,13 +684,7 @@
 }
 
 // Tests maximized window size during root window resize.
-#if defined(OS_WIN) && !defined(USE_ASH)
-// TODO(msw): Broken on Windows. http://crbug.com/584038
-#define MAYBE_MaximizeRootWindowResize DISABLED_MaximizeRootWindowResize
-#else
-#define MAYBE_MaximizeRootWindowResize MaximizeRootWindowResize
-#endif
-TEST_F(WorkspaceLayoutManagerSoloTest, MAYBE_MaximizeRootWindowResize) {
+TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeRootWindowResize) {
   gfx::Rect bounds(100, 100, 200, 200);
   std::unique_ptr<WindowOwner> window_owner(CreateTestWindow(bounds));
   WmWindow* window = window_owner->window();
diff --git a/ash/common/wm_root_window_controller.cc b/ash/common/wm_root_window_controller.cc
index 77dc157..f2851f6ac 100644
--- a/ash/common/wm_root_window_controller.cc
+++ b/ash/common/wm_root_window_controller.cc
@@ -454,12 +454,10 @@
   overlay_container->SetBoundsInScreenBehaviorForChildren(
       WmWindow::BoundsInScreenBehavior::USE_SCREEN_COORDINATES);
 
-#if defined(OS_CHROMEOS)
   WmWindow* mouse_cursor_container = CreateContainer(
       kShellWindowId_MouseCursorContainer, "MouseCursorContainer", root_);
   mouse_cursor_container->SetBoundsInScreenBehaviorForChildren(
       WmWindow::BoundsInScreenBehavior::USE_SCREEN_COORDINATES);
-#endif
 
   CreateContainer(kShellWindowId_PowerButtonAnimationContainer,
                   "PowerButtonAnimationContainer", root_);
diff --git a/ash/common/wm_shell.cc b/ash/common/wm_shell.cc
index 5a8c34b..9f140c18 100644
--- a/ash/common/wm_shell.cc
+++ b/ash/common/wm_shell.cc
@@ -27,6 +27,10 @@
 #include "ash/common/shell_delegate.h"
 #include "ash/common/shutdown_controller.h"
 #include "ash/common/system/brightness_control_delegate.h"
+#include "ash/common/system/chromeos/brightness/brightness_controller_chromeos.h"
+#include "ash/common/system/chromeos/keyboard_brightness_controller.h"
+#include "ash/common/system/chromeos/network/vpn_list.h"
+#include "ash/common/system/chromeos/session/logout_confirmation_controller.h"
 #include "ash/common/system/keyboard_brightness_control_delegate.h"
 #include "ash/common/system/locale/locale_notification_controller.h"
 #include "ash/common/system/toast/toast_manager.h"
@@ -55,13 +59,6 @@
 #include "ui/display/display.h"
 #include "ui/views/focus/focus_manager_factory.h"
 
-#if defined(OS_CHROMEOS)
-#include "ash/common/system/chromeos/brightness/brightness_controller_chromeos.h"
-#include "ash/common/system/chromeos/keyboard_brightness_controller.h"
-#include "ash/common/system/chromeos/network/vpn_list.h"
-#include "ash/common/system/chromeos/session/logout_confirmation_controller.h"
-#endif
-
 namespace ash {
 
 // static
@@ -251,9 +248,13 @@
 WmShell::WmShell(std::unique_ptr<ShellDelegate> shell_delegate)
     : delegate_(std::move(shell_delegate)),
       app_list_(base::MakeUnique<app_list::AppList>()),
+      brightness_control_delegate_(
+          base::MakeUnique<system::BrightnessControllerChromeos>()),
       cast_config_(base::MakeUnique<CastConfigController>()),
       focus_cycler_(base::MakeUnique<FocusCycler>()),
       immersive_context_(base::MakeUnique<ImmersiveContextAsh>()),
+      keyboard_brightness_control_delegate_(
+          base::MakeUnique<KeyboardBrightnessController>()),
       locale_notification_controller_(
           base::MakeUnique<LocaleNotificationController>()),
       media_controller_(base::MakeUnique<MediaController>()),
@@ -263,15 +264,11 @@
       shutdown_controller_(base::MakeUnique<ShutdownController>()),
       system_tray_controller_(base::MakeUnique<SystemTrayController>()),
       system_tray_notifier_(base::MakeUnique<SystemTrayNotifier>()),
+      vpn_list_(base::MakeUnique<VpnList>()),
       wallpaper_delegate_(delegate_->CreateWallpaperDelegate()),
       window_cycle_controller_(base::MakeUnique<WindowCycleController>()),
       window_selector_controller_(
           base::MakeUnique<WindowSelectorController>()) {
-#if defined(OS_CHROMEOS)
-  brightness_control_delegate_.reset(new system::BrightnessControllerChromeos);
-  keyboard_brightness_control_delegate_.reset(new KeyboardBrightnessController);
-  vpn_list_ = base::MakeUnique<VpnList>();
-#endif
   session_controller_->AddSessionStateObserver(this);
 
   prefs::mojom::PreferencesManagerPtr pref_manager_ptr;
@@ -373,20 +370,16 @@
   DCHECK(delegate);
   system_tray_delegate_ = std::move(delegate);
   system_tray_delegate_->Initialize();
-#if defined(OS_CHROMEOS)
   // Accesses WmShell in its constructor.
   logout_confirmation_controller_.reset(new LogoutConfirmationController(
       base::Bind(&SystemTrayController::SignOut,
                  base::Unretained(system_tray_controller_.get()))));
-#endif
 }
 
 void WmShell::DeleteSystemTrayDelegate() {
   DCHECK(system_tray_delegate_);
-#if defined(OS_CHROMEOS)
   // Accesses WmShell in its destructor.
   logout_confirmation_controller_.reset();
-#endif
   system_tray_delegate_.reset();
 }
 
diff --git a/ash/common/wm_shell.h b/ash/common/wm_shell.h
index 45e4f5c8..46e28b5 100644
--- a/ash/common/wm_shell.h
+++ b/ash/common/wm_shell.h
@@ -60,6 +60,7 @@
 class KeyboardBrightnessControlDelegate;
 class KeyboardUI;
 class LocaleNotificationController;
+class LogoutConfirmationController;
 class MaximizeModeController;
 class MediaController;
 class MruWindowTracker;
@@ -79,6 +80,7 @@
 class SystemTrayController;
 class SystemTrayNotifier;
 class ToastManager;
+class VpnList;
 class WallpaperController;
 class WallpaperDelegate;
 class WindowCycleController;
@@ -99,11 +101,6 @@
 class WindowState;
 }
 
-#if defined(OS_CHROMEOS)
-class LogoutConfirmationController;
-class VpnList;
-#endif
-
 // Similar to ash::Shell. Eventually the two will be merged.
 class ASH_EXPORT WmShell : public SessionStateObserver {
  public:
@@ -146,6 +143,10 @@
     return locale_notification_controller_.get();
   }
 
+  LogoutConfirmationController* logout_confirmation_controller() {
+    return logout_confirmation_controller_.get();
+  }
+
   MaximizeModeController* maximize_mode_controller() {
     return maximize_mode_controller_.get();
   }
@@ -193,6 +194,8 @@
 
   ToastManager* toast_manager() { return toast_manager_.get(); }
 
+  VpnList* vpn_list() { return vpn_list_.get(); }
+
   WallpaperController* wallpaper_controller() {
     return wallpaper_controller_.get();
   }
@@ -207,6 +210,10 @@
     return window_selector_controller_.get();
   }
 
+  const scoped_refptr<base::SequencedWorkerPool>& blocking_pool() {
+    return blocking_pool_;
+  }
+
   // Returns true when ash is running as a service_manager::Service.
   virtual bool IsRunningInMash() const = 0;
 
@@ -430,23 +437,11 @@
   // True if any touch points are down.
   virtual bool IsTouchDown() = 0;
 
-  const scoped_refptr<base::SequencedWorkerPool>& blocking_pool() {
-    return blocking_pool_;
-  }
-
-#if defined(OS_CHROMEOS)
-  LogoutConfirmationController* logout_confirmation_controller() {
-    return logout_confirmation_controller_.get();
-  }
-
-  VpnList* vpn_list() { return vpn_list_.get(); }
-
   // TODO(jamescook): Remove this when VirtualKeyboardController has been moved.
   virtual void ToggleIgnoreExternalKeyboard() = 0;
 
   // Enable or disable the laser pointer.
   virtual void SetLaserPointerEnabled(bool enabled) = 0;
-#endif
 
  protected:
   explicit WmShell(std::unique_ptr<ShellDelegate> shell_delegate);
@@ -505,6 +500,7 @@
       keyboard_brightness_control_delegate_;
   std::unique_ptr<KeyboardUI> keyboard_ui_;
   std::unique_ptr<LocaleNotificationController> locale_notification_controller_;
+  std::unique_ptr<LogoutConfirmationController> logout_confirmation_controller_;
   std::unique_ptr<MaximizeModeController> maximize_mode_controller_;
   std::unique_ptr<MediaController> media_controller_;
   std::unique_ptr<MruWindowTracker> mru_window_tracker_;
@@ -519,6 +515,7 @@
   std::unique_ptr<SystemTrayNotifier> system_tray_notifier_;
   std::unique_ptr<SystemTrayDelegate> system_tray_delegate_;
   std::unique_ptr<ToastManager> toast_manager_;
+  std::unique_ptr<VpnList> vpn_list_;
   std::unique_ptr<WallpaperController> wallpaper_controller_;
   std::unique_ptr<WallpaperDelegate> wallpaper_delegate_;
   std::unique_ptr<WindowCycleController> window_cycle_controller_;
@@ -534,11 +531,6 @@
   bool simulate_modal_window_open_for_testing_ = false;
 
   scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
-
-#if defined(OS_CHROMEOS)
-  std::unique_ptr<LogoutConfirmationController> logout_confirmation_controller_;
-  std::unique_ptr<VpnList> vpn_list_;
-#endif
 };
 
 }  // namespace ash
diff --git a/ash/mus/bridge/wm_window_mus.cc b/ash/mus/bridge/wm_window_mus.cc
index 6761fbfe..3e58848 100644
--- a/ash/mus/bridge/wm_window_mus.cc
+++ b/ash/mus/bridge/wm_window_mus.cc
@@ -75,15 +75,15 @@
 }
 
 void WmWindowMus::CloseWidget() {
-  views::Widget* widget = views::Widget::GetWidgetForNativeView(aura_window());
-  DCHECK(widget);
-  // Allow the client to service the close request for remote widgets.
+  // NOTE: in the FOR_CLIENT case there is not necessarily a widget associated
+  // with the window. Mash only creates widgets for top level windows if mash
+  // renders the non-client frame.
   if (aura_window()->GetProperty(kWidgetCreationTypeKey) ==
       WidgetCreationType::FOR_CLIENT) {
     WmShellMus::Get()->window_manager()->window_manager_client()->RequestClose(
         aura_window());
   } else {
-    widget->Close();
+    WmWindowAura::CloseWidget();
   }
 }
 
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 960c5cc..93a5320 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -66,9 +66,6 @@
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_observer.h"
 #include "ui/aura/window_tracker.h"
-#include "ui/display/display.h"
-#include "ui/display/manager/display_manager.h"
-#include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/keyboard/keyboard_controller.h"
 #include "ui/keyboard/keyboard_util.h"
diff --git a/base/task_scheduler/scheduler_worker.cc b/base/task_scheduler/scheduler_worker.cc
index 5853bf6..970a7d4 100644
--- a/base/task_scheduler/scheduler_worker.cc
+++ b/base/task_scheduler/scheduler_worker.cc
@@ -78,11 +78,10 @@
         continue;
       }
 
-      std::unique_ptr<Task> task = sequence->TakeTask();
-      const TaskPriority task_priority = task->traits.priority();
-      const TimeDelta task_latency = TimeTicks::Now() - task->sequenced_time;
-      if (outer_->task_tracker_->RunTask(std::move(task), sequence->token()))
-        outer_->delegate_->DidRunTaskWithPriority(task_priority, task_latency);
+      if (outer_->task_tracker_->RunTask(sequence->TakeTask(),
+                                         sequence->token())) {
+        outer_->delegate_->DidRunTask();
+      }
 
       const bool sequence_became_empty = sequence->Pop();
 
diff --git a/base/task_scheduler/scheduler_worker.h b/base/task_scheduler/scheduler_worker.h
index 1b81b03..f75d45f 100644
--- a/base/task_scheduler/scheduler_worker.h
+++ b/base/task_scheduler/scheduler_worker.h
@@ -53,11 +53,8 @@
     // run a Task.
     virtual scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) = 0;
 
-    // Called by the SchedulerWorker after it ran a task with |task_priority|.
-    // |task_latency| is the time elapsed between when the task was posted and
-    // when it started to run.
-    virtual void DidRunTaskWithPriority(TaskPriority task_priority,
-                                        TimeDelta task_latency) = 0;
+    // Called by the SchedulerWorker after it ran a task.
+    virtual void DidRunTask() = 0;
 
     // Called when |sequence| isn't empty after the SchedulerWorker pops a Task
     // from it. |sequence| is the last Sequence returned by GetWork().
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.cc b/base/task_scheduler/scheduler_worker_pool_impl.cc
index 28f473e..912d270 100644
--- a/base/task_scheduler/scheduler_worker_pool_impl.cc
+++ b/base/task_scheduler/scheduler_worker_pool_impl.cc
@@ -22,6 +22,7 @@
 #include "base/task_runner.h"
 #include "base/task_scheduler/delayed_task_manager.h"
 #include "base/task_scheduler/task_tracker.h"
+#include "base/task_scheduler/task_traits.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread_local.h"
 #include "base/threading/thread_restrictions.h"
@@ -38,7 +39,6 @@
     "TaskScheduler.NumTasksBeforeDetach.";
 constexpr char kNumTasksBetweenWaitsHistogramPrefix[] =
     "TaskScheduler.NumTasksBetweenWaits.";
-constexpr char kTaskLatencyHistogramPrefix[] = "TaskScheduler.TaskLatency.";
 
 // SchedulerWorkerPool that owns the current thread, if any.
 LazyInstance<ThreadLocalPointer<const SchedulerWorkerPool>>::Leaky
@@ -128,29 +128,6 @@
   DISALLOW_COPY_AND_ASSIGN(SchedulerSequencedTaskRunner);
 };
 
-HistogramBase* GetTaskLatencyHistogram(const std::string& pool_name,
-                                       TaskPriority task_priority) {
-  const char* task_priority_suffix = nullptr;
-  switch (task_priority) {
-    case TaskPriority::BACKGROUND:
-      task_priority_suffix = ".BackgroundTaskPriority";
-      break;
-    case TaskPriority::USER_VISIBLE:
-      task_priority_suffix = ".UserVisibleTaskPriority";
-      break;
-    case TaskPriority::USER_BLOCKING:
-      task_priority_suffix = ".UserBlockingTaskPriority";
-      break;
-  }
-
-  // Mimics the UMA_HISTOGRAM_TIMES macro.
-  return Histogram::FactoryTimeGet(kTaskLatencyHistogramPrefix + pool_name +
-                                       kPoolNameSuffix + task_priority_suffix,
-                                   TimeDelta::FromMilliseconds(1),
-                                   TimeDelta::FromSeconds(10), 50,
-                                   HistogramBase::kUmaTargetedHistogramFlag);
-}
-
 // Only used in DCHECKs.
 bool ContainsWorker(
     const std::vector<std::unique_ptr<SchedulerWorker>>& workers,
@@ -238,8 +215,7 @@
   // SchedulerWorker::Delegate:
   void OnMainEntry(SchedulerWorker* worker) override;
   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override;
-  void DidRunTaskWithPriority(TaskPriority task_priority,
-                              TimeDelta task_latency) override;
+  void DidRunTask() override;
   void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override;
   TimeDelta GetSleepTimeout() override;
   bool CanDetach(SchedulerWorker* worker) override;
@@ -599,28 +575,9 @@
   return sequence;
 }
 
-void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::
-    DidRunTaskWithPriority(TaskPriority task_priority, TimeDelta task_latency) {
+void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::DidRunTask() {
   ++num_tasks_since_last_wait_;
   ++num_tasks_since_last_detach_;
-
-  const int priority_index = static_cast<int>(task_priority);
-
-  // As explained in the header file, histograms are allocated on demand. It
-  // doesn't matter if an element of |task_latency_histograms_| is set multiple
-  // times since GetTaskLatencyHistogram() is idempotent. As explained in the
-  // comment at the top of histogram_macros.h, barriers are required.
-  HistogramBase* task_latency_histogram = reinterpret_cast<HistogramBase*>(
-      subtle::Acquire_Load(&outer_->task_latency_histograms_[priority_index]));
-  if (!task_latency_histogram) {
-    task_latency_histogram =
-        GetTaskLatencyHistogram(outer_->name_, task_priority);
-    subtle::Release_Store(
-        &outer_->task_latency_histograms_[priority_index],
-        reinterpret_cast<subtle::AtomicWord>(task_latency_histogram));
-  }
-
-  task_latency_histogram->AddTime(task_latency);
 }
 
 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.h b/base/task_scheduler/scheduler_worker_pool_impl.h
index 3485995..4693d63 100644
--- a/base/task_scheduler/scheduler_worker_pool_impl.h
+++ b/base/task_scheduler/scheduler_worker_pool_impl.h
@@ -11,7 +11,6 @@
 #include <string>
 #include <vector>
 
-#include "base/atomicops.h"
 #include "base/base_export.h"
 #include "base/callback.h"
 #include "base/logging.h"
@@ -28,13 +27,13 @@
 #include "base/task_scheduler/scheduler_worker_stack.h"
 #include "base/task_scheduler/sequence.h"
 #include "base/task_scheduler/task.h"
-#include "base/task_scheduler/task_traits.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 
 namespace base {
 
 class HistogramBase;
+class TaskTraits;
 
 namespace internal {
 
@@ -204,14 +203,6 @@
   // Intentionally leaked.
   HistogramBase* const num_tasks_between_waits_histogram_;
 
-  // TaskScheduler.TaskLatency.[worker pool name].[task priority] histograms.
-  // Indexed by task priority. Histograms are allocated on demand to reduce
-  // memory usage (some task priorities might never run in this
-  // SchedulerThreadPoolImpl). Intentionally leaked.
-  subtle::AtomicWord
-      task_latency_histograms_[static_cast<int>(TaskPriority::HIGHEST) + 1] =
-          {};
-
   TaskTracker* const task_tracker_;
   DelayedTaskManager* const delayed_task_manager_;
 
diff --git a/base/task_scheduler/scheduler_worker_stack_unittest.cc b/base/task_scheduler/scheduler_worker_stack_unittest.cc
index ec49420..520e52c 100644
--- a/base/task_scheduler/scheduler_worker_stack_unittest.cc
+++ b/base/task_scheduler/scheduler_worker_stack_unittest.cc
@@ -26,9 +26,8 @@
   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
     return nullptr;
   }
-  void DidRunTaskWithPriority(TaskPriority task_priority,
-                              TimeDelta task_latency) override {
-    ADD_FAILURE() << "Unexpected call to DidRunTaskWithPriority()";
+  void DidRunTask() override {
+    ADD_FAILURE() << "Unexpected call to DidRunTask()";
   }
   void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
     ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()";
diff --git a/base/task_scheduler/scheduler_worker_unittest.cc b/base/task_scheduler/scheduler_worker_unittest.cc
index 8e82edfb..948cf1dd 100644
--- a/base/task_scheduler/scheduler_worker_unittest.cc
+++ b/base/task_scheduler/scheduler_worker_unittest.cc
@@ -45,9 +45,8 @@
   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
     return nullptr;
   }
-  void DidRunTaskWithPriority(TaskPriority task_priority,
-                              TimeDelta task_latency) override {
-    ADD_FAILURE() << "Unexpected call to DidRunTaskWithPriority()";
+  void DidRunTask() override {
+    ADD_FAILURE() << "Unexpected call to DidRunTask()";
   }
   void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
     ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()";
@@ -127,14 +126,14 @@
         : outer_(outer) {}
 
     ~TestSchedulerWorkerDelegate() override {
-      EXPECT_FALSE(IsCallToDidRunTaskWithPriorityExpected());
+      EXPECT_FALSE(IsCallToDidRunTaskExpected());
     }
 
     // SchedulerWorker::Delegate:
     void OnMainEntry(SchedulerWorker* worker) override {
       outer_->worker_set_.Wait();
       EXPECT_EQ(outer_->worker_.get(), worker);
-      EXPECT_FALSE(IsCallToDidRunTaskWithPriorityExpected());
+      EXPECT_FALSE(IsCallToDidRunTaskExpected());
 
       // Without synchronization, OnMainEntry() could be called twice without
       // generating an error.
@@ -144,7 +143,7 @@
     }
 
     scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
-      EXPECT_FALSE(IsCallToDidRunTaskWithPriorityExpected());
+      EXPECT_FALSE(IsCallToDidRunTaskExpected());
       EXPECT_EQ(outer_->worker_.get(), worker);
 
       {
@@ -174,7 +173,7 @@
         sequence->PushTask(std::move(task));
       }
 
-      ExpectCallToDidRunTaskWithPriority(sequence->PeekTaskTraits().priority());
+      ExpectCallToDidRunTask();
 
       {
         // Add the Sequence to the vector of created Sequences.
@@ -185,13 +184,10 @@
       return sequence;
     }
 
-    void DidRunTaskWithPriority(TaskPriority task_priority,
-                                TimeDelta task_latency) override {
-      AutoSchedulerLock auto_lock(expect_did_run_task_with_priority_lock_);
-      EXPECT_TRUE(expect_did_run_task_with_priority_);
-      EXPECT_EQ(expected_task_priority_, task_priority);
-      EXPECT_FALSE(task_latency.is_max());
-      expect_did_run_task_with_priority_ = false;
+    void DidRunTask() override {
+      AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
+      EXPECT_TRUE(expect_did_run_task_);
+      expect_did_run_task_ = false;
     }
 
     // This override verifies that |sequence| contains the expected number of
@@ -199,7 +195,7 @@
     // EnqueueSequence implementation, it doesn't reinsert |sequence| into a
     // queue for further execution.
     void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
-      EXPECT_FALSE(IsCallToDidRunTaskWithPriorityExpected());
+      EXPECT_FALSE(IsCallToDidRunTaskExpected());
       EXPECT_GT(outer_->TasksPerSequence(), 1U);
 
       // Verify that |sequence| contains TasksPerSequence() - 1 Tasks.
@@ -216,31 +212,27 @@
     }
 
    private:
-    // Expect a call to DidRunTaskWithPriority() with |task_priority| as
-    // argument before the next call to any other method of this delegate.
-    void ExpectCallToDidRunTaskWithPriority(TaskPriority task_priority) {
-      AutoSchedulerLock auto_lock(expect_did_run_task_with_priority_lock_);
-      expect_did_run_task_with_priority_ = true;
-      expected_task_priority_ = task_priority;
+    // Expect a call to DidRunTask() before the next call to any other method of
+    // this delegate.
+    void ExpectCallToDidRunTask() {
+      AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
+      expect_did_run_task_ = true;
     }
 
-    bool IsCallToDidRunTaskWithPriorityExpected() const {
-      AutoSchedulerLock auto_lock(expect_did_run_task_with_priority_lock_);
-      return expect_did_run_task_with_priority_;
+    bool IsCallToDidRunTaskExpected() const {
+      AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
+      return expect_did_run_task_;
     }
 
     TaskSchedulerWorkerTest* outer_;
 
-    // Synchronizes access to |expect_did_run_task_with_priority_| and
-    // |expected_task_priority_|.
-    mutable SchedulerLock expect_did_run_task_with_priority_lock_;
+    // Synchronizes access to |expect_did_run_task_|.
+    mutable SchedulerLock expect_did_run_task_lock_;
 
-    // Whether the next method called on this delegate should be
-    // DidRunTaskWithPriority().
-    bool expect_did_run_task_with_priority_ = false;
+    // Whether the next method called on this delegate should be DidRunTask().
+    bool expect_did_run_task_ = false;
 
-    // Expected priority for the next call to DidRunTaskWithPriority().
-    TaskPriority expected_task_priority_ = TaskPriority::BACKGROUND;
+    DISALLOW_COPY_AND_ASSIGN(TestSchedulerWorkerDelegate);
   };
 
   void RunTaskCallback() {
@@ -390,8 +382,7 @@
     return sequence;
   }
 
-  void DidRunTaskWithPriority(TaskPriority task,
-                              TimeDelta task_latency) override {}
+  void DidRunTask() override {}
 
   bool CanDetach(SchedulerWorker* worker) override {
     detach_requested_.Signal();
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
index 07b0d12..da21d5ea 100644
--- a/base/task_scheduler/task_tracker.cc
+++ b/base/task_scheduler/task_tracker.cc
@@ -5,6 +5,7 @@
 #include "base/task_scheduler/task_tracker.h"
 
 #include <limits>
+#include <string>
 
 #include "base/callback.h"
 #include "base/debug/task_annotator.h"
@@ -18,6 +19,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 
@@ -71,6 +73,14 @@
 // its implementation details.
 const char kRunFunctionName[] = "TaskSchedulerRunTask";
 
+HistogramBase* GetTaskLatencyHistogram(const char* suffix) {
+  // Mimics the UMA_HISTOGRAM_TIMES macro.
+  return Histogram::FactoryTimeGet(
+      std::string("TaskScheduler.TaskLatency.") + suffix,
+      TimeDelta::FromMilliseconds(1), TimeDelta::FromSeconds(10), 50,
+      HistogramBase::kUmaTargetedHistogramFlag);
+}
+
 // Upper bound for the
 // TaskScheduler.BlockShutdownTasksPostedDuringShutdown histogram.
 const HistogramBase::Sample kMaxBlockShutdownTasksPostedDuringShutdown = 1000;
@@ -174,7 +184,20 @@
 TaskTracker::TaskTracker()
     : state_(new State),
       flush_cv_(flush_lock_.CreateConditionVariable()),
-      shutdown_lock_(&flush_lock_) {}
+      shutdown_lock_(&flush_lock_),
+      task_latency_histograms_{
+          {GetTaskLatencyHistogram("BackgroundTaskPriority"),
+           GetTaskLatencyHistogram("BackgroundTaskPriority.MayBlock")},
+          {GetTaskLatencyHistogram("UserVisibleTaskPriority"),
+           GetTaskLatencyHistogram("UserVisibleTaskPriority.MayBlock")},
+          {GetTaskLatencyHistogram("UserBlockingTaskPriority"),
+           GetTaskLatencyHistogram("UserBlockingTaskPriority.MayBlock")}} {
+  // Confirm that all |task_latency_histograms_| have been initialized above.
+  DCHECK(*(&task_latency_histograms_[static_cast<int>(TaskPriority::HIGHEST) +
+                                     1][0] -
+           1));
+}
+
 TaskTracker::~TaskTracker() = default;
 
 void TaskTracker::Shutdown() {
@@ -220,6 +243,8 @@
   const bool is_delayed = !task->delayed_run_time.is_null();
 
   if (can_run_task) {
+    RecordTaskLatencyHistogram(task.get());
+
     const bool previous_singleton_allowed =
         ThreadRestrictions::SetSingletonAllowed(
             task->traits.shutdown_behavior() !=
@@ -463,5 +488,15 @@
   }
 }
 
+void TaskTracker::RecordTaskLatencyHistogram(Task* task) {
+  const TimeDelta task_latency = TimeTicks::Now() - task->sequenced_time;
+  task_latency_histograms_[static_cast<int>(task->traits.priority())]
+                          [task->traits.may_block() ||
+                                   task->traits.with_base_sync_primitives()
+                               ? 1
+                               : 0]
+                              ->AddTime(task_latency);
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/task_scheduler/task_tracker.h b/base/task_scheduler/task_tracker.h
index a5caf21..14ca1f4 100644
--- a/base/task_scheduler/task_tracker.h
+++ b/base/task_scheduler/task_tracker.h
@@ -21,13 +21,15 @@
 namespace base {
 
 class ConditionVariable;
+class HistogramBase;
 class SequenceToken;
 
 namespace internal {
 
 // All tasks go through the scheduler's TaskTracker when they are posted and
-// when they are executed. The TaskTracker enforces shutdown semantics and takes
-// care of tracing and profiling. This class is thread-safe.
+// when they are executed. The TaskTracker sets up the environment to run tasks,
+// enforces shutdown semantics, records metrics, and takes care of tracing and
+// profiling. This class is thread-safe.
 class BASE_EXPORT TaskTracker {
  public:
   TaskTracker();
@@ -106,6 +108,10 @@
   // it reaches zero.
   void DecrementNumPendingUndelayedTasks();
 
+  // Records the TaskScheduler.TaskLatency.[task priority].[may block] histogram
+  // for |task|.
+  void RecordTaskLatencyHistogram(Task* task);
+
   // Number of tasks blocking shutdown and boolean indicating whether shutdown
   // has started.
   const std::unique_ptr<State> state_;
@@ -133,6 +139,12 @@
   // completes.
   std::unique_ptr<WaitableEvent> shutdown_event_;
 
+  // TaskScheduler.TaskLatency.[task priority].[may block] histograms. The first
+  // index is a TaskPriority. The second index is 0 for non-blocking tasks, 1
+  // for blocking tasks. Intentionally leaked.
+  HistogramBase* const
+      task_latency_histograms_[static_cast<int>(TaskPriority::HIGHEST) + 1][2];
+
   // Number of BLOCK_SHUTDOWN tasks posted during shutdown.
   HistogramBase::Sample num_block_shutdown_tasks_posted_during_shutdown_ = 0;
 
diff --git a/base/task_scheduler/task_tracker_unittest.cc b/base/task_scheduler/task_tracker_unittest.cc
index 161aabb0..85378b6 100644
--- a/base/task_scheduler/task_tracker_unittest.cc
+++ b/base/task_scheduler/task_tracker_unittest.cc
@@ -16,6 +16,9 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
 #include "base/sequence_token.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
@@ -25,6 +28,7 @@
 #include "base/task_scheduler/task.h"
 #include "base/task_scheduler/task_traits.h"
 #include "base/test/gtest_util.h"
+#include "base/test/histogram_tester.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
@@ -885,5 +889,52 @@
   wait_allowed_test_thread.Join();
 }
 
+// Verify that TaskScheduler.TaskLatency.* histograms are correctly recorded
+// when a task runs.
+TEST(TaskSchedulerTaskTrackerHistogramTest, TaskLatency) {
+  auto statistics_recorder = StatisticsRecorder::CreateTemporaryForTesting();
+
+  TaskTracker tracker;
+
+  struct {
+    const TaskTraits traits;
+    const char* const expected_histogram;
+  } tests[] = {
+      {TaskTraits().WithPriority(TaskPriority::BACKGROUND),
+       "TaskScheduler.TaskLatency.BackgroundTaskPriority"},
+      {TaskTraits().WithPriority(TaskPriority::BACKGROUND).MayBlock(),
+       "TaskScheduler.TaskLatency.BackgroundTaskPriority.MayBlock"},
+      {TaskTraits()
+           .WithPriority(TaskPriority::BACKGROUND)
+           .WithBaseSyncPrimitives(),
+       "TaskScheduler.TaskLatency.BackgroundTaskPriority.MayBlock"},
+      {TaskTraits().WithPriority(TaskPriority::USER_VISIBLE),
+       "TaskScheduler.TaskLatency.UserVisibleTaskPriority"},
+      {TaskTraits().WithPriority(TaskPriority::USER_VISIBLE).MayBlock(),
+       "TaskScheduler.TaskLatency.UserVisibleTaskPriority.MayBlock"},
+      {TaskTraits()
+           .WithPriority(TaskPriority::USER_VISIBLE)
+           .WithBaseSyncPrimitives(),
+       "TaskScheduler.TaskLatency.UserVisibleTaskPriority.MayBlock"},
+      {TaskTraits().WithPriority(TaskPriority::USER_BLOCKING),
+       "TaskScheduler.TaskLatency.UserBlockingTaskPriority"},
+      {TaskTraits().WithPriority(TaskPriority::USER_BLOCKING).MayBlock(),
+       "TaskScheduler.TaskLatency.UserBlockingTaskPriority.MayBlock"},
+      {TaskTraits()
+           .WithPriority(TaskPriority::USER_BLOCKING)
+           .WithBaseSyncPrimitives(),
+       "TaskScheduler.TaskLatency.UserBlockingTaskPriority.MayBlock"}};
+
+  for (const auto& test : tests) {
+    auto task =
+        MakeUnique<Task>(FROM_HERE, Bind(&DoNothing), test.traits, TimeDelta());
+    ASSERT_TRUE(tracker.WillPostTask(task.get()));
+
+    HistogramTester tester;
+    EXPECT_TRUE(tracker.RunTask(std::move(task), SequenceToken::Create()));
+    tester.ExpectTotalCount(test.expected_histogram, 1);
+  }
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/chrome/VERSION b/chrome/VERSION
index 13ff98b..3a0c887 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=57
 MINOR=0
-BUILD=2974
+BUILD=2975
 PATCH=0
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 2da5e07..5196282 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -875,6 +875,9 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotificationHelper$TimeoutReceiver"
+            android:exported="false"/>
+
         <receiver android:name="org.chromium.base.PowerStatusReceiver">
             <intent-filter>
                 <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 038bf91..6dada45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -51,6 +51,7 @@
     // Alphabetical:
     public static final String ANDROID_PAY_INTEGRATION_V1 = "AndroidPayIntegrationV1";
     public static final String ANDROID_PAY_INTEGRATION_V2 = "AndroidPayIntegrationV2";
+    public static final String ANDROID_PAYMENT_APPS = "AndroidPaymentApps";
     public static final String AUTOFILL_SCAN_CARDHOLDER_NAME = "AutofillScanCardholderName";
     public static final String CCT_EXTERNAL_LINK_HANDLING = "CCTExternalLinkHandling";
     public static final String CCT_POST_MESSAGE_API = "CCTPostMessageAPI";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index b3f30ef..2cd0847 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -37,6 +37,7 @@
 import org.chromium.chrome.browser.init.BrowserParts;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.EmptyBrowserParts;
+import org.chromium.chrome.browser.notifications.NotificationConstants;
 import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBridge;
 import org.chromium.chrome.browser.util.IntentUtils;
 
@@ -45,6 +46,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Service responsible for creating and updating download notifications even after
@@ -73,21 +75,25 @@
 
     public static final int INVALID_DOWNLOAD_PERCENTAGE = -1;
     @VisibleForTesting
-    static final String PENDING_DOWNLOAD_NOTIFICATIONS = "PendingDownloadNotifications";
     static final String NOTIFICATION_NAMESPACE = "DownloadNotificationService";
     private static final String TAG = "DownloadNotification";
-    private static final String NEXT_DOWNLOAD_NOTIFICATION_ID = "NextDownloadNotificationId";
-    // Notification Id starting value, to avoid conflicts from IDs used in prior versions.
+
+    /** Notification Id starting value, to avoid conflicts from IDs used in prior versions. */
     private static final int STARTING_NOTIFICATION_ID = 1000000;
-    private static final String AUTO_RESUMPTION_ATTEMPT_LEFT = "ResumptionAttemptLeft";
     private static final int MAX_RESUMPTION_ATTEMPT_LEFT = 5;
-    @VisibleForTesting static final int SECONDS_PER_MINUTE = 60;
-    @VisibleForTesting static final int SECONDS_PER_HOUR = 60 * 60;
-    @VisibleForTesting static final int SECONDS_PER_DAY = 24 * 60 * 60;
+    @VisibleForTesting static final long SECONDS_PER_MINUTE = TimeUnit.MINUTES.toSeconds(1);
+    @VisibleForTesting static final long SECONDS_PER_HOUR = TimeUnit.HOURS.toSeconds(1);
+    @VisibleForTesting static final long SECONDS_PER_DAY = TimeUnit.DAYS.toSeconds(1);
+
+    private static final String KEY_AUTO_RESUMPTION_ATTEMPT_LEFT = "ResumptionAttemptLeft";
+    private static final String KEY_NEXT_DOWNLOAD_NOTIFICATION_ID = "NextDownloadNotificationId";
+    static final String KEY_PENDING_DOWNLOAD_NOTIFICATIONS = "PendingDownloadNotifications";
+
     private final IBinder mBinder = new LocalBinder();
     private final List<DownloadSharedPreferenceEntry> mDownloadSharedPreferenceEntries =
             new ArrayList<DownloadSharedPreferenceEntry>();
     private final List<String> mDownloadsInProgress = new ArrayList<String>();
+
     private NotificationManager mNotificationManager;
     private SharedPreferences mSharedPrefs;
     private Context mContext;
@@ -141,7 +147,7 @@
             onBrowserKilled();
         }
         mNextNotificationId = mSharedPrefs.getInt(
-                NEXT_DOWNLOAD_NOTIFICATION_ID, STARTING_NOTIFICATION_ID);
+                KEY_NEXT_DOWNLOAD_NOTIFICATION_ID, STARTING_NOTIFICATION_ID);
 
     }
 
@@ -207,7 +213,7 @@
      */
     private void updateResumptionAttemptLeft() {
         SharedPreferences.Editor editor = mSharedPrefs.edit();
-        editor.putInt(AUTO_RESUMPTION_ATTEMPT_LEFT, mNumAutoResumptionAttemptLeft);
+        editor.putInt(KEY_AUTO_RESUMPTION_ATTEMPT_LEFT, mNumAutoResumptionAttemptLeft);
         editor.apply();
     }
 
@@ -217,7 +223,7 @@
     static void clearResumptionAttemptLeft() {
         SharedPreferences SharedPrefs = ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor editor = SharedPrefs.edit();
-        editor.remove(AUTO_RESUMPTION_ATTEMPT_LEFT);
+        editor.remove(KEY_AUTO_RESUMPTION_ATTEMPT_LEFT);
         editor.apply();
     }
 
@@ -546,7 +552,8 @@
                 .setSmallIcon(iconId)
                 .setLocalOnly(true)
                 .setAutoCancel(true)
-                .setContentText(contentText);
+                .setContentText(contentText)
+                .setGroup(NotificationConstants.GROUP_DOWNLOADS);
         return builder;
     }
 
@@ -746,7 +753,7 @@
      */
     @VisibleForTesting
     void updateNotification(int id, Notification notification) {
-        mNotificationManager.notify(NOTIFICATION_NAMESPACE, id, notification);
+        mNotificationManager.notify(id, notification);
     }
 
     /**
@@ -835,11 +842,11 @@
      * left from the shared preference.
      */
     void parseDownloadSharedPrefs() {
-        mNumAutoResumptionAttemptLeft = mSharedPrefs.getInt(AUTO_RESUMPTION_ATTEMPT_LEFT,
+        mNumAutoResumptionAttemptLeft = mSharedPrefs.getInt(KEY_AUTO_RESUMPTION_ATTEMPT_LEFT,
                 MAX_RESUMPTION_ATTEMPT_LEFT);
-        if (!mSharedPrefs.contains(PENDING_DOWNLOAD_NOTIFICATIONS)) return;
+        if (!mSharedPrefs.contains(KEY_PENDING_DOWNLOAD_NOTIFICATIONS)) return;
         Set<String> entries = DownloadManagerService.getStoredDownloadInfo(
-                mSharedPrefs, PENDING_DOWNLOAD_NOTIFICATIONS);
+                mSharedPrefs, KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
         for (String entryString : entries) {
             DownloadSharedPreferenceEntry entry =
                     DownloadSharedPreferenceEntry.parseFromString(entryString);
@@ -873,7 +880,7 @@
             entries.add(mDownloadSharedPreferenceEntries.get(i).getSharedPreferenceString());
         }
         DownloadManagerService.storeDownloadInfo(
-                mSharedPrefs, PENDING_DOWNLOAD_NOTIFICATIONS, entries);
+                mSharedPrefs, KEY_PENDING_DOWNLOAD_NOTIFICATIONS, entries);
     }
 
     /**
@@ -887,7 +894,7 @@
         mNextNotificationId = mNextNotificationId == Integer.MAX_VALUE
                 ? STARTING_NOTIFICATION_ID : mNextNotificationId + 1;
         SharedPreferences.Editor editor = mSharedPrefs.edit();
-        editor.putInt(NEXT_DOWNLOAD_NOTIFICATION_ID, mNextNotificationId);
+        editor.putInt(KEY_NEXT_DOWNLOAD_NOTIFICATION_ID, mNextNotificationId);
         editor.apply();
         return notificationId;
     }
@@ -910,15 +917,15 @@
         int minutes = 0;
         if (secondsLong >= SECONDS_PER_DAY) {
             days = (int) (secondsLong / SECONDS_PER_DAY);
-            secondsLong -= (long) days * SECONDS_PER_DAY;
+            secondsLong -= days * SECONDS_PER_DAY;
         }
         if (secondsLong >= SECONDS_PER_HOUR) {
             hours = (int) (secondsLong / SECONDS_PER_HOUR);
-            secondsLong -= (long) hours * SECONDS_PER_HOUR;
+            secondsLong -= hours * SECONDS_PER_HOUR;
         }
         if (secondsLong >= SECONDS_PER_MINUTE) {
             minutes = (int) (secondsLong / SECONDS_PER_MINUTE);
-            secondsLong -= (long) minutes * SECONDS_PER_MINUTE;
+            secondsLong -= minutes * SECONDS_PER_MINUTE;
         }
         int seconds = (int) secondsLong;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunGlueImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunGlueImpl.java
index 3a7f7ad..c0eab35 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunGlueImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunGlueImpl.java
@@ -24,15 +24,28 @@
     private static final String CACHED_TOS_ACCEPTED_PREF = "first_run_tos_accepted";
 
     /**
-     * Synchronizes first run native and Java preferences. Previous versions would
-     * set only the native pref so this function synchronizes that state to Java.
+     * Synchronizes first run native and Java preferences.
      * Must be called after native initialization.
      */
     public static void cacheFirstRunPrefs() {
         SharedPreferences javaPrefs = ContextUtils.getAppSharedPreferences();
-        if (!javaPrefs.getBoolean(CACHED_TOS_ACCEPTED_PREF, false)
-                && PrefServiceBridge.getInstance().isFirstRunEulaAccepted()) {
-            javaPrefs.edit().putBoolean(CACHED_TOS_ACCEPTED_PREF, true).apply();
+        PrefServiceBridge prefsBridge = PrefServiceBridge.getInstance();
+        // Set both Java and native prefs if any of the three indicators indicate ToS has been
+        // accepted. This needed because:
+        //   - Old versions only set native pref, so this syncs Java pref.
+        //   - Backup & restore does not restore native pref, so this needs to update it.
+        //   - checkAnyUserHasSeenToS() may be true which needs to sync its state to the prefs.
+        boolean javaPrefValue = javaPrefs.getBoolean(CACHED_TOS_ACCEPTED_PREF, false);
+        boolean nativePrefValue = prefsBridge.isFirstRunEulaAccepted();
+        boolean userHasSeenTos =
+                ToSAckedReceiver.checkAnyUserHasSeenToS(ContextUtils.getApplicationContext());
+        if (javaPrefValue || nativePrefValue || userHasSeenTos) {
+            if (!javaPrefValue) {
+                javaPrefs.edit().putBoolean(CACHED_TOS_ACCEPTED_PREF, true).apply();
+            }
+            if (!nativePrefValue) {
+                prefsBridge.setEulaAccepted();
+            }
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
index fb843214..a8e3557d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
@@ -11,6 +11,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.notifications.NotificationConstants;
 
 /**
  * Manages the notification indicating that there are incognito tabs opened in Document mode.
@@ -37,7 +38,8 @@
                 .setVisibility(Notification.VISIBILITY_SECRET)
                 .setSmallIcon(R.drawable.incognito_statusbar)
                 .setShowWhen(false)
-                .setLocalOnly(true);
+                .setLocalOnly(true)
+                .setGroup(NotificationConstants.GROUP_INCOGNITO);
         NotificationManager nm =
                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         nm.notify(INCOGNITO_TABS_OPEN_TAG, INCOGNITO_TABS_OPEN_ID, builder.build());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index 8a95b7c..d7dcd05b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -37,6 +37,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.blink.mojom.MediaSessionAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.notifications.NotificationConstants;
 import org.chromium.content_public.common.MediaMetadata;
 
 import java.util.ArrayList;
@@ -270,7 +271,6 @@
      */
     public static final class PlaybackListenerService extends ListenerService {
         private static final int NOTIFICATION_ID = R.id.media_playback_notification;
-        private static final String NOTIFICATION_GROUP_NAME = "MediaPlayback";
 
         @Override
         public void onCreate() {
@@ -310,7 +310,6 @@
      */
     public static final class PresentationListenerService extends ListenerService {
         private static final int NOTIFICATION_ID = R.id.presentation_notification;
-        private static final String NOTIFICATION_GROUP_NAME = "MediaPresentation";
 
         @Override
         @Nullable
@@ -324,7 +323,6 @@
      */
     public static final class CastListenerService extends ListenerService {
         private static final int NOTIFICATION_ID = R.id.remote_notification;
-        private static final String NOTIFICATION_GROUP_NAME = "MediaRemote";
 
         @Override
         @Nullable
@@ -399,11 +397,11 @@
     // Returns the notification group name used to prevent automatic grouping.
     private String getNotificationGroupName() {
         if (mMediaNotificationInfo.id == PlaybackListenerService.NOTIFICATION_ID) {
-            return PlaybackListenerService.NOTIFICATION_GROUP_NAME;
+            return NotificationConstants.GROUP_MEDIA_PLAYBACK;
         } else if (mMediaNotificationInfo.id == PresentationListenerService.NOTIFICATION_ID) {
-            return PresentationListenerService.NOTIFICATION_GROUP_NAME;
+            return NotificationConstants.GROUP_MEDIA_PRESENTATION;
         } else if (mMediaNotificationInfo.id == CastListenerService.NOTIFICATION_ID) {
-            return CastListenerService.NOTIFICATION_GROUP_NAME;
+            return NotificationConstants.GROUP_MEDIA_REMOTE;
         }
 
         assert false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
index 521d7de94..dc92632 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
@@ -59,4 +59,11 @@
      * Key for retrieving the results of user input from notification text action intents.
      */
     static final String KEY_TEXT_REPLY = "key_text_reply";
+
+    // Notification groups for features that show notifications to the user.
+    public static final String GROUP_DOWNLOADS = "Downloads";
+    public static final String GROUP_INCOGNITO = "Incognito";
+    public static final String GROUP_MEDIA_PLAYBACK = "MediaPlayback";
+    public static final String GROUP_MEDIA_PRESENTATION = "MediaPresentation";
+    public static final String GROUP_MEDIA_REMOTE = "MediaRemote";
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
index dcf20b8..337a3a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
@@ -4,13 +4,18 @@
 
 package org.chromium.chrome.browser.ntp;
 
+import android.annotation.TargetApi;
+import android.app.AlarmManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.Build;
 import android.provider.Browser;
+import android.service.notification.StatusBarNotification;
 import android.support.v4.app.NotificationCompat;
 
 import org.chromium.base.ContextUtils;
@@ -27,10 +32,21 @@
  */
 public class ContentSuggestionsNotificationHelper {
     private static final String NOTIFICATION_TAG = "ContentSuggestionsNotification";
-    private static final int NOTIFICATION_ID = 0;
+    private static final String NOTIFICATION_ID_EXTRA = "notification_id";
 
     private ContentSuggestionsNotificationHelper() {} // Prevent instantiation
 
+    /**
+     * Removes the notification after a timeout period.
+     */
+    public static final class TimeoutReceiver extends BroadcastReceiver {
+        public void onReceive(Context context, Intent intent) {
+            int id = intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1);
+            if (id < 0) return;
+            hideNotification(id);
+        }
+    }
+
     @CalledByNative
     private static void openUrl(String url) {
         Context context = ContextUtils.getApplicationContext();
@@ -45,11 +61,26 @@
         context.startActivity(intent);
     }
 
+    @TargetApi(Build.VERSION_CODES.M)
     @CalledByNative
-    private static void showNotification(String url, String title, String text, Bitmap image) {
+    private static void showNotification(
+            String url, String title, String text, Bitmap image, long timeoutAtMillis) {
+        // Post notification.
         Context context = ContextUtils.getApplicationContext();
         NotificationManager manager =
                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        // Find an available notification ID.
+        int nextId = 0;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            for (StatusBarNotification activeNotification : manager.getActiveNotifications()) {
+                if (activeNotification.getTag() != NOTIFICATION_TAG) continue;
+                if (activeNotification.getId() >= nextId) {
+                    nextId = activeNotification.getId() + 1;
+                }
+            }
+        }
+
         Intent intent = new Intent()
                                 .setAction(Intent.ACTION_VIEW)
                                 .setData(Uri.parse(url))
@@ -65,14 +96,42 @@
                         .setGroup(NOTIFICATION_TAG)
                         .setLargeIcon(image)
                         .setSmallIcon(R.drawable.ic_chrome);
-        manager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+        manager.notify(NOTIFICATION_TAG, nextId, builder.build());
+
+        // Set timeout.
+        if (timeoutAtMillis != Long.MAX_VALUE) {
+            AlarmManager alarmManager =
+                    (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+            Intent timeoutIntent = new Intent(context, TimeoutReceiver.class)
+                                           .setData(Uri.parse(url))
+                                           .putExtra(NOTIFICATION_ID_EXTRA, nextId);
+            alarmManager.set(AlarmManager.RTC, timeoutAtMillis,
+                    PendingIntent.getBroadcast(
+                            context, 0, timeoutIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+        }
     }
 
-    @CalledByNative
-    private static void hideNotification() {
+    private static void hideNotification(int id) {
         Context context = ContextUtils.getApplicationContext();
         NotificationManager manager =
                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-        manager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+        manager.cancel(NOTIFICATION_TAG, id);
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    @CalledByNative
+    private static void hideAllNotifications() {
+        Context context = ContextUtils.getApplicationContext();
+        NotificationManager manager =
+                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            for (StatusBarNotification activeNotification : manager.getActiveNotifications()) {
+                if (activeNotification.getTag() == NOTIFICATION_TAG) {
+                    manager.cancel(NOTIFICATION_TAG, activeNotification.getId());
+                }
+            }
+        } else {
+            manager.cancel(NOTIFICATION_TAG, 0);
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
new file mode 100644
index 0000000..b9c40de
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
@@ -0,0 +1,222 @@
+// 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.payments;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.JsonWriter;
+
+import org.chromium.chrome.R;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.payments.mojom.PaymentItem;
+import org.chromium.payments.mojom.PaymentMethodData;
+import org.chromium.ui.base.WindowAndroid;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** The point of interaction with a locally installed 3rd party native Android payment app. */
+public class AndroidPaymentApp extends PaymentInstrument implements PaymentApp,
+        WindowAndroid.IntentCallback {
+    private static final String EXTRA_METHOD_NAME = "methodName";
+    private static final String EXTRA_DATA = "data";
+    private static final String EXTRA_ORIGIN = "origin";
+    private static final String EXTRA_DETAILS = "details";
+    private static final String EXTRA_INSTRUMENT_DETAILS = "instrumentDetails";
+    private static final String EMPTY_JSON_DATA = "{}";
+
+    private final Handler mHandler;
+    private final WebContents mWebContents;
+    private final Intent mPayIntent;
+    private final Set<String> mMethodNames;
+    private String mIsReadyToPayService;
+    private InstrumentDetailsCallback mInstrumentDetailsCallback;
+
+    /**
+     * Builds the point of interaction with a locally installed 3rd party native Android payment
+     * app.
+     *
+     * @param webContents The web contents.
+     * @param packageName The name of the package of the payment app.
+     * @param activity    The name of the payment activity in the payment app.
+     * @param label       The UI label to use for the payment app.
+     * @param icon        The icon to use in UI for the payment app.
+     */
+    public AndroidPaymentApp(WebContents webContents, String packageName, String activity,
+            String label, Drawable icon) {
+        super(packageName, label, null, icon);
+        mHandler = new Handler();
+        mWebContents = webContents;
+        mPayIntent = new Intent();
+        mPayIntent.setClassName(packageName, activity);
+        mMethodNames = new HashSet<>();
+    }
+
+    /** @param methodName A payment method that this app supports, e.g., "https://bobpay.com". */
+    public void addMethodName(String methodName) {
+        mMethodNames.add(methodName);
+    }
+
+    /** @param service The name of the "is ready to pay" service in the payment app. */
+    public void setIsReadyToPayService(String service) {
+        mIsReadyToPayService = service;
+    }
+
+    @Override
+    public void getInstruments(Map<String, PaymentMethodData> methodData, String origin,
+            final InstrumentsCallback callback) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                List<PaymentInstrument> instruments = new ArrayList<>();
+                instruments.add(AndroidPaymentApp.this);
+                callback.onInstrumentsReady(AndroidPaymentApp.this, instruments);
+            }
+        });
+    }
+
+    @Override
+    public boolean supportsMethodsAndData(Map<String, PaymentMethodData> methodsAndData) {
+        assert methodsAndData != null;
+        Set<String> methodNames = new HashSet<>(methodsAndData.keySet());
+        methodNames.retainAll(getAppMethodNames());
+        return !methodNames.isEmpty();
+    }
+
+    @Override
+    public String getAppIdentifier() {
+        return getIdentifier();
+    }
+
+    @Override
+    public Set<String> getAppMethodNames() {
+        return Collections.unmodifiableSet(mMethodNames);
+    }
+
+    @Override
+    public Set<String> getInstrumentMethodNames() {
+        return getAppMethodNames();
+    }
+
+    @Override
+    public void invokePaymentApp(String merchantName, String origin, PaymentItem total,
+            List<PaymentItem> cart, Map<String, PaymentMethodData> methodDataMap,
+            InstrumentDetailsCallback callback) {
+        assert !mMethodNames.isEmpty();
+        Bundle extras = new Bundle();
+        extras.putString(EXTRA_ORIGIN, origin);
+
+        String methodName = mMethodNames.iterator().next();
+        extras.putString(EXTRA_METHOD_NAME, methodName);
+
+        PaymentMethodData methodData = methodDataMap.get(methodName);
+        extras.putString(
+                EXTRA_DATA, methodData == null ? EMPTY_JSON_DATA : methodData.stringifiedData);
+
+        String details = serializeDetails(total, cart);
+        extras.putString(EXTRA_DETAILS, details == null ? EMPTY_JSON_DATA : details);
+        mPayIntent.putExtras(extras);
+
+        mInstrumentDetailsCallback = callback;
+
+        ContentViewCore contentView = ContentViewCore.fromWebContents(mWebContents);
+        if (contentView == null) {
+            notifyError();
+            return;
+        }
+
+        WindowAndroid window = contentView.getWindowAndroid();
+        if (window == null) {
+            notifyError();
+            return;
+        }
+
+        if (!window.showIntent(mPayIntent, this, R.string.payments_android_app_error)) {
+            notifyError();
+        }
+    }
+
+    private void notifyError() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mInstrumentDetailsCallback.onInstrumentDetailsError();
+            }
+        });
+    }
+
+    private static String serializeDetails(PaymentItem total, List<PaymentItem> cart) {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter json = new JsonWriter(stringWriter);
+        try {
+            // details {{{
+            json.beginObject();
+
+            // total {{{
+            json.name("total");
+            serializePaymentItem(json, total);
+            // }}} total
+
+            // displayitems {{{
+            if (cart != null) {
+                json.name("displayItems").beginArray();
+                for (int i = 0; i < cart.size(); i++) {
+                    serializePaymentItem(json, cart.get(i));
+                }
+                json.endArray();
+            }
+            // }}} displayItems
+
+            json.endObject();
+            // }}} details
+        } catch (IOException e) {
+            return null;
+        }
+
+        return stringWriter.toString();
+    }
+
+    private static void serializePaymentItem(JsonWriter json, PaymentItem item) throws IOException {
+        // item {{{
+        json.beginObject();
+        json.name("label").value(item.label);
+
+        // amount {{{
+        json.name("amount").beginObject();
+        json.name("currency").value(item.amount.currency);
+        json.name("value").value(item.amount.value);
+        json.endObject();
+        // }}} amount
+
+        json.endObject();
+        // }}} item
+    }
+
+    @Override
+    public void onIntentCompleted(WindowAndroid window, int resultCode, Intent data) {
+        window.removeIntentCallback(this);
+        if (data == null || data.getExtras() == null || resultCode != Activity.RESULT_OK) {
+            mInstrumentDetailsCallback.onInstrumentDetailsError();
+        } else {
+            mInstrumentDetailsCallback.onInstrumentDetailsReady(
+                    data.getExtras().getString(EXTRA_METHOD_NAME),
+                    data.getExtras().getString(EXTRA_INSTRUMENT_DETAILS));
+        }
+        mInstrumentDetailsCallback = null;
+    }
+
+    @Override
+    public void dismissInstrument() {}
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
new file mode 100644
index 0000000..1914bb02
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
@@ -0,0 +1,68 @@
+// 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.payments;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+
+import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppCreatedCallback;
+import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryAddition;
+import org.chromium.content_public.browser.WebContents;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** Builds instances of payment apps based on installed third party Android payment apps. */
+public class AndroidPaymentAppFactory implements PaymentAppFactoryAddition {
+    private static final String ACTION_PAY = "org.chromium.intent.action.PAY";
+    private static final String ACTION_IS_READY_TO_PAY =
+            "org.chromium.intent.action.IS_READY_TO_PAY";
+    private static final String METHOD_PREFIX = "https://";
+
+    @Override
+    public void create(Context context, WebContents webContents, Set<String> methods,
+            PaymentAppCreatedCallback callback) {
+        Map<String, AndroidPaymentApp> installedApps = new HashMap<>();
+        PackageManager pm = context.getPackageManager();
+        Intent payIntent = new Intent(ACTION_PAY);
+
+        for (String methodName : methods) {
+            if (!methodName.startsWith(METHOD_PREFIX)) continue;
+
+            payIntent.setData(Uri.parse(methodName));
+            List<ResolveInfo> matches = pm.queryIntentActivities(payIntent, 0);
+            for (int i = 0; i < matches.size(); i++) {
+                ResolveInfo match = matches.get(i);
+                String packageName = match.activityInfo.packageName;
+                AndroidPaymentApp installedApp = installedApps.get(packageName);
+                if (installedApp == null) {
+                    CharSequence label = match.loadLabel(pm);
+                    installedApp =
+                            new AndroidPaymentApp(webContents, packageName, match.activityInfo.name,
+                                    label == null ? "" : label.toString(), match.loadIcon(pm));
+                    callback.onPaymentAppCreated(installedApp);
+                    installedApps.put(packageName, installedApp);
+                }
+                installedApp.addMethodName(methodName);
+            }
+        }
+
+        List<ResolveInfo> matches =
+                pm.queryIntentServices(new Intent(ACTION_IS_READY_TO_PAY), 0);
+        for (int i = 0; i < matches.size(); i++) {
+            ResolveInfo match = matches.get(i);
+            String packageName = match.serviceInfo.packageName;
+            AndroidPaymentApp installedApp = installedApps.get(packageName);
+            if (installedApp != null) installedApp.setIsReadyToPayService(match.serviceInfo.name);
+        }
+
+        callback.onAllPaymentAppsCreated();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
index 5f6d3b4..45e3435e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge.DropdownKeyValue;
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.WindowAndroid;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -407,10 +408,14 @@
 
         // Card scanner is expensive to query.
         if (mCardScanner == null) {
-            mCardScanner = CreditCardScanner.create(mContext,
-                    ContentViewCore.fromWebContents(mWebContents).getWindowAndroid(),
-                    this);
-            mCanScan = mCardScanner.canScan();
+            ContentViewCore contentView = ContentViewCore.fromWebContents(mWebContents);
+            if (contentView != null) {
+                WindowAndroid window = contentView.getWindowAndroid();
+                if (window != null) {
+                    mCardScanner = CreditCardScanner.create(mContext, window, this);
+                    mCanScan = mCardScanner.canScan();
+                }
+            }
         }
 
         // Card number is validated.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactory.java
index a93ef0d..dd8af08f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactory.java
@@ -52,14 +52,20 @@
          *
          * @param context     The application context.
          * @param webContents The web contents that invoked PaymentRequest.
+         * @param methods     The methods that the merchant supports.
          * @param callback    The callback to invoke when apps are created.
          */
-        void create(Context context, WebContents webContents, PaymentAppCreatedCallback callback);
+        void create(Context context, WebContents webContents, Set<String> methods,
+                PaymentAppCreatedCallback callback);
     }
 
     private PaymentAppFactory() {
         mAdditionalFactories = new ArrayList<>();
 
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_PAYMENT_APPS)) {
+            mAdditionalFactories.add(new AndroidPaymentAppFactory());
+        }
+
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.SERVICE_WORKER_PAYMENT_APPS)) {
             mAdditionalFactories.add(new ServiceWorkerPaymentAppBridge());
         }
@@ -88,10 +94,11 @@
      *
      * @param context     The context.
      * @param webContents The web contents where PaymentRequest was invoked.
+     * @param methods     The methods that the merchant supports.
      * @param callback    The callback to invoke when apps are created.
      */
-    public void create(
-            Context context, WebContents webContents, final PaymentAppCreatedCallback callback) {
+    public void create(Context context, WebContents webContents, Set<String> methods,
+            final PaymentAppCreatedCallback callback) {
         callback.onPaymentAppCreated(new AutofillPaymentApp(context, webContents));
 
         if (mAdditionalFactories.isEmpty()) {
@@ -104,7 +111,7 @@
 
         for (int i = 0; i < mAdditionalFactories.size(); i++) {
             final PaymentAppFactoryAddition additionalFactory = mAdditionalFactories.get(i);
-            additionalFactory.create(context, webContents, new PaymentAppCreatedCallback() {
+            PaymentAppCreatedCallback cb = new PaymentAppCreatedCallback() {
                 @Override
                 public void onPaymentAppCreated(PaymentApp paymentApp) {
                     callback.onPaymentAppCreated(paymentApp);
@@ -115,7 +122,8 @@
                     mPendingTasks.remove(additionalFactory);
                     if (mPendingTasks.isEmpty()) callback.onAllPaymentAppsCreated();
                 }
-            });
+            };
+            additionalFactory.create(context, webContents, methods, cb);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index f7c6cbcc4..dbe3313 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -207,6 +207,7 @@
 
     private final Handler mHandler = new Handler();
     private final ChromeActivity mContext;
+    private final WebContents mWebContents;
     private final String mMerchantName;
     private final String mOrigin;
     private final AddressEditor mAddressEditor;
@@ -251,7 +252,6 @@
     private SectionInformation mShippingAddressesSection;
     private SectionInformation mContactSection;
     private List<PaymentApp> mApps;
-    private boolean mAllAppsCreated;
     private List<PaymentApp> mPendingApps;
     private List<PaymentInstrument> mPendingInstruments;
     private List<PaymentInstrument> mPendingAutofillInstruments;
@@ -280,8 +280,8 @@
     /**
      * Builds the PaymentRequest service implementation.
      *
-     * @param context         The context where PaymentRequest has been invoked.
-     * @param webContents     The web contents that have invoked the PaymentRequest API.
+     * @param context     The context where PaymentRequest has been invoked.
+     * @param webContents The web contents that have invoked the PaymentRequest API.
      */
     public PaymentRequestImpl(Activity context, WebContents webContents) {
         assert context != null;
@@ -289,6 +289,7 @@
 
         assert context instanceof ChromeActivity;
         mContext = (ChromeActivity) context;
+        mWebContents = webContents;
 
         mMerchantName = webContents.getTitle();
         // The feature is available only in secure context, so it's OK to not show HTTPS.
@@ -313,7 +314,6 @@
                 });
 
         mApps = new ArrayList<>();
-        PaymentAppFactory.getInstance().create(mContext, webContents, this);
 
         mAddressEditor = new AddressEditor();
         mCardEditor = new CardEditor(webContents, mAddressEditor, sObserverForTest);
@@ -349,7 +349,8 @@
 
         if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return;
 
-        getMatchingPaymentInstruments();
+        PaymentAppFactory.getInstance().create(
+                mContext, mWebContents, Collections.unmodifiableSet(mMethodData.keySet()), this);
 
         boolean requestShipping = options != null && options.requestShipping;
         boolean requestPayerName = options != null && options.requestPayerName;
@@ -573,13 +574,10 @@
 
     @Override
     public void onAllPaymentAppsCreated() {
-        mAllAppsCreated = true;
-        getMatchingPaymentInstruments();
-    }
+        if (mClient == null) return;
 
-    /** Queries the installed payment apps for their instruments that merchant supports. */
-    private void getMatchingPaymentInstruments() {
-        if (!mAllAppsCreated || mClient == null || mPendingApps != null) return;
+        assert mPendingApps == null;
+
         mPendingApps = new ArrayList<>(mApps);
         mPendingInstruments = new ArrayList<>();
         mPendingAutofillInstruments = new ArrayList<>();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
index 2f8c37e..72dbc39 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
@@ -56,7 +56,7 @@
     }
 
     @Override
-    public void create(Context context, WebContents webContents,
+    public void create(Context context, WebContents webContents, Set<String> methodNames,
             PaymentAppFactory.PaymentAppCreatedCallback callback) {
         nativeGetAllAppManifests(webContents, callback);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/ListUrlsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/ListUrlsActivity.java
index 6e80ab6..8c9f8d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/ListUrlsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/ListUrlsActivity.java
@@ -121,7 +121,7 @@
         mPwsClient = new PwsClientImpl(this);
         int referer = getIntent().getIntExtra(REFERER_KEY, 0);
         if (savedInstanceState == null) {  // Ensure this is a newly-created activity.
-            PhysicalWebUma.onActivityReferral(this, referer);
+            PhysicalWebUma.onActivityReferral(referer);
         }
         mIsInitialDisplayRecorded = false;
         mIsRefreshing = false;
@@ -211,9 +211,9 @@
             public void onPwsResults(Collection<PwsResult> pwsResults) {
                 long duration = SystemClock.elapsedRealtime() - timestamp;
                 if (isUserInitiated) {
-                    PhysicalWebUma.onRefreshPwsResolution(ListUrlsActivity.this, duration);
+                    PhysicalWebUma.onRefreshPwsResolution(duration);
                 } else {
-                    PhysicalWebUma.onForegroundPwsResolution(ListUrlsActivity.this, duration);
+                    PhysicalWebUma.onForegroundPwsResolution(duration);
                 }
 
                 // filter out duplicate groups.
@@ -241,7 +241,7 @@
      */
     @Override
     public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
-        PhysicalWebUma.onUrlSelected(this);
+        PhysicalWebUma.onUrlSelected();
         UrlInfo nearestUrlInfo = null;
         PwsResult nearestPwsResult = mAdapter.getItem(position);
         String groupId = nearestPwsResult.groupId;
@@ -326,9 +326,9 @@
         // Record refresh-related UMA.
         if (!mIsInitialDisplayRecorded) {
             mIsInitialDisplayRecorded = true;
-            PhysicalWebUma.onUrlsDisplayed(this, mAdapter.getCount());
+            PhysicalWebUma.onUrlsDisplayed(mAdapter.getCount());
         } else if (mIsRefreshUserInitiated) {
-            PhysicalWebUma.onUrlsRefreshed(this, mAdapter.getCount());
+            PhysicalWebUma.onUrlsRefreshed(mAdapter.getCount());
         }
 
         mIsRefreshing = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebOptInActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebOptInActivity.java
index a8244ae9..2c80f0f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebOptInActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebOptInActivity.java
@@ -35,7 +35,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.physical_web_optin);
-        PhysicalWebUma.onOptInNotificationPressed(this);
+        PhysicalWebUma.onOptInNotificationPressed();
 
         TextView description = (TextView) findViewById(R.id.physical_web_optin_description);
         description.setMovementMethod(LinkMovementMethod.getInstance());
@@ -45,7 +45,7 @@
         declineButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                PhysicalWebUma.onOptInDeclineButtonPressed(PhysicalWebOptInActivity.this);
+                PhysicalWebUma.onOptInDeclineButtonPressed();
                 PrivacyPreferencesManager.getInstance().setPhysicalWebEnabled(false);
                 finish();
             }
@@ -55,7 +55,7 @@
         enableButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                PhysicalWebUma.onOptInEnableButtonPressed(PhysicalWebOptInActivity.this);
+                PhysicalWebUma.onOptInEnableButtonPressed();
                 PrivacyPreferencesManager.getInstance().setPhysicalWebEnabled(true);
                 startActivity(createListUrlsIntent(PhysicalWebOptInActivity.this));
                 finish();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebUma.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebUma.java
index 4212d7e3..35c75e37 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebUma.java
@@ -4,18 +4,18 @@
 
 package org.chromium.chrome.browser.physicalweb;
 
-import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.AsyncTask;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.components.location.LocationUtils;
-import org.json.JSONArray;
-import org.json.JSONException;
 
 import java.util.concurrent.TimeUnit;
 
@@ -69,81 +69,81 @@
     /**
      * Records a URL selection.
      */
-    public static void onUrlSelected(Context context) {
-        handleAction(context, URL_SELECTED_COUNT);
+    public static void onUrlSelected() {
+        handleAction(URL_SELECTED_COUNT);
     }
 
     /**
      * Records a tap on the opt-in decline button.
      */
-    public static void onOptInDeclineButtonPressed(Context context) {
-        handleAction(context, OPT_IN_DECLINE_BUTTON_PRESS_COUNT);
+    public static void onOptInDeclineButtonPressed() {
+        handleAction(OPT_IN_DECLINE_BUTTON_PRESS_COUNT);
     }
 
     /**
      * Records a tap on the opt-in enable button.
      */
-    public static void onOptInEnableButtonPressed(Context context) {
-        handleAction(context, OPT_IN_ENABLE_BUTTON_PRESS_COUNT);
+    public static void onOptInEnableButtonPressed() {
+        handleAction(OPT_IN_ENABLE_BUTTON_PRESS_COUNT);
     }
 
     /**
      * Records a display of a high priority opt-in notification.
      */
-    public static void onOptInHighPriorityNotificationShown(Context context) {
-        handleAction(context, OPT_IN_HIGH_PRIORITY_NOTIFICATION_COUNT);
+    public static void onOptInHighPriorityNotificationShown() {
+        handleAction(OPT_IN_HIGH_PRIORITY_NOTIFICATION_COUNT);
     }
 
     /**
      * Records a display of a min priority opt-in notification.
      */
-    public static void onOptInMinPriorityNotificationShown(Context context) {
-        handleAction(context, OPT_IN_MIN_PRIORITY_NOTIFICATION_COUNT);
+    public static void onOptInMinPriorityNotificationShown() {
+        handleAction(OPT_IN_MIN_PRIORITY_NOTIFICATION_COUNT);
     }
 
     /**
      * Records a display of the opt-in activity.
      */
-    public static void onOptInNotificationPressed(Context context) {
-        handleAction(context, OPT_IN_NOTIFICATION_PRESS_COUNT);
+    public static void onOptInNotificationPressed() {
+        handleAction(OPT_IN_NOTIFICATION_PRESS_COUNT);
     }
 
     /**
      * Records when the user disables the Physical Web fetaure.
      */
-    public static void onPrefsFeatureDisabled(Context context) {
-        handleAction(context, PREFS_FEATURE_DISABLED_COUNT);
+    public static void onPrefsFeatureDisabled() {
+        handleAction(PREFS_FEATURE_DISABLED_COUNT);
     }
 
     /**
      * Records when the user enables the Physical Web fetaure.
      */
-    public static void onPrefsFeatureEnabled(Context context) {
-        handleAction(context, PREFS_FEATURE_ENABLED_COUNT);
+    public static void onPrefsFeatureEnabled() {
+        handleAction(PREFS_FEATURE_ENABLED_COUNT);
     }
 
     /**
      * Records when the user denies the location permission when enabling the Physical Web from the
      * privacy settings menu.
      */
-    public static void onPrefsLocationDenied(Context context) {
-        handleAction(context, PREFS_LOCATION_DENIED_COUNT);
+    public static void onPrefsLocationDenied() {
+        handleAction(PREFS_LOCATION_DENIED_COUNT);
     }
 
     /**
      * Records when the user grants the location permission when enabling the Physical Web from the
      * privacy settings menu.
      */
-    public static void onPrefsLocationGranted(Context context) {
-        handleAction(context, PREFS_LOCATION_GRANTED_COUNT);
+    public static void onPrefsLocationGranted() {
+        handleAction(PREFS_LOCATION_GRANTED_COUNT);
     }
 
     /**
      * Records a response time from PWS for a resolution during a background scan.
      * @param duration The length of time PWS took to respond.
      */
-    public static void onBackgroundPwsResolution(Context context, long duration) {
-        handleTime(context, PWS_BACKGROUND_RESOLVE_TIMES, duration, TimeUnit.MILLISECONDS);
+    public static void onBackgroundPwsResolution(long duration) {
+        handleTime(PWS_BACKGROUND_RESOLVE_TIMES, duration, TimeUnit.MILLISECONDS);
     }
 
     /**
@@ -151,8 +151,8 @@
      * explicitly user-initiated through a refresh.
      * @param duration The length of time PWS took to respond.
      */
-    public static void onForegroundPwsResolution(Context context, long duration) {
-        handleTime(context, PWS_FOREGROUND_RESOLVE_TIMES, duration, TimeUnit.MILLISECONDS);
+    public static void onForegroundPwsResolution(long duration) {
+        handleTime(PWS_FOREGROUND_RESOLVE_TIMES, duration, TimeUnit.MILLISECONDS);
     }
 
     /**
@@ -160,19 +160,19 @@
      * user-initiated through a refresh.
      * @param duration The length of time PWS took to respond.
      */
-    public static void onRefreshPwsResolution(Context context, long duration) {
-        handleTime(context, PWS_REFRESH_RESOLVE_TIMES, duration, TimeUnit.MILLISECONDS);
+    public static void onRefreshPwsResolution(long duration) {
+        handleTime(PWS_REFRESH_RESOLVE_TIMES, duration, TimeUnit.MILLISECONDS);
     }
 
     /**
      * Records number of URLs displayed to a user when the URL list is first displayed.
      * @param numUrls The number of URLs displayed to a user.
      */
-    public static void onUrlsDisplayed(Context context, int numUrls) {
+    public static void onUrlsDisplayed(int numUrls) {
         if (LibraryLoader.isInitialized()) {
             RecordHistogram.recordCountHistogram(TOTAL_URLS_INITIAL_COUNTS, numUrls);
         } else {
-            storeValue(context, TOTAL_URLS_INITIAL_COUNTS, numUrls);
+            storeValue(TOTAL_URLS_INITIAL_COUNTS, numUrls);
         }
     }
 
@@ -180,11 +180,11 @@
      * Records number of URLs displayed to a user when the user refreshes the URL list.
      * @param numUrls The number of URLs displayed to a user.
      */
-    public static void onUrlsRefreshed(Context context, int numUrls) {
+    public static void onUrlsRefreshed(int numUrls) {
         if (LibraryLoader.isInitialized()) {
             RecordHistogram.recordCountHistogram(TOTAL_URLS_REFRESH_COUNTS, numUrls);
         } else {
-            storeValue(context, TOTAL_URLS_REFRESH_COUNTS, numUrls);
+            storeValue(TOTAL_URLS_REFRESH_COUNTS, numUrls);
         }
     }
 
@@ -193,24 +193,24 @@
      * @param refer The type of referral.  This enum is listed as PhysicalWebActivityReferer in
      *     histograms.xml.
      */
-    public static void onActivityReferral(Context context, int referer) {
-        handleEnum(context, ACTIVITY_REFERRALS, referer, ListUrlsActivity.REFERER_BOUNDARY);
+    public static void onActivityReferral(int referer) {
+        handleEnum(ACTIVITY_REFERRALS, referer, ListUrlsActivity.REFERER_BOUNDARY);
         switch (referer) {
             case ListUrlsActivity.NOTIFICATION_REFERER:
-                handleTime(context, STANDARD_NOTIFICATION_PRESS_DELAYS,
+                handleTime(STANDARD_NOTIFICATION_PRESS_DELAYS,
                         UrlManager.getInstance().getTimeSinceNotificationUpdate(),
                         TimeUnit.MILLISECONDS);
                 break;
             case ListUrlsActivity.OPTIN_REFERER:
-                handleTime(context, OPT_IN_NOTIFICATION_PRESS_DELAYS,
+                handleTime(OPT_IN_NOTIFICATION_PRESS_DELAYS,
                         UrlManager.getInstance().getTimeSinceNotificationUpdate(),
                         TimeUnit.MILLISECONDS);
                 break;
             case ListUrlsActivity.PREFERENCE_REFERER:
-                recordPhysicalWebState(context, LAUNCH_FROM_PREFERENCES);
+                recordPhysicalWebState(LAUNCH_FROM_PREFERENCES);
                 break;
             case ListUrlsActivity.DIAGNOSTICS_REFERER:
-                recordPhysicalWebState(context, LAUNCH_FROM_DIAGNOSTICS);
+                recordPhysicalWebState(LAUNCH_FROM_DIAGNOSTICS);
                 break;
             default:
                 break;
@@ -226,21 +226,21 @@
      * - The data connection status
      * - The Physical Web preference status
      */
-    public static void recordPhysicalWebState(Context context, String actionName) {
+    public static void recordPhysicalWebState(String actionName) {
         LocationUtils locationUtils = LocationUtils.getInstance();
-        handleEnum(context, createStateString(LOCATION_SERVICES, actionName),
+        handleEnum(createStateString(LOCATION_SERVICES, actionName),
                 locationUtils.isSystemLocationSettingEnabled() ? 1 : 0, BOOLEAN_BOUNDARY);
-        handleEnum(context, createStateString(LOCATION_PERMISSION, actionName),
+        handleEnum(createStateString(LOCATION_PERMISSION, actionName),
                 locationUtils.hasAndroidLocationPermission() ? 1 : 0, BOOLEAN_BOUNDARY);
-        handleEnum(context, createStateString(BLUETOOTH, actionName),
+        handleEnum(createStateString(BLUETOOTH, actionName),
                 Utils.getBluetoothEnabledStatus(), TRISTATE_BOUNDARY);
-        handleEnum(context, createStateString(DATA_CONNECTION, actionName),
+        handleEnum(createStateString(DATA_CONNECTION, actionName),
                 Utils.isDataConnectionActive() ? 1 : 0, BOOLEAN_BOUNDARY);
         int preferenceState = 2;
         if (!PhysicalWeb.isOnboarding()) {
             preferenceState = PhysicalWeb.isPhysicalWebPreferenceEnabled() ? 1 : 0;
         }
-        handleEnum(context, createStateString(PREFERENCE, actionName),
+        handleEnum(createStateString(PREFERENCE, actionName),
                 preferenceState, TRISTATE_BOUNDARY);
     }
 
@@ -259,7 +259,7 @@
         return PHYSICAL_WEB_STATE + "." + stateName + "." + actionName;
     }
 
-    private static void storeAction(Context context, String key) {
+    private static void storeAction(String key) {
         SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
         int count = prefs.getInt(key, 0);
         prefs.edit()
@@ -268,7 +268,7 @@
                 .apply();
     }
 
-    private static void storeValue(Context context, String key, Object value) {
+    private static void storeValue(String key, Object value) {
         SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor prefsEditor = prefs.edit();
         JSONArray values = null;
@@ -287,27 +287,27 @@
         prefsEditor.putString(key, values.toString()).apply();
     }
 
-    private static void handleAction(Context context, String key) {
+    private static void handleAction(String key) {
         if (LibraryLoader.isInitialized()) {
             RecordUserAction.record(key);
         } else {
-            storeAction(context, key);
+            storeAction(key);
         }
     }
 
-    private static void handleTime(Context context, String key, long duration, TimeUnit tu) {
+    private static void handleTime(String key, long duration, TimeUnit tu) {
         if (LibraryLoader.isInitialized()) {
             RecordHistogram.recordTimesHistogram(key, duration, tu);
         } else {
-            storeValue(context, key, duration);
+            storeValue(key, duration);
         }
     }
 
-    private static void handleEnum(Context context, String key, int value, int boundary) {
+    private static void handleEnum(String key, int value, int boundary) {
         if (LibraryLoader.isInitialized()) {
             RecordHistogram.recordEnumeratedHistogram(key, value, boundary);
         } else {
-            storeValue(context, key, value);
+            storeValue(key, value);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java
index 99890d7..4fda676 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java
@@ -503,8 +503,7 @@
             @Override
             public void onPwsResults(final Collection<PwsResult> pwsResults) {
                 long duration = SystemClock.elapsedRealtime() - timestamp;
-                PhysicalWebUma.onBackgroundPwsResolution(
-                        ContextUtils.getApplicationContext(), duration);
+                PhysicalWebUma.onBackgroundPwsResolution(duration);
                 new Handler(Looper.getMainLooper()).post(new Runnable() {
                     @Override
                     public void run() {
@@ -556,13 +555,11 @@
                 // high priority notification
                 createOptInNotification(true);
                 PhysicalWeb.recordOptInNotification();
-                PhysicalWebUma.onOptInHighPriorityNotificationShown(
-                        ContextUtils.getApplicationContext());
+                PhysicalWebUma.onOptInHighPriorityNotificationShown();
             } else {
                 // min priority notification
                 createOptInNotification(false);
-                PhysicalWebUma.onOptInMinPriorityNotificationShown(
-                        ContextUtils.getApplicationContext());
+                PhysicalWebUma.onOptInMinPriorityNotificationShown();
             }
         } else if (PhysicalWeb.isPhysicalWebPreferenceEnabled()) {
             createNotification();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PhysicalWebPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PhysicalWebPreferenceFragment.java
index 083aab5..f6851a37 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PhysicalWebPreferenceFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PhysicalWebPreferenceFragment.java
@@ -62,11 +62,11 @@
             case REQUEST_ID:
                 if (grantResults.length > 0
                         && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                    PhysicalWebUma.onPrefsLocationGranted(getActivity());
+                    PhysicalWebUma.onPrefsLocationGranted();
                     Log.d(TAG, "Location permission granted");
                     PhysicalWeb.startPhysicalWeb();
                 } else {
-                    PhysicalWebUma.onPrefsLocationDenied(getActivity());
+                    PhysicalWebUma.onPrefsLocationDenied();
                     Log.d(TAG, "Location permission denied");
                 }
                 break;
@@ -86,10 +86,10 @@
             public boolean onPreferenceChange(Preference preference, Object newValue) {
                 boolean enabled = (boolean) newValue;
                 if (enabled) {
-                    PhysicalWebUma.onPrefsFeatureEnabled(getActivity());
+                    PhysicalWebUma.onPrefsFeatureEnabled();
                     ensureLocationPermission();
                 } else {
-                    PhysicalWebUma.onPrefsFeatureDisabled(getActivity());
+                    PhysicalWebUma.onPrefsFeatureDisabled();
                 }
                 PrivacyPreferencesManager.getInstance().setPhysicalWebEnabled(enabled);
                 return true;
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index f619481..a98f7517 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2762,6 +2762,9 @@
       <message name="IDS_PAYMENTS_UNSUPPORTED_PICKUP_ADDRESS" desc="Text implying that a user needs to choose a different pickup address, because the currently selected address is not supported. This address can be used, for example, for laundry pickup.">
         Unsupported pickup address. Select a different address.
       </message>
+      <message name="IDS_PAYMENTS_ANDROID_APP_ERROR" desc="Error message that is shown when an Android payment application fails to start.">
+        Unable to launch payment app.
+      </message>
 
       <!-- Migration strings -->
       <message name="IDS_TAB_SWITCHER_CALLOUT_HEADER" desc="Header for the Tab Switcher callout.">
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index d27f5f2e..651bdec 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -689,6 +689,8 @@
   "java/src/org/chromium/chrome/browser/password_manager/AutoSigninFirstRunDialog.java",
   "java/src/org/chromium/chrome/browser/password_manager/Credential.java",
   "java/src/org/chromium/chrome/browser/payments/AddressEditor.java",
+  "java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java",
+  "java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java",
   "java/src/org/chromium/chrome/browser/payments/AutofillAddress.java",
   "java/src/org/chromium/chrome/browser/payments/AutofillContact.java",
   "java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
index d762e0bee..6f8fe64 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java
@@ -80,7 +80,7 @@
         super.setupService();
         SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor editor = sharedPrefs.edit();
-        editor.remove(DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS);
+        editor.remove(DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
         editor.apply();
         super.tearDown();
     }
@@ -148,7 +148,7 @@
         SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor editor = sharedPrefs.edit();
         editor.putStringSet(
-                DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
+                DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
         editor.apply();
         startNotificationService();
         assertTrue(scheduler.mScheduled);
@@ -195,7 +195,7 @@
         SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor editor = sharedPrefs.edit();
         editor.putStringSet(
-                DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
+                DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
         editor.apply();
         startNotificationService();
         assertFalse(scheduler.mScheduled);
@@ -223,15 +223,15 @@
                 ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor editor = sharedPrefs.edit();
         editor.putStringSet(
-                DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
+                DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
         editor.apply();
         startNotificationService();
         assertTrue(getService().isPaused());
         assertEquals(2, getService().getNotificationIds().size());
         assertTrue(getService().getNotificationIds().contains(1));
         assertTrue(getService().getNotificationIds().contains(2));
-        assertTrue(
-                sharedPrefs.contains(DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS));
+        assertTrue(sharedPrefs.contains(
+                DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS));
     }
 
     /**
@@ -255,7 +255,7 @@
         SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor editor = sharedPrefs.edit();
         editor.putStringSet(
-                DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
+                DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
         editor.apply();
         startNotificationService();
         assertEquals(2, getService().getNotificationIds().size());
@@ -268,17 +268,17 @@
         assertEquals(3, getService().getNotificationIds().size());
         int lastNotificationId = getService().getLastAddedNotificationId();
         Set<String> entries = DownloadManagerService.getStoredDownloadInfo(
-                sharedPrefs, DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS);
+                sharedPrefs, DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
         assertEquals(3, entries.size());
 
         service.notifyDownloadSuccessful(guid1, "/path/to/success", "success", 100L, false, false);
         entries = DownloadManagerService.getStoredDownloadInfo(
-                sharedPrefs, DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS);
+                sharedPrefs, DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
         assertEquals(2, entries.size());
 
         service.notifyDownloadFailed(guid2, "failed");
         entries = DownloadManagerService.getStoredDownloadInfo(
-                sharedPrefs, DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS);
+                sharedPrefs, DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS);
         assertEquals(1, entries.size());
 
         service.notifyDownloadCanceled(guid3);
@@ -326,7 +326,7 @@
         SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor editor = sharedPrefs.edit();
         editor.putStringSet(
-                DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
+                DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
         editor.apply();
         startNotificationService();
         DownloadNotificationService service = bindNotificationService();
@@ -370,12 +370,12 @@
                 ContextUtils.getAppSharedPreferences();
         SharedPreferences.Editor editor = sharedPrefs.edit();
         editor.putStringSet(
-                DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
+                DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS, notifications);
         editor.apply();
         startNotificationService();
         assertTrue(getService().isPaused());
         assertFalse(sharedPrefs.contains(
-                DownloadNotificationService.PENDING_DOWNLOAD_NOTIFICATIONS));
+                DownloadNotificationService.KEY_PENDING_DOWNLOAD_NOTIFICATIONS));
     }
 
     @SmallTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
index e2efac1..d1a4650 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
@@ -11,6 +11,7 @@
 import org.chromium.content_public.browser.WebContents;
 
 import java.util.Arrays;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -42,6 +43,7 @@
                 new PaymentAppFactory.PaymentAppFactoryAddition() {
                     @Override
                     public void create(Context context, WebContents webContents,
+                            Set<String> methodNames,
                             PaymentAppFactory.PaymentAppCreatedCallback callback) {
                         ServiceWorkerPaymentAppBridge.Manifest testManifest =
                                 new ServiceWorkerPaymentAppBridge.Manifest();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestBase.java
index d423ebd..1c6045e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestBase.java
@@ -856,7 +856,7 @@
         final TestPay app = new TestPay(methodName, instrumentPresence, responseSpeed);
         PaymentAppFactory.getInstance().addAdditionalFactory(new PaymentAppFactoryAddition() {
             @Override
-            public void create(Context context, WebContents webContents,
+            public void create(Context context, WebContents webContents, Set<String> methodNames,
                     final PaymentAppFactory.PaymentAppCreatedCallback callback) {
                 if (creationSpeed == IMMEDIATE_CREATION) {
                     callback.onPaymentAppCreated(app);
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 16e3d17..6e7ffef 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -15394,6 +15394,12 @@
       <message name="IDS_FLAGS_ENABLE_ANDROID_PAY_INTEGRATION_V2_DESCRIPTION" desc="Description for the flag to enable the second version of Android Pay integration" translateable="false">
         Enable integration with Android Pay using the second version of the API
       </message>
+      <message name="IDS_FLAGS_ANDROID_PAYMENT_APPS_NAME" desc="Name of the flag to enable third party Android payment apps" translateable="false">
+        Android payment apps
+      </message>
+      <message name="IDS_FLAGS_ANDROID_PAYMENT_APPS_DESCRIPTION" desc="Description for the flag to enable third party Android payment apps" translateable="false">
+        Enable third party Android apps to integrate as payment apps
+      </message>
     </if>
 
     <message name="IDS_FLAGS_FEATURE_POLICY_NAME" desc="Name for the flag to enable feature policy.">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4b952e6d..257aa32 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2016,6 +2016,10 @@
      IDS_FLAGS_ENABLE_ANDROID_PAY_INTEGRATION_V2_NAME,
      IDS_FLAGS_ENABLE_ANDROID_PAY_INTEGRATION_V2_DESCRIPTION, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kAndroidPayIntegrationV2)},
+    {"android-payment-apps",
+     IDS_FLAGS_ANDROID_PAYMENT_APPS_NAME,
+     IDS_FLAGS_ANDROID_PAYMENT_APPS_DESCRIPTION, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kAndroidPaymentApps)},
 #endif  // OS_ANDROID
 #if defined(OS_CHROMEOS)
     {"disable-eol-notification", IDS_FLAGS_EOL_NOTIFICATION_NAME,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 5d399dc..908982e3c 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -40,6 +40,7 @@
     &features::kWebPayments,
     &kAndroidPayIntegrationV1,
     &kAndroidPayIntegrationV2,
+    &kAndroidPaymentApps,
     &kCCTExternalLinkHandling,
     &kCCTPostMessageAPI,
     &kChromeHomeFeature,
@@ -79,6 +80,9 @@
 const base::Feature kAndroidPayIntegrationV2{"AndroidPayIntegrationV2",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kAndroidPaymentApps{"AndroidPaymentApps",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kCCTExternalLinkHandling{"CCTExternalLinkHandling",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 5641bda..44f0f07b 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -15,6 +15,7 @@
 // Alphabetical:
 extern const base::Feature kAndroidPayIntegrationV1;
 extern const base::Feature kAndroidPayIntegrationV2;
+extern const base::Feature kAndroidPaymentApps;
 extern const base::Feature kCCTExternalLinkHandling;
 extern const base::Feature kCCTPostMessageAPI;
 extern const base::Feature kChromeHomeFeature;
diff --git a/chrome/browser/android/ntp/content_suggestions_notification_helper.cc b/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
index 03f4573..db5abdd3 100644
--- a/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
+++ b/chrome/browser/android/ntp/content_suggestions_notification_helper.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/android/ntp/content_suggestions_notification_helper.h"
 
+#include <limits>
+
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/strings/utf_string_conversions.h"
@@ -24,22 +26,28 @@
     const GURL& url,
     const base::string16& title,
     const base::string16& text,
-    const gfx::Image& image) {
+    const gfx::Image& image,
+    base::Time timeout_at) {
   JNIEnv* env = base::android::AttachCurrentThread();
   SkBitmap skimage = image.AsImageSkia().GetRepresentation(1.0f).sk_bitmap();
   if (skimage.empty())
     return;
 
+  jint timeout_at_millis = timeout_at.ToJavaTime();
+  if (timeout_at == base::Time::Max()) {
+    timeout_at_millis = std::numeric_limits<jint>::max();
+  }
+
   Java_ContentSuggestionsNotificationHelper_showNotification(
       env, base::android::ConvertUTF8ToJavaString(env, url.spec()),
       base::android::ConvertUTF16ToJavaString(env, title),
       base::android::ConvertUTF16ToJavaString(env, text),
-      gfx::ConvertToJavaBitmap(&skimage));
+      gfx::ConvertToJavaBitmap(&skimage), timeout_at_millis);
 }
 
-void ContentSuggestionsNotificationHelper::HideNotification() {
+void ContentSuggestionsNotificationHelper::HideAllNotifications() {
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_ContentSuggestionsNotificationHelper_hideNotification(env);
+  Java_ContentSuggestionsNotificationHelper_hideAllNotifications(env);
 }
 
 }  // namespace ntp_snippets
diff --git a/chrome/browser/android/ntp/content_suggestions_notification_helper.h b/chrome/browser/android/ntp/content_suggestions_notification_helper.h
index 97a06f2..e9c7d93c 100644
--- a/chrome/browser/android/ntp/content_suggestions_notification_helper.h
+++ b/chrome/browser/android/ntp/content_suggestions_notification_helper.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "base/time/time.h"
 #include "url/gurl.h"
 
 namespace gfx {
@@ -23,8 +24,9 @@
   static void SendNotification(const GURL& url,
                                const base::string16& title,
                                const base::string16& text,
-                               const gfx::Image& image);
-  static void HideNotification();
+                               const gfx::Image& image,
+                               base::Time timeout_at);
+  static void HideAllNotifications();
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(ContentSuggestionsNotificationHelper);
diff --git a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
index 61fe726..cb7bfab 100644
--- a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
+++ b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
@@ -81,12 +81,15 @@
     if (!suggestion) {
       return;
     }
+    base::Time timeout_at = suggestion->notification_extra()
+                                ? suggestion->notification_extra()->deadline
+                                : base::Time::Max();
     service_->FetchSuggestionImage(
         suggestion->id(),
         base::Bind(&NotifyingObserver::ImageFetched,
                    weak_ptr_factory_.GetWeakPtr(), suggestion->id(),
                    suggestion->url(), suggestion->title(),
-                   suggestion->publisher_name()));
+                   suggestion->publisher_name(), timeout_at));
   }
 
   void OnCategoryStatusChanged(Category category,
@@ -104,7 +107,7 @@
       case CategoryStatus::LOADING_ERROR:
       case CategoryStatus::NOT_PROVIDED:
       case CategoryStatus::SIGNED_OUT:
-        ContentSuggestionsNotificationHelper::HideNotification();
+        ContentSuggestionsNotificationHelper::HideAllNotifications();
         break;
     }
   }
@@ -114,16 +117,16 @@
     if (suggestion_id.category().IsKnownCategory(KnownCategories::ARTICLES) &&
         (suggestion_id.id_within_category() ==
          prefs_->GetString(kNotificationIDWithinCategory))) {
-      ContentSuggestionsNotificationHelper::HideNotification();
+      ContentSuggestionsNotificationHelper::HideAllNotifications();
     }
   }
 
   void OnFullRefreshRequired() override {
-    ContentSuggestionsNotificationHelper::HideNotification();
+    ContentSuggestionsNotificationHelper::HideAllNotifications();
   }
 
   void ContentSuggestionsServiceShutdown() override {
-    ContentSuggestionsNotificationHelper::HideNotification();
+    ContentSuggestionsNotificationHelper::HideAllNotifications();
   }
 
  private:
@@ -150,7 +153,7 @@
 
   void AppStatusChanged(base::android::ApplicationState state) {
     if (!ShouldNotifyInState(state)) {
-      ContentSuggestionsNotificationHelper::HideNotification();
+      ContentSuggestionsNotificationHelper::HideAllNotifications();
     }
   }
 
@@ -158,6 +161,7 @@
                     const GURL& url,
                     const base::string16& title,
                     const base::string16& publisher,
+                    base::Time timeout_at,
                     const gfx::Image& image) {
     if (!ShouldNotifyInState(app_status_listener_.GetState())) {
       return;  // Became foreground while we were fetching the image; forget it.
@@ -167,7 +171,7 @@
              << image.Size().height() << " image for " << url.spec();
     prefs_->SetString(kNotificationIDWithinCategory, id.id_within_category());
     ContentSuggestionsNotificationHelper::SendNotification(
-        url, title, publisher, CropSquare(image));
+        url, title, publisher, CropSquare(image), timeout_at);
   }
 
   ContentSuggestionsService* const service_;
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
index 0fa26a4a..ec11a572 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.cc
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -185,7 +185,9 @@
 
   // If omnibox text is empty, set it to the current URL for the purposes of
   // populating the verbatim match.
-  if (omnibox_text.empty())
+  if (omnibox_text.empty() &&
+      !current_url.SchemeIs(content::kChromeUIScheme) &&
+      !current_url.SchemeIs(chrome::kChromeUINativeScheme))
     omnibox_text = url;
 
   input_ = AutocompleteInput(
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 1eeea04..12d6b2f1 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/top_sites_factory.h"
@@ -153,7 +154,7 @@
 
 physical_web::PhysicalWebDataSource*
 ChromeAutocompleteProviderClient::GetPhysicalWebDataSource() {
-  return nullptr;
+  return g_browser_process->GetPhysicalWebDataSource();
 }
 
 std::string ChromeAutocompleteProviderClient::GetAcceptLanguages() const {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index dbfff0a..7e894b0f 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2798,22 +2798,21 @@
   }
 }
 
-#if defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
 void ChromeContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
     const base::CommandLine& command_line,
     int child_process_id,
-    FileDescriptorInfo* mappings,
-    std::map<int, base::MemoryMappedFile::Region>* regions) {
-  int fd = ui::GetMainAndroidPackFd(
-      &(*regions)[kAndroidUIResourcesPakDescriptor]);
-  mappings->Share(kAndroidUIResourcesPakDescriptor, fd);
+    FileDescriptorInfo* mappings) {
+#if defined(OS_ANDROID)
+  base::MemoryMappedFile::Region region;
+  int fd = ui::GetMainAndroidPackFd(&region);
+  mappings->ShareWithRegion(kAndroidUIResourcesPakDescriptor, fd, region);
 
-  fd = ui::GetCommonResourcesPackFd(
-      &(*regions)[kAndroidChrome100PercentPakDescriptor]);
-  mappings->Share(kAndroidChrome100PercentPakDescriptor, fd);
+  fd = ui::GetCommonResourcesPackFd(&region);
+  mappings->ShareWithRegion(kAndroidChrome100PercentPakDescriptor, fd, region);
 
-  fd = ui::GetLocalePackFd(&(*regions)[kAndroidLocalePakDescriptor]);
-  mappings->Share(kAndroidLocalePakDescriptor, fd);
+  fd = ui::GetLocalePackFd(&region);
+  mappings->ShareWithRegion(kAndroidLocalePakDescriptor, fd, region);
 
   if (breakpad::IsCrashReporterEnabled()) {
     base::File file =
@@ -2831,18 +2830,14 @@
   base::FilePath app_data_path;
   PathService::Get(base::DIR_ANDROID_APP_DATA, &app_data_path);
   DCHECK(!app_data_path.empty());
-}
-#elif defined(OS_POSIX) && !defined(OS_MACOSX)
-void ChromeContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
-    const base::CommandLine& command_line,
-    int child_process_id,
-    FileDescriptorInfo* mappings) {
+#else
   int crash_signal_fd = GetCrashSignalFD(command_line);
   if (crash_signal_fd >= 0) {
     mappings->Share(kCrashDumpSignal, crash_signal_fd);
   }
-}
 #endif  // defined(OS_ANDROID)
+}
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
 
 #if defined(OS_WIN)
 base::string16 ChromeContentBrowserClient::GetAppContainerSidForSandboxType(
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index dd23bde..6d6f8bb 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -258,18 +258,12 @@
       content::RenderFrameHost* render_frame_host,
       blink::WebPageVisibilityState* visibility_state) override;
 
-#if defined(OS_ANDROID)
-  void GetAdditionalMappedFilesForChildProcess(
-      const base::CommandLine& command_line,
-      int child_process_id,
-      content::FileDescriptorInfo* mappings,
-      std::map<int, base::MemoryMappedFile::Region>* regions) override;
-#elif defined(OS_POSIX) && !defined(OS_MACOSX)
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
   void GetAdditionalMappedFilesForChildProcess(
       const base::CommandLine& command_line,
       int child_process_id,
       content::FileDescriptorInfo* mappings) override;
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
 #if defined(OS_WIN)
   bool PreSpawnRenderer(sandbox::TargetPolicy* policy) override;
   base::string16 GetAppContainerSidForSandboxType(
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 4b1daa1..6e52ba5c 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -948,10 +948,16 @@
   return function;
 }
 
+// https://crbug.com/678967
+#if defined(OS_WIN)
+#define MAYBE_DownloadExtensionTest_FileIcon_Active DISABLED_DownloadExtensionTest_FileIcon_Active
+#else
+#define MAYBE_DownloadExtensionTest_FileIcon_Active DownloadExtensionTest_FileIcon_Active
+#endif
 // Test downloads.getFileIcon() on in-progress, finished, cancelled and deleted
 // download items.
 IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
-    DownloadExtensionTest_FileIcon_Active) {
+    MAYBE_DownloadExtensionTest_FileIcon_Active) {
   DownloadItem* download_item = CreateSlowTestDownload();
   ASSERT_TRUE(download_item);
   ASSERT_FALSE(download_item->GetTargetFilePath().empty());
diff --git a/chrome/browser/media/webrtc/window_icon_util_win.cc b/chrome/browser/media/webrtc/window_icon_util_win.cc
index 1a6039b..7e3b2ec 100644
--- a/chrome/browser/media/webrtc/window_icon_util_win.cc
+++ b/chrome/browser/media/webrtc/window_icon_util_win.cc
@@ -36,5 +36,8 @@
   std::unique_ptr<SkBitmap> icon_bitmap(
       IconUtil::CreateSkBitmapFromHICON(icon_handle));
 
+  if (!icon_bitmap)
+    return gfx::ImageSkia();
+
   return gfx::ImageSkia::CreateFrom1xBitmap(*icon_bitmap);
 }
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos.cc b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
index f25d392..2d6e2866 100644
--- a/chrome/browser/memory/tab_manager_delegate_chromeos.cc
+++ b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
@@ -249,16 +249,24 @@
 int TabManagerDelegate::MemoryStat::TargetMemoryToFreeKB() {
   static const int kRamVsSwapWeight = 4;
   static const char kMinFilelistConfig[] = "/proc/sys/vm/min_filelist_kbytes";
+  static const char kMinFreeKbytes[] = "/proc/sys/vm/min_free_kbytes";
 
   base::SystemMemoryInfoKB system_mem;
   base::GetSystemMemoryInfo(&system_mem);
   const int file_mem_kb = system_mem.active_file + system_mem.inactive_file;
   const int min_filelist_kb = ReadIntFromFile(kMinFilelistConfig, 0);
+  const int min_free_kb = ReadIntFromFile(kMinFreeKbytes, 0);
   // Calculate current available memory in system.
   // File-backed memory should be easy to reclaim, unless they're dirty.
+  // TODO(cylee): On ChromeOS, kernel reports low memory condition when
+  // available memory is low. The following formula duplicates the logic in
+  // kernel to calculate how much memory should be released. In the future,
+  // kernel should try to report the amount of memory to release directly to
+  // eliminate the duplication here.
   const int available_mem_kb = system_mem.free +
       file_mem_kb - system_mem.dirty - min_filelist_kb +
-      system_mem.swap_free / kRamVsSwapWeight;
+      system_mem.swap_free / kRamVsSwapWeight -
+      min_free_kb;
 
   return LowMemoryMarginKB() - available_mem_kb;
 }
@@ -573,7 +581,8 @@
     const TabStatsList& tab_list,
     const std::vector<arc::ArcProcess>& arc_processes) {
 
-  VLOG(2) << "LowMemoryKilleImpl";
+  VLOG(2) << "LowMemoryKillImpl";
+
   const std::vector<TabManagerDelegate::Candidate> candidates =
       GetSortedCandidates(tab_list, arc_processes);
 
diff --git a/chrome/browser/payments/OWNERS b/chrome/browser/payments/OWNERS
new file mode 100644
index 0000000..9e364d9
--- /dev/null
+++ b/chrome/browser/payments/OWNERS
@@ -0,0 +1 @@
+file://components/payments/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service.cc b/chrome/browser/safe_browsing/certificate_reporting_service.cc
index 9bcecb8..d0fb362c 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service.cc
@@ -167,12 +167,14 @@
     uint32_t server_public_key_version,
     size_t max_queued_report_count,
     base::TimeDelta max_report_age,
-    base::Clock* clock)
+    base::Clock* clock,
+    const base::Callback<void()>& reset_callback)
     : pref_service_(*profile->GetPrefs()),
       url_request_context_(nullptr),
       max_queued_report_count_(max_queued_report_count),
       max_report_age_(max_report_age),
       clock_(clock),
+      reset_callback_(reset_callback),
       server_public_key_(server_public_key),
       server_public_key_version_(server_public_key_version) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -188,12 +190,13 @@
           base::Bind(&CertificateReportingService::OnPreferenceChanged,
                      base::Unretained(this)));
 
-  content::BrowserThread::PostTask(
+  content::BrowserThread::PostTaskAndReply(
       content::BrowserThread::IO, FROM_HERE,
       base::Bind(&CertificateReportingService::InitializeOnIOThread,
                  base::Unretained(this), true, url_request_context_getter,
                  max_queued_report_count_, max_report_age_, clock_,
-                 server_public_key_, server_public_key_version_));
+                 server_public_key_, server_public_key_version_),
+      reset_callback_);
 }
 
 CertificateReportingService::~CertificateReportingService() {
@@ -253,12 +256,13 @@
   if (!url_request_context_)
     return;
 
-  content::BrowserThread::PostTask(
+  content::BrowserThread::PostTaskAndReply(
       content::BrowserThread::IO, FROM_HERE,
       base::Bind(&CertificateReportingService::ResetOnIOThread,
                  base::Unretained(this), enabled, url_request_context_,
                  max_queued_report_count_, max_report_age_, clock_,
-                 server_public_key_, server_public_key_version_));
+                 server_public_key_, server_public_key_version_),
+      reset_callback_);
 }
 
 CertificateReportingService::Reporter*
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service.h b/chrome/browser/safe_browsing/certificate_reporting_service.h
index 7137655..4f30f12f 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service.h
+++ b/chrome/browser/safe_browsing/certificate_reporting_service.h
@@ -152,7 +152,8 @@
       uint32_t server_public_key_version,
       size_t max_queued_report_count,
       base::TimeDelta max_report_age,
-      base::Clock* clock);
+      base::Clock* clock,
+      const base::Callback<void()>& reset_callback);
 
   ~CertificateReportingService() override;
 
@@ -226,6 +227,9 @@
 
   base::Clock* const clock_;
 
+  // Called when the service is reset. Used for testing.
+  base::Callback<void()> reset_callback_;
+
   // Encryption parameters.
   uint8_t* server_public_key_;
   uint32_t server_public_key_version_;
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
index 89efeca0..eb5249a 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
@@ -36,6 +36,7 @@
 #include "url/scheme_host_port.h"
 
 using certificate_reporting_test_utils::CertificateReportingServiceTestHelper;
+using certificate_reporting_test_utils::CertificateReportingServiceObserver;
 using certificate_reporting_test_utils::ReportExpectation;
 
 namespace {
@@ -53,24 +54,16 @@
 
 // These tests check the whole mechanism to send and queue invalid certificate
 // reports. Each test triggers reports by visiting broken SSL pages. The reports
-// succeed, fail or hang indefinitely. The test waits for the URL requests
-// corresponding to the reports to to be created via the URL request
-// interceptor. When reports are expected to succeed or fail, test teardown
-// checks that there are no in-flight or pending reports in the
-// CertificateReportingService queue. When a report is to be delayed, a single
-// in-flight report is expected in CertificateReportingService. Since the actual
-// URL requests for reports are sent from the IO thread, the tests wait for the
-// IO thread to finish before checking the expected report counts.
-//
-// Note that these browser tests differ from the unit tests in how they check
-// expected reports: Unit tests create a network delegate and observe the
-// destruction of the URL requests, whereas browser tests wait for the URL
-// requests to be created instead.
+// succeed, fail or hang indefinitely:
+// - If a report is expected to fail or succeed, the test waits for the
+//   corresponding URL request jobs to be destroyed.
+// - If a report is expected to hang, the test waits for the corresponding URL
+//   request job to be created. Only after resuming the hung request job the
+//   test waits for the request to be destroyed.
 class CertificateReportingServiceBrowserTest : public InProcessBrowserTest {
  public:
   CertificateReportingServiceBrowserTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
-        expect_delayed_report_on_teardown_(false) {}
+      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
   void SetUpOnMainThread() override {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -80,19 +73,25 @@
     https_server_.ServeFilesFromSourceDirectory("chrome/test/data");
     ASSERT_TRUE(https_server_.Start());
 
-    test_helper_.SetUpInterceptor();
+    test_helper()->SetUpInterceptor();
 
     CertificateReportingServiceFactory::GetInstance()
         ->SetReportEncryptionParamsForTesting(
-            test_helper_.server_public_key(),
-            test_helper_.server_public_key_version());
+            test_helper()->server_public_key(),
+            test_helper()->server_public_key_version());
+    CertificateReportingServiceFactory::GetInstance()
+        ->SetServiceResetCallbackForTesting(
+            base::Bind(&CertificateReportingServiceObserver::OnServiceReset,
+                       base::Unretained(&service_observer_)));
     InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void TearDownOnMainThread() override {
-    CheckExpectedReportCounts(expect_delayed_report_on_teardown_);
+    test_helper()->ExpectNoRequests(service());
     content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
                                      base::Bind(&CleanUpOnIOThread));
+    EXPECT_GE(num_expected_failed_report_, 0)
+        << "Don't forget to set expected failed report count.";
     // Check the histogram as the last thing. This makes sure no in-flight
     // report is missed.
     if (num_expected_failed_report_ != 0) {
@@ -138,16 +137,10 @@
     // Navigate away from the interstitial to trigger report upload.
     ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
     content::WaitForInterstitialDetach(contents);
-    WaitForIOThread();
   }
 
   void SendPendingReports() { service()->SendPending(); }
 
-  // Checks that there are no outstanding reports.
-  // If |expect_delayed_report_on_teardown| is true, expects a single delayed
-  // report.
-  void CheckNoReports() { CheckExpectedReportCounts(false); }
-
   // Changes opt-in status and waits for the cert reporting service to reset.
   // Can only be used after the service is initialized. When changing the
   // value at the beginning of a test,
@@ -159,119 +152,52 @@
   // a task to the IO thread to reset the service. Waiting for the IO thread
   // ensures that the service is reset before returning from this method.
   void ChangeOptInAndWait(certificate_reporting_test_utils::OptIn opt_in) {
+    service_observer_.Clear();
     certificate_reporting_test_utils::SetCertReportingOptIn(browser(), opt_in);
-    WaitForIOThread();
+    service_observer_.WaitForReset();
   }
 
   // Same as ChangeOptInAndWait, but enables/disables SafeBrowsing instead.
   void ToggleSafeBrowsingAndWaitForServiceReset(bool safebrowsing_enabled) {
+    service_observer_.Clear();
     browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled,
                                                  safebrowsing_enabled);
-    WaitForIOThread();
+    service_observer_.WaitForReset();
   }
 
-  void ShutdownServiceAndWait() {
-    service()->Shutdown();
-    WaitForIOThread();
-  }
-
-  // Waits for a number of URL requests to be created for the reports in
-  // |expectation| and checks that the reports in |expectation| matches the
-  // reports observed by URL request interceptor.
-  void WaitForReports(const ReportExpectation& expectation) {
-    test_helper_.interceptor()->WaitForReports(expectation.num_reports());
-    std::set<std::string> expected_hostnames;
-    CheckReports(expectation.successful_reports,
-                 test_helper_.interceptor()->successful_reports());
-    CheckReports(expectation.failed_reports,
-                 test_helper_.interceptor()->failed_reports());
-    CheckReports(expectation.delayed_reports,
-                 test_helper_.interceptor()->delayed_reports());
-    test_helper_.interceptor()->ClearObservedReports();
-  }
-
-  // Resumes the delayed request and waits for the resume task to complete which
-  // in turn means the response starts.
-  void ResumeDelayedRequestAndWait() {
-    base::RunLoop run_loop;
-    test_helper_.ResumeDelayedRequest(run_loop.QuitClosure());
-    run_loop.Run();
-  }
-
-  // Tells the test to expect a delayed report during test teardown. If not set,
-  // the tests expect no in-flight reports during teardown.
-  void SetExpectDelayedReportOnTeardown() {
-    expect_delayed_report_on_teardown_ = true;
-  }
-
-  void SetExpectedHistogramCountOnTeardown(
-      unsigned int num_expected_failed_report) {
+  void SetExpectedHistogramCountOnTeardown(int num_expected_failed_report) {
     num_expected_failed_report_ = num_expected_failed_report;
   }
 
- private:
   CertificateReportingService* service() const {
     return CertificateReportingServiceFactory::GetForBrowserContext(
         browser()->profile());
   }
 
-  // Waits for pending tasks on the IO thread to complete.
-  void WaitForIOThread() {
-    scoped_refptr<base::ThreadTestHelper> io_helper(new base::ThreadTestHelper(
-        content::BrowserThread::GetTaskRunnerForThread(
-            content::BrowserThread::IO)
-            .get()));
-    ASSERT_TRUE(io_helper->Run());
-  }
-
+ private:
   // Checks that the serialized reports in |received_reports| have the same
   // hostnames as |expected_hostnames|.
   void CheckReports(const std::set<std::string>& expected_hostnames,
-                    const std::set<std::string>& received_reports) {
+                    const std::set<std::string>& received_reports,
+                    const std::string type) {
     std::set<std::string> received_hostnames;
     for (const std::string& serialized_report : received_reports) {
       certificate_reporting::ErrorReport report;
       ASSERT_TRUE(report.InitializeFromString(serialized_report));
       received_hostnames.insert(report.hostname());
     }
-    EXPECT_EQ(expected_hostnames, received_hostnames);
-  }
-
-  // Checks that there are no remaining successful and failed reports observed
-  // by the interceptor. If |expect_delayed_report| is true, expects a single
-  // delayed report. Otherwise, expects no delayed reports.
-  void CheckExpectedReportCounts(bool expect_delayed_report) {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    // Wait for the IO thread to ensure that any report-sending tasks previously
-    // posted to the IO thread hav run (and thus been observed by the
-    // interceptor).
-    WaitForIOThread();
-    EXPECT_TRUE(test_helper_.interceptor()->successful_reports().empty());
-    EXPECT_TRUE(test_helper_.interceptor()->failed_reports().empty());
-
-    if (expect_delayed_report)
-      EXPECT_EQ(1u, test_helper_.interceptor()->delayed_reports().size());
-    else
-      EXPECT_TRUE(test_helper_.interceptor()->delayed_reports().empty());
-
-    if (service()->GetReporterForTesting()) {
-      // Reporter can be null if reporting is disabled.
-      size_t num_inflight_reports = expect_delayed_report ? 1u : 0u;
-      EXPECT_EQ(num_inflight_reports,
-                service()
-                    ->GetReporterForTesting()
-                    ->inflight_report_count_for_testing());
-    }
+    EXPECT_EQ(expected_hostnames, received_hostnames) << type
+                                                      << " comparison failed";
   }
 
   net::EmbeddedTestServer https_server_;
-  // If true, the test will expect to see a delayed report during test teardown.
-  bool expect_delayed_report_on_teardown_ = false;
 
-  unsigned int num_expected_failed_report_ = 0;
+  int num_expected_failed_report_ = -1;
 
   CertificateReportingServiceTestHelper test_helper_;
 
+  CertificateReportingServiceObserver service_observer_;
+
   base::HistogramTester histogram_tester_;
 
   DISALLOW_COPY_AND_ASSIGN(CertificateReportingServiceBrowserTest);
@@ -307,7 +233,8 @@
 
   // Reporting is opted in, so the report should succeed.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Successful({"report0"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({"report0"}));
 }
 
 // Tests that report send attempts are not cancelled when extended reporting is
@@ -325,11 +252,13 @@
 
   // Send a failed report.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Failed({"report0"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0"}));
 
   // Send another failed report.
   SendReport("report1");
-  WaitForReports(ReportExpectation::Failed({"report1"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report1"}));
 
   // Let all report uploads complete successfully now.
   test_helper()->SetFailureMode(certificate_reporting_test_utils::
@@ -337,12 +266,14 @@
 
   // Send another report. This time the report should be successfully sent.
   SendReport("report2");
-  WaitForReports(ReportExpectation::Successful({"report2"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({"report2"}));
 
   // Send all pending reports. The two previously failed reports should have
   // been queued, and now be sent successfully.
   SendPendingReports();
-  WaitForReports(ReportExpectation::Successful({"report0", "report1"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({"report0", "report1"}));
 
   // Try sending pending reports again. Since there is no pending report,
   // nothing should be sent this time. If any report is sent, test teardown
@@ -364,7 +295,8 @@
 
   // Send a failed report.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Failed({"report0"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0"}));
 
   // Disable reporting. This should clear all pending reports.
   ChangeOptInAndWait(
@@ -388,7 +320,7 @@
 
   // Send attempt should be cancelled since reporting is opted out.
   SendReport("no-report");
-  CheckNoReports();
+  test_helper()->ExpectNoRequests(service());
 
   // Enable reporting.
   ChangeOptInAndWait(
@@ -396,7 +328,8 @@
 
   // A failed report should be observed.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Failed({"report0"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0"}));
 
   // Disable reporting. This should reset the reporting service and
   // clear all pending reports.
@@ -405,7 +338,7 @@
 
   // Report should be cancelled since reporting is opted out.
   SendReport("report1");
-  CheckNoReports();
+  test_helper()->ExpectNoRequests(service());
 
   // Send pending reports. Nothing should be sent since there aren't any
   // pending reports. If any report is sent, test teardown will catch it.
@@ -424,27 +357,30 @@
   test_helper()->SetFailureMode(
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
 
-  // Send a failed report.
+  // Send a delayed report.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Failed({"report0"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0"}));
 
   // Disable SafeBrowsing. This should clear all pending reports.
   ToggleSafeBrowsingAndWaitForServiceReset(false);
 
   // Send pending reports. No reports should be observed.
   SendPendingReports();
-  CheckNoReports();
+  test_helper()->ExpectNoRequests(service());
 
   // Re-enable SafeBrowsing and trigger another report which will be queued.
   ToggleSafeBrowsingAndWaitForServiceReset(true);
   SendReport("report1");
-  WaitForReports(ReportExpectation::Failed({"report1"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report1"}));
 
   // Queued report should now be successfully sent.
   test_helper()->SetFailureMode(certificate_reporting_test_utils::
                                     ReportSendingResult::REPORTS_SUCCESSFUL);
   SendPendingReports();
-  WaitForReports(ReportExpectation::Successful({"report1"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({"report1"}));
 }
 
 // CertificateReportingService should ignore reports older than the report TTL.
@@ -469,12 +405,14 @@
 
   // Send a failed report.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Failed({"report0"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0"}));
 
   // Advance the clock a bit and trigger another failed report.
   clock->Advance(base::TimeDelta::FromHours(5));
   SendReport("report1");
-  WaitForReports(ReportExpectation::Failed({"report1"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report1"}));
 
   // Advance the clock to 20 hours, putting it 25 hours ahead of the reference
   // time. This makes report0 older than 24 hours. report1 is now 20 hours.
@@ -483,11 +421,13 @@
   // Send pending reports. report0 should be discarded since it's too old.
   // report1 should be queued again.
   SendPendingReports();
-  WaitForReports(ReportExpectation::Failed({"report1"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report1"}));
 
   // Trigger another failed report.
   SendReport("report2");
-  WaitForReports(ReportExpectation::Failed({"report2"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report2"}));
 
   // Advance the clock 5 hours. report1 will now be 25 hours old.
   clock->Advance(base::TimeDelta::FromHours(5));
@@ -495,7 +435,8 @@
   // Send pending reports. report1 should be discarded since it's too old.
   // report2 should be queued again.
   SendPendingReports();
-  WaitForReports(ReportExpectation::Failed({"report2"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report2"}));
 
   // Advance the clock 20 hours again so that report2 is 25 hours old and is
   // older than max age (24 hours).
@@ -531,7 +472,8 @@
 
   // Trigger a failed report.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Failed({"report0"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0"}));
 
   // Trigger three more reports within five hours of each other. After this:
   // report0 is 0 hours after reference time (15 hours old).
@@ -547,13 +489,15 @@
   clock->Advance(base::TimeDelta::FromHours(5));
   SendReport("report3");
 
-  WaitForReports(ReportExpectation::Failed({"report1", "report2", "report3"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report1", "report2", "report3"}));
 
   // Send pending reports. Four reports were generated above, but the service
   // only queues three reports, so report0 should be dropped since it's the
   // oldest.
   SendPendingReports();
-  WaitForReports(ReportExpectation::Failed({"report1", "report2", "report3"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report1", "report2", "report3"}));
 
   // Let all reports succeed.
   test_helper()->SetFailureMode(certificate_reporting_test_utils::
@@ -569,26 +513,8 @@
   // Send pending reports. Only reports 2 and 3 should be sent, report 1
   // should be ignored because it's too old.
   SendPendingReports();
-  WaitForReports(ReportExpectation::Successful({"report2", "report3"}));
-}
-
-// Resume a delayed report after CertificateReportingService shuts down. Should
-// not crash.
-IN_PROC_BROWSER_TEST_F(CertificateReportingServiceBrowserTest,
-                       Delayed_NotResumed_ShouldNotCrash) {
-  SetExpectedHistogramCountOnTeardown(0);
-
-  certificate_reporting_test_utils::SetCertReportingOptIn(
-      browser(), certificate_reporting_test_utils::EXTENDED_REPORTING_OPT_IN);
-  // Let reports hang.
-  test_helper()->SetFailureMode(
-      certificate_reporting_test_utils::ReportSendingResult::REPORTS_DELAY);
-
-  // Navigate to and away from an interstitial to trigger a report. The report
-  // is triggered but hangs, so no error or success callbacks should be called.
-  SendReport("no-report");
-
-  SetExpectDelayedReportOnTeardown();
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({"report2", "report3"}));
 }
 
 IN_PROC_BROWSER_TEST_F(CertificateReportingServiceBrowserTest,
@@ -597,18 +523,21 @@
 
   certificate_reporting_test_utils::SetCertReportingOptIn(
       browser(), certificate_reporting_test_utils::EXTENDED_REPORTING_OPT_IN);
-  // Let all reports fail.
+  // Let all reports hang.
   test_helper()->SetFailureMode(
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_DELAY);
 
   // Trigger a report that hangs.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Delayed({"report0"}));
+  test_helper()->WaitForRequestsCreated(
+      ReportExpectation::Delayed({"report0"}));
 
   // Resume the report upload. The report upload should successfully complete.
   // The interceptor only observes request creations and not response
   // completions, so there is nothing to observe.
-  ResumeDelayedRequestAndWait();
+  test_helper()->ResumeDelayedRequest();
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Delayed({"report0"}));
 }
 
 // Same as above, but the service is shut down before resuming the delayed
@@ -619,21 +548,20 @@
 
   certificate_reporting_test_utils::SetCertReportingOptIn(
       browser(), certificate_reporting_test_utils::EXTENDED_REPORTING_OPT_IN);
-  // Let all reports fail.
+  // Let all reports hang.
   test_helper()->SetFailureMode(
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_DELAY);
 
   // Trigger a report that hangs.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Delayed({"report0"}));
+  test_helper()->WaitForRequestsCreated(
+      ReportExpectation::Delayed({"report0"}));
 
-  // Shutdown the service. Resuming the delayed request shouldn't crash.
-  ShutdownServiceAndWait();
-
-  // Resume the report upload. The report upload should successfully complete.
-  // The interceptor only observes request creations and not response
-  // completions, so there is nothing to observe.
-  ResumeDelayedRequestAndWait();
+  // Shutdown the service and resume the report upload. Shouldn't crash.
+  service()->Shutdown();
+  test_helper()->ResumeDelayedRequest();
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Delayed({"report0"}));
 }
 
 // Trigger a delayed report, then disable Safebrowsing. Certificate reporting
@@ -643,34 +571,37 @@
 
   certificate_reporting_test_utils::SetCertReportingOptIn(
       browser(), certificate_reporting_test_utils::EXTENDED_REPORTING_OPT_IN);
-  // Let all reports fail.
+  // Let all reports hang.
   test_helper()->SetFailureMode(
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_DELAY);
 
   // Trigger a report that hangs.
   SendReport("report0");
-  WaitForReports(ReportExpectation::Delayed({"report0"}));
+  test_helper()->WaitForRequestsCreated(
+      ReportExpectation::Delayed({"report0"}));
 
   // Disable SafeBrowsing. This should clear all pending reports.
   ToggleSafeBrowsingAndWaitForServiceReset(false);
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Delayed({"report0"}));
 
   // Resume delayed report. No response should be observed since all pending
   // reports should be cleared.
-  ResumeDelayedRequestAndWait();
-  CheckNoReports();
+  test_helper()->ResumeDelayedRequest();
+  test_helper()->ExpectNoRequests(service());
 
   // Re-enable SafeBrowsing.
   ToggleSafeBrowsingAndWaitForServiceReset(true);
 
   // Trigger a report that hangs.
   SendReport("report1");
-  WaitForReports(ReportExpectation::Delayed({"report1"}));
+  test_helper()->WaitForRequestsCreated(
+      ReportExpectation::Delayed({"report1"}));
 
-  // Resume delayed report. By the time the runloop is finished, the response
-  // will be complete and CertificateReportingService will process the
-  // error/success callback for the report. There will be no inflight reports
-  // remaining.
-  ResumeDelayedRequestAndWait();
+  // Resume the delayed report and wait for it to complete.
+  test_helper()->ResumeDelayedRequest();
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Delayed({"report1"}));
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_factory.cc b/chrome/browser/safe_browsing/certificate_reporting_service_factory.cc
index 606e05e..e05af7f 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_factory.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_factory.cc
@@ -60,6 +60,11 @@
   max_queued_report_count_ = max_queued_report_count;
 }
 
+void CertificateReportingServiceFactory::SetServiceResetCallbackForTesting(
+    const base::Callback<void()>& service_reset_callback) {
+  service_reset_callback_ = service_reset_callback;
+}
+
 CertificateReportingServiceFactory::CertificateReportingServiceFactory()
     : BrowserContextKeyedServiceFactory(
           "cert_reporting::Factory",
@@ -68,7 +73,8 @@
       server_public_key_version_(0),
       clock_(new base::DefaultClock()),
       queued_report_ttl_(base::TimeDelta::FromSeconds(kMaxReportAgeInSeconds)),
-      max_queued_report_count_(kMaxReportCountInQueue) {}
+      max_queued_report_count_(kMaxReportCountInQueue),
+      service_reset_callback_(base::Bind(&base::DoNothing)) {}
 
 CertificateReportingServiceFactory::~CertificateReportingServiceFactory() {}
 
@@ -80,7 +86,7 @@
       safe_browsing_service, safe_browsing_service->url_request_context(),
       static_cast<Profile*>(profile), server_public_key_,
       server_public_key_version_, max_queued_report_count_, queued_report_ttl_,
-      clock_.get());
+      clock_.get(), service_reset_callback_);
 }
 
 content::BrowserContext*
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_factory.h b/chrome/browser/safe_browsing/certificate_reporting_service_factory.h
index fe18a5a..63535db6 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_factory.h
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_factory.h
@@ -30,6 +30,8 @@
   void SetClockForTesting(std::unique_ptr<base::Clock> clock);
   void SetQueuedReportTTLForTesting(base::TimeDelta queued_report_ttl);
   void SetMaxQueuedReportCountForTesting(size_t max_report_count);
+  void SetServiceResetCallbackForTesting(
+      const base::Callback<void()>& service_reset_callback);
 
  private:
   friend struct base::DefaultSingletonTraits<
@@ -51,6 +53,7 @@
   std::unique_ptr<base::Clock> clock_;
   base::TimeDelta queued_report_ttl_;
   size_t max_queued_report_count_;
+  base::Callback<void()> service_reset_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(CertificateReportingServiceFactory);
 };
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.cc b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.cc
index ccffd40..d7e2f4b 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.cc
@@ -6,13 +6,12 @@
 
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/certificate_reporting/encrypted_cert_logger.pb.h"
+#include "components/certificate_reporting/error_report.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_utils.h"
 #include "crypto/curve25519.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_data_stream.h"
-#include "net/test/url_request/url_request_failed_job.h"
-#include "net/test/url_request/url_request_mock_data_job.h"
 #include "net/url_request/url_request_filter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -55,16 +54,129 @@
   return decrypted_report;
 }
 
+// Checks that the serialized reports in |observed_reports| have the same
+// hostnames as |expected_hostnames|.
+void CompareHostnames(const std::set<std::string>& expected_hostnames,
+                      const std::set<std::string>& observed_reports,
+                      const std::string& comparison_type) {
+  std::set<std::string> observed_hostnames;
+  for (const std::string& serialized_report : observed_reports) {
+    certificate_reporting::ErrorReport report;
+    ASSERT_TRUE(report.InitializeFromString(serialized_report));
+    observed_hostnames.insert(report.hostname());
+  }
+  EXPECT_EQ(expected_hostnames, observed_hostnames)
+      << "Comparison failed for " << comparison_type << " reports.";
+}
+
+void WaitReports(
+    certificate_reporting_test_utils::RequestObserver* observer,
+    const certificate_reporting_test_utils::ReportExpectation& expectation) {
+  observer->Wait(expectation.num_reports());
+  CompareHostnames(expectation.successful_reports,
+                   observer->successful_reports(), "successful");
+  CompareHostnames(expectation.failed_reports, observer->failed_reports(),
+                   "failed");
+  CompareHostnames(expectation.delayed_reports, observer->delayed_reports(),
+                   "delayed");
+  observer->ClearObservedReports();
+}
+
 }  // namespace
 
 namespace certificate_reporting_test_utils {
 
-DelayableCertReportURLRequestJob::DelayableCertReportURLRequestJob(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate)
-    : net::URLRequestJob(request, network_delegate), weak_factory_(this) {}
+RequestObserver::RequestObserver()
+    : num_events_to_wait_for_(0u), num_received_events_(0u) {}
+RequestObserver::~RequestObserver() {}
 
-DelayableCertReportURLRequestJob::~DelayableCertReportURLRequestJob() {}
+void RequestObserver::Wait(unsigned int num_events_to_wait_for) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(!run_loop_);
+  ASSERT_LE(num_received_events_, num_events_to_wait_for)
+      << "Observed unexpected report";
+
+  if (num_received_events_ < num_events_to_wait_for) {
+    num_events_to_wait_for_ = num_events_to_wait_for;
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+    run_loop_.reset(nullptr);
+    EXPECT_EQ(0u, num_received_events_);
+    EXPECT_EQ(0u, num_events_to_wait_for_);
+  } else if (num_received_events_ == num_events_to_wait_for) {
+    num_received_events_ = 0u;
+    num_events_to_wait_for_ = 0u;
+  }
+}
+
+void RequestObserver::OnRequest(const std::string& serialized_report,
+                                ReportSendingResult report_type) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  switch (report_type) {
+    case REPORTS_SUCCESSFUL:
+      successful_reports_.insert(serialized_report);
+      break;
+    case REPORTS_FAIL:
+      failed_reports_.insert(serialized_report);
+      break;
+    case REPORTS_DELAY:
+      delayed_reports_.insert(serialized_report);
+      break;
+  }
+
+  num_received_events_++;
+  if (!run_loop_) {
+    return;
+  }
+  ASSERT_LE(num_received_events_, num_events_to_wait_for_)
+      << "Observed unexpected report";
+
+  if (num_received_events_ == num_events_to_wait_for_) {
+    num_events_to_wait_for_ = 0u;
+    num_received_events_ = 0u;
+    run_loop_->Quit();
+  }
+}
+
+const std::set<std::string>& RequestObserver::successful_reports() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return successful_reports_;
+}
+
+const std::set<std::string>& RequestObserver::failed_reports() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return failed_reports_;
+}
+
+const std::set<std::string>& RequestObserver::delayed_reports() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return delayed_reports_;
+}
+
+void RequestObserver::ClearObservedReports() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  successful_reports_.clear();
+  failed_reports_.clear();
+  delayed_reports_.clear();
+}
+
+DelayableCertReportURLRequestJob::DelayableCertReportURLRequestJob(
+    bool delayed,
+    bool should_fail,
+    net::URLRequest* request,
+    net::NetworkDelegate* network_delegate,
+    const base::Callback<void()>& destruction_callback)
+    : net::URLRequestJob(request, network_delegate),
+      delayed_(delayed),
+      should_fail_(should_fail),
+      started_(false),
+      destruction_callback_(destruction_callback),
+      weak_factory_(this) {}
+
+DelayableCertReportURLRequestJob::~DelayableCertReportURLRequestJob() {
+  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+                                   destruction_callback_);
+}
 
 base::WeakPtr<DelayableCertReportURLRequestJob>
 DelayableCertReportURLRequestJob::GetWeakPtr() {
@@ -98,14 +210,17 @@
 
 void DelayableCertReportURLRequestJob::Resume() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  DCHECK(delayed_);
   if (!started_) {
-    // If Start() hasn't been called yet, then unset |delayed_| so
-    // that when Start() is called, the request will begin
-    // immediately.
+    // If Start() hasn't been called yet, then unset |delayed_| so that when
+    // Start() is called, the request will begin immediately.
     delayed_ = false;
     return;
   }
+  if (should_fail_) {
+    NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+                                           net::ERR_SSL_PROTOCOL_ERROR));
+    return;
+  }
   // Start reading asynchronously as would a normal network request.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
@@ -136,18 +251,29 @@
                  expected_report_result_));
 
   if (expected_report_result_ == REPORTS_FAIL) {
-    return new net::URLRequestFailedJob(request, network_delegate,
-                                        net::ERR_SSL_PROTOCOL_ERROR);
+    return new DelayableCertReportURLRequestJob(
+        false, true, request, network_delegate,
+        base::Bind(&CertReportJobInterceptor::RequestDestructed,
+                   base::Unretained(this), uploaded_report,
+                   expected_report_result_));
+
   } else if (expected_report_result_ == REPORTS_DELAY) {
     DCHECK(!delayed_request_) << "Supports only one delayed request at a time";
     DelayableCertReportURLRequestJob* job =
-        new DelayableCertReportURLRequestJob(request, network_delegate);
+        new DelayableCertReportURLRequestJob(
+            true, false, request, network_delegate,
+            base::Bind(&CertReportJobInterceptor::RequestDestructed,
+                       base::Unretained(this), uploaded_report,
+                       expected_report_result_));
     delayed_request_ = job->GetWeakPtr();
     return job;
   }
   // Successful url request job.
-  return new net::URLRequestMockDataJob(request, network_delegate, "some data",
-                                        1, false);
+  return new DelayableCertReportURLRequestJob(
+      false, false, request, network_delegate,
+      base::Bind(&CertReportJobInterceptor::RequestDestructed,
+                 base::Unretained(this), uploaded_report,
+                 expected_report_result_));
 }
 
 void CertReportJobInterceptor::SetFailureMode(
@@ -159,41 +285,22 @@
                  weak_factory_.GetWeakPtr(), expected_report_result));
 }
 
-void CertReportJobInterceptor::Resume(const base::Callback<void()>& callback) {
+void CertReportJobInterceptor::Resume() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  content::BrowserThread::PostTaskAndReply(
+  content::BrowserThread::PostTask(
       content::BrowserThread::IO, FROM_HERE,
       base::Bind(&CertReportJobInterceptor::ResumeOnIOThread,
-                 base::Unretained(this)),
-      callback);
+                 base::Unretained(this)));
 }
 
-const std::set<std::string>& CertReportJobInterceptor::successful_reports()
-    const {
+RequestObserver* CertReportJobInterceptor::request_created_observer() const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return successful_reports_;
+  return &request_created_observer_;
 }
 
-const std::set<std::string>& CertReportJobInterceptor::failed_reports() const {
+RequestObserver* CertReportJobInterceptor::request_destroyed_observer() const {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return failed_reports_;
-}
-
-const std::set<std::string>& CertReportJobInterceptor::delayed_reports() const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return delayed_reports_;
-}
-
-void CertReportJobInterceptor::ClearObservedReports() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  successful_reports_.clear();
-  failed_reports_.clear();
-  delayed_reports_.clear();
-}
-
-void CertReportJobInterceptor::WaitForReports(int num_reports) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  wait_helper_.Wait(num_reports);
+  return &request_destroyed_observer_;
 }
 
 void CertReportJobInterceptor::SetFailureModeOnIOThread(
@@ -211,20 +318,15 @@
 
 void CertReportJobInterceptor::RequestCreated(
     const std::string& uploaded_report,
-    ReportSendingResult expected_report_result) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  switch (expected_report_result) {
-    case REPORTS_SUCCESSFUL:
-      successful_reports_.insert(uploaded_report);
-      break;
-    case REPORTS_FAIL:
-      failed_reports_.insert(uploaded_report);
-      break;
-    case REPORTS_DELAY:
-      delayed_reports_.insert(uploaded_report);
-      break;
-  }
-  wait_helper_.OnEvent();
+    ReportSendingResult expected_report_result) const {
+  request_created_observer_.OnRequest(uploaded_report, expected_report_result);
+}
+
+void CertReportJobInterceptor::RequestDestructed(
+    const std::string& uploaded_report,
+    ReportSendingResult expected_report_result) const {
+  request_destroyed_observer_.OnRequest(uploaded_report,
+                                        expected_report_result);
 }
 
 ReportExpectation::ReportExpectation() {}
@@ -262,6 +364,29 @@
          delayed_reports.size();
 }
 
+CertificateReportingServiceObserver::CertificateReportingServiceObserver() {}
+
+CertificateReportingServiceObserver::~CertificateReportingServiceObserver() {}
+
+void CertificateReportingServiceObserver::Clear() {
+  did_reset_ = false;
+}
+
+void CertificateReportingServiceObserver::WaitForReset() {
+  DCHECK(!run_loop_);
+  if (did_reset_)
+    return;
+  run_loop_.reset(new base::RunLoop());
+  run_loop_->Run();
+  run_loop_.reset();
+}
+
+void CertificateReportingServiceObserver::OnServiceReset() {
+  did_reset_ = true;
+  if (run_loop_)
+    run_loop_->Quit();
+}
+
 CertificateReportingServiceTestHelper::CertificateReportingServiceTestHelper() {
   memset(server_private_key_, 1, sizeof(server_private_key_));
   crypto::curve25519::ScalarBaseMult(server_private_key_, server_public_key_);
@@ -281,17 +406,15 @@
                      url_request_interceptor_))));
 }
 
-// Changes the behavior of report uploads to fail or succeed.
 void CertificateReportingServiceTestHelper::SetFailureMode(
     ReportSendingResult expected_report_result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   url_request_interceptor_->SetFailureMode(expected_report_result);
 }
 
-void CertificateReportingServiceTestHelper::ResumeDelayedRequest(
-    const base::Callback<void()>& callback) {
+void CertificateReportingServiceTestHelper::ResumeDelayedRequest() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  url_request_interceptor_->Resume(callback);
+  url_request_interceptor_->Resume();
 }
 
 uint8_t* CertificateReportingServiceTestHelper::server_public_key() {
@@ -303,41 +426,34 @@
   return kServerPublicKeyTestVersion;
 }
 
-ReportWaitHelper::ReportWaitHelper()
-    : num_events_to_wait_for_(0), num_received_events_(0) {}
-ReportWaitHelper::~ReportWaitHelper() {}
-
-void ReportWaitHelper::Wait(int num_events_to_wait_for) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(!run_loop_);
-  ASSERT_LE(num_received_events_, num_events_to_wait_for)
-      << "Observed unexpected report";
-
-  if (num_received_events_ < num_events_to_wait_for) {
-    num_events_to_wait_for_ = num_events_to_wait_for;
-    run_loop_.reset(new base::RunLoop());
-    run_loop_->Run();
-    run_loop_.reset(nullptr);
-    EXPECT_EQ(0, num_received_events_);
-    EXPECT_EQ(0, num_events_to_wait_for_);
-  } else if (num_received_events_ == num_events_to_wait_for) {
-    num_received_events_ = 0;
-    num_events_to_wait_for_ = 0;
-  }
+void CertificateReportingServiceTestHelper::WaitForRequestsCreated(
+    const ReportExpectation& expectation) {
+  WaitReports(interceptor()->request_created_observer(), expectation);
 }
 
-void ReportWaitHelper::OnEvent() {
+void CertificateReportingServiceTestHelper::WaitForRequestsDestroyed(
+    const ReportExpectation& expectation) {
+  WaitReports(interceptor()->request_destroyed_observer(), expectation);
+}
+
+void CertificateReportingServiceTestHelper::ExpectNoRequests(
+    CertificateReportingService* service) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  num_received_events_++;
-  if (!run_loop_) {
-    return;
-  }
-  ASSERT_LE(num_received_events_, num_events_to_wait_for_)
-      << "Observed unexpected report";
-  if (num_received_events_ == num_events_to_wait_for_) {
-    num_events_to_wait_for_ = 0;
-    num_received_events_ = 0;
-    run_loop_->Quit();
+  // Check that all requests have been destroyed.
+  EXPECT_TRUE(interceptor()
+                  ->request_destroyed_observer()
+                  ->successful_reports()
+                  .empty());
+  EXPECT_TRUE(
+      interceptor()->request_destroyed_observer()->failed_reports().empty());
+  EXPECT_TRUE(
+      interceptor()->request_destroyed_observer()->delayed_reports().empty());
+
+  if (service->GetReporterForTesting()) {
+    // Reporter can be null if reporting is disabled.
+    EXPECT_EQ(
+        0u,
+        service->GetReporterForTesting()->inflight_report_count_for_testing());
   }
 }
 
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
index 632bec9..33da0bd 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
@@ -28,7 +28,7 @@
 // Example:
 // The following expects report0 and report1 to be successfully sent and their
 // URL requests to be deleted:
-// WaitForRequestDeletions(
+// WaitForRequestsDestroyed(
 //     ReportExpectation::Successful("report0, report1"));
 struct ReportExpectation {
   ReportExpectation();
@@ -47,23 +47,6 @@
   std::set<std::string> delayed_reports;
 };
 
-// Helper class to wait for a number of events (e.g. request destroyed, report
-// observed).
-class ReportWaitHelper {
- public:
-  ReportWaitHelper();
-  ~ReportWaitHelper();
-  // Waits for |num_events_to_wait_for|.
-  void Wait(int num_events_to_wait_for);
-  // Must be called when an event is observed.
-  void OnEvent();
-
- private:
-  int num_events_to_wait_for_;
-  int num_received_events_;
-  std::unique_ptr<base::RunLoop> run_loop_;
-};
-
 // Failure mode of the report sending attempts.
 enum ReportSendingResult {
   // Report send attempts should be successful.
@@ -74,14 +57,51 @@
   REPORTS_DELAY,
 };
 
+// Helper class to wait for a number of events (e.g. request destroyed, report
+// observed).
+class RequestObserver {
+ public:
+  RequestObserver();
+  ~RequestObserver();
+
+  // Waits for |num_request| requests to be created or destroyed, depending on
+  // whichever one this class observes.
+  void Wait(unsigned int num_events_to_wait_for);
+
+  // Called when a request created or destroyed, depending on whichever one this
+  // class observes.
+  void OnRequest(const std::string& serialized_report,
+                 ReportSendingResult report_type);
+
+  // These must be called on the UI thread.
+  const std::set<std::string>& successful_reports() const;
+  const std::set<std::string>& failed_reports() const;
+  const std::set<std::string>& delayed_reports() const;
+  void ClearObservedReports();
+
+ private:
+  unsigned int num_events_to_wait_for_;
+  unsigned int num_received_events_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  std::set<std::string> successful_reports_;
+  std::set<std::string> failed_reports_;
+  std::set<std::string> delayed_reports_;
+};
+
 // A URLRequestJob that can be delayed until Resume() is called. Returns an
 // empty response. If Resume() is called before a request is made, then the
-// request will not be delayed.
+// request will not be delayed. If not delayed, it can return a failed or a
+// successful URL request job.
 class DelayableCertReportURLRequestJob : public net::URLRequestJob,
                                          public base::NonThreadSafe {
  public:
-  DelayableCertReportURLRequestJob(net::URLRequest* request,
-                                   net::NetworkDelegate* network_delegate);
+  DelayableCertReportURLRequestJob(
+      bool delayed,
+      bool should_fail,
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate,
+      const base::Callback<void()>& destruction_callback);
   ~DelayableCertReportURLRequestJob() override;
 
   base::WeakPtr<DelayableCertReportURLRequestJob> GetWeakPtr();
@@ -98,8 +118,10 @@
   void Resume();
 
  private:
-  bool delayed_ = true;
-  bool started_ = false;
+  bool delayed_;
+  bool should_fail_;
+  bool started_;
+  base::Callback<void()> destruction_callback_;
   base::WeakPtrFactory<DelayableCertReportURLRequestJob> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DelayableCertReportURLRequestJob);
@@ -122,35 +144,30 @@
   void SetFailureMode(ReportSendingResult expected_report_result);
   // Resumes any hanging URL request and runs callback when the request
   // is resumed (i.e. response starts). Must be called on the UI thread.
-  void Resume(const base::Closure& callback);
+  void Resume();
 
-  // These must be called on the UI thread.
-  const std::set<std::string>& successful_reports() const;
-  const std::set<std::string>& failed_reports() const;
-  const std::set<std::string>& delayed_reports() const;
-  void ClearObservedReports();
-
-  // Waits for requests for |num_reports| reports to be created. Only used in
-  // browser tests. Unit tests wait for requests to be destroyed instead.
-  // Must be called on the UI thread.
-  void WaitForReports(int num_reports);
+  RequestObserver* request_created_observer() const;
+  RequestObserver* request_destroyed_observer() const;
 
  private:
   void SetFailureModeOnIOThread(ReportSendingResult expected_report_result);
   void ResumeOnIOThread();
   void RequestCreated(const std::string& uploaded_report,
-                      ReportSendingResult expected_report_result);
+                      ReportSendingResult expected_report_result) const;
+  void RequestDestructed(const std::string& uploaded_report,
+                         ReportSendingResult expected_report_result) const;
 
-  std::set<std::string> successful_reports_;
-  std::set<std::string> failed_reports_;
-  std::set<std::string> delayed_reports_;
+  mutable std::set<std::string> successful_reports_;
+  mutable std::set<std::string> failed_reports_;
+  mutable std::set<std::string> delayed_reports_;
 
   ReportSendingResult expected_report_result_;
 
   // Private key to decrypt certificate reports.
   const uint8_t* server_private_key_;
 
-  ReportWaitHelper wait_helper_;
+  mutable RequestObserver request_created_observer_;
+  mutable RequestObserver request_destroyed_observer_;
 
   mutable base::WeakPtr<DelayableCertReportURLRequestJob> delayed_request_ =
       nullptr;
@@ -159,20 +176,24 @@
   DISALLOW_COPY_AND_ASSIGN(CertReportJobInterceptor);
 };
 
-// A network delegate used to observe URL request destructions. The tests check
-// that no outstanding URL request is present during tear down.
-class CertificateReportingServiceTestNetworkDelegate
-    : public net::NetworkDelegateImpl {
+// Class to wait for the CertificateReportingService to reset.
+class CertificateReportingServiceObserver {
  public:
-  CertificateReportingServiceTestNetworkDelegate(
-      const base::Callback<void()>& url_request_destroyed_callback);
-  ~CertificateReportingServiceTestNetworkDelegate() override;
+  CertificateReportingServiceObserver();
+  ~CertificateReportingServiceObserver();
 
-  // net::NetworkDelegate method:
-  void OnURLRequestDestroyed(net::URLRequest* request) override;
+  // Clears the state of the observer. Must be called before waiting each time.
+  void Clear();
+
+  // Waits for the service to reset.
+  void WaitForReset();
+
+  // Must be called when the service is reset.
+  void OnServiceReset();
 
  private:
-  base::Callback<void()> url_request_destroyed_callback_;
+  bool did_reset_ = false;
+  std::unique_ptr<base::RunLoop> run_loop_;
 };
 
 // Base class for CertificateReportingService tests. Sets up an interceptor to
@@ -189,14 +210,20 @@
 
   // Resumes delayed report request. Failure mode should be REPORTS_DELAY when
   // calling this method.
-  void ResumeDelayedRequest(const base::Callback<void()>& callback);
+  void ResumeDelayedRequest();
+
+  void WaitForRequestsCreated(const ReportExpectation& expectation);
+  void WaitForRequestsDestroyed(const ReportExpectation& expectation);
+
+  // Checks that all requests are destroyed and that there are no in-flight
+  // reports in |service|.
+  void ExpectNoRequests(CertificateReportingService* service);
 
   uint8_t* server_public_key();
   uint32_t server_public_key_version() const;
 
-  CertReportJobInterceptor* interceptor() { return url_request_interceptor_; }
-
  private:
+  CertReportJobInterceptor* interceptor() { return url_request_interceptor_; }
   void SetUpInterceptorOnIOThread();
 
   CertReportJobInterceptor* url_request_interceptor_;
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_unittest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_unittest.cc
index 8ef06293d..e9a6e02 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_unittest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/atomic_sequence_num.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
@@ -19,10 +20,14 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/certificate_reporting/error_report.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
-#include "net/base/network_delegate_impl.h"
+#include "crypto/rsa_private_key.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/ssl/ssl_info.h"
 #include "net/test/url_request/url_request_failed_job.h"
 #include "net/test/url_request/url_request_mock_data_job.h"
 #include "net/url_request/url_request_filter.h"
@@ -30,6 +35,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using certificate_reporting_test_utils::CertificateReportingServiceTestHelper;
+using certificate_reporting_test_utils::CertificateReportingServiceObserver;
 using certificate_reporting_test_utils::ReportExpectation;
 
 namespace {
@@ -40,32 +46,41 @@
 
 const char* kFailedReportHistogram = "SSL.CertificateErrorReportFailure";
 
+// NSS requires that serial numbers be unique even for the same issuer;
+// as all fake certificates will contain the same issuer name, it's
+// necessary to ensure the serial number is unique, as otherwise
+// NSS will fail to parse.
+base::StaticAtomicSequenceNumber g_serial_number;
+
+scoped_refptr<net::X509Certificate> CreateFakeCert() {
+  std::unique_ptr<crypto::RSAPrivateKey> unused_key;
+  std::string cert_der;
+  if (!net::x509_util::CreateKeyAndSelfSignedCert(
+          "CN=Error", static_cast<uint32_t>(g_serial_number.GetNext()),
+          base::Time::Now() - base::TimeDelta::FromMinutes(5),
+          base::Time::Now() + base::TimeDelta::FromMinutes(5), &unused_key,
+          &cert_der)) {
+    return nullptr;
+  }
+  return net::X509Certificate::CreateFromBytes(cert_der.data(),
+                                               cert_der.size());
+}
+
+std::string MakeReport(const std::string& hostname) {
+  net::SSLInfo ssl_info;
+  ssl_info.cert = ssl_info.unverified_cert = CreateFakeCert();
+
+  certificate_reporting::ErrorReport report(hostname, ssl_info);
+  std::string serialized_report;
+  EXPECT_TRUE(report.Serialize(&serialized_report));
+  return serialized_report;
+}
+
 void ClearURLHandlers() {
   net::URLRequestFilter::GetInstance()->ClearHandlers();
 }
 
-// A network delegate used to observe URL request destructions. The tests check
-// that no outstanding URL request is present during tear down.
-class TestNetworkDelegate : public net::NetworkDelegateImpl {
- public:
-  TestNetworkDelegate(
-      const base::Callback<void()>& url_request_destroyed_callback)
-      : url_request_destroyed_callback_(url_request_destroyed_callback) {}
-
-  ~TestNetworkDelegate() override {}
-
-  // net::NetworkDelegate method:
-  void OnURLRequestDestroyed(net::URLRequest* request) override {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
-                                     url_request_destroyed_callback_);
-  }
-
- private:
-  base::Callback<void()> url_request_destroyed_callback_;
-};
-
-// Base class for histogram testing. The failed report histogram is checked once
+// Class for histogram testing. The failed report histogram is checked once
 // after teardown to ensure all in flight requests have completed.
 class ReportHistogramTestHelper {
  public:
@@ -122,8 +137,7 @@
   // Adding a report older than the oldest report in the list (report2) is
   // a no-op.
   list.Add(CertificateReportingService::Report(
-      0, base::Time::Now() - base::TimeDelta::FromMinutes(
-                                 10) /* 5 minutes older than report2 */,
+      0, base::Time::Now() - base::TimeDelta::FromMinutes(10),
       std::string("report0_ten_minutes_old")));
   EXPECT_EQ(2u, list.items().size());
   EXPECT_EQ("report3_zero_minutes_old", list.items()[0].serialized_report);
@@ -325,6 +339,7 @@
   ~CertificateReportingServiceTest() override {}
 
   void SetUp() override {
+    service_observer_.Clear();
     test_helper_.SetUpInterceptor();
     WaitForIOThread();
 
@@ -343,19 +358,15 @@
         sb_service_.get(), url_request_context_getter(), &profile_,
         test_helper_.server_public_key(),
         test_helper_.server_public_key_version(), kMaxReportCountInQueue,
-        base::TimeDelta::FromHours(24), clock_.get()));
-    // Wait for service reset.
-    WaitForIOThread();
+        base::TimeDelta::FromHours(24), clock_.get(),
+        base::Bind(&CertificateReportingServiceObserver::OnServiceReset,
+                   base::Unretained(&service_observer_))));
+    service_observer_.WaitForReset();
   }
 
   void TearDown() override {
     WaitForIOThread();
-    EXPECT_TRUE(test_helper_.interceptor()->successful_reports().empty());
-    EXPECT_TRUE(test_helper_.interceptor()->failed_reports().empty());
-    EXPECT_TRUE(test_helper_.interceptor()->delayed_reports().empty());
-    EXPECT_EQ(0u, service()
-                      ->GetReporterForTesting()
-                      ->inflight_report_count_for_testing());
+    test_helper()->ExpectNoRequests(service());
 
     service_->Shutdown();
     WaitForIOThread();
@@ -373,17 +384,6 @@
   }
 
  protected:
-  void WaitForRequestsDestroyed(const ReportExpectation& expectation) {
-    wait_helper_.Wait(expectation.num_reports());
-    EXPECT_EQ(expectation.successful_reports,
-              test_helper_.interceptor()->successful_reports());
-    EXPECT_EQ(expectation.failed_reports,
-              test_helper_.interceptor()->failed_reports());
-    EXPECT_EQ(expectation.delayed_reports,
-              test_helper_.interceptor()->delayed_reports());
-    test_helper_.interceptor()->ClearObservedReports();
-  }
-
   net::URLRequestContextGetter* url_request_context_getter() {
     return url_request_context_getter_.get();
   }
@@ -398,8 +398,9 @@
 
   // Sets service enabled state and waits for a reset event.
   void SetServiceEnabledAndWait(bool enabled) {
+    service_observer_.Clear();
     service()->SetEnabled(enabled);
-    WaitForIOThread();
+    service_observer_.WaitForReset();
   }
 
   void AdvanceClock(base::TimeDelta delta) {
@@ -424,34 +425,19 @@
 
  private:
   void SetUpURLRequestContextOnIOThread() {
-    network_delegate_.reset(new TestNetworkDelegate(
-        base::Bind(&CertificateReportingServiceTest::OnURLRequestDestroyed,
-                   base::Unretained(this))));
-
     std::unique_ptr<net::TestURLRequestContext> url_request_context(
-        new net::TestURLRequestContext(true));
-    url_request_context->set_network_delegate(network_delegate_.get());
-    url_request_context->Init();
+        new net::TestURLRequestContext(false));
     url_request_context_getter_ = new net::TestURLRequestContextGetter(
         io_task_runner_, std::move(url_request_context));
   }
 
   void TearDownOnIOThread() {
     url_request_context_getter_ = nullptr;
-    network_delegate_.reset(nullptr);
-  }
-
-  void OnURLRequestDestroyed() {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    wait_helper_.OnEvent();
   }
 
   // Must be initialized before url_request_context_getter_
   content::TestBrowserThreadBundle thread_bundle_;
 
-  std::unique_ptr<TestNetworkDelegate> network_delegate_;
-  certificate_reporting_test_utils::ReportWaitHelper wait_helper_;
-
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
 
@@ -464,6 +450,7 @@
 
   CertificateReportingServiceTestHelper test_helper_;
   ReportHistogramTestHelper histogram_test_helper_;
+  CertificateReportingServiceObserver service_observer_;
 };
 
 TEST_F(CertificateReportingServiceTest, Send) {
@@ -474,29 +461,29 @@
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
 
   // Send two reports. Both should fail and get queued.
-  service()->Send("report0");
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report0"}));
+  service()->Send(MakeReport("report0"));
+  service()->Send(MakeReport("report1"));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0", "report1"}));
 
-  service()->Send("report1");
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report1"}));
-
-  // Send pending reports. Previously queued reports should be observed. They
-  // will also be queued again.
+  // Send pending reports. Previously queued reports should be queued again.
   service()->SendPending();
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report0", "report1"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0", "report1"}));
 
   // Let all reports succeed.
   test_helper()->SetFailureMode(certificate_reporting_test_utils::
                                     ReportSendingResult::REPORTS_SUCCESSFUL);
 
   // Send a third report. This should not be queued.
-  service()->Send("report2");
-  WaitForRequestsDestroyed(ReportExpectation::Successful({"report2"}));
+  service()->Send(MakeReport("report2"));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({"report2"}));
 
   // Send pending reports. Previously failed and queued two reports should be
   // observed.
   service()->SendPending();
-  WaitForRequestsDestroyed(
+  test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Successful({"report0", "report1"}));
 }
 
@@ -512,13 +499,14 @@
 
   // Send a report. Report attempt should be cancelled and no sent reports
   // should be observed.
-  service()->Send("report0");
+  service()->Send(MakeReport("report0"));
 
   // Enable the service and send a report again. It should be sent successfully.
   SetServiceEnabledAndWait(true);
 
-  service()->Send("report1");
-  WaitForRequestsDestroyed(ReportExpectation::Successful({"report1"}));
+  service()->Send(MakeReport("report1"));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({"report1"}));
 }
 
 TEST_F(CertificateReportingServiceTest, Disabled_ShouldClearPendingReports) {
@@ -528,8 +516,9 @@
   test_helper()->SetFailureMode(
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
 
-  service()->Send("report0");
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report0"}));
+  service()->Send(MakeReport("report0"));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0"}));
 
   // Disable the service.
   SetServiceEnabledAndWait(false);
@@ -553,15 +542,12 @@
   test_helper()->SetFailureMode(
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
 
-  // Send a report.
-  service()->Send("report0");
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report0"}));
-
-  // Advance the clock a bit and trigger another report.
+  // Send a report, then advance the clock and send another report.
+  service()->Send(MakeReport("report0"));
   AdvanceClock(base::TimeDelta::FromHours(5));
-
-  service()->Send("report1");
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report1"}));
+  service()->Send(MakeReport("report1"));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0", "report1"}));
 
   // Advance the clock to 20 hours, putting it 25 hours ahead of the reference
   // time. This makes the report0 older than max age (24 hours). The report1 is
@@ -570,18 +556,21 @@
   // Send pending reports. report0 should be discarded since it's too old.
   // report1 should be queued again.
   service()->SendPending();
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report1"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report1"}));
 
   // Send a third report.
-  service()->Send("report2");
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report2"}));
+  service()->Send(MakeReport("report2"));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report2"}));
 
   // Advance the clock 5 hours. The report1 will now be 25 hours old.
   AdvanceClock(base::TimeDelta::FromHours(5));
   // Send pending reports. report1 should be discarded since it's too old.
   // report2 should be queued again.
   service()->SendPending();
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report2"}));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report2"}));
 
   // Advance the clock 20 hours again so that report2 is 25 hours old and is
   // older than max age (24 hours)
@@ -599,31 +588,29 @@
   test_helper()->SetFailureMode(
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
 
-  // Send a failed report.
-  service()->Send("report0");
-  WaitForRequestsDestroyed(ReportExpectation::Failed({"report0"}));
-
   // Send three more reports within five hours of each other. After this:
   // report0 is 0 hours after reference time (15 hours old).
   // report1 is 5 hours after reference time (10 hours old).
   // report2 is 10 hours after reference time (5 hours old).
   // report3 is 15 hours after reference time (0 hours old).
-  AdvanceClock(base::TimeDelta::FromHours(5));
-  service()->Send("report1");
+  service()->Send(MakeReport("report0"));
 
   AdvanceClock(base::TimeDelta::FromHours(5));
-  service()->Send("report2");
+  service()->Send(MakeReport("report1"));
 
   AdvanceClock(base::TimeDelta::FromHours(5));
-  service()->Send("report3");
-  WaitForRequestsDestroyed(
-      ReportExpectation::Failed({"report1", "report2", "report3"}));
+  service()->Send(MakeReport("report2"));
+
+  AdvanceClock(base::TimeDelta::FromHours(5));
+  service()->Send(MakeReport("report3"));
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Failed({"report0", "report1", "report2", "report3"}));
 
   // Send pending reports. Four reports were generated above, but the service
   // only queues three reports, so the very first one should be dropped since
   // it's the oldest.
   service()->SendPending();
-  WaitForRequestsDestroyed(
+  test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Failed({"report1", "report2", "report3"}));
 
   // Let all reports succeed.
@@ -639,7 +626,7 @@
   // Send pending reports. Only report2 and report3 should be sent, report1
   // should be ignored because it's too old.
   service()->SendPending();
-  WaitForRequestsDestroyed(
+  test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Successful({"report2", "report3"}));
 
   // Do a final send. No reports should be sent.
@@ -655,12 +642,13 @@
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_DELAY);
   // Send a report. The report upload hangs, so no error or success callbacks
   // should be called.
-  service()->Send("report0");
+  service()->Send(MakeReport("report0"));
 
   // Resume the report upload and run the callbacks. The report should be
   // successfully sent.
-  test_helper()->ResumeDelayedRequest(base::Bind(&base::DoNothing));
-  WaitForRequestsDestroyed(ReportExpectation::Delayed({"report0"}));
+  test_helper()->ResumeDelayedRequest();
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Delayed({"report0"}));
 }
 
 // Delayed reports should cleaned when the service is reset.
@@ -672,7 +660,7 @@
       certificate_reporting_test_utils::ReportSendingResult::REPORTS_DELAY);
   // Send a report. The report is triggered but hangs, so no error or success
   // callbacks should be called.
-  service()->Send("report0");
+  service()->Send(MakeReport("report0"));
 
   // Disable the service. This should reset the reporting service and
   // clear all pending reports.
@@ -680,8 +668,8 @@
 
   // Resume delayed report. No report should be observed since the service
   // should have reset and all pending reports should be cleared. If any report
-  // is observed, the next WaitForRequestsDestroyed() will fail.
-  test_helper()->ResumeDelayedRequest(base::Bind(&base::DoNothing));
+  // is observed, the next test_helper()->WaitForRequestsDestroyed() will fail.
+  test_helper()->ResumeDelayedRequest();
 
   // Enable the service.
   SetServiceEnabledAndWait(true);
@@ -689,9 +677,10 @@
   // Send a report. The report is triggered but hangs, so no error or success
   // callbacks should be called. The report id is again 0 since the pending
   // report queue has been cleared above.
-  service()->Send("report1");
+  service()->Send(MakeReport("report1"));
 
   // Resume delayed report. Two reports are successfully sent.
-  test_helper()->ResumeDelayedRequest(base::Bind(&base::DoNothing));
-  WaitForRequestsDestroyed(ReportExpectation::Delayed({"report0", "report1"}));
+  test_helper()->ResumeDelayedRequest();
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Delayed({"report0", "report1"}));
 }
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index bad033e..d5077fd 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -250,6 +250,10 @@
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
 }
 
+bool ChromeSyncClient::HasPasswordStore() {
+  return password_store_ != nullptr;
+}
+
 autofill::PersonalDataManager* ChromeSyncClient::GetPersonalDataManager() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return autofill::PersonalDataManagerFactory::GetForProfile(profile_);
diff --git a/chrome/browser/sync/chrome_sync_client.h b/chrome/browser/sync/chrome_sync_client.h
index 483fed5..4004061 100644
--- a/chrome/browser/sync/chrome_sync_client.h
+++ b/chrome/browser/sync/chrome_sync_client.h
@@ -42,6 +42,7 @@
   bookmarks::BookmarkModel* GetBookmarkModel() override;
   favicon::FaviconService* GetFaviconService() override;
   history::HistoryService* GetHistoryService() override;
+  bool HasPasswordStore() override;
   base::Closure GetPasswordStateChangedCallback() override;
   syncer::SyncApiComponentFactory::RegisterDataTypesMethod
   GetRegisterPlatformTypesCallback() override;
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 9c9392d..bf25719 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -6,7 +6,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/extensions/extension_app_icon_loader.h"
-#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
@@ -80,7 +80,7 @@
     return;
 
   // The pref helper functions return default values for invalid display ids.
-  PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+  PrefService* prefs = profile_->GetPrefs();
   for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
     shelf_controller_->SetAutoHideBehavior(
         ash::launcher::GetShelfAutoHideBehaviorPref(prefs, display.id()),
@@ -93,7 +93,7 @@
     return;
 
   // The pref helper functions return default values for invalid display ids.
-  PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+  PrefService* prefs = profile_->GetPrefs();
   for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
     shelf_controller_->SetAlignment(
         ash::launcher::GetShelfAlignmentPref(prefs, display.id()),
diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc
index 826af71..81fefa61 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.cc
+++ b/chrome/browser/ui/libgtkui/gtk_util.cc
@@ -394,6 +394,13 @@
   cairo_t* cairo_;
 };
 
+void RenderBackground(cairo_t* cr, GtkStyleContext* context) {
+  if (!context)
+    return;
+  RenderBackground(cr, gtk_style_context_get_parent(context));
+  gtk_render_background(context, cr, 0, 0, 1, 1);
+}
+
 SkColor GetBgColor(const char* css_selector) {
   // Backgrounds are more general than solid colors (eg. gradients),
   // but chromium requires us to boil this down to one color.  We
@@ -404,7 +411,7 @@
   auto context = GetStyleContextFromCss(css_selector);
   RemoveBorders(context);
   PixelSurface surface;
-  gtk_render_background(context, surface.cairo(), 0, 0, 1, 1);
+  RenderBackground(surface.cairo(), context);
   return surface.GetPixelValue();
 }
 
@@ -426,6 +433,7 @@
 
   AddBorders(context);
   PixelSurface surface;
+  RenderBackground(surface.cairo(), context);
   gtk_render_frame(context, surface.cairo(), 0, 0, 1, 1);
   return surface.GetPixelValue();
 }
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
index 6ae46d3..481e367b 100644
--- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
+++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
@@ -307,8 +307,8 @@
   DISALLOW_COPY_AND_ASSIGN(InstantPolicyTest);
 };
 
-// Flaky on Windows in the CQ: https://crbug.com/678975
-#if defined(OS_WIN)
+// Flaky on Windows in the CQ and Linux memory bot: https://crbug.com/678975
+#if defined(OS_WIN) || defined(OS_LINUX)
 #define MAYBE_SearchDoesntReuseInstantTab DISABLED_SearchDoesntReuseInstantTab
 #else
 #define MAYBE_SearchDoesntReuseInstantTab SearchDoesntReuseInstantTab
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index ef876004..5ed6dc0 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -334,6 +334,13 @@
       if (browser && browser->window())
         browser->window()->Activate();
     }
+  } else if (source.type == DesktopMediaID::TYPE_WINDOW) {
+#if defined(USE_AURA)
+    aura::Window* window = DesktopMediaID::GetAuraWindowById(source);
+    Browser* browser = chrome::FindBrowserWithWindow(window);
+    if (browser && browser->window())
+      browser->window()->Activate();
+#endif
   }
 
   if (parent_)
diff --git a/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc b/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
index 0f41cca..b263e5b 100644
--- a/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
+++ b/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
@@ -173,6 +173,9 @@
  private:
   friend class WebsiteSettingsPopupView;
 
+  // Used around icon and inside bubble border.
+  static constexpr int kSpacing = 12;
+
   DISALLOW_COPY_AND_ASSIGN(InternalPageInfoPopupView);
 };
 
@@ -354,7 +357,6 @@
   set_anchor_view_insets(gfx::Insets(
       GetLayoutConstant(LOCATION_BAR_BUBBLE_ANCHOR_VERTICAL_INSET), 0));
 
-  const int kSpacing = 16;
   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, kSpacing,
                                         kSpacing, kSpacing));
   set_margins(gfx::Insets());
@@ -379,9 +381,9 @@
     views::Widget* widget) {
   views::BubbleFrameView* frame = static_cast<views::BubbleFrameView*>(
       BubbleDialogDelegateView::CreateNonClientFrameView(widget));
-  // 16px padding + half of icon width comes out to 24px.
+  // Padding around icon + half of icon width.
   frame->bubble_border()->set_arrow_offset(
-      24 + frame->bubble_border()->GetBorderThickness());
+      kSpacing + 8 + frame->bubble_border()->GetBorderThickness());
   return frame;
 }
 
diff --git a/chrome/installer/linux/debian/expected_deps_ia32_jessie b/chrome/installer/linux/debian/expected_deps_ia32_jessie
index 6e6d44c..600a2ea 100644
--- a/chrome/installer/linux/debian/expected_deps_ia32_jessie
+++ b/chrome/installer/linux/debian/expected_deps_ia32_jessie
@@ -2,7 +2,7 @@
 libasound2 (>= 1.0.16)
 libatk1.0-0 (>= 1.12.4)
 libc6 (>= 2.11)
-libcairo2 (>= 1.6.0)
+libcairo2 (>= 1.2.4)
 libcups2 (>= 1.4.0)
 libdbus-1-3 (>= 1.2.14)
 libexpat1 (>= 2.0.1)
diff --git a/chrome/installer/linux/debian/expected_deps_ia32_wheezy b/chrome/installer/linux/debian/expected_deps_ia32_wheezy
index 6e6d44c..600a2ea 100644
--- a/chrome/installer/linux/debian/expected_deps_ia32_wheezy
+++ b/chrome/installer/linux/debian/expected_deps_ia32_wheezy
@@ -2,7 +2,7 @@
 libasound2 (>= 1.0.16)
 libatk1.0-0 (>= 1.12.4)
 libc6 (>= 2.11)
-libcairo2 (>= 1.6.0)
+libcairo2 (>= 1.2.4)
 libcups2 (>= 1.4.0)
 libdbus-1-3 (>= 1.2.14)
 libexpat1 (>= 2.0.1)
diff --git a/chrome/installer/linux/debian/expected_deps_x64_jessie b/chrome/installer/linux/debian/expected_deps_x64_jessie
index 87c2fc50..ab8362a 100644
--- a/chrome/installer/linux/debian/expected_deps_x64_jessie
+++ b/chrome/installer/linux/debian/expected_deps_x64_jessie
@@ -2,7 +2,7 @@
 libasound2 (>= 1.0.16)
 libatk1.0-0 (>= 1.12.4)
 libc6 (>= 2.15)
-libcairo2 (>= 1.6.0)
+libcairo2 (>= 1.2.4)
 libcups2 (>= 1.4.0)
 libdbus-1-3 (>= 1.1.4)
 libexpat1 (>= 2.0.1)
diff --git a/chrome/installer/linux/debian/expected_deps_x64_wheezy b/chrome/installer/linux/debian/expected_deps_x64_wheezy
index b23857d..98c6bd6 100644
--- a/chrome/installer/linux/debian/expected_deps_x64_wheezy
+++ b/chrome/installer/linux/debian/expected_deps_x64_wheezy
@@ -2,7 +2,7 @@
 libasound2 (>= 1.0.16)
 libatk1.0-0 (>= 1.12.4)
 libc6 (>= 2.11)
-libcairo2 (>= 1.6.0)
+libcairo2 (>= 1.2.4)
 libcups2 (>= 1.4.0)
 libdbus-1-3 (>= 1.1.4)
 libexpat1 (>= 2.0.1)
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index c675e6ee..7e21f0d4 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -163,6 +163,22 @@
   DCHECK(cast_browser_main_parts_);
   return cast_browser_main_parts_->media_pipeline_backend_manager();
 }
+
+::media::ScopedAudioManagerPtr CastContentBrowserClient::CreateAudioManager(
+    ::media::AudioLogFactory* audio_log_factory) {
+  return ::media::ScopedAudioManagerPtr(new media::CastAudioManager(
+      GetMediaTaskRunner(), GetMediaTaskRunner(), audio_log_factory,
+      media_pipeline_backend_manager()));
+}
+
+std::unique_ptr<::media::CdmFactory>
+CastContentBrowserClient::CreateCdmFactory() {
+#if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
+  return base::MakeUnique<media::CastCdmFactory>(GetMediaTaskRunner(),
+                                                 media_resource_tracker());
+#endif  // defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
+  return nullptr;
+}
 #endif  // !defined(OS_ANDROID)
 
 media::MediaCapsImpl* CastContentBrowserClient::media_caps() {
@@ -443,19 +459,15 @@
   return base::JSONReader::Read(manifest_contents);
 }
 
-#if defined(OS_ANDROID)
-
 void CastContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
     const base::CommandLine& command_line,
     int child_process_id,
-    content::FileDescriptorInfo* mappings,
-    std::map<int, base::MemoryMappedFile::Region>* regions) {
-  mappings->Share(
+    content::FileDescriptorInfo* mappings) {
+#if defined(OS_ANDROID)
+  mappings->ShareWithRegion(
       kAndroidPakDescriptor,
-      base::GlobalDescriptors::GetInstance()->Get(kAndroidPakDescriptor));
-  regions->insert(std::make_pair(
-      kAndroidPakDescriptor, base::GlobalDescriptors::GetInstance()->GetRegion(
-                                 kAndroidPakDescriptor)));
+      base::GlobalDescriptors::GetInstance()->Get(kAndroidPakDescriptor),
+      base::GlobalDescriptors::GetInstance()->GetRegion(kAndroidPakDescriptor));
 
   if (breakpad::IsCrashReporterEnabled()) {
     base::File minidump_file(
@@ -469,36 +481,13 @@
                          base::ScopedFD(minidump_file.TakePlatformFile()));
     }
   }
-}
-
 #else
-::media::ScopedAudioManagerPtr CastContentBrowserClient::CreateAudioManager(
-    ::media::AudioLogFactory* audio_log_factory) {
-  return ::media::ScopedAudioManagerPtr(new media::CastAudioManager(
-      GetMediaTaskRunner(), GetMediaTaskRunner(), audio_log_factory,
-      media_pipeline_backend_manager()));
-}
-
-std::unique_ptr<::media::CdmFactory>
-CastContentBrowserClient::CreateCdmFactory() {
-#if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
-  return base::MakeUnique<media::CastCdmFactory>(GetMediaTaskRunner(),
-                                                 media_resource_tracker());
-#endif  // defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
-  return nullptr;
-}
-
-void CastContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
-    const base::CommandLine& command_line,
-    int child_process_id,
-    content::FileDescriptorInfo* mappings) {
-  int crash_signal_fd = GetCrashSignalFD(command_line);
+    int crash_signal_fd = GetCrashSignalFD(command_line);
   if (crash_signal_fd >= 0) {
     mappings->Share(kCrashDumpSignal, crash_signal_fd);
   }
-}
-
 #endif  // defined(OS_ANDROID)
+}
 
 void CastContentBrowserClient::GetAdditionalWebUISchemes(
     std::vector<std::string>* additional_schemes) {
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 38f97c8d..5515558f5 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -91,6 +91,10 @@
   media::MediaResourceTracker* media_resource_tracker();
 
   media::MediaPipelineBackendManager* media_pipeline_backend_manager();
+
+  ::media::ScopedAudioManagerPtr CreateAudioManager(
+      ::media::AudioLogFactory* audio_log_factory) override;
+  std::unique_ptr<::media::CdmFactory> CreateCdmFactory() override;
 #endif
   media::MediaCapsImpl* media_caps();
 
@@ -153,21 +157,10 @@
   void RegisterInProcessServices(StaticServiceMap* services) override;
   std::unique_ptr<base::Value> GetServiceManifestOverlay(
       base::StringPiece service_name) override;
-#if defined(OS_ANDROID)
-  void GetAdditionalMappedFilesForChildProcess(
-      const base::CommandLine& command_line,
-      int child_process_id,
-      content::FileDescriptorInfo* mappings,
-      std::map<int, base::MemoryMappedFile::Region>* regions) override;
-#else
-  ::media::ScopedAudioManagerPtr CreateAudioManager(
-      ::media::AudioLogFactory* audio_log_factory) override;
-  std::unique_ptr<::media::CdmFactory> CreateCdmFactory() override;
   void GetAdditionalMappedFilesForChildProcess(
       const base::CommandLine& command_line,
       int child_process_id,
       content::FileDescriptorInfo* mappings) override;
-#endif  // defined(OS_ANDROID)
   void GetAdditionalWebUISchemes(
       std::vector<std::string>* additional_schemes) override;
   content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override;
diff --git a/components/autofill/core/browser/proto/BUILD.gn b/components/autofill/core/browser/proto/BUILD.gn
index ad52e78..2a8069f6 100644
--- a/components/autofill/core/browser/proto/BUILD.gn
+++ b/components/autofill/core/browser/proto/BUILD.gn
@@ -6,6 +6,7 @@
 
 proto_library("proto") {
   sources = [
+    "autofill_sync.proto",
     "server.proto",
   ]
 }
diff --git a/components/autofill/core/browser/proto/autofill_sync.proto b/components/autofill/core/browser/proto/autofill_sync.proto
new file mode 100644
index 0000000..613bdbac
--- /dev/null
+++ b/components/autofill/core/browser/proto/autofill_sync.proto
@@ -0,0 +1,17 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package autofill;
+
+// Used to convert between autofill::AutofillKey and a std::string that can be
+// passed to sync as storage key to uniquely identify an entity of ModelType
+// syncer::AUTOFILL.
+message AutofillSyncStorageKey {
+  optional string name = 1;
+  optional string value = 2;
+}
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
index 9b8e201..2edafc505 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
@@ -5,10 +5,13 @@
 #include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
 
 #include <unordered_set>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/proto/autofill_sync.pb.h"
 #include "components/autofill/core/browser/webdata/autofill_metadata_change_list.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
@@ -19,9 +22,12 @@
 #include "components/sync/model/sync_error.h"
 #include "net/base/escape.h"
 
+namespace autofill {
+
 namespace {
 
 const char kAutocompleteEntryNamespaceTag[] = "autofill_entry|";
+const char kAutocompleteTagDelimiter[] = "|";
 
 void* UserDataKey() {
   // Use the address of a static that COMDAT folding won't ever collide
@@ -31,7 +37,7 @@
 }
 
 std::unique_ptr<syncer::EntityData> CreateEntityData(
-    const autofill::AutofillEntry& entry) {
+    const AutofillEntry& entry) {
   auto entity_data = base::MakeUnique<syncer::EntityData>();
   entity_data->non_unique_name = base::UTF16ToUTF8(entry.key().name());
   sync_pb::AutofillSpecifics* autofill =
@@ -44,9 +50,20 @@
   return entity_data;
 }
 
-}  // namespace
+std::string BuildSerializedStorageKey(const std::string& name,
+                                      const std::string& value) {
+  AutofillSyncStorageKey proto;
+  proto.set_name(name);
+  proto.set_value(value);
+  return proto.SerializeAsString();
+}
 
-namespace autofill {
+std::string GetStorageKeyFromModel(const AutofillKey& key) {
+  return BuildSerializedStorageKey(base::UTF16ToUTF8(key.name()),
+                                   base::UTF16ToUTF8(key.value()));
+}
+
+}  // namespace
 
 // static
 void AutocompleteSyncBridge::CreateForWebDataServiceAndBackend(
@@ -117,7 +134,7 @@
   std::vector<AutofillEntry> entries;
   GetAutofillTable()->GetAllAutofillEntries(&entries);
   for (const AutofillEntry& entry : entries) {
-    std::string key = GetStorageKeyFromAutofillEntry(entry);
+    std::string key = GetStorageKeyFromModel(entry.key());
     if (keys_set.find(key) != keys_set.end()) {
       batch->Put(key, CreateEntityData(entry));
     }
@@ -131,7 +148,7 @@
   std::vector<AutofillEntry> entries;
   GetAutofillTable()->GetAllAutofillEntries(&entries);
   for (const AutofillEntry& entry : entries) {
-    batch->Put(GetStorageKeyFromAutofillEntry(entry), CreateEntityData(entry));
+    batch->Put(GetStorageKeyFromModel(entry.key()), CreateEntityData(entry));
   }
   callback.Run(syncer::SyncError(), std::move(batch));
 }
@@ -139,18 +156,18 @@
 std::string AutocompleteSyncBridge::GetClientTag(
     const syncer::EntityData& entity_data) {
   DCHECK(entity_data.specifics.has_autofill());
-
   const sync_pb::AutofillSpecifics specifics = entity_data.specifics.autofill();
-  std::string storage_key =
-      FormatStorageKey(specifics.name(), specifics.value());
-  std::string prefix(kAutocompleteEntryNamespaceTag);
-  return prefix + storage_key;
+  return std::string(kAutocompleteEntryNamespaceTag) +
+         net::EscapePath(specifics.name()) +
+         std::string(kAutocompleteTagDelimiter) +
+         net::EscapePath(specifics.value());
 }
 
 std::string AutocompleteSyncBridge::GetStorageKey(
     const syncer::EntityData& entity_data) {
+  DCHECK(entity_data.specifics.has_autofill());
   const sync_pb::AutofillSpecifics specifics = entity_data.specifics.autofill();
-  return FormatStorageKey(specifics.name(), specifics.value());
+  return BuildSerializedStorageKey(specifics.name(), specifics.value());
 }
 
 // AutofillWebDataServiceObserverOnDBThread implementation.
@@ -178,16 +195,4 @@
   return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
 }
 
-std::string AutocompleteSyncBridge::GetStorageKeyFromAutofillEntry(
-    const autofill::AutofillEntry& entry) {
-  return FormatStorageKey(base::UTF16ToUTF8(entry.key().name()),
-                          base::UTF16ToUTF8(entry.key().value()));
-}
-
-// static
-std::string AutocompleteSyncBridge::FormatStorageKey(const std::string& name,
-                                                     const std::string& value) {
-  return net::EscapePath(name) + "|" + net::EscapePath(value);
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
index 00a69f1b..8bf3460 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_
 
+#include <memory>
+#include <string>
+
 #include "base/scoped_observer.h"
 #include "base/supports_user_data.h"
 #include "base/threading/non_thread_safe.h"
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
index de05b01..596d67d1 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
@@ -4,7 +4,8 @@
 
 #include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
 
-#include <memory>
+#include <map>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/files/scoped_temp_dir.h"
@@ -21,10 +22,12 @@
 #include "components/sync/model/fake_model_type_change_processor.h"
 #include "components/sync/model/metadata_batch.h"
 #include "components/webdata/common/web_database.h"
-#include "net/base/escape.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using sync_pb::AutofillSpecifics;
+using sync_pb::EntitySpecifics;
+using syncer::EntityDataPtr;
+using syncer::EntityData;
 using syncer::SyncError;
 
 namespace autofill {
@@ -60,6 +63,28 @@
   return base::MakeUnique<syncer::FakeModelTypeChangeProcessor>();
 }
 
+AutofillEntry CreateAutofillEntry(
+    const sync_pb::AutofillSpecifics& autofill_specifics) {
+  AutofillKey key(base::UTF8ToUTF16(autofill_specifics.name()),
+                  base::UTF8ToUTF16(autofill_specifics.value()));
+  base::Time date_created, date_last_used;
+  const google::protobuf::RepeatedField<int64_t>& timestamps =
+      autofill_specifics.usage_timestamp();
+  if (!timestamps.empty()) {
+    date_created = base::Time::FromInternalValue(*timestamps.begin());
+    date_last_used = base::Time::FromInternalValue(*timestamps.rbegin());
+  }
+  return AutofillEntry(key, date_created, date_last_used);
+}
+
+// Creates an EntityData/EntityDataPtr around a copy of the given specifics.
+EntityDataPtr SpecificsToEntity(const AutofillSpecifics& specifics) {
+  EntityData data;
+  data.client_tag_hash = "ignored";
+  *data.specifics.mutable_autofill() = specifics;
+  return data.PassToPtr();
+}
+
 class FakeAutofillBackend : public AutofillWebDataBackend {
  public:
   FakeAutofillBackend() {}
@@ -101,8 +126,7 @@
       const std::vector<AutofillSpecifics>& specifics_list) {
     std::vector<AutofillEntry> new_entries;
     for (const auto& specifics : specifics_list) {
-      new_entries.push_back(
-          AutocompleteSyncBridge::CreateAutofillEntry(specifics));
+      new_entries.push_back(CreateAutofillEntry(specifics));
     }
     table_.UpdateAutofillEntries(new_entries);
   }
@@ -116,8 +140,10 @@
   }
 
   std::string GetStorageKey(const AutofillSpecifics& specifics) {
-    return net::EscapePath(specifics.name()) + "|" +
-           net::EscapePath(specifics.value());
+    std::string key =
+        bridge()->GetStorageKey(SpecificsToEntity(specifics).value());
+    EXPECT_FALSE(key.empty());
+    return key;
   }
 
  private:
@@ -131,6 +157,56 @@
   DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncBridgeTest);
 };
 
+TEST_F(AutocompleteSyncBridgeTest, GetClientTag) {
+  // TODO(skym, crbug.com/675991): Implementation.
+}
+
+TEST_F(AutocompleteSyncBridgeTest, GetStorageKey) {
+  std::string key = GetStorageKey(CreateSpecifics(1));
+  EXPECT_EQ(key, GetStorageKey(CreateSpecifics(1)));
+  EXPECT_NE(key, GetStorageKey(CreateSpecifics(2)));
+}
+
+// Timestamps should not affect storage keys.
+TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyTimestamp) {
+  AutofillSpecifics specifics = CreateSpecifics(1);
+  std::string key = GetStorageKey(specifics);
+
+  specifics.add_usage_timestamp(1);
+  EXPECT_EQ(key, GetStorageKey(specifics));
+
+  specifics.add_usage_timestamp(0);
+  EXPECT_EQ(key, GetStorageKey(specifics));
+
+  specifics.add_usage_timestamp(-1);
+  EXPECT_EQ(key, GetStorageKey(specifics));
+}
+
+// Verify that the \0 character is respected as a difference.
+TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyNull) {
+  AutofillSpecifics specifics;
+  std::string key = GetStorageKey(specifics);
+
+  specifics.set_value(std::string("\0", 1));
+  EXPECT_NE(key, GetStorageKey(specifics));
+}
+
+// The storage key should never accidentally change for existing data. This
+// would cause lookups to fail and either lose or duplicate user data. It should
+// be possible for the model type to migrate storage key formats, but doing so
+// would need to be done very carefully.
+TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyFixed) {
+  EXPECT_EQ("\n\x6name 1\x12\avalue 1", GetStorageKey(CreateSpecifics(1)));
+  EXPECT_EQ("\n\x6name 2\x12\avalue 2", GetStorageKey(CreateSpecifics(2)));
+  // This literal contains the null terminating character, which causes
+  // std::string to stop copying early if we don't tell it how much to read.
+  EXPECT_EQ(std::string("\n\0\x12\0", 4), GetStorageKey(AutofillSpecifics()));
+  AutofillSpecifics specifics;
+  specifics.set_name("\xEC\xA4\x91");
+  specifics.set_value("\xD0\x80");
+  EXPECT_EQ("\n\x3\xEC\xA4\x91\x12\x2\xD0\x80", GetStorageKey(specifics));
+}
+
 TEST_F(AutocompleteSyncBridgeTest, GetData) {
   const AutofillSpecifics specifics1 = CreateSpecifics(1);
   const AutofillSpecifics specifics2 = CreateSpecifics(2);
diff --git a/components/bookmarks/browser/BUILD.gn b/components/bookmarks/browser/BUILD.gn
index a4df5f1a..b606dc4 100644
--- a/components/bookmarks/browser/BUILD.gn
+++ b/components/bookmarks/browser/BUILD.gn
@@ -100,6 +100,7 @@
     "bookmark_index_unittest.cc",
     "bookmark_model_unittest.cc",
     "bookmark_utils_unittest.cc",
+    "titled_url_match_unittest.cc",
   ]
 
   if (toolkit_views) {
diff --git a/components/bookmarks/browser/titled_url_index.h b/components/bookmarks/browser/titled_url_index.h
index 13a6455..d6a0fba 100644
--- a/components/bookmarks/browser/titled_url_index.h
+++ b/components/bookmarks/browser/titled_url_index.h
@@ -29,6 +29,9 @@
 // TitledUrlNodes that contain that string in their title or URL.
 class TitledUrlIndex {
  public:
+  // Constructs a TitledUrlIndex. |sorter| is used to construct a sorted list
+  // of matches when matches are returned from the index. If null, matches are
+  // returned unsorted.
   TitledUrlIndex(std::unique_ptr<TitledUrlNodeSorter> sorter);
   ~TitledUrlIndex();
 
diff --git a/components/bookmarks/browser/titled_url_match_unittest.cc b/components/bookmarks/browser/titled_url_match_unittest.cc
new file mode 100644
index 0000000..8ccd2da
--- /dev/null
+++ b/components/bookmarks/browser/titled_url_match_unittest.cc
@@ -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.
+
+#include "components/bookmarks/browser/titled_url_match.h"
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace bookmarks {
+
+using MatchPositions = TitledUrlMatch::MatchPositions;
+
+TEST(TitledUrlMatchTest, EmptyOffsetsForEmptyMatchPositions) {
+  auto offsets = TitledUrlMatch::OffsetsFromMatchPositions(MatchPositions());
+  EXPECT_TRUE(offsets.empty());
+}
+
+TEST(TitledUrlMatchTest, OffsetsFromMatchPositions) {
+  MatchPositions match_positions = {{1, 3}, {4, 5}, {10, 15}};
+  std::vector<size_t> expected_offsets = {1, 3, 4, 5, 10, 15};
+  auto offsets = TitledUrlMatch::OffsetsFromMatchPositions(match_positions);
+  EXPECT_TRUE(
+      std::equal(offsets.begin(), offsets.end(), expected_offsets.begin()));
+}
+
+TEST(TitledUrlMatchTest, ReplaceOffsetsInEmptyMatchPositions) {
+  auto match_positions = TitledUrlMatch::ReplaceOffsetsInMatchPositions(
+      MatchPositions(), std::vector<size_t>());
+  EXPECT_TRUE(match_positions.empty());
+}
+
+TEST(TitledUrlMatchTest, ReplaceOffsetsInMatchPositions) {
+  MatchPositions orig_match_positions = {{1, 3}, {4, 5}, {10, 15}};
+  std::vector<size_t> offsets = {0, 2, 3, 4, 9, 14};
+  MatchPositions expected_match_positions = {{0, 2}, {3, 4}, {9, 14}};
+  auto match_positions = TitledUrlMatch::ReplaceOffsetsInMatchPositions(
+      orig_match_positions, offsets);
+  EXPECT_TRUE(std::equal(match_positions.begin(), match_positions.end(),
+                         expected_match_positions.begin()));
+}
+
+TEST(TitledUrlMatchTest, ReplaceOffsetsRemovesItemsWithNposOffsets) {
+  MatchPositions orig_match_positions = {{1, 3}, {4, 5}, {10, 15}, {17, 20}};
+  std::vector<size_t> offsets = {0,
+                                 base::string16::npos,
+                                 base::string16::npos,
+                                 4,
+                                 base::string16::npos,
+                                 base::string16::npos,
+                                 17,
+                                 20};
+  MatchPositions expected_match_positions = {{17, 20}};
+  auto match_positions = TitledUrlMatch::ReplaceOffsetsInMatchPositions(
+      orig_match_positions, offsets);
+  EXPECT_TRUE(std::equal(match_positions.begin(), match_positions.end(),
+                         expected_match_positions.begin()));
+}
+
+}
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 360eb4ff..93d83a74 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -304,9 +304,9 @@
     const syncer::DataTypeEncryptionHandler* encryption_handler,
     syncer::ModelTypeConfigurer* configurer,
     DataTypeManagerObserver* observer) {
-  return new DataTypeManagerImpl(initial_types, debug_info_listener,
-                                 controllers, encryption_handler, configurer,
-                                 observer);
+  return new DataTypeManagerImpl(sync_client_, initial_types,
+                                 debug_info_listener, controllers,
+                                 encryption_handler, configurer, observer);
 }
 
 syncer::SyncEngine* ProfileSyncComponentsFactoryImpl::CreateSyncEngine(
diff --git a/components/browser_sync/profile_sync_service_autofill_unittest.cc b/components/browser_sync/profile_sync_service_autofill_unittest.cc
index 77c09e3..6a37f1d 100644
--- a/components/browser_sync/profile_sync_service_autofill_unittest.cc
+++ b/components/browser_sync/profile_sync_service_autofill_unittest.cc
@@ -369,9 +369,11 @@
   DISALLOW_COPY_AND_ASSIGN(WebDataServiceFake);
 };
 
-ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) {
-  return new syncer::DataTypeManagerImpl(arg0, debug_listener, arg2, arg3, arg4,
-                                         arg5);
+ACTION_P2(ReturnNewDataTypeManagerWithDebugListener,
+          sync_client,
+          debug_listener) {
+  return new syncer::DataTypeManagerImpl(sync_client, arg0, debug_listener,
+                                         arg2, arg3, arg4, arg5);
 }
 
 class MockPersonalDataManager : public PersonalDataManager {
@@ -478,6 +480,7 @@
     EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
                 CreateDataTypeManager(_, _, _, _, _, _))
         .WillOnce(ReturnNewDataTypeManagerWithDebugListener(
+            sync_client_,
             syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr())));
 
     EXPECT_CALL(personal_data_manager(), IsDataLoaded())
diff --git a/components/browser_sync/profile_sync_service_typed_url_unittest.cc b/components/browser_sync/profile_sync_service_typed_url_unittest.cc
index da6c619..440cd85 100644
--- a/components/browser_sync/profile_sync_service_typed_url_unittest.cc
+++ b/components/browser_sync/profile_sync_service_typed_url_unittest.cc
@@ -60,8 +60,9 @@
 // Visits with this timestamp are treated as expired.
 static const int EXPIRED_VISIT = -1;
 
-ACTION(ReturnNewDataTypeManager) {
-  return new syncer::DataTypeManagerImpl(arg0, arg1, arg2, arg3, arg4, arg5);
+ACTION_P(ReturnNewDataTypeManager, sync_client) {
+  return new syncer::DataTypeManagerImpl(sync_client, arg0, arg1, arg2, arg3,
+                                         arg4, arg5);
 }
 
 class HistoryBackendMock : public HistoryBackend {
@@ -247,10 +248,10 @@
       SigninManagerBase* signin =
           profile_sync_service_bundle()->signin_manager();
       signin->SetAuthenticatedAccountInfo("gaia_id", "test");
-      CreateSyncService(std::move(sync_client_), callback);
       EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
                   CreateDataTypeManager(_, _, _, _, _, _))
-          .WillOnce(ReturnNewDataTypeManager());
+          .WillOnce(ReturnNewDataTypeManager(sync_client_.get()));
+      CreateSyncService(std::move(sync_client_), callback);
 
       profile_sync_service_bundle()->auth_service()->UpdateCredentials(
           account_id, "oauth2_login_token");
diff --git a/components/cronet/README.md b/components/cronet/README.md
index a2d48aef..663cae0 100644
--- a/components/cronet/README.md
+++ b/components/cronet/README.md
@@ -58,7 +58,7 @@
 
         @Override
         public void onFailed(UrlRequest request,
-                UrlResponseInfo responseInfo, UrlRequestException error) {
+                UrlResponseInfo responseInfo, CronetException error) {
              // Request has failed. responseInfo might be null.
              Log.e("MyCallback", "Request failed. " + error.getMessage());
              // Maybe handle error here. Typical errors include hostname
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 42942ae..04fa14f 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -288,7 +288,6 @@
     "api/src/org/chromium/net/UploadDataProviders.java",
     "api/src/org/chromium/net/UploadDataSink.java",
     "api/src/org/chromium/net/UrlRequest.java",
-    "api/src/org/chromium/net/UrlRequestException.java",
     "api/src/org/chromium/net/UrlResponseInfo.java",
   ]
 
diff --git a/components/cronet/android/api.txt b/components/cronet/android/api.txt
index 8be7b222..b72e3dc 100644
--- a/components/cronet/android/api.txt
+++ b/components/cronet/android/api.txt
@@ -231,9 +231,8 @@
   public abstract void onResponseStarted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) throws java.lang.Exception;
   public abstract void onReadCompleted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer) throws java.lang.Exception;
   public abstract void onSucceeded(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo);
-  public void onFailed(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, org.chromium.net.CronetException);
+  public abstract void onFailed(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, org.chromium.net.CronetException);
   public void onCanceled(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo);
-  public void onFailed(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, org.chromium.net.UrlRequestException);
 }
 public class org.chromium.net.UrlRequest$Status {
   public static final int INVALID;
@@ -266,24 +265,6 @@
   public abstract boolean isDone();
   public abstract void getStatus(org.chromium.net.UrlRequest$StatusListener);
 }
-public class org.chromium.net.UrlRequestException extends java.io.IOException {
-  public static final int ERROR_LISTENER_EXCEPTION_THROWN;
-  public static final int ERROR_HOSTNAME_NOT_RESOLVED;
-  public static final int ERROR_INTERNET_DISCONNECTED;
-  public static final int ERROR_NETWORK_CHANGED;
-  public static final int ERROR_TIMED_OUT;
-  public static final int ERROR_CONNECTION_CLOSED;
-  public static final int ERROR_CONNECTION_TIMED_OUT;
-  public static final int ERROR_CONNECTION_REFUSED;
-  public static final int ERROR_CONNECTION_RESET;
-  public static final int ERROR_ADDRESS_UNREACHABLE;
-  public static final int ERROR_QUIC_PROTOCOL_FAILED;
-  public static final int ERROR_OTHER;
-  public org.chromium.net.UrlRequestException(org.chromium.net.CronetException);
-  public int getErrorCode();
-  public int getCronetInternalErrorCode();
-  public boolean immediatelyRetryable();
-}
 public abstract class org.chromium.net.UrlResponseInfo$HeaderBlock {
   public org.chromium.net.UrlResponseInfo$HeaderBlock();
   public abstract java.util.List<java.util.Map$Entry<java.lang.String, java.lang.String>> getAsList();
diff --git a/components/cronet/android/api/src/org/chromium/net/UrlRequest.java b/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
index 726e10ec..cf5250a 100644
--- a/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
+++ b/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
@@ -214,11 +214,8 @@
          *         received.
          * @param error information about error.
          */
-        public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
-            // TODO(mef): Remove fallback to legacy api and make this method abstract
-            // after complete transition to CronetException.
-            onFailed(request, info, new UrlRequestException(error));
-        }
+        public abstract void onFailed(
+                UrlRequest request, UrlResponseInfo info, CronetException error);
 
         /**
          * Invoked if request was canceled via {@link UrlRequest#cancel}. Once
@@ -230,16 +227,6 @@
          *         received.
          */
         public void onCanceled(UrlRequest request, UrlResponseInfo info) {}
-
-        /**
-         * @deprecated Use {@code onFailed} instead.
-         * {@hide This method will be removed after complete transition to CronetException}.
-         */
-        @Deprecated
-        // TODO(mef): Remove this after complete transition to CronetException.
-        public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
-            assert false;
-        }
     }
 
     /**
diff --git a/components/cronet/android/api/src/org/chromium/net/UrlRequestException.java b/components/cronet/android/api/src/org/chromium/net/UrlRequestException.java
deleted file mode 100644
index 0bca019..0000000
--- a/components/cronet/android/api/src/org/chromium/net/UrlRequestException.java
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.net;
-
-import java.io.IOException;
-
-/**
- * @deprecated Use {@link CronetException} instead.
- * {@hide This class will be removed after complete transition to CronetException}.
- */
-@Deprecated
-public class UrlRequestException extends IOException {
-    /**
-     * Error code indicating this class wraps an exception thrown by {@link UrlRequest.Callback} or
-     * {@link UploadDataProvider}. Wrapped exception can be retrieved using
-     * {@link IOException#getCause}.
-     */
-    public static final int ERROR_LISTENER_EXCEPTION_THROWN = 0;
-    /**
-     * Error code indicating the host being sent the request could not be resolved to an IP address.
-     */
-    public static final int ERROR_HOSTNAME_NOT_RESOLVED = 1;
-    /**
-     * Error code indicating the device was not connected to any network.
-     */
-    public static final int ERROR_INTERNET_DISCONNECTED = 2;
-    /**
-     * Error code indicating that as the request was processed the network configuration changed.
-     */
-    public static final int ERROR_NETWORK_CHANGED = 3;
-    /**
-     * Error code indicating a timeout expired. Timeouts expiring while attempting to connect will
-     * be reported as the more specific {@link #ERROR_CONNECTION_TIMED_OUT}.
-     */
-    public static final int ERROR_TIMED_OUT = 4;
-    /**
-     * Error code indicating the connection was closed unexpectedly.
-     */
-    public static final int ERROR_CONNECTION_CLOSED = 5;
-    /**
-     * Error code indicating the connection attempt timed out.
-     */
-    public static final int ERROR_CONNECTION_TIMED_OUT = 6;
-    /**
-     * Error code indicating the connection attempt was refused.
-     */
-    public static final int ERROR_CONNECTION_REFUSED = 7;
-    /**
-     * Error code indicating the connection was unexpectedly reset.
-     */
-    public static final int ERROR_CONNECTION_RESET = 8;
-    /**
-     * Error code indicating the IP address being contacted is unreachable, meaning there is no
-     * route to the specified host or network.
-     */
-    public static final int ERROR_ADDRESS_UNREACHABLE = 9;
-    /**
-     * Error code indicating an error related to the <a href="https://www.chromium.org/quic">
-     * QUIC</a> protocol. When {@link #getErrorCode} returns this code, this exception can be cast
-     * to {@link QuicException} for more information.
-     */
-    public static final int ERROR_QUIC_PROTOCOL_FAILED = 10;
-    /**
-     * Error code indicating another type of error was encountered.
-     * {@link #getCronetInternalErrorCode} can be consulted to get a more specific cause.
-     */
-    public static final int ERROR_OTHER = 11;
-
-    // Error code, one of ERROR_*
-    private final int mErrorCode;
-    // Cronet internal error code.
-    private final int mCronetInternalErrorCode;
-
-    public UrlRequestException(CronetException error) {
-        super(error.getMessage(), error.getCause());
-        if (error instanceof NetworkException) {
-            mErrorCode = ((NetworkException) error).getErrorCode();
-            mCronetInternalErrorCode = ((NetworkException) error).getCronetInternalErrorCode();
-        } else {
-            mErrorCode = 0;
-            mCronetInternalErrorCode = ERROR_LISTENER_EXCEPTION_THROWN;
-        }
-    }
-
-    /**
-     * Returns error code, one of {@link #ERROR_LISTENER_EXCEPTION_THROWN ERROR_*}.
-     *
-     * @return error code, one of {@link #ERROR_LISTENER_EXCEPTION_THROWN ERROR_*}.
-     */
-    public int getErrorCode() {
-        return mErrorCode;
-    }
-
-    /**
-     * Returns a Cronet internal error code. This may provide more specific error
-     * diagnosis than {@link #getErrorCode}, but the constant values are not exposed to Java and
-     * may change over time. See
-     * <a href=https://chromium.googlesource.com/chromium/src/+/master/net/base/net_error_list.h>
-     * here</a> for the lastest list of values.
-     *
-     * @return Cronet internal error code.
-     */
-    public int getCronetInternalErrorCode() {
-        return mCronetInternalErrorCode;
-    }
-
-    /**
-     * Returns {@code true} if retrying this request right away might succeed, {@code false}
-     * otherwise. For example returns {@code true} when {@link #getErrorCode} returns
-     * {@link #ERROR_NETWORK_CHANGED} because trying the request might succeed using the new
-     * network configuration, but {@code false} when {@code getErrorCode()} returns
-     * {@link #ERROR_INTERNET_DISCONNECTED} because retrying the request right away will
-     * encounter the same failure (instead retrying should be delayed until device regains
-     * network connectivity). Returns {@code false} when {@code getErrorCode()} returns
-     * {@link #ERROR_LISTENER_EXCEPTION_THROWN}.
-     *
-     * @return {@code true} if retrying this request right away might succeed, {@code false}
-     *         otherwise.
-     */
-    public boolean immediatelyRetryable() {
-        switch (mErrorCode) {
-            case ERROR_LISTENER_EXCEPTION_THROWN:
-            case ERROR_HOSTNAME_NOT_RESOLVED:
-            case ERROR_INTERNET_DISCONNECTED:
-            case ERROR_CONNECTION_REFUSED:
-            case ERROR_ADDRESS_UNREACHABLE:
-            case ERROR_OTHER:
-            default:
-                return false;
-            case ERROR_NETWORK_CHANGED:
-            case ERROR_TIMED_OUT:
-            case ERROR_CONNECTION_CLOSED:
-            case ERROR_CONNECTION_TIMED_OUT:
-            case ERROR_CONNECTION_RESET:
-                return true;
-        }
-    }
-}
diff --git a/components/cronet/android/api_version.txt b/components/cronet/android/api_version.txt
index 56a6051..0cfbf08 100644
--- a/components/cronet/android/api_version.txt
+++ b/components/cronet/android/api_version.txt
@@ -1 +1 @@
-1
\ No newline at end of file
+2
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java b/components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java
index d0c4ace9..52854ec 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java
@@ -13,7 +13,6 @@
 import org.chromium.net.UploadDataProvider;
 import org.chromium.net.UploadDataSink;
 import org.chromium.net.UrlRequest;
-import org.chromium.net.UrlRequestException;
 import org.chromium.net.UrlResponseInfo;
 
 import java.io.IOException;
@@ -72,11 +71,6 @@
         public void onCanceled(UrlRequest request, UrlResponseInfo info) {
             mWrappedCallback.onCanceled(request, info);
         }
-
-        @Override
-        public void onFailed(UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
-            mWrappedCallback.onFailed(request, info, error);
-        }
     }
 
     /**
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
index a5afdf2..e0d06b7e 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -2036,10 +2036,11 @@
             }
 
             @Override
-            public void onFailed(
-                    UrlRequest request, UrlResponseInfo info, UrlRequestException error) {
-                assertEquals(netError, error.getCronetInternalErrorCode());
-                failedExpectation.set(error.getCronetInternalErrorCode() != netError);
+            public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
+                assertTrue(error instanceof NetworkException);
+                assertEquals(netError, ((NetworkException) error).getCronetInternalErrorCode());
+                failedExpectation.set(
+                        ((NetworkException) error).getCronetInternalErrorCode() != netError);
                 done.open();
             }
 
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index be1ac41..7ba8379 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -95,6 +95,8 @@
     "shortcuts_provider.h",
     "suggestion_answer.cc",
     "suggestion_answer.h",
+    "titled_url_match_utils.cc",
+    "titled_url_match_utils.h",
     "url_index_private_data.cc",
     "url_index_private_data.h",
     "url_prefix.cc",
@@ -243,6 +245,7 @@
     "shortcuts_database_unittest.cc",
     "shortcuts_provider_unittest.cc",
     "suggestion_answer_unittest.cc",
+    "titled_url_match_utils_unittest.cc",
     "url_prefix_unittest.cc",
     "zero_suggest_provider_unittest.cc",
   ]
diff --git a/components/omnibox/browser/autocomplete_classifier.cc b/components/omnibox/browser/autocomplete_classifier.cc
index 4e50bbfd..c6e81cf 100644
--- a/components/omnibox/browser/autocomplete_classifier.cc
+++ b/components/omnibox/browser/autocomplete_classifier.cc
@@ -17,8 +17,11 @@
 
 // static
 const int AutocompleteClassifier::kDefaultOmniboxProviders =
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
-    // Custom search engines cannot be used on mobile..
+#if defined(OS_ANDROID) || defined(OS_IOS)
+    // The Physical Web currently is only implemented on mobile devices.
+    AutocompleteProvider::TYPE_PHYSICAL_WEB |
+#else
+    // Custom search engines cannot be used on mobile.
     AutocompleteProvider::TYPE_KEYWORD |
 #endif
 #if !defined(OS_IOS)
@@ -29,8 +32,6 @@
 #else
     // "URL from clipboard" can only be used on iOS.
     AutocompleteProvider::TYPE_CLIPBOARD_URL |
-    // Physical Web omnibox results are only implemented on iOS.
-    AutocompleteProvider::TYPE_PHYSICAL_WEB |
 #endif
     AutocompleteProvider::TYPE_BOOKMARK |
     AutocompleteProvider::TYPE_HISTORY_QUICK |
diff --git a/components/omnibox/browser/bookmark_provider.cc b/components/omnibox/browser/bookmark_provider.cc
index cee15775a..88cdc26 100644
--- a/components/omnibox/browser/bookmark_provider.cc
+++ b/components/omnibox/browser/bookmark_provider.cc
@@ -17,40 +17,14 @@
 #include "components/metrics/proto/omnibox_input_type.pb.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_result.h"
-#include "components/omnibox/browser/history_provider.h"
-#include "components/omnibox/browser/url_prefix.h"
+#include "components/omnibox/browser/titled_url_match_utils.h"
 #include "components/prefs/pref_service.h"
-#include "components/url_formatter/url_formatter.h"
 #include "url/url_constants.h"
 
 using bookmarks::BookmarkNode;
 using bookmarks::TitledUrlMatch;
 using TitledUrlMatches = std::vector<TitledUrlMatch>;
 
-namespace {
-
-// Removes leading spaces from |title| before displaying, otherwise it looks
-// funny.  In the process, corrects |title_match_positions| so the correct
-// characters are highlighted.
-void CorrectTitleAndMatchPositions(
-    base::string16* title,
-    TitledUrlMatch::MatchPositions* title_match_positions) {
-  size_t leading_whitespace_chars = title->length();
-  base::TrimWhitespace(*title, base::TRIM_LEADING, title);
-  leading_whitespace_chars-= title->length();
-  if (leading_whitespace_chars == 0)
-    return;
-  for (query_parser::Snippet::MatchPositions::iterator it =
-           title_match_positions->begin();
-       it != title_match_positions->end(); ++it) {
-    (*it) = query_parser::Snippet::MatchPosition(
-        it->first - leading_whitespace_chars,
-        it->second - leading_whitespace_chars);
-  }
-}
-
-}  // namespace
-
 // BookmarkProvider ------------------------------------------------------------
 
 BookmarkProvider::BookmarkProvider(AutocompleteProviderClient* client)
@@ -107,13 +81,15 @@
   if (matches.empty())
     return;  // There were no matches.
   const base::string16 fixed_up_input(FixupUserInput(input).second);
-  for (TitledUrlMatches::const_iterator i = matches.begin(); i != matches.end();
-       ++i) {
-    // Create and score the AutocompleteMatch. If its score is 0 then the
-    // match is discarded.
-    AutocompleteMatch match(TitledUrlMatchToACMatch(input, fixed_up_input, *i));
-    if (match.relevance > 0)
-      matches_.push_back(match);
+  for (const auto& bookmark_match : matches) {
+    // Score the TitledUrlMatch. If its score is greater than 0 then the
+    // AutocompleteMatch is created and added to matches_.
+    int relevance = CalculateBookmarkMatchRelevance(bookmark_match);
+    if (relevance > 0) {
+      matches_.push_back(TitledUrlMatchToAutocompleteMatch(
+          bookmark_match, AutocompleteMatchType::BOOKMARK_TITLE, relevance,
+          this, client_->GetSchemeClassifier(), input, fixed_up_input));
+    }
   }
 
   // Sort and clip the resulting matches.
@@ -155,69 +131,8 @@
 
 }  // namespace
 
-AutocompleteMatch BookmarkProvider::TitledUrlMatchToACMatch(
-    const AutocompleteInput& input,
-    const base::string16& fixed_up_input_text,
-    const TitledUrlMatch& bookmark_match) {
-  // The AutocompleteMatch we construct is non-deletable because the only
-  // way to support this would be to delete the underlying bookmark, which is
-  // unlikely to be what the user intends.
-  AutocompleteMatch match(this, 0, false,
-                          AutocompleteMatchType::BOOKMARK_TITLE);
-  base::string16 title(bookmark_match.node->GetTitledUrlNodeTitle());
-  TitledUrlMatch::MatchPositions new_title_match_positions =
-      bookmark_match.title_match_positions;
-  CorrectTitleAndMatchPositions(&title, &new_title_match_positions);
-  const GURL& url(bookmark_match.node->GetTitledUrlNodeUrl());
-  const base::string16& url_utf16 = base::UTF8ToUTF16(url.spec());
-  size_t inline_autocomplete_offset = URLPrefix::GetInlineAutocompleteOffset(
-      input.text(), fixed_up_input_text, false, url_utf16);
-  match.destination_url = url;
-  const size_t match_start = bookmark_match.url_match_positions.empty() ?
-      0 : bookmark_match.url_match_positions[0].first;
-  const bool trim_http = !AutocompleteInput::HasHTTPScheme(input.text()) &&
-      ((match_start == base::string16::npos) || (match_start != 0));
-  std::vector<size_t> offsets = TitledUrlMatch::OffsetsFromMatchPositions(
-      bookmark_match.url_match_positions);
-  // In addition to knowing how |offsets| is transformed, we need to know how
-  // |inline_autocomplete_offset| is transformed.  We add it to the end of
-  // |offsets|, compute how everything is transformed, then remove it from the
-  // end.
-  offsets.push_back(inline_autocomplete_offset);
-  match.contents = url_formatter::FormatUrlWithOffsets(
-      url, url_formatter::kFormatUrlOmitAll &
-               ~(trim_http ? 0 : url_formatter::kFormatUrlOmitHTTP),
-      net::UnescapeRule::SPACES, nullptr, nullptr, &offsets);
-  inline_autocomplete_offset = offsets.back();
-  offsets.pop_back();
-  TitledUrlMatch::MatchPositions new_url_match_positions =
-      TitledUrlMatch::ReplaceOffsetsInMatchPositions(
-          bookmark_match.url_match_positions, offsets);
-  match.contents_class =
-      ClassificationsFromMatch(new_url_match_positions,
-                               match.contents.size(),
-                               true);
-  match.fill_into_edit =
-      AutocompleteInput::FormattedStringWithEquivalentMeaning(
-          url, match.contents, client_->GetSchemeClassifier());
-  if (inline_autocomplete_offset != base::string16::npos) {
-    // |inline_autocomplete_offset| may be beyond the end of the
-    // |fill_into_edit| if the user has typed an URL with a scheme and the
-    // last character typed is a slash.  That slash is removed by the
-    // FormatURLWithOffsets call above.
-    if (inline_autocomplete_offset < match.fill_into_edit.length()) {
-      match.inline_autocompletion =
-          match.fill_into_edit.substr(inline_autocomplete_offset);
-    }
-    match.allowed_to_be_default_match = match.inline_autocompletion.empty() ||
-        !HistoryProvider::PreventInlineAutocomplete(input);
-  }
-  match.description = title;
-  match.description_class =
-      ClassificationsFromMatch(bookmark_match.title_match_positions,
-                               match.description.size(),
-                               false);
-
+int BookmarkProvider::CalculateBookmarkMatchRelevance(
+    const TitledUrlMatch& bookmark_match) const {
   // Summary on how a relevance score is determined for the match:
   //
   // For each match within the bookmark's title or URL (or both), calculate a
@@ -268,7 +183,9 @@
   // and, for each additional reference beyond the one for the bookmark being
   // scored up to a maximum of three, the score is boosted by a fixed amount
   // given by |kURLCountBoost|, below.
-  //
+
+  base::string16 title(bookmark_match.node->GetTitledUrlNodeTitle());
+  const GURL& url(bookmark_match.node->GetTitledUrlNodeUrl());
 
   // Pretend empty titles are identical to the URL.
   if (title.empty())
@@ -297,46 +214,19 @@
   const int kMaxBookmarkScore = bookmarklet_without_title_match ? 799 : 1199;
   const double kBookmarkScoreRange =
       static_cast<double>(kMaxBookmarkScore - kBaseBookmarkScore);
-  match.relevance = static_cast<int>(normalized_sum * kBookmarkScoreRange) +
-      kBaseBookmarkScore;
+  int relevance = static_cast<int>(normalized_sum * kBookmarkScoreRange) +
+                  kBaseBookmarkScore;
   // Don't waste any time searching for additional referenced URLs if we
   // already have a perfect title match.
-  if (match.relevance >= kMaxBookmarkScore)
-    return match;
+  if (relevance >= kMaxBookmarkScore)
+    return relevance;
   // Boost the score if the bookmark's URL is referenced by other bookmarks.
   const int kURLCountBoost[4] = { 0, 75, 125, 150 };
   std::vector<const BookmarkNode*> nodes;
   bookmark_model_->GetNodesByURL(url, &nodes);
   DCHECK_GE(std::min(arraysize(kURLCountBoost), nodes.size()), 1U);
-  match.relevance +=
+  relevance +=
       kURLCountBoost[std::min(arraysize(kURLCountBoost), nodes.size()) - 1];
-  match.relevance = std::min(kMaxBookmarkScore, match.relevance);
-  return match;
-}
-
-// static
-ACMatchClassifications BookmarkProvider::ClassificationsFromMatch(
-    const query_parser::Snippet::MatchPositions& positions,
-    size_t text_length,
-    bool is_url) {
-  ACMatchClassification::Style url_style =
-      is_url ? ACMatchClassification::URL : ACMatchClassification::NONE;
-  ACMatchClassifications classifications;
-  if (positions.empty()) {
-    if (text_length > 0)
-      classifications.push_back(ACMatchClassification(0, url_style));
-    return classifications;
-  }
-
-  for (query_parser::Snippet::MatchPositions::const_iterator i =
-           positions.begin();
-       i != positions.end();
-       ++i) {
-    AutocompleteMatch::ACMatchClassifications new_class;
-    AutocompleteMatch::ClassifyLocationInString(i->first, i->second - i->first,
-        text_length, url_style, &new_class);
-    classifications = AutocompleteMatch::MergeClassifications(
-        classifications, new_class);
-  }
-  return classifications;
+  relevance = std::min(kMaxBookmarkScore, relevance);
+  return relevance;
 }
diff --git a/components/omnibox/browser/bookmark_provider.h b/components/omnibox/browser/bookmark_provider.h
index bb2c71b..25a2bcb 100644
--- a/components/omnibox/browser/bookmark_provider.h
+++ b/components/omnibox/browser/bookmark_provider.h
@@ -12,9 +12,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "components/omnibox/browser/autocomplete_input.h"
-#include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
-#include "components/query_parser/snippet.h"
 
 class AutocompleteProviderClient;
 
@@ -55,24 +53,9 @@
   // |matches_|.
   void DoAutocomplete(const AutocompleteInput& input);
 
-  // Compose an AutocompleteMatch based on |match| that has 1) the URL of
-  // |match|'s bookmark, and 2) the bookmark's title, not the URL's page
-  // title, as the description.  |input| is used to compute the match's
-  // inline_autocompletion.  |fixed_up_input_text| is used in that way as well;
-  // it's passed separately so this function doesn't have to compute it.
-  AutocompleteMatch TitledUrlMatchToACMatch(
-      const AutocompleteInput& input,
-      const base::string16& fixed_up_input_text,
-      const bookmarks::TitledUrlMatch& match);
-
-  // Converts |positions| into ACMatchClassifications and returns the
-  // classifications. |text_length| is used to determine the need to add an
-  // 'unhighlighted' classification span so the tail of the source string
-  // properly highlighted.
-  static ACMatchClassifications ClassificationsFromMatch(
-      const query_parser::Snippet::MatchPositions& positions,
-      size_t text_length,
-      bool is_url);
+  // Calculates the relevance score for |match|.
+  int CalculateBookmarkMatchRelevance(
+      const bookmarks::TitledUrlMatch& match) const;
 
   AutocompleteProviderClient* client_;
   bookmarks::BookmarkModel* bookmark_model_;
diff --git a/components/omnibox/browser/bookmark_provider_unittest.cc b/components/omnibox/browser/bookmark_provider_unittest.cc
index 277f3c38..586892e 100644
--- a/components/omnibox/browser/bookmark_provider_unittest.cc
+++ b/components/omnibox/browser/bookmark_provider_unittest.cc
@@ -25,6 +25,7 @@
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/mock_autocomplete_provider_client.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
+#include "components/omnibox/browser/titled_url_match_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -422,8 +423,10 @@
     node.SetTitle(base::ASCIIToUTF16(query_data[i].url));
     TitledUrlMatch bookmark_match;
     bookmark_match.node = &node;
-    const AutocompleteMatch& ac_match = provider_->TitledUrlMatchToACMatch(
-        input, fixed_up_input, bookmark_match);
+    int relevance = provider_->CalculateBookmarkMatchRelevance(bookmark_match);
+    const AutocompleteMatch& ac_match = TitledUrlMatchToAutocompleteMatch(
+        bookmark_match, AutocompleteMatchType::BOOKMARK_TITLE, relevance,
+        provider_.get(), classifier_, input, fixed_up_input);
     EXPECT_EQ(query_data[i].allowed_to_be_default_match,
               ac_match.allowed_to_be_default_match) << description;
     EXPECT_EQ(base::ASCIIToUTF16(query_data[i].inline_autocompletion),
diff --git a/components/omnibox/browser/titled_url_match_utils.cc b/components/omnibox/browser/titled_url_match_utils.cc
new file mode 100644
index 0000000..f60d0d5
--- /dev/null
+++ b/components/omnibox/browser/titled_url_match_utils.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 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/omnibox/browser/titled_url_match_utils.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/bookmarks/browser/titled_url_node.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/history_provider.h"
+#include "components/omnibox/browser/url_prefix.h"
+#include "components/url_formatter/url_formatter.h"
+
+namespace {
+
+// Converts |positions| into ACMatchClassifications and returns the
+// classifications. |text_length| is used to determine the need to add an
+// 'unhighlighted' classification span so the tail of the source string
+// properly highlighted.
+ACMatchClassifications ClassificationsFromMatchPositions(
+    const bookmarks::TitledUrlMatch::MatchPositions& positions,
+    size_t text_length,
+    bool is_url) {
+  ACMatchClassification::Style url_style =
+      is_url ? ACMatchClassification::URL : ACMatchClassification::NONE;
+  ACMatchClassifications classifications;
+  if (positions.empty()) {
+    if (text_length > 0) {
+      classifications.push_back(ACMatchClassification(0, url_style));
+    }
+    return classifications;
+  }
+
+  for (bookmarks::TitledUrlMatch::MatchPositions::const_iterator i =
+           positions.begin();
+       i != positions.end(); ++i) {
+    AutocompleteMatch::ACMatchClassifications new_class;
+    AutocompleteMatch::ClassifyLocationInString(
+        i->first, i->second - i->first, text_length, url_style, &new_class);
+    classifications =
+        AutocompleteMatch::MergeClassifications(classifications, new_class);
+  }
+  return classifications;
+}
+
+}  // namespace
+
+namespace bookmarks {
+
+AutocompleteMatch TitledUrlMatchToAutocompleteMatch(
+    const TitledUrlMatch& titled_url_match,
+    AutocompleteMatchType::Type type,
+    int relevance,
+    AutocompleteProvider* provider,
+    const AutocompleteSchemeClassifier& scheme_classifier,
+    const AutocompleteInput& input,
+    const base::string16& fixed_up_input_text) {
+  const GURL& url = titled_url_match.node->GetTitledUrlNodeUrl();
+  base::string16 title = titled_url_match.node->GetTitledUrlNodeTitle();
+
+  // The AutocompleteMatch we construct is non-deletable because the only way to
+  // support this would be to delete the underlying object that created the
+  // titled_url_match. E.g., for the bookmark provider this would mean deleting
+  // the underlying bookmark, which is unlikely to be what the user intends.
+  AutocompleteMatch match(provider, relevance, false, type);
+  TitledUrlMatch::MatchPositions new_title_match_positions =
+      titled_url_match.title_match_positions;
+  CorrectTitleAndMatchPositions(&title, &new_title_match_positions);
+  const base::string16& url_utf16 = base::UTF8ToUTF16(url.spec());
+  size_t inline_autocomplete_offset = URLPrefix::GetInlineAutocompleteOffset(
+      input.text(), fixed_up_input_text, false, url_utf16);
+  match.destination_url = url;
+  const size_t match_start =
+      titled_url_match.url_match_positions.empty()
+          ? 0
+          : titled_url_match.url_match_positions[0].first;
+  const bool trim_http =
+      !AutocompleteInput::HasHTTPScheme(input.text()) &&
+      ((match_start == base::string16::npos) || (match_start != 0));
+  std::vector<size_t> offsets = TitledUrlMatch::OffsetsFromMatchPositions(
+      titled_url_match.url_match_positions);
+  // In addition to knowing how |offsets| is transformed, we need to know how
+  // |inline_autocomplete_offset| is transformed.  We add it to the end of
+  // |offsets|, compute how everything is transformed, then remove it from the
+  // end.
+  offsets.push_back(inline_autocomplete_offset);
+  match.contents = url_formatter::FormatUrlWithOffsets(
+      url, url_formatter::kFormatUrlOmitAll &
+               ~(trim_http ? 0 : url_formatter::kFormatUrlOmitHTTP),
+      net::UnescapeRule::SPACES, nullptr, nullptr, &offsets);
+  inline_autocomplete_offset = offsets.back();
+  offsets.pop_back();
+  TitledUrlMatch::MatchPositions new_url_match_positions =
+      TitledUrlMatch::ReplaceOffsetsInMatchPositions(
+          titled_url_match.url_match_positions, offsets);
+  match.contents_class = ClassificationsFromMatchPositions(
+      new_url_match_positions, match.contents.size(), true);
+  match.fill_into_edit =
+      AutocompleteInput::FormattedStringWithEquivalentMeaning(
+          url, match.contents, scheme_classifier);
+  if (inline_autocomplete_offset != base::string16::npos) {
+    // |inline_autocomplete_offset| may be beyond the end of the
+    // |fill_into_edit| if the user has typed an URL with a scheme and the
+    // last character typed is a slash.  That slash is removed by the
+    // FormatURLWithOffsets call above.
+    if (inline_autocomplete_offset < match.fill_into_edit.length()) {
+      match.inline_autocompletion =
+          match.fill_into_edit.substr(inline_autocomplete_offset);
+    }
+    match.allowed_to_be_default_match =
+        match.inline_autocompletion.empty() ||
+        !HistoryProvider::PreventInlineAutocomplete(input);
+  }
+  match.description = title;
+  match.description_class = ClassificationsFromMatchPositions(
+      titled_url_match.title_match_positions, match.description.size(), false);
+
+  return match;
+}
+
+void CorrectTitleAndMatchPositions(
+    base::string16* title,
+    TitledUrlMatch::MatchPositions* title_match_positions) {
+  size_t leading_whitespace_chars = title->length();
+  base::TrimWhitespace(*title, base::TRIM_LEADING, title);
+  leading_whitespace_chars -= title->length();
+  if (leading_whitespace_chars == 0)
+    return;
+  for (TitledUrlMatch::MatchPositions::iterator it =
+           title_match_positions->begin();
+       it != title_match_positions->end(); ++it) {
+    it->first -= leading_whitespace_chars;
+    it->second -= leading_whitespace_chars;
+  }
+}
+
+}  // namespace bookmarks
diff --git a/components/omnibox/browser/titled_url_match_utils.h b/components/omnibox/browser/titled_url_match_utils.h
new file mode 100644
index 0000000..62bb694
--- /dev/null
+++ b/components/omnibox/browser/titled_url_match_utils.h
@@ -0,0 +1,42 @@
+// Copyright (c) 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_OMNIBOX_BROWSER_TITLED_URL_MATCH_UTILS_H_
+#define COMPONENTS_OMNIBOX_BROWSER_TITLED_URL_MATCH_UTILS_H_
+
+#include "base/strings/string16.h"
+#include "components/bookmarks/browser/titled_url_match.h"
+#include "components/omnibox/browser/autocomplete_match_type.h"
+
+class AutocompleteInput;
+class AutocompleteProvider;
+class AutocompleteSchemeClassifier;
+struct AutocompleteMatch;
+
+namespace bookmarks {
+
+// Compose an AutocompleteMatch based on |match| that has the match's URL and
+// page title, type |type|, and relevance score |relevance|. |input| is used to
+// compute the match's inline_autocompletion. |fixed_up_input_text| is used in
+// that way as well; it's passed separately so this function doesn't have to
+// compute it.
+AutocompleteMatch TitledUrlMatchToAutocompleteMatch(
+    const TitledUrlMatch& match,
+    AutocompleteMatchType::Type type,
+    int relevance,
+    AutocompleteProvider* provider,
+    const AutocompleteSchemeClassifier& scheme_classifier,
+    const AutocompleteInput& input,
+    const base::string16& fixed_up_input_text);
+
+// Removes leading spaces from |title| before displaying, otherwise it looks
+// funny. In the process, corrects |title_match_positions| so the correct
+// characters are highlighted.
+void CorrectTitleAndMatchPositions(
+    base::string16* title,
+    TitledUrlMatch::MatchPositions* title_match_positions);
+
+}  // namespace bookmarks
+
+#endif  // COMPONENTS_OMNIBOX_BROWSER_TITLED_URL_MATCH_UTILS_H_
diff --git a/components/omnibox/browser/titled_url_match_utils_unittest.cc b/components/omnibox/browser/titled_url_match_utils_unittest.cc
new file mode 100644
index 0000000..bc51287
--- /dev/null
+++ b/components/omnibox/browser/titled_url_match_utils_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 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/omnibox/browser/titled_url_match_utils.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/bookmarks/browser/titled_url_match.h"
+#include "components/bookmarks/browser/titled_url_node.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/autocomplete_provider.h"
+#include "components/omnibox/browser/test_scheme_classifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using bookmarks::TitledUrlMatchToAutocompleteMatch;
+using bookmarks::CorrectTitleAndMatchPositions;
+
+namespace {
+
+// A simple AutocompleteProvider that does nothing.
+class MockAutocompleteProvider : public AutocompleteProvider {
+ public:
+  MockAutocompleteProvider(Type type) : AutocompleteProvider(type) {}
+
+  void Start(const AutocompleteInput& input, bool minimal_changes) override {}
+
+ private:
+  ~MockAutocompleteProvider() override {}
+};
+
+class MockTitledUrlNode : public bookmarks::TitledUrlNode {
+ public:
+  MockTitledUrlNode(const base::string16& title, const GURL& url)
+      : title_(title), url_(url) {}
+
+  // TitledUrlNode
+  const base::string16& GetTitledUrlNodeTitle() const override {
+    return title_;
+  }
+  const GURL& GetTitledUrlNodeUrl() const override { return url_; }
+
+ private:
+  base::string16 title_;
+  GURL url_;
+};
+
+}  // namespace
+
+bool operator==(const ACMatchClassification& lhs,
+                const ACMatchClassification& rhs) {
+  return (lhs.offset == rhs.offset) && (lhs.style == rhs.style);
+}
+
+TEST(TitledUrlMatchUtilsTest, TitledUrlMatchToAutocompleteMatch) {
+  base::string16 input_text(base::ASCIIToUTF16("goo"));
+  base::string16 match_title(base::ASCIIToUTF16("Google Search"));
+  base::string16 match_url_string(
+      base::ASCIIToUTF16("https://www.google.com/"));
+  GURL match_url(match_url_string);
+  bookmarks::TitledUrlMatch::MatchPositions title_match_positions = {{0, 3}};
+  bookmarks::TitledUrlMatch::MatchPositions url_match_positions = {{12, 15}};
+  AutocompleteMatchType::Type type = AutocompleteMatchType::BOOKMARK_TITLE;
+  int relevance = 123;
+
+  MockTitledUrlNode node(match_title, match_url);
+  bookmarks::TitledUrlMatch titled_url_match;
+  titled_url_match.node = &node;
+  titled_url_match.title_match_positions = title_match_positions;
+  titled_url_match.url_match_positions = url_match_positions;
+
+  scoped_refptr<MockAutocompleteProvider> provider =
+      new MockAutocompleteProvider(AutocompleteProvider::Type::TYPE_BOOKMARK);
+  TestSchemeClassifier classifier;
+  AutocompleteInput input(input_text, base::string16::npos, std::string(),
+                          GURL(), metrics::OmniboxEventProto::NTP, false, false,
+                          true, true, false, classifier);
+  const base::string16 fixed_up_input(input_text);
+
+  AutocompleteMatch autocomplete_match = TitledUrlMatchToAutocompleteMatch(
+      titled_url_match, type, relevance, provider.get(), classifier, input,
+      fixed_up_input);
+
+  ACMatchClassifications expected_contents_class = {
+      {0, ACMatchClassification::URL},
+      {12, ACMatchClassification::URL | ACMatchClassification::MATCH},
+      {15, ACMatchClassification::URL},
+  };
+  ACMatchClassifications expected_description_class = {
+      {0, ACMatchClassification::MATCH}, {3, ACMatchClassification::NONE},
+  };
+  base::string16 expected_inline_autocompletion(base::ASCIIToUTF16("gle.com"));
+  base::string16 expected_contents(
+      base::ASCIIToUTF16("https://www.google.com"));
+
+  EXPECT_EQ(provider.get(), autocomplete_match.provider);
+  EXPECT_EQ(type, autocomplete_match.type);
+  EXPECT_EQ(relevance, autocomplete_match.relevance);
+  EXPECT_EQ(match_url, autocomplete_match.destination_url);
+  EXPECT_EQ(expected_contents, autocomplete_match.contents);
+  EXPECT_TRUE(std::equal(expected_contents_class.begin(),
+                         expected_contents_class.end(),
+                         autocomplete_match.contents_class.begin()));
+  EXPECT_EQ(match_title, autocomplete_match.description);
+  EXPECT_TRUE(std::equal(expected_description_class.begin(),
+                         expected_description_class.end(),
+                         autocomplete_match.description_class.begin()));
+  EXPECT_EQ(expected_contents, autocomplete_match.fill_into_edit);
+  EXPECT_TRUE(autocomplete_match.allowed_to_be_default_match);
+  EXPECT_EQ(expected_inline_autocompletion,
+            autocomplete_match.inline_autocompletion);
+}
+
+TEST(TitledUrlMatchUtilsTest, EmptyInlineAutocompletion) {
+  // The search term matches the title but not the URL. Since there is no URL
+  // match, the inline autocompletion string will be empty.
+  base::string16 input_text(base::ASCIIToUTF16("goo"));
+  base::string16 match_title(base::ASCIIToUTF16("Email by Google"));
+  base::string16 match_url_string(base::ASCIIToUTF16("https://www.gmail.com/"));
+  GURL match_url(match_url_string);
+  bookmarks::TitledUrlMatch::MatchPositions title_match_positions = {{9, 12}};
+  bookmarks::TitledUrlMatch::MatchPositions url_match_positions;
+  AutocompleteMatchType::Type type = AutocompleteMatchType::BOOKMARK_TITLE;
+  int relevance = 123;
+
+  MockTitledUrlNode node(match_title, match_url);
+  bookmarks::TitledUrlMatch titled_url_match;
+  titled_url_match.node = &node;
+  titled_url_match.title_match_positions = title_match_positions;
+  titled_url_match.url_match_positions = url_match_positions;
+
+  scoped_refptr<MockAutocompleteProvider> provider =
+      new MockAutocompleteProvider(AutocompleteProvider::Type::TYPE_BOOKMARK);
+  TestSchemeClassifier classifier;
+  AutocompleteInput input(input_text, base::string16::npos, std::string(),
+                          GURL(), metrics::OmniboxEventProto::NTP, false, false,
+                          true, true, false, classifier);
+  const base::string16 fixed_up_input(input_text);
+
+  AutocompleteMatch autocomplete_match = TitledUrlMatchToAutocompleteMatch(
+      titled_url_match, type, relevance, provider.get(), classifier, input,
+      fixed_up_input);
+
+  ACMatchClassifications expected_contents_class = {
+      {0, ACMatchClassification::URL},
+  };
+  ACMatchClassifications expected_description_class = {
+      {0, ACMatchClassification::NONE},
+      {9, ACMatchClassification::MATCH},
+      {12, ACMatchClassification::NONE},
+  };
+  base::string16 expected_contents(base::ASCIIToUTF16("https://www.gmail.com"));
+
+  EXPECT_EQ(provider.get(), autocomplete_match.provider);
+  EXPECT_EQ(type, autocomplete_match.type);
+  EXPECT_EQ(relevance, autocomplete_match.relevance);
+  EXPECT_EQ(match_url, autocomplete_match.destination_url);
+  EXPECT_EQ(expected_contents, autocomplete_match.contents);
+  EXPECT_TRUE(std::equal(expected_contents_class.begin(),
+                         expected_contents_class.end(),
+                         autocomplete_match.contents_class.begin()));
+  EXPECT_EQ(match_title, autocomplete_match.description);
+  EXPECT_TRUE(std::equal(expected_description_class.begin(),
+                         expected_description_class.end(),
+                         autocomplete_match.description_class.begin()));
+  EXPECT_EQ(expected_contents, autocomplete_match.fill_into_edit);
+  EXPECT_FALSE(autocomplete_match.allowed_to_be_default_match);
+  EXPECT_TRUE(autocomplete_match.inline_autocompletion.empty());
+}
+
+TEST(TitledUrlMatchUtilsTest, CorrectTitleAndMatchPositions) {
+  bookmarks::TitledUrlMatch::MatchPositions match_positions = {{2, 6},
+                                                               {10, 15}};
+  base::string16 title = base::ASCIIToUTF16("  Leading whitespace");
+  bookmarks::TitledUrlMatch::MatchPositions expected_match_positions = {
+      {0, 4}, {8, 13}};
+  base::string16 expected_title = base::ASCIIToUTF16("Leading whitespace");
+  CorrectTitleAndMatchPositions(&title, &match_positions);
+  EXPECT_EQ(expected_title, title);
+  EXPECT_TRUE(std::equal(match_positions.begin(), match_positions.end(),
+                         expected_match_positions.begin()));
+}
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index afd0f877..15c30f1 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -19,6 +19,7 @@
 #include "components/sync/driver/data_type_encryption_handler.h"
 #include "components/sync/driver/data_type_manager_observer.h"
 #include "components/sync/driver/data_type_status_table.h"
+#include "components/sync/driver/sync_client.h"
 #include "components/sync/engine/data_type_debug_info_listener.h"
 
 namespace syncer {
@@ -44,13 +45,15 @@
 DataTypeManagerImpl::AssociationTypesInfo::~AssociationTypesInfo() {}
 
 DataTypeManagerImpl::DataTypeManagerImpl(
+    SyncClient* sync_client,
     ModelTypeSet initial_types,
     const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
     const DataTypeController::TypeMap* controllers,
     const DataTypeEncryptionHandler* encryption_handler,
     ModelTypeConfigurer* configurer,
     DataTypeManagerObserver* observer)
-    : configurer_(configurer),
+    : sync_client_(sync_client),
+      configurer_(configurer),
       controllers_(controllers),
       state_(DataTypeManager::STOPPED),
       needs_reconfigure_(false),
@@ -78,17 +81,19 @@
 
   desired_types.PutAll(CoreTypes());
 
-  // Only allow control types and types that have controllers.
-  ModelTypeSet filtered_desired_types;
-  for (ModelTypeSet::Iterator type = desired_types.First(); type.Good();
-       type.Inc()) {
-    DataTypeController::TypeMap::const_iterator iter =
-        controllers_->find(type.Get());
-    if (IsControlType(type.Get()) || iter != controllers_->end()) {
-      filtered_desired_types.Put(type.Get());
-    }
+  ModelTypeSet allowed_types = ControlTypes();
+  // Add types with controllers.
+  for (const auto& kv : *controllers_) {
+    allowed_types.Put(kv.first);
   }
-  ConfigureImpl(filtered_desired_types, reason);
+
+  // Remove types we cannot sync.
+  if (!sync_client_->HasPasswordStore())
+    allowed_types.Remove(PASSWORDS);
+  if (!sync_client_->GetHistoryService())
+    allowed_types.Remove(TYPED_URLS);
+
+  ConfigureImpl(Intersection(desired_types, allowed_types), reason);
 }
 
 void DataTypeManagerImpl::ReenableType(ModelType type) {
diff --git a/components/sync/driver/data_type_manager_impl.h b/components/sync/driver/data_type_manager_impl.h
index dd6f3ce4..b20f75b 100644
--- a/components/sync/driver/data_type_manager_impl.h
+++ b/components/sync/driver/data_type_manager_impl.h
@@ -26,6 +26,7 @@
 class DataTypeDebugInfoListener;
 class DataTypeEncryptionHandler;
 class DataTypeManagerObserver;
+class SyncClient;
 struct DataTypeConfigurationStats;
 
 // List of data types grouped by priority and ordered from high priority to
@@ -36,6 +37,7 @@
                             public ModelAssociationManagerDelegate {
  public:
   DataTypeManagerImpl(
+      SyncClient* sync_client,
       ModelTypeSet initial_types,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       const DataTypeController::TypeMap* controllers,
@@ -142,7 +144,9 @@
   // Returns the currently enabled types.
   ModelTypeSet GetEnabledTypes() const;
 
+  SyncClient* sync_client_;
   ModelTypeConfigurer* configurer_;
+
   // Map of all data type controllers that are available for sync.
   // This list is determined at startup by various command line flags.
   const DataTypeController::TypeMap* controllers_;
diff --git a/components/sync/driver/data_type_manager_impl_unittest.cc b/components/sync/driver/data_type_manager_impl_unittest.cc
index 039443f..b1d450ae 100644
--- a/components/sync/driver/data_type_manager_impl_unittest.cc
+++ b/components/sync/driver/data_type_manager_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/sync/driver/data_type_manager_observer.h"
 #include "components/sync/driver/data_type_status_table.h"
 #include "components/sync/driver/fake_data_type_controller.h"
+#include "components/sync/driver/fake_sync_client.h"
 #include "components/sync/engine/activation_context.h"
 #include "components/sync/engine/configure_reason.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -60,6 +61,11 @@
   return status_table;
 }
 
+class TestSyncClient : public FakeSyncClient {
+ public:
+  bool HasPasswordStore() override { return true; }
+};
+
 // Fake ModelTypeConfigurer implementation that simply stores away the
 // callback passed into ConfigureDataTypes.
 class FakeModelTypeConfigurer : public ModelTypeConfigurer {
@@ -216,20 +222,7 @@
 
 class TestDataTypeManager : public DataTypeManagerImpl {
  public:
-  TestDataTypeManager(
-      ModelTypeSet initial_types,
-      const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
-      ModelTypeConfigurer* configurer,
-      const DataTypeController::TypeMap* controllers,
-      const DataTypeEncryptionHandler* encryption_handler,
-      DataTypeManagerObserver* observer)
-      : DataTypeManagerImpl(initial_types,
-                            debug_info_listener,
-                            controllers,
-                            encryption_handler,
-                            configurer,
-                            observer),
-        custom_priority_types_(ControlTypes()) {}
+  using DataTypeManagerImpl::DataTypeManagerImpl;
 
   void set_priority_types(const ModelTypeSet& priority_types) {
     custom_priority_types_ = priority_types;
@@ -250,7 +243,7 @@
     return custom_priority_types_;
   }
 
-  ModelTypeSet custom_priority_types_;
+  ModelTypeSet custom_priority_types_ = ControlTypes();
   DataTypeManager::ConfigureResult configure_result_;
 };
 
@@ -265,8 +258,8 @@
  protected:
   void SetUp() override {
     dtm_ = base::MakeUnique<TestDataTypeManager>(
-        ModelTypeSet(), WeakHandle<DataTypeDebugInfoListener>(), &configurer_,
-        &controllers_, &encryption_handler_, &observer_);
+        &sync_client_, ModelTypeSet(), WeakHandle<DataTypeDebugInfoListener>(),
+        &controllers_, &encryption_handler_, &configurer_, &observer_);
   }
 
   void SetConfigureStartExpectation() { observer_.ExpectStart(); }
@@ -333,6 +326,7 @@
 
   base::MessageLoopForUI ui_loop_;
   DataTypeController::TypeMap controllers_;
+  TestSyncClient sync_client_;
   FakeModelTypeConfigurer configurer_;
   FakeDataTypeManagerObserver observer_;
   std::unique_ptr<TestDataTypeManager> dtm_;
diff --git a/components/sync/driver/fake_sync_client.cc b/components/sync/driver/fake_sync_client.cc
index 3b59eab29..16a46df 100644
--- a/components/sync/driver/fake_sync_client.cc
+++ b/components/sync/driver/fake_sync_client.cc
@@ -60,6 +60,10 @@
   return nullptr;
 }
 
+bool FakeSyncClient::HasPasswordStore() {
+  return false;
+}
+
 base::Closure FakeSyncClient::GetPasswordStateChangedCallback() {
   return base::Bind(&base::DoNothing);
 }
diff --git a/components/sync/driver/fake_sync_client.h b/components/sync/driver/fake_sync_client.h
index b68999e..8a8928d 100644
--- a/components/sync/driver/fake_sync_client.h
+++ b/components/sync/driver/fake_sync_client.h
@@ -29,6 +29,7 @@
   bookmarks::BookmarkModel* GetBookmarkModel() override;
   favicon::FaviconService* GetFaviconService() override;
   history::HistoryService* GetHistoryService() override;
+  bool HasPasswordStore() override;
   base::Closure GetPasswordStateChangedCallback() override;
   SyncApiComponentFactory::RegisterDataTypesMethod
   GetRegisterPlatformTypesCallback() override;
diff --git a/components/sync/driver/sync_client.h b/components/sync/driver/sync_client.h
index c514e51..bf9ab4c 100644
--- a/components/sync/driver/sync_client.h
+++ b/components/sync/driver/sync_client.h
@@ -71,6 +71,7 @@
   virtual bookmarks::BookmarkModel* GetBookmarkModel() = 0;
   virtual favicon::FaviconService* GetFaviconService() = 0;
   virtual history::HistoryService* GetHistoryService() = 0;
+  virtual bool HasPasswordStore() = 0;
 
   // Returns a callback that will register the types specific to the current
   // platform.
diff --git a/content/browser/android/child_process_launcher_android.cc b/content/browser/android/child_process_launcher_android.cc
index 77b45d5..a196b1a 100644
--- a/content/browser/android/child_process_launcher_android.cc
+++ b/content/browser/android/child_process_launcher_android.cc
@@ -113,7 +113,6 @@
     const base::CommandLine::StringVector& argv,
     int child_process_id,
     std::unique_ptr<content::FileDescriptorInfo> files_to_register,
-    const std::map<int, base::MemoryMappedFile::Region>& regions,
     const StartChildProcessCallback& callback) {
   JNIEnv* env = AttachCurrentThread();
   DCHECK(env);
@@ -135,13 +134,10 @@
     PCHECK(0 <= fd);
     int id = files_to_register->GetIDAt(i);
     bool auto_close = files_to_register->OwnsFD(fd);
-    int64_t offset = 0L;
-    int64_t size = 0L;
-    auto found_region_iter = regions.find(id);
-    if (found_region_iter != regions.end()) {
-      offset = found_region_iter->second.offset;
-      size = found_region_iter->second.size;
-    }
+    const base::MemoryMappedFile::Region& region =
+        files_to_register->GetRegionAt(i);
+    int64_t offset = region.offset;
+    int64_t size = region.size;
     ScopedJavaLocalRef<jobject> j_file_info =
         Java_ChildProcessLauncher_makeFdInfo(env, id, fd, auto_close, offset,
                                              size);
diff --git a/content/browser/android/child_process_launcher_android.h b/content/browser/android/child_process_launcher_android.h
index b99fbd7..78f088a6 100644
--- a/content/browser/android/child_process_launcher_android.h
+++ b/content/browser/android/child_process_launcher_android.h
@@ -29,7 +29,6 @@
     const base::CommandLine::StringVector& argv,
     int child_process_id,
     const std::unique_ptr<FileDescriptorInfo> files_to_register,
-    const std::map<int, base::MemoryMappedFile::Region>& regions,
     const StartChildProcessCallback& callback);
 
 // Stops a child process based on the handle returned form
diff --git a/content/browser/battery_status/battery_monitor_integration_browsertest.cc b/content/browser/battery_status/battery_monitor_integration_browsertest.cc
index e024d6d7..9856a66 100644
--- a/content/browser/battery_status/battery_monitor_integration_browsertest.cc
+++ b/content/browser/battery_status/battery_monitor_integration_browsertest.cc
@@ -109,10 +109,9 @@
   void GetAdditionalMappedFilesForChildProcess(
       const base::CommandLine& command_line,
       int child_process_id,
-      FileDescriptorInfo* mappings,
-      std::map<int, base::MemoryMappedFile::Region>* regions) override {
+      FileDescriptorInfo* mappings) override {
     ShellContentBrowserClient::Get()->GetAdditionalMappedFilesForChildProcess(
-        command_line, child_process_id, mappings, regions);
+        command_line, child_process_id, mappings);
   }
 #endif  // defined(OS_ANDROID)
 };
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index e93bb08..51f5696 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -174,23 +174,15 @@
 #endif
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
-  std::map<int, base::MemoryMappedFile::Region> regions;
   GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
-      *cmd_line, child_process_id, files_to_register.get()
-#if defined(OS_ANDROID)
-      , &regions
-#endif
-      );
+      *cmd_line, child_process_id, files_to_register.get());
 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
   bool snapshot_loaded = false;
-#if defined(OS_ANDROID)
   base::MemoryMappedFile::Region region;
-  auto maybe_register = [&region, &regions, &files_to_register](int key,
-                                                                int fd) {
-    if (fd != -1) {
-      files_to_register->Share(key, fd);
-      regions.insert(std::make_pair(key, region));
-    }
+#if defined(OS_ANDROID)
+  auto maybe_register = [&region, &files_to_register](int key, int fd) {
+    if (fd != -1)
+      files_to_register->ShareWithRegion(key, fd, region);
   };
   maybe_register(
       kV8NativesDataDescriptor,
@@ -205,21 +197,19 @@
   snapshot_loaded = true;
 #else
   base::PlatformFile natives_pf =
-      gin::V8Initializer::GetOpenNativesFileForChildProcesses(
-          &regions[kV8NativesDataDescriptor]);
+      gin::V8Initializer::GetOpenNativesFileForChildProcesses(&region);
   DCHECK_GE(natives_pf, 0);
-  files_to_register->Share(kV8NativesDataDescriptor, natives_pf);
+  files_to_register->ShareWithRegion(
+      kV8NativesDataDescriptor, natives_pf, region);
 
-  base::MemoryMappedFile::Region snapshot_region;
   base::PlatformFile snapshot_pf =
-      gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(
-          &snapshot_region);
+      gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(&region);
   // Failure to load the V8 snapshot is not necessarily an error. V8 can start
   // up (slower) without the snapshot.
   if (snapshot_pf != -1) {
     snapshot_loaded = true;
-    files_to_register->Share(kV8SnapshotDataDescriptor, snapshot_pf);
-    regions.insert(std::make_pair(kV8SnapshotDataDescriptor, snapshot_region));
+    files_to_register->ShareWithRegion(
+        kV8SnapshotDataDescriptor, snapshot_pf, region);
   }
 #endif
 
@@ -234,9 +224,10 @@
 
 #if defined(OS_ANDROID)
 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
-  files_to_register->Share(
-      kAndroidICUDataDescriptor,
-      base::i18n::GetIcuDataFileHandle(&regions[kAndroidICUDataDescriptor]));
+  base::MemoryMappedFile::Region icu_region;
+  base::PlatformFile icu_pf = base::i18n::GetIcuDataFileHandle(&icu_region);
+  files_to_register->ShareWithRegion(
+      kAndroidICUDataDescriptor, icu_pf, icu_region);
 #endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
 
   // Android WebView runs in single process, ensure that we never get here
@@ -244,7 +235,7 @@
   CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
 
   StartChildProcess(
-      cmd_line->argv(), child_process_id, std::move(files_to_register), regions,
+      cmd_line->argv(), child_process_id, std::move(files_to_register),
       base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id,
                  begin_launch_time, base::Passed(&mojo_fd)));
 
diff --git a/content/browser/compositor/software_output_device_win.cc b/content/browser/compositor/software_output_device_win.cc
index 0a1bc20..4cb2d87 100644
--- a/content/browser/compositor/software_output_device_win.cc
+++ b/content/browser/compositor/software_output_device_win.cc
@@ -9,6 +9,7 @@
 #include "cc/resources/shared_bitmap.h"
 #include "content/public/browser/browser_thread.h"
 #include "skia/ext/platform_canvas.h"
+#include "skia/ext/skia_utils_win.h"
 #include "ui/compositor/compositor.h"
 #include "ui/gfx/gdi_util.h"
 #include "ui/gfx/skia_util.h"
@@ -165,6 +166,8 @@
   if (rect.IsEmpty())
     return;
 
+  HDC dib_dc = skia::GetNativeDrawingContext(contents_.get());
+
   if (is_hwnd_composited_) {
     RECT wr;
     GetWindowRect(hwnd_, &wr);
@@ -178,14 +181,19 @@
     style |= WS_EX_LAYERED;
     SetWindowLong(hwnd_, GWL_EXSTYLE, style);
 
-    HDC dib_dc = skia::GetNativeDrawingContext(contents_.get());
     ::UpdateLayeredWindow(hwnd_, NULL, &position, &size, dib_dc, &zero,
                           RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA);
   } else {
     HDC hdc = ::GetDC(hwnd_);
     RECT src_rect = rect.ToRECT();
-    skia::DrawToNativeContext(contents_.get(), hdc, rect.x(), rect.y(),
-                              &src_rect);
+    skia::CopyHDC(dib_dc,
+                  hdc,
+                  rect.x(),
+                  rect.y(),
+                  contents_.get()->imageInfo().isOpaque(),
+                  src_rect,
+                  contents_.get()->getTotalMatrix());
+
     ::ReleaseDC(hwnd_, hdc);
   }
 }
diff --git a/content/browser/file_descriptor_info_impl.cc b/content/browser/file_descriptor_info_impl.cc
index bfab1438..3392819 100644
--- a/content/browser/file_descriptor_info_impl.cc
+++ b/content/browser/file_descriptor_info_impl.cc
@@ -22,11 +22,16 @@
 }
 
 void FileDescriptorInfoImpl::Share(int id, base::PlatformFile fd) {
-  AddToMapping(id, fd);
+  ShareWithRegion(id, fd, base::MemoryMappedFile::Region::kWholeFile);
+}
+
+void FileDescriptorInfoImpl::ShareWithRegion(int id, base::PlatformFile fd,
+    const base::MemoryMappedFile::Region& region) {
+  AddToMapping(id, fd, region);
 }
 
 void FileDescriptorInfoImpl::Transfer(int id, base::ScopedFD fd) {
-  AddToMapping(id, fd.get());
+  AddToMapping(id, fd.get(), base::MemoryMappedFile::Region::kWholeFile);
   owned_descriptors_.push_back(std::move(fd));
 }
 
@@ -38,6 +43,13 @@
   return mapping_[i].second;
 }
 
+const base::MemoryMappedFile::Region& FileDescriptorInfoImpl::GetRegionAt(
+    size_t i) const {
+  auto iter = ids_to_regions_.find(GetIDAt(i));
+  return (iter != ids_to_regions_.end()) ?
+      iter->second : base::MemoryMappedFile::Region::kWholeFile;
+}
+
 size_t FileDescriptorInfoImpl::GetMappingSize() const {
   return mapping_.size();
 }
@@ -68,9 +80,12 @@
   return fd;
 }
 
-void FileDescriptorInfoImpl::AddToMapping(int id, base::PlatformFile fd) {
+void FileDescriptorInfoImpl::AddToMapping(int id, base::PlatformFile fd,
+    const base::MemoryMappedFile::Region& region) {
   DCHECK(!HasID(id));
   mapping_.push_back(std::make_pair(fd, id));
+  if (region != base::MemoryMappedFile::Region::kWholeFile)
+    ids_to_regions_[id] = region;
 }
 
 const base::FileHandleMappingVector& FileDescriptorInfoImpl::GetMapping()
diff --git a/content/browser/file_descriptor_info_impl.h b/content/browser/file_descriptor_info_impl.h
index 010e797..eba0937 100644
--- a/content/browser/file_descriptor_info_impl.h
+++ b/content/browser/file_descriptor_info_impl.h
@@ -7,9 +7,11 @@
 
 #include <stddef.h>
 
+#include <map>
 #include <memory>
 #include <vector>
 
+#include "base/files/memory_mapped_file.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/file_descriptor_info.h"
 
@@ -21,12 +23,15 @@
 
   ~FileDescriptorInfoImpl() override;
   void Share(int id, base::PlatformFile fd) override;
+  void ShareWithRegion(int id, base::PlatformFile fd,
+      const base::MemoryMappedFile::Region& region) override;
   void Transfer(int id, base::ScopedFD fd) override;
   const base::FileHandleMappingVector& GetMapping() const override;
   base::FileHandleMappingVector GetMappingWithIDAdjustment(
       int delta) const override;
   base::PlatformFile GetFDAt(size_t i) const override;
   int GetIDAt(size_t i) const override;
+  const base::MemoryMappedFile::Region& GetRegionAt(size_t i) const override;
   size_t GetMappingSize() const override;
   bool OwnsFD(base::PlatformFile file) const override;
   base::ScopedFD ReleaseFD(base::PlatformFile file) override;
@@ -34,9 +39,13 @@
  private:
   FileDescriptorInfoImpl();
 
-  void AddToMapping(int id, base::PlatformFile fd);
+  void AddToMapping(int id, base::PlatformFile fd,
+      const base::MemoryMappedFile::Region& region);
   bool HasID(int id) const;
   base::FileHandleMappingVector mapping_;
+  // Maps the ID of a FD to the region to use for that FD, the whole file if not
+  // in the map.
+  std::map<int, base::MemoryMappedFile::Region> ids_to_regions_;
   std::vector<base::ScopedFD> owned_descriptors_;
 };
 }
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 5bde4bb..06c2fb1 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -1240,9 +1240,11 @@
   if (entry->update_virtual_url_with_url())
     UpdateVirtualURLToURL(entry, params.url);
 
-  // The site instance will normally be the same except during session restore,
-  // when no site instance will be assigned.
+  // The site instance will normally be the same except
+  // 1) session restore, when no site instance will be assigned or
+  // 2) redirect, when the site instance is reset.
   DCHECK(entry->site_instance() == nullptr ||
+         !entry->GetRedirectChain().empty() ||
          entry->site_instance() == rfh->GetSiteInstance());
 
   // Update the existing FrameNavigationEntry to ensure all of its members
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 637dec0..2fb7893 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -2196,12 +2196,9 @@
     subframe->PrepareForCommit();
     subframe->SendNavigateWithParams(&params);
 
-    // In UseSubframeNavigationEntries mode, we notify of a PageState update to
-    // the entry here rather than during UpdateState.
-    if (SiteIsolationPolicy::UseSubframeNavigationEntries())
-      EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
-    else
-      EXPECT_EQ(0U, notifications.size());
+    // We notify of a PageState update here rather than during UpdateState for
+    // auto subframe navigations.
+    EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
   }
 
   // Now do a new navigation in the frame.
@@ -2236,15 +2233,9 @@
   // reflect the toplevel URL).
   EXPECT_EQ(url1, entry->GetURL());
 
-  // Verify subframe entries if they're enabled (e.g. in --site-per-process).
-  if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
-    // The entry should have a subframe FrameNavigationEntry.
-    ASSERT_EQ(1U, entry->root_node()->children.size());
-    EXPECT_EQ(url2, entry->root_node()->children[0]->frame_entry->url());
-  } else {
-    // There are no subframe FrameNavigationEntries by default.
-    EXPECT_EQ(0U, entry->root_node()->children.size());
-  }
+  // The entry should have a subframe FrameNavigationEntry.
+  ASSERT_EQ(1U, entry->root_node()->children.size());
+  EXPECT_EQ(url2, entry->root_node()->children[0]->frame_entry->url());
 }
 
 // Auto subframes are ones the page loads automatically like ads. They should
@@ -2286,12 +2277,9 @@
     subframe->PrepareForCommit();
     subframe->SendNavigateWithParams(&params);
 
-    // In UseSubframeNavigationEntries mode, we notify of a PageState update to
-    // the entry here rather than during UpdateState.
-    if (SiteIsolationPolicy::UseSubframeNavigationEntries())
-      EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
-    else
-      EXPECT_EQ(0U, notifications.size());
+    // We notify of a PageState update here rather than during UpdateState for
+    // auto subframe navigations.
+    EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
   }
 
   // There should still be only one entry.
@@ -2301,17 +2289,11 @@
   FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
   EXPECT_EQ(url1, root_entry->url());
 
-  // Verify subframe entries if they're enabled (e.g. in --site-per-process).
-  if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
-    // The entry should now have a subframe FrameNavigationEntry.
-    ASSERT_EQ(1U, entry->root_node()->children.size());
-    FrameNavigationEntry* frame_entry =
-        entry->root_node()->children[0]->frame_entry.get();
-    EXPECT_EQ(url2, frame_entry->url());
-  } else {
-    // There are no subframe FrameNavigationEntries by default.
-    EXPECT_EQ(0U, entry->root_node()->children.size());
-  }
+  // The entry should now have a subframe FrameNavigationEntry.
+  ASSERT_EQ(1U, entry->root_node()->children.size());
+  FrameNavigationEntry* frame_entry =
+      entry->root_node()->children[0]->frame_entry.get();
+  EXPECT_EQ(url2, frame_entry->url());
 
   // Add a second subframe and navigate.
   std::string unique_name1("uniqueName1");
@@ -2339,12 +2321,9 @@
     subframe2->PrepareForCommit();
     subframe2->SendNavigateWithParams(&params);
 
-    // In UseSubframeNavigationEntries mode, we notify of a PageState update to
-    // the entry here rather than during UpdateState.
-    if (SiteIsolationPolicy::UseSubframeNavigationEntries())
-      EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
-    else
-      EXPECT_EQ(0U, notifications.size());
+    // We notify of a PageState update here rather than during UpdateState for
+    // auto subframe navigations.
+    EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
   }
 
   // There should still be only one entry, mostly unchanged.
@@ -2354,17 +2333,11 @@
   EXPECT_EQ(root_entry, entry->root_node()->frame_entry.get());
   EXPECT_EQ(url1, root_entry->url());
 
-  // Verify subframe entries if they're enabled (e.g. in --site-per-process).
-  if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
-    // The entry should now have 2 subframe FrameNavigationEntries.
-    ASSERT_EQ(2U, entry->root_node()->children.size());
-    FrameNavigationEntry* new_frame_entry =
-        entry->root_node()->children[1]->frame_entry.get();
-    EXPECT_EQ(url3, new_frame_entry->url());
-  } else {
-    // There are no subframe FrameNavigationEntries by default.
-    EXPECT_EQ(0U, entry->root_node()->children.size());
-  }
+  // The entry should now have 2 subframe FrameNavigationEntries.
+  ASSERT_EQ(2U, entry->root_node()->children.size());
+  FrameNavigationEntry* new_frame_entry =
+      entry->root_node()->children[1]->frame_entry.get();
+  EXPECT_EQ(url3, new_frame_entry->url());
 
   // Add a nested subframe and navigate.
   std::string unique_name2("uniqueName2");
@@ -2397,12 +2370,9 @@
     subframe3->PrepareForCommit();
     subframe3->SendNavigateWithParams(&params);
 
-    // In UseSubframeNavigationEntries mode, we notify of a PageState update to
-    // the entry here rather than during UpdateState.
-    if (SiteIsolationPolicy::UseSubframeNavigationEntries())
-      EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
-    else
-      EXPECT_EQ(0U, notifications.size());
+    // We notify of a PageState update here rather than during UpdateState for
+    // auto subframe navigations.
+    EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
   }
 
   // There should still be only one entry, mostly unchanged.
@@ -2412,18 +2382,12 @@
   EXPECT_EQ(root_entry, entry->root_node()->frame_entry.get());
   EXPECT_EQ(url1, root_entry->url());
 
-  // Verify subframe entries if they're enabled (e.g. in --site-per-process).
-  if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
-    // The entry should now have a nested FrameNavigationEntry.
-    EXPECT_EQ(2U, entry->root_node()->children.size());
-    ASSERT_EQ(1U, entry->root_node()->children[0]->children.size());
-    FrameNavigationEntry* new_frame_entry =
-        entry->root_node()->children[0]->children[0]->frame_entry.get();
-    EXPECT_EQ(url4, new_frame_entry->url());
-  } else {
-    // There are no subframe FrameNavigationEntries by default.
-    EXPECT_EQ(0U, entry->root_node()->children.size());
-  }
+  // The entry should now have a nested FrameNavigationEntry.
+  EXPECT_EQ(2U, entry->root_node()->children.size());
+  ASSERT_EQ(1U, entry->root_node()->children[0]->children.size());
+  new_frame_entry =
+      entry->root_node()->children[0]->children[0]->frame_entry.get();
+  EXPECT_EQ(url4, new_frame_entry->url());
 }
 
 // Tests navigation and then going back to a subframe navigation.
@@ -2473,12 +2437,9 @@
     subframe->PrepareForCommit();
     subframe->SendNavigateWithParams(&params);
 
-    // In UseSubframeNavigationEntries mode, we notify of a PageState update to
-    // the entry here rather than during UpdateState.
-    if (SiteIsolationPolicy::UseSubframeNavigationEntries())
-      EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
-    else
-      EXPECT_EQ(0U, notifications.size());
+    // We notify of a PageState update here rather than during UpdateState for
+    // auto subframe navigations.
+    EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_ENTRY_CHANGED));
   }
 
   // First manual subframe navigation.
@@ -2508,16 +2469,9 @@
   navigation_entry_committed_counter_ = 0;
   EXPECT_EQ(2, controller.GetEntryCount());
 
-  // Verify subframe entries if they're enabled (e.g. in --site-per-process).
-  if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
-    // The entry should have a subframe FrameNavigationEntry.
-    ASSERT_EQ(1U, entry2->root_node()->children.size());
-    EXPECT_EQ(url2, entry2->root_node()->children[0]->frame_entry->url());
-  } else {
-    // There are no subframe FrameNavigationEntries by default.
-    EXPECT_EQ(0U, entry2->root_node()->children.size());
-  }
-
+  // The entry should have a subframe FrameNavigationEntry.
+  ASSERT_EQ(1U, entry2->root_node()->children.size());
+  EXPECT_EQ(url2, entry2->root_node()->children[0]->frame_entry->url());
 
   // Second manual subframe navigation should also make a new entry.
   const GURL url3("http://foo3");
@@ -2541,15 +2495,9 @@
   EXPECT_EQ(3, controller.GetEntryCount());
   EXPECT_EQ(2, controller.GetCurrentEntryIndex());
 
-  // Verify subframe entries if they're enabled (e.g. in --site-per-process).
-  if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
-    // The entry should have a subframe FrameNavigationEntry.
-    ASSERT_EQ(1U, entry3->root_node()->children.size());
-    EXPECT_EQ(url3, entry3->root_node()->children[0]->frame_entry->url());
-  } else {
-    // There are no subframe FrameNavigationEntries by default.
-    EXPECT_EQ(0U, entry3->root_node()->children.size());
-  }
+  // The entry should have a subframe FrameNavigationEntry.
+  ASSERT_EQ(1U, entry3->root_node()->children.size());
+  EXPECT_EQ(url3, entry3->root_node()->children[0]->frame_entry->url());
 
   // Go back one.
   controller.GoBack();
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 898679f2..8a1007d 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -403,6 +403,10 @@
 void NavigationRequest::OnRequestRedirected(
     const net::RedirectInfo& redirect_info,
     const scoped_refptr<ResourceResponse>& response) {
+  // If a redirect occurs, the original site instance we thought is the
+  // destination could change.
+  dest_site_instance_ = nullptr;
+
   // If the navigation is no longer a POST, the POST data should be reset.
   if (redirect_info.new_method != "POST")
     common_params_.post_data = nullptr;
diff --git a/content/browser/permissions/permission_service_context.cc b/content/browser/permissions/permission_service_context.cc
index cffb065..9292a31 100644
--- a/content/browser/permissions/permission_service_context.cc
+++ b/content/browser/permissions/permission_service_context.cc
@@ -27,7 +27,15 @@
         &PermissionSubscription::OnConnectionError, base::Unretained(this)));
   }
 
-  ~PermissionSubscription() = default;
+  ~PermissionSubscription() {
+    DCHECK_NE(id_, 0);
+    BrowserContext* browser_context = context_->GetBrowserContext();
+    DCHECK(browser_context);
+    if (browser_context->GetPermissionManager()) {
+      browser_context->GetPermissionManager()
+          ->UnsubscribePermissionStatusChange(id_);
+    }
+  }
 
   void OnConnectionError() {
     DCHECK_NE(id_, 0);
@@ -105,13 +113,6 @@
 }
 
 void PermissionServiceContext::ObserverHadConnectionError(int subscription_id) {
-  BrowserContext* browser_context = GetBrowserContext();
-  DCHECK(browser_context);
-  if (browser_context->GetPermissionManager()) {
-    browser_context->GetPermissionManager()->UnsubscribePermissionStatusChange(
-        subscription_id);
-  }
-
   auto it = subscriptions_.find(subscription_id);
   DCHECK(it != subscriptions_.end());
   subscriptions_.erase(it);
@@ -139,13 +140,15 @@
 }
 
 void PermissionServiceContext::CancelPendingOperations(
-    RenderFrameHost* render_frame_host) const {
+    RenderFrameHost* render_frame_host) {
   DCHECK(render_frame_host_);
   if (render_frame_host != render_frame_host_)
     return;
 
   for (const auto& service : services_)
     service->CancelPendingOperations();
+
+  subscriptions_.clear();
 }
 
 BrowserContext* PermissionServiceContext::GetBrowserContext() const {
diff --git a/content/browser/permissions/permission_service_context.h b/content/browser/permissions/permission_service_context.h
index 83274b4..11bcc5b 100644
--- a/content/browser/permissions/permission_service_context.h
+++ b/content/browser/permissions/permission_service_context.h
@@ -70,7 +70,7 @@
                            const LoadCommittedDetails& details,
                            const FrameNavigateParams& params) override;
 
-  void CancelPendingOperations(RenderFrameHost*) const;
+  void CancelPendingOperations(RenderFrameHost*);
 
   RenderFrameHost* render_frame_host_;
   RenderProcessHost* render_process_host_;
diff --git a/content/browser/permissions/permission_service_impl.cc b/content/browser/permissions/permission_service_impl.cc
index 75e8a6cd..90767d0 100644
--- a/content/browser/permissions/permission_service_impl.cc
+++ b/content/browser/permissions/permission_service_impl.cc
@@ -257,11 +257,6 @@
     last_known_status = current_status;
   }
 
-  BrowserContext* browser_context = context_->GetBrowserContext();
-  DCHECK(browser_context);
-  if (!browser_context->GetPermissionManager())
-    return;
-
   context_->CreateSubscription(PermissionDescriptorToPermissionType(permission),
                                origin, std::move(observer));
 }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 18976390..343b875 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -764,18 +764,12 @@
 
   // Populates |mappings| with all files that need to be mapped before launching
   // a child process.
-#if defined(OS_ANDROID)
-  virtual void GetAdditionalMappedFilesForChildProcess(
-      const base::CommandLine& command_line,
-      int child_process_id,
-      content::FileDescriptorInfo* mappings,
-      std::map<int, base::MemoryMappedFile::Region>* regions) {}
-#elif defined(OS_POSIX) && !defined(OS_MACOSX)
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
   virtual void GetAdditionalMappedFilesForChildProcess(
       const base::CommandLine& command_line,
       int child_process_id,
       content::FileDescriptorInfo* mappings) {}
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
 
 #if defined(OS_WIN)
   // This is called on the PROCESS_LAUNCHER thread before the renderer process
diff --git a/content/public/browser/file_descriptor_info.h b/content/public/browser/file_descriptor_info.h
index 255ac2c..4fc686b 100644
--- a/content/public/browser/file_descriptor_info.h
+++ b/content/public/browser/file_descriptor_info.h
@@ -8,45 +8,51 @@
 #include <stddef.h>
 
 #include "base/files/file.h"
+#include "base/files/memory_mapped_file.h"
 #include "base/process/launch.h"
 
 namespace content {
 
-// FileDescriptorInfo is a collection of file descriptors which is
-// needed to launch a process. You should tell FileDescriptorInfo
-// which FD should be closed and which shouldn't so that it can take care
-// of the lifetime of FDs.
+// FileDescriptorInfo is a collection of file descriptors which is needed to
+// launch a process. You should tell FileDescriptorInfo which FDs should be
+// closed and which shouldn't so that it can take care of the lifetime of FDs.
 //
-// See base/process/launcher.h for more details about launching a
-// process.
+// See base/process/launcher.h for more details about launching a process.
 class FileDescriptorInfo {
  public:
   virtual ~FileDescriptorInfo() {}
 
-  // Add an FD associated with an ID, without delegating the ownerhip
-  // of ID.
+  // Adds an FD associated with an ID, without delegating the ownerhip of ID.
   virtual void Share(int id, base::PlatformFile fd) = 0;
 
-  // Add an FD associated with an ID, passing the FD ownership to
+  // Similar to Share but also provides a region in that file that should be
+  // read in the launched process (accessible with GetRegionAt()).
+  virtual void ShareWithRegion(
+      int id,
+      base::PlatformFile fd,
+      const base::MemoryMappedFile::Region& region) = 0;
+
+  // Adds an FD associated with an ID, passing the FD ownership to
   // FileDescriptorInfo.
   virtual void Transfer(int id, base::ScopedFD fd) = 0;
 
-  // A vecotr backed map of registered ID-FD pairs.
+  // A vector backed map of registered ID-FD pairs.
   virtual const base::FileHandleMappingVector& GetMapping() const = 0;
 
-  // A GetMapping() variant what adjusts the ID value by |delta|.
+  // A GetMapping() variant that adjusts the ID value by |delta|.
   // Some environments need this trick.
   virtual base::FileHandleMappingVector GetMappingWithIDAdjustment(
       int delta) const = 0;
 
-  // API for iterating registered ID-FD pairs.
+  // API for iterating over the registered ID-FD pairs.
   virtual base::PlatformFile GetFDAt(size_t i) const = 0;
   virtual int GetIDAt(size_t i) const = 0;
+  virtual const base::MemoryMappedFile::Region& GetRegionAt(size_t i) const = 0;
   virtual size_t GetMappingSize() const = 0;
 
-  // True if |this| has an ownership of |file|.
+  // Returns true if |this| has ownership of |file|.
   virtual bool OwnsFD(base::PlatformFile file) const = 0;
-  // Assuming |OwnsFD(file)|, release the ownership.
+  // Assuming |OwnsFD(file)|, releases the ownership.
   virtual base::ScopedFD ReleaseFD(base::PlatformFile file) = 0;
 };
 
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 5e5695af..6ecd9f2 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -592,13 +592,8 @@
       "document.getElementById('elt_text').value = 'foo';");
   ProcessPendingMessages();
 
-  if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
-    EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
-        FrameHostMsg_UpdateState::ID));
-  } else {
-    EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
-        ViewHostMsg_UpdateState::ID));
-  }
+  EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
+      FrameHostMsg_UpdateState::ID));
 }
 
 TEST_F(RenderViewImplTest, OnNavigationHttpPost) {
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 3783b3c..323544f 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -330,18 +330,16 @@
                                       gfx::Size())->web_contents());
 }
 
-#if defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
 void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
     const base::CommandLine& command_line,
     int child_process_id,
-    content::FileDescriptorInfo* mappings,
-    std::map<int, base::MemoryMappedFile::Region>* regions) {
-  mappings->Share(
+    content::FileDescriptorInfo* mappings) {
+#if defined(OS_ANDROID)
+  mappings->ShareWithRegion(
       kShellPakDescriptor,
-      base::GlobalDescriptors::GetInstance()->Get(kShellPakDescriptor));
-  regions->insert(std::make_pair(
-      kShellPakDescriptor,
-      base::GlobalDescriptors::GetInstance()->GetRegion(kShellPakDescriptor)));
+      base::GlobalDescriptors::GetInstance()->Get(kShellPakDescriptor),
+      base::GlobalDescriptors::GetInstance()->GetRegion(kShellPakDescriptor));
 
   if (breakpad::IsCrashReporterEnabled()) {
     base::File f(breakpad::CrashDumpManager::GetInstance()->CreateMinidumpFile(
@@ -354,18 +352,15 @@
                          base::ScopedFD(f.TakePlatformFile()));
     }
   }
-}
-#elif defined(OS_POSIX) && !defined(OS_MACOSX)
-void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
-    const base::CommandLine& command_line,
-    int child_process_id,
-    content::FileDescriptorInfo* mappings) {
+
+#else
   int crash_signal_fd = GetCrashSignalFD(command_line);
   if (crash_signal_fd >= 0) {
     mappings->Share(kCrashDumpSignal, crash_signal_fd);
   }
-}
 #endif  // defined(OS_ANDROID)
+}
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
 
 #if defined(OS_WIN)
 bool ShellContentBrowserClient::PreSpawnRenderer(
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index 7b9d9d8..cd788ad7 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -63,18 +63,12 @@
                const OpenURLParams& params,
                const base::Callback<void(WebContents*)>& callback) override;
 
-#if defined(OS_ANDROID)
-  void GetAdditionalMappedFilesForChildProcess(
-      const base::CommandLine& command_line,
-      int child_process_id,
-      content::FileDescriptorInfo* mappings,
-      std::map<int, base::MemoryMappedFile::Region>* regions) override;
-#elif defined(OS_POSIX) && !defined(OS_MACOSX)
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
   void GetAdditionalMappedFilesForChildProcess(
       const base::CommandLine& command_line,
       int child_process_id,
       content::FileDescriptorInfo* mappings) override;
-#endif  // defined(OS_ANDROID)
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
 #if defined(OS_WIN)
   bool PreSpawnRenderer(sandbox::TargetPolicy* policy) override;
 #endif
diff --git a/extensions/renderer/api_binding.cc b/extensions/renderer/api_binding.cc
index 7e596719..9856a81 100644
--- a/extensions/renderer/api_binding.cc
+++ b/extensions/renderer/api_binding.cc
@@ -200,12 +200,10 @@
   {
     v8::TryCatch try_catch(isolate);
     APIBindingHooks::RequestResult hooks_result =
-        APIBindingHooks::RequestResult::NOT_HANDLED;
-    hooks_result = binding_hooks_->HandleRequest(api_name_, name, context,
-                                                 signature, &argument_list,
-                                                 *type_refs_);
+        binding_hooks_->HandleRequest(api_name_, name, context,
+                                      signature, &argument_list, *type_refs_);
 
-    switch (hooks_result) {
+    switch (hooks_result.code) {
       case APIBindingHooks::RequestResult::INVALID_INVOCATION:
         invalid_invocation = true;
         // Throw a type error below so that it's not caught by our try-catch.
@@ -215,6 +213,8 @@
         try_catch.ReThrow();
         return;
       case APIBindingHooks::RequestResult::HANDLED:
+        if (!hooks_result.return_value.IsEmpty())
+          arguments->Return(hooks_result.return_value);
         return;  // Our work here is done.
       case APIBindingHooks::RequestResult::NOT_HANDLED:
         break;  // Handle in the default manner.
diff --git a/extensions/renderer/api_binding_hooks.cc b/extensions/renderer/api_binding_hooks.cc
index b46d785..c4bc12c 100644
--- a/extensions/renderer/api_binding_hooks.cc
+++ b/extensions/renderer/api_binding_hooks.cc
@@ -164,6 +164,11 @@
 
 }  // namespace
 
+APIBindingHooks::RequestResult::RequestResult(ResultCode code) : code(code) {}
+APIBindingHooks::RequestResult::~RequestResult() {}
+APIBindingHooks::RequestResult::RequestResult(const RequestResult& other) =
+    default;
+
 APIBindingHooks::APIBindingHooks(const binding::RunJSFunctionSync& run_js)
     : run_js_(run_js) {}
 APIBindingHooks::~APIBindingHooks() {}
@@ -195,7 +200,7 @@
             signature, context, arguments, type_refs);
     // Right now, it doesn't make sense to register a request handler that
     // doesn't handle the request.
-    DCHECK_NE(RequestResult::NOT_HANDLED, result);
+    DCHECK_NE(RequestResult::NOT_HANDLED, result.code);
     return result;
   }
 
@@ -204,7 +209,7 @@
   v8::Local<v8::Object> hook_interface_object =
       GetJSHookInterfaceObject(api_name, context, false);
   if (hook_interface_object.IsEmpty())
-    return RequestResult::NOT_HANDLED;
+    return RequestResult(RequestResult::NOT_HANDLED);
 
   v8::Isolate* isolate = context->GetIsolate();
 
@@ -223,7 +228,7 @@
     UpdateArguments(pre_validate_hook, context, arguments);
     if (try_catch.HasCaught()) {
       try_catch.ReThrow();
-      return RequestResult::THROWN;
+      return RequestResult(RequestResult::THROWN);
     }
   }
 
@@ -237,17 +242,25 @@
                                                  &parsed_v8_args, &error);
     if (try_catch.HasCaught()) {
       try_catch.ReThrow();
-      return RequestResult::THROWN;
+      return RequestResult(RequestResult::THROWN);
     }
     if (!success)
-      return RequestResult::INVALID_INVOCATION;
+      return RequestResult(RequestResult::INVALID_INVOCATION);
 
-    run_js_.Run(handle_request, context, parsed_v8_args.size(),
-                parsed_v8_args.data());
-    return RequestResult::HANDLED;
+    v8::Global<v8::Value> global_result =
+        run_js_.Run(handle_request, context, parsed_v8_args.size(),
+                    parsed_v8_args.data());
+    if (try_catch.HasCaught()) {
+      try_catch.ReThrow();
+      return RequestResult(RequestResult::THROWN);
+    }
+    RequestResult result(RequestResult::HANDLED);
+    if (!global_result.IsEmpty())
+      result.return_value = global_result.Get(isolate);
+    return result;
   }
 
-  return RequestResult::NOT_HANDLED;
+  return RequestResult(RequestResult::NOT_HANDLED);
 }
 
 void APIBindingHooks::InitializeInContext(
diff --git a/extensions/renderer/api_binding_hooks.h b/extensions/renderer/api_binding_hooks.h
index d6620275..e326d8cf 100644
--- a/extensions/renderer/api_binding_hooks.h
+++ b/extensions/renderer/api_binding_hooks.h
@@ -30,11 +30,21 @@
 class APIBindingHooks {
  public:
   // The result of checking for hooks to handle a request.
-  enum class RequestResult {
-    HANDLED,             // A custom hook handled the request.
-    THROWN,              // An exception was thrown during parsing or handling.
-    INVALID_INVOCATION,  // The request was called with invalid arguments.
-    NOT_HANDLED,         // The request was not handled.
+  struct RequestResult {
+    enum ResultCode {
+      HANDLED,             // A custom hook handled the request.
+      THROWN,              // An exception was thrown during parsing or
+                           // handling.
+      INVALID_INVOCATION,  // The request was called with invalid arguments.
+      NOT_HANDLED,         // The request was not handled.
+    };
+
+    explicit RequestResult(ResultCode code);
+    RequestResult(const RequestResult& other);
+    ~RequestResult();
+
+    ResultCode code;
+    v8::Local<v8::Value> return_value;  // Only valid if code == HANDLED.
   };
 
   // The callback to handle an API method. We pass in the expected signature
@@ -68,8 +78,7 @@
                            const std::string& api_name);
 
   // Looks for a custom hook to handle the given request and, if one exists,
-  // runs it. Returns the result of trying to run the hook, or NOT_HANDLED if no
-  // hook was found.
+  // runs it. Returns the result of running the hook, if any.
   RequestResult HandleRequest(const std::string& api_name,
                               const std::string& method_name,
                               v8::Local<v8::Context> context,
diff --git a/extensions/renderer/api_binding_unittest.cc b/extensions/renderer/api_binding_unittest.cc
index d526b7d..a210705 100644
--- a/extensions/renderer/api_binding_unittest.cc
+++ b/extensions/renderer/api_binding_unittest.cc
@@ -478,12 +478,14 @@
                  std::vector<v8::Local<v8::Value>>* arguments,
                  const ArgumentSpec::RefMap& ref_map) {
     *did_call = true;
-    if (arguments->size() != 1u) {
+    APIBindingHooks::RequestResult result(
+        APIBindingHooks::RequestResult::HANDLED);
+    if (arguments->size() != 1u) {  // ASSERT* messes with the return type.
       EXPECT_EQ(1u, arguments->size());
-      return APIBindingHooks::RequestResult::HANDLED;
+      return result;
     }
     EXPECT_EQ("foo", gin::V8ToString(arguments->at(0)));
-    return APIBindingHooks::RequestResult::HANDLED;
+    return result;
   };
   hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call));
 
@@ -691,4 +693,195 @@
   ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]");
 }
 
+// Tests that custom JS hooks can return results synchronously.
+TEST_F(APIBindingUnittest, TestReturningResultFromCustomJSHook) {
+  // Register a hook for the test.oneString method.
+  auto hooks = base::MakeUnique<APIBindingHooks>(
+      base::Bind(&RunFunctionOnGlobalAndReturnHandle));
+
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = ContextLocal();
+  const char kRegisterHook[] =
+      "(function(hooks) {\n"
+      "  hooks.setHandleRequest('oneString', str => {\n"
+      "    return str + ' pong';\n"
+      "  });\n"
+      "})";
+  v8::Local<v8::String> source_string =
+      gin::StringToV8(isolate(), kRegisterHook);
+  v8::Local<v8::String> source_name =
+      gin::StringToV8(isolate(), "custom_hook");
+  hooks->RegisterJsSource(
+      v8::Global<v8::String>(isolate(), source_string),
+      v8::Global<v8::String>(isolate(), source_name));
+
+  std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions);
+  ASSERT_TRUE(functions);
+  ArgumentSpec::RefMap refs;
+
+  APIBinding binding(
+      "test", functions.get(), nullptr, nullptr,
+      base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)),
+      std::move(hooks), &refs);
+  EXPECT_TRUE(refs.empty());
+
+  APIEventHandler event_handler(
+      base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
+  v8::Local<v8::Object> binding_object = binding.CreateInstance(
+      context, isolate(), &event_handler, base::Bind(&AllowAllAPIs));
+
+  v8::Local<v8::Function> function =
+      FunctionFromString(context,
+                         "(function(obj) { return obj.oneString('ping'); })");
+  v8::Local<v8::Value> args[] = {binding_object};
+  v8::Local<v8::Value> result =
+      RunFunction(function, context, arraysize(args), args);
+  ASSERT_FALSE(result.IsEmpty());
+  std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context);
+  ASSERT_TRUE(json_result);
+  EXPECT_EQ("\"ping pong\"", ValueToString(*json_result));
+}
+
+// Tests that JS custom hooks can throw exceptions for bad invocations.
+TEST_F(APIBindingUnittest, TestThrowingFromCustomJSHook) {
+  // Our testing handlers for running functions expect a pre-determined success
+  // or failure. Since we're testing throwing exceptions here, we need a way of
+  // running that allows exceptions to be thrown, but we still expect most JS
+  // calls to succeed.
+  // TODO(devlin): This is a bit clunky. If we need to do this enough, we could
+  // figure out a different solution, like having a stack object for allowing
+  // errors/exceptions. But given this is the only place we need it so far, this
+  // is sufficient.
+  auto run_js_and_expect_error = [](v8::Local<v8::Function> function,
+                                          v8::Local<v8::Context> context,
+                                          int argc,
+                                          v8::Local<v8::Value> argv[]) {
+    v8::MaybeLocal<v8::Value> maybe_result =
+        function->Call(context, context->Global(), argc, argv);
+    v8::Global<v8::Value> result;
+    v8::Local<v8::Value> local;
+    if (maybe_result.ToLocal(&local))
+      result.Reset(context->GetIsolate(), local);
+    return result;
+  };
+  // Register a hook for the test.oneString method.
+  auto hooks = base::MakeUnique<APIBindingHooks>(
+      base::Bind(run_js_and_expect_error));
+
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = ContextLocal();
+  const char kRegisterHook[] =
+      "(function(hooks) {\n"
+      "  hooks.setHandleRequest('oneString', str => {\n"
+      "    throw new Error('Custom Hook Error');\n"
+      "  });\n"
+      "})";
+  v8::Local<v8::String> source_string =
+      gin::StringToV8(isolate(), kRegisterHook);
+  v8::Local<v8::String> source_name =
+      gin::StringToV8(isolate(), "custom_hook");
+  hooks->RegisterJsSource(
+      v8::Global<v8::String>(isolate(), source_string),
+      v8::Global<v8::String>(isolate(), source_name));
+
+  std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions);
+  ASSERT_TRUE(functions);
+  ArgumentSpec::RefMap refs;
+
+  APIBinding binding(
+      "test", functions.get(), nullptr, nullptr,
+      base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)),
+      std::move(hooks), &refs);
+  EXPECT_TRUE(refs.empty());
+
+  APIEventHandler event_handler(
+      base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
+  v8::Local<v8::Object> binding_object = binding.CreateInstance(
+      context, isolate(), &event_handler, base::Bind(&AllowAllAPIs));
+
+  v8::Local<v8::Function> function =
+      FunctionFromString(context,
+                         "(function(obj) { return obj.oneString('ping'); })");
+  v8::Local<v8::Value> args[] = {binding_object};
+  RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
+                            arraysize(args), args,
+                            "Uncaught Error: Custom Hook Error");
+}
+
+// Tests that native custom hooks can return results synchronously, or throw
+// exceptions for bad invocations.
+TEST_F(APIBindingUnittest,
+       TestReturningResultAndThrowingExceptionFromCustomNativeHook) {
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = ContextLocal();
+
+  // Register a hook for the test.oneString method.
+  auto hooks = base::MakeUnique<APIBindingHooks>(
+      base::Bind(&RunFunctionOnGlobalAndReturnHandle));
+  bool did_call = false;
+  auto hook = [](bool* did_call, const APISignature* signature,
+                 v8::Local<v8::Context> context,
+                 std::vector<v8::Local<v8::Value>>* arguments,
+                 const ArgumentSpec::RefMap& ref_map) {
+    APIBindingHooks::RequestResult result(
+        APIBindingHooks::RequestResult::HANDLED);
+    if (arguments->size() != 1u) {  // ASSERT* messes with the return type.
+      EXPECT_EQ(1u, arguments->size());
+      return result;
+    }
+    v8::Isolate* isolate = context->GetIsolate();
+    std::string arg_value = gin::V8ToString(arguments->at(0));
+    if (arg_value == "throw") {
+      isolate->ThrowException(v8::Exception::Error(
+          gin::StringToV8(isolate, "Custom Hook Error")));
+      result.code = APIBindingHooks::RequestResult::THROWN;
+      return result;
+    }
+    result.return_value =
+        gin::StringToV8(context->GetIsolate(), arg_value + " pong");
+    return result;
+  };
+  hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call));
+
+  std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions);
+  ASSERT_TRUE(functions);
+  ArgumentSpec::RefMap refs;
+
+  APIBinding binding(
+      "test", functions.get(), nullptr, nullptr,
+      base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)),
+      std::move(hooks), &refs);
+  EXPECT_TRUE(refs.empty());
+
+  APIEventHandler event_handler(
+      base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
+  v8::Local<v8::Object> binding_object = binding.CreateInstance(
+      context, isolate(), &event_handler, base::Bind(&AllowAllAPIs));
+
+  {
+    // Test an invocation that we expect to throw an exception.
+    v8::Local<v8::Function> function =
+        FunctionFromString(
+            context, "(function(obj) { return obj.oneString('throw'); })");
+    v8::Local<v8::Value> args[] = {binding_object};
+    RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
+                              arraysize(args), args,
+                              "Uncaught Error: Custom Hook Error");
+  }
+
+  {
+    // Test an invocation we expect to succeed.
+    v8::Local<v8::Function> function =
+        FunctionFromString(context,
+                           "(function(obj) { return obj.oneString('ping'); })");
+    v8::Local<v8::Value> args[] = {binding_object};
+    v8::Local<v8::Value> result =
+        RunFunction(function, context, arraysize(args), args);
+    ASSERT_FALSE(result.IsEmpty());
+    std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context);
+    ASSERT_TRUE(json_result);
+    EXPECT_EQ("\"ping pong\"", ValueToString(*json_result));
+  }
+}
+
 }  // namespace extensions
diff --git a/extensions/renderer/api_bindings_system_unittest.cc b/extensions/renderer/api_bindings_system_unittest.cc
index 2390607..37ac5e064 100644
--- a/extensions/renderer/api_bindings_system_unittest.cc
+++ b/extensions/renderer/api_bindings_system_unittest.cc
@@ -300,22 +300,24 @@
                  std::vector<v8::Local<v8::Value>>* arguments,
                  const ArgumentSpec::RefMap& type_refs) {
     *did_call = true;
+    APIBindingHooks::RequestResult result(
+        APIBindingHooks::RequestResult::HANDLED);
     if (arguments->size() != 2) {  // ASSERT* messes with the return type.
       EXPECT_EQ(2u, arguments->size());
-      return APIBindingHooks::RequestResult::HANDLED;
+      return result;
     }
     std::string argument;
     EXPECT_EQ("foo", gin::V8ToString(arguments->at(0)));
     if (!arguments->at(1)->IsFunction()) {
       EXPECT_TRUE(arguments->at(1)->IsFunction());
-      return APIBindingHooks::RequestResult::HANDLED;
+      return result;
     }
     v8::Local<v8::String> response =
         gin::StringToV8(context->GetIsolate(), "bar");
     v8::Local<v8::Value> response_args[] = {response};
     RunFunctionOnGlobal(arguments->at(1).As<v8::Function>(),
                         context, 1, response_args);
-    return APIBindingHooks::RequestResult::HANDLED;
+    return result;
   };
 
   APIBindingHooks* hooks = bindings_system()->GetHooksForAPI(kAlphaAPIName);
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc
index 1875024..399beb8 100644
--- a/gpu/config/gpu_driver_bug_list_json.cc
+++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -19,7 +19,7 @@
 {
   "name": "gpu driver bug list",
   // Please update the version number whenever you change this file.
-  "version": "9.27",
+  "version": "9.28",
   "entries": [
     {
       "id": 1,
@@ -2313,6 +2313,16 @@
       "features": [
         "disable_program_caching_for_transform_feedback"
       ]
+    },
+    {
+      "id": 213,
+      "description": "The Mali-Gxx driver does not guarantee flush ordering",
+      "cr_bugs": [678508],
+      "gl_vendor": "ARM.*",
+      "gl_renderer": "Mali-G.*",
+      "features": [
+        "use_virtualized_gl_contexts"
+      ]
     }
   ]
   // Please update the version number at beginning of this file whenever you
diff --git a/ios/chrome/browser/application_context_impl.cc b/ios/chrome/browser/application_context_impl.cc
index 88bc2271..43e89458 100644
--- a/ios/chrome/browser/application_context_impl.cc
+++ b/ios/chrome/browser/application_context_impl.cc
@@ -289,7 +289,8 @@
 ApplicationContextImpl::GetPhysicalWebDataSource() {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (!physical_web_data_source_) {
-    physical_web_data_source_ = CreateIOSChromePhysicalWebDataSource();
+    physical_web_data_source_ =
+        CreateIOSChromePhysicalWebDataSource(GetLocalState());
     DCHECK(physical_web_data_source_);
   }
   return physical_web_data_source_.get();
diff --git a/ios/chrome/browser/physical_web/BUILD.gn b/ios/chrome/browser/physical_web/BUILD.gn
index f7d034e..832f113 100644
--- a/ios/chrome/browser/physical_web/BUILD.gn
+++ b/ios/chrome/browser/physical_web/BUILD.gn
@@ -9,6 +9,8 @@
     "ios_chrome_physical_web_data_source.h",
     "ios_chrome_physical_web_data_source.mm",
     "physical_web_constants.h",
+    "physical_web_initial_state_recorder.h",
+    "physical_web_initial_state_recorder.mm",
     "physical_web_prefs_registration.cc",
     "physical_web_prefs_registration.h",
     "start_physical_web_discovery.h",
diff --git a/ios/chrome/browser/physical_web/create_physical_web_data_source.h b/ios/chrome/browser/physical_web/create_physical_web_data_source.h
index 7ecddef..4646959 100644
--- a/ios/chrome/browser/physical_web/create_physical_web_data_source.h
+++ b/ios/chrome/browser/physical_web/create_physical_web_data_source.h
@@ -10,10 +10,11 @@
 namespace physical_web {
 class PhysicalWebDataSource;
 }
+class PrefService;
 
 // Creates a new instance of IOSChromePhysicalWebDataSource. The returned object
 // is fully initialized and can be registered as global singleton.
 std::unique_ptr<physical_web::PhysicalWebDataSource>
-CreateIOSChromePhysicalWebDataSource();
+CreateIOSChromePhysicalWebDataSource(PrefService* prefService);
 
 #endif  // IOS_CHROME_BROWSER_PHYSICAL_WEB_CREATE_PHYSICAL_WEB_DATA_SOURCE_H_
diff --git a/ios/chrome/browser/physical_web/create_physical_web_data_source.mm b/ios/chrome/browser/physical_web/create_physical_web_data_source.mm
index 8ea22fab..5844c61f 100644
--- a/ios/chrome/browser/physical_web/create_physical_web_data_source.mm
+++ b/ios/chrome/browser/physical_web/create_physical_web_data_source.mm
@@ -8,8 +8,6 @@
 #import "ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.h"
 
 std::unique_ptr<physical_web::PhysicalWebDataSource>
-CreateIOSChromePhysicalWebDataSource() {
-  std::unique_ptr<physical_web::PhysicalWebDataSource> datasource =
-      base::MakeUnique<IOSChromePhysicalWebDataSource>();
-  return datasource;
+CreateIOSChromePhysicalWebDataSource(PrefService* prefService) {
+  return base::MakeUnique<IOSChromePhysicalWebDataSource>(prefService);
 }
diff --git a/ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.h b/ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.h
index 40d0ebb..20d0756b 100644
--- a/ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.h
+++ b/ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.h
@@ -12,14 +12,16 @@
 namespace base {
 class ListValue;
 }
+class PrefService;
 
 @class PhysicalWebScanner;
+@class PhysicalWebInitialStateRecorder;
 
 // iOS implementation of PhysicalWebDataSource
 class IOSChromePhysicalWebDataSource
     : public physical_web::PhysicalWebDataSourceImpl {
  public:
-  IOSChromePhysicalWebDataSource();
+  IOSChromePhysicalWebDataSource(PrefService* pref_service);
   ~IOSChromePhysicalWebDataSource() override;
 
   // Starts scanning for Physical Web URLs. If |network_request_enabled| is
@@ -41,6 +43,9 @@
   // Scanner for nearby Physical Web URL devices.
   base::scoped_nsobject<PhysicalWebScanner> scanner_;
 
+  // Utility for fetching initial application state for logging purposes.
+  base::scoped_nsobject<PhysicalWebInitialStateRecorder> initialStateRecorder_;
+
   DISALLOW_COPY_AND_ASSIGN(IOSChromePhysicalWebDataSource);
 };
 
diff --git a/ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.mm b/ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.mm
index 80c0744..f2dcdee 100644
--- a/ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.mm
+++ b/ios/chrome/browser/physical_web/ios_chrome_physical_web_data_source.mm
@@ -6,11 +6,18 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
+#import "ios/chrome/browser/physical_web/physical_web_initial_state_recorder.h"
 #import "ios/chrome/common/physical_web/physical_web_scanner.h"
 
-IOSChromePhysicalWebDataSource::IOSChromePhysicalWebDataSource() {}
+IOSChromePhysicalWebDataSource::IOSChromePhysicalWebDataSource(
+    PrefService* pref_service) {
+  initialStateRecorder_.reset([[PhysicalWebInitialStateRecorder alloc]
+      initWithPrefService:pref_service]);
+  [initialStateRecorder_ collectAndRecordState];
+}
 
 IOSChromePhysicalWebDataSource::~IOSChromePhysicalWebDataSource() {
+  [initialStateRecorder_ invalidate];
   StopDiscovery();
 }
 
diff --git a/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.h b/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.h
new file mode 100644
index 0000000..1d0aabfb
--- /dev/null
+++ b/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_PHYSICAL_WEB_PHYSICAL_WEB_INITIAL_STATE_RECORDER_H_
+#define IOS_CHROME_BROWSER_PHYSICAL_WEB_PHYSICAL_WEB_INITIAL_STATE_RECORDER_H_
+
+#import <CoreBluetooth/CoreBluetooth.h>
+
+class PrefService;
+
+@interface PhysicalWebInitialStateRecorder : NSObject<CBCentralManagerDelegate>
+
+- (instancetype)initWithPrefService:(PrefService*)prefService
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Fetches and logs the current state of settings relevant to the Physical Web
+// feature.
+- (void)collectAndRecordState;
+
+// If the initial state has not yet been recorded, abort collection and
+// invalidate the timer.
+- (void)invalidate;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_PHYSICAL_WEB_PHYSICAL_WEB_INITIAL_STATE_RECORDER_H_
diff --git a/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.mm b/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.mm
new file mode 100644
index 0000000..623a8ef8
--- /dev/null
+++ b/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.mm
@@ -0,0 +1,170 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/physical_web/physical_web_initial_state_recorder.h"
+
+#import <CoreBluetooth/CoreBluetooth.h>
+#import <CoreLocation/CoreLocation.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/metrics/histogram.h"
+#include "components/prefs/pref_service.h"
+#include "ios/chrome/browser/physical_web/physical_web_constants.h"
+#include "ios/chrome/browser/pref_names.h"
+
+namespace {
+
+const double kStartupDelaySeconds = 10.0;
+
+// Initial state of settings relevant to the Physical Web feature. These are
+// ordered so that bits 2:0 encode the Bluetooth enabled state, the Location
+// Services enabled state, and whether Chrome has been granted the Location app
+// permission. Bits 4:3 encode the Physical Web preference tristate.
+// This enum is used in a user metrics histogram. The states should not be
+// reordered or removed.
+enum PhysicalWebInitialStateIosChrome {
+  OPTOUT_BTOFF_LOCOFF_UNAUTH,
+  OPTOUT_BTOFF_LOCOFF_AUTH,
+  OPTOUT_BTOFF_LOCON_UNAUTH,
+  OPTOUT_BTOFF_LOCON_AUTH,
+  OPTOUT_BTON_LOCOFF_UNAUTH,
+  OPTOUT_BTON_LOCOFF_AUTH,
+  OPTOUT_BTON_LOCON_UNAUTH,
+  OPTOUT_BTON_LOCON_AUTH,
+  OPTIN_BTOFF_LOCOFF_UNAUTH,
+  OPTIN_BTOFF_LOCOFF_AUTH,
+  OPTIN_BTOFF_LOCON_UNAUTH,
+  OPTIN_BTOFF_LOCON_AUTH,
+  OPTIN_BTON_LOCOFF_UNAUTH,
+  OPTIN_BTON_LOCOFF_AUTH,
+  OPTIN_BTON_LOCON_UNAUTH,
+  OPTIN_BTON_LOCON_AUTH,
+  ONBOARDING_BTOFF_LOCOFF_UNAUTH,
+  ONBOARDING_BTOFF_LOCOFF_AUTH,
+  ONBOARDING_BTOFF_LOCON_UNAUTH,
+  ONBOARDING_BTOFF_LOCON_AUTH,
+  ONBOARDING_BTON_LOCOFF_UNAUTH,
+  ONBOARDING_BTON_LOCOFF_AUTH,
+  ONBOARDING_BTON_LOCON_UNAUTH,
+  ONBOARDING_BTON_LOCON_AUTH,
+  PHYSICAL_WEB_INITIAL_STATE_COUNT,
+
+  // Helper flag values
+  LOCATION_AUTHORIZED_FLAG = 1 << 0,
+  LOCATION_SERVICES_FLAG = 1 << 1,
+  BLUETOOTH_FLAG = 1 << 2,
+  OPTIN_FLAG = 1 << 3,
+  ONBOARDING_FLAG = 1 << 4,
+};
+}  // namespace
+
+@implementation PhysicalWebInitialStateRecorder {
+  int preferenceState_;
+  BOOL recordedState_;
+  base::scoped_nsobject<NSTimer> startupDelayTimer_;
+  base::scoped_nsobject<CBCentralManager> centralManager_;
+}
+
+- (instancetype)initWithPrefService:(PrefService*)prefService {
+  self = [super init];
+  if (self) {
+    preferenceState_ = prefService->GetInteger(prefs::kIosPhysicalWebEnabled);
+  }
+  return self;
+}
+
+- (instancetype)init {
+  NOTREACHED();
+  return nil;
+}
+
+- (void)dealloc {
+  [self invalidate];
+  [super dealloc];
+}
+
+- (void)invalidate {
+  if (startupDelayTimer_.get()) {
+    [startupDelayTimer_ invalidate];
+    startupDelayTimer_.reset();
+  }
+  [centralManager_ setDelegate:nil];
+  centralManager_.reset();
+}
+
+- (void)centralManagerDidUpdateState:(CBCentralManager*)central {
+  [centralManager_ setDelegate:nil];
+  centralManager_.reset();
+
+  BOOL bluetoothEnabled = [centralManager_ state] == CBManagerStatePoweredOn;
+
+  BOOL locationServicesEnabled = [CLLocationManager locationServicesEnabled];
+
+  CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus];
+  BOOL locationAuthorized =
+      authStatus == kCLAuthorizationStatusAuthorizedWhenInUse ||
+      authStatus == kCLAuthorizationStatusAuthorizedAlways;
+
+  [self recordStateWithPreferenceState:preferenceState_
+                      bluetoothEnabled:bluetoothEnabled
+               locationServicesEnabled:locationServicesEnabled
+                    locationAuthorized:locationAuthorized];
+}
+
+- (void)collectAndRecordState {
+  if (recordedState_) {
+    return;
+  }
+  recordedState_ = YES;
+  startupDelayTimer_.reset(
+      [[NSTimer scheduledTimerWithTimeInterval:kStartupDelaySeconds
+                                        target:self
+                                      selector:@selector(startupDelayElapsed:)
+                                      userInfo:nil
+                                       repeats:NO] retain]);
+}
+
+- (void)startupDelayElapsed:(NSTimer*)timer {
+  startupDelayTimer_.reset();
+
+  // The Bluetooth enabled state must be checked asynchronously. When the state
+  // is ready, it will call our centralManagerDidUpdateState method.
+  centralManager_.reset([[CBCentralManager alloc]
+      initWithDelegate:self
+                 queue:dispatch_get_main_queue()
+               options:@{
+                 // By default, creating a CBCentralManager object with
+                 // Bluetooth disabled will prompt the user to enable Bluetooth.
+                 // Passing ShowPowerAlert=NO disables the prompt so we can
+                 // check the Bluetooth enabled state silently.
+                 CBCentralManagerOptionShowPowerAlertKey : @NO
+               }]);
+}
+
+- (void)recordStateWithPreferenceState:(int)preferenceState
+                      bluetoothEnabled:(BOOL)bluetoothEnabled
+               locationServicesEnabled:(BOOL)locationServicesEnabled
+                    locationAuthorized:(BOOL)locationAuthorized {
+  int state = 0;
+  if (preferenceState == physical_web::kPhysicalWebOn) {
+    state |= OPTIN_FLAG;
+  } else if (preferenceState == physical_web::kPhysicalWebOnboarding) {
+    state |= ONBOARDING_FLAG;
+  }
+  if (locationServicesEnabled) {
+    state |= LOCATION_SERVICES_FLAG;
+  }
+  if (locationAuthorized) {
+    state |= LOCATION_AUTHORIZED_FLAG;
+  }
+  if (bluetoothEnabled) {
+    state |= BLUETOOTH_FLAG;
+  }
+
+  DCHECK(state < PHYSICAL_WEB_INITIAL_STATE_COUNT);
+  UMA_HISTOGRAM_ENUMERATION("PhysicalWeb.InitialState.IosChrome", state,
+                            PHYSICAL_WEB_INITIAL_STATE_COUNT);
+}
+
+@end
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.h b/ios/chrome/browser/sync/ios_chrome_sync_client.h
index 013c5e4..48c5f6e 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.h
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.h
@@ -42,6 +42,7 @@
   bookmarks::BookmarkModel* GetBookmarkModel() override;
   favicon::FaviconService* GetFaviconService() override;
   history::HistoryService* GetHistoryService() override;
+  bool HasPasswordStore() override;
   base::Closure GetPasswordStateChangedCallback() override;
   syncer::SyncApiComponentFactory::RegisterDataTypesMethod
   GetRegisterPlatformTypesCallback() override;
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index 79d38cd..5ed9a2b 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -202,6 +202,11 @@
       browser_state_, ServiceAccessType::EXPLICIT_ACCESS);
 }
 
+bool IOSChromeSyncClient::HasPasswordStore() {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  return password_store_ != nullptr;
+}
+
 autofill::PersonalDataManager* IOSChromeSyncClient::GetPersonalDataManager() {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   return autofill::PersonalDataManagerFactory::GetForBrowserState(
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index 6c6a2a9..23cdb8e3 100644
--- a/ios/third_party/material_components_ios/README.chromium
+++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Components for iOS
 URL: https://github.com/material-components/material-components-ios
 Version: 0
-Revision: 4e5a18715bedf3ada7fd35ba9d2d41082fbf7437
+Revision: b1eb638d70792b7aa06118b128e3518c20e2aaae
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
@@ -16,4 +16,4 @@
 http://www.google.com/design/spec/material-design/introduction.html
 
 Local Modifications:
-None
+None
\ No newline at end of file
diff --git a/ios/third_party/material_font_disk_loader_ios/README.chromium b/ios/third_party/material_font_disk_loader_ios/README.chromium
index e29a1dc..0e21813e 100644
--- a/ios/third_party/material_font_disk_loader_ios/README.chromium
+++ b/ios/third_party/material_font_disk_loader_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Font Disk Loader iOS
 URL: https://github.com/material-foundation/material-font-disk-loader-ios
 Version: 0
-Revision: 20c8fe37329cb18826f90159ce4ee445079e2e46
+Revision: 93acc021e3034898716028822cb802a3a816be7e
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
@@ -10,4 +10,4 @@
 Registers a single custom font asset from disk.
 
 Local Modifications:
-None
+None
\ No newline at end of file
diff --git a/ios/third_party/material_sprited_animation_view_ios/README.chromium b/ios/third_party/material_sprited_animation_view_ios/README.chromium
index 268f933..a1f29d9 100644
--- a/ios/third_party/material_sprited_animation_view_ios/README.chromium
+++ b/ios/third_party/material_sprited_animation_view_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Sprited Animation View
 URL: https://github.com/material-foundation/material-sprited-animation-view-ios
 Version: 0
-Revision: e240cdcd4538f0763ca5bd8c5afc2991eb482f1a
+Revision: c6e16d06bdafd95540c62b3402d9414692fbca81
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
@@ -12,4 +12,4 @@
 and animation simply consists of updating the layer contentsRect.
 
 Local Modifications:
-None
+None
\ No newline at end of file
diff --git a/ios/third_party/material_text_accessibility_ios/README.chromium b/ios/third_party/material_text_accessibility_ios/README.chromium
index 256c030..6b142c7 100644
--- a/ios/third_party/material_text_accessibility_ios/README.chromium
+++ b/ios/third_party/material_text_accessibility_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Text Accessibility iOS
 URL: https://github.com/material-foundation/material-text-accessibility-ios
 Version: 0
-Revision: 96d2b0f13976a897bc7a41daf67f36d9548cff94
+Revision: 318d5100f2976e59c94643e5dcab69e7a830ee43
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
@@ -11,4 +11,4 @@
 standards for accessibility.
 
 Local Modifications:
-None
+None
\ No newline at end of file
diff --git a/ios/web/public/web_state/crw_web_user_interface_delegate.h b/ios/web/public/web_state/crw_web_user_interface_delegate.h
index f5b2748..39cc3bb 100644
--- a/ios/web/public/web_state/crw_web_user_interface_delegate.h
+++ b/ios/web/public/web_state/crw_web_user_interface_delegate.h
@@ -8,7 +8,6 @@
 #import <Foundation/Foundation.h>
 
 @class CRWWebController;
-class GURL;
 
 // DEPRECATED, do not conform to this protocol and do not add any methods to it.
 // Use web::WebStateDelegate instead.
@@ -16,47 +15,6 @@
 @protocol CRWWebUserInterfaceDelegate<NSObject>
 
  @optional
-// The JavaScript panel selectors below are only called by the web controller
-// for builds with WKWebView enabled.
-
-// Displays a JavaScript alert with an OK button, showing the provided message.
-// |completionHandler| is called after the OK button on the alert is tapped.
-// If this selector isn't implemented, the completion handler provided by the
-// web view will be called without any UI displayed. If this method is
-// implemented, but |completionHandler| is not called then
-// NSInternalInconsistencyException will be thrown.
-- (void)webController:(CRWWebController*)webController
-    runJavaScriptAlertPanelWithMessage:(NSString*)message
-                            requestURL:(const GURL&)requestURL
-                     completionHandler:(void (^)(void))completionHandler;
-
-// Displays a JavaScript confirm alert with an OK and Cancel button, showing the
-// provided message.  |completionHandler| is called after a button is pressed,
-// with |isConfirmed| indicating whether OK was pressed.  If this selector isn't
-// implemented, the completion handler provided by the web view will be called
-// with |isConfirmed| = NO. If this method is implemented, but
-// |completionHandler| is not called then NSInternalInconsistencyException will
-// be thrown.
-- (void)webController:(CRWWebController*)webController
-    runJavaScriptConfirmPanelWithMessage:(NSString*)message
-                              requestURL:(const GURL&)requestURL
-                       completionHandler:
-        (void (^)(BOOL isConfirmed))completionHandler;
-
-// Displays a JavaScript input alert with an OK and Cancel button, showing the
-// provided message and default text.  |completionHandler| is called after a
-// button is pressed.  If the OK button is pressed, |input| contains the user
-// text.  If the cancel but is pressed, |input| will be nil.  If this selector
-// isn't implemented, the completion handler provided by the web view will be
-// called with |input| = nil. If this method is implemented, but
-// |completionHandler| is not called then NSInternalInconsistencyException will
-// be thrown.
-- (void)webController:(CRWWebController*)webController
-    runJavaScriptTextInputPanelWithPrompt:(NSString*)message
-                              defaultText:(NSString*)defaultText
-                               requestURL:(const GURL&)requestURL
-                        completionHandler:
-                            (void (^)(NSString* input))completionHandler;
 
 // Displays an HTTP authentication dialog.  |completionHandler| should be called
 // with non-nil |username| and |password| if embedder wants to proceed with
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 7aabe25b..df53e2a 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -3872,6 +3872,13 @@
                           message:(NSString*)message
                       defaultText:(NSString*)defaultText
                        completion:(void (^)(BOOL, NSString*))completionHandler {
+  DCHECK(completionHandler);
+  if (self.shouldSuppressDialogs) {
+    [self didSuppressDialog];
+    completionHandler(NO, nil);
+    return;
+  }
+
   self.webStateImpl->RunJavaScriptDialog(
       net::GURLWithNSURL(frame.request.URL), type, message, defaultText,
       base::BindBlock(^(bool success, NSString* input) {
@@ -4947,30 +4954,13 @@
     runJavaScriptAlertPanelWithMessage:(NSString*)message
                       initiatedByFrame:(WKFrameInfo*)frame
                      completionHandler:(void (^)())completionHandler {
-  if (self.shouldSuppressDialogs) {
-    [self didSuppressDialog];
-    completionHandler();
-    return;
-  }
-
-  SEL alertSelector = @selector(webController:
-           runJavaScriptAlertPanelWithMessage:
-                                   requestURL:
-                            completionHandler:);
-  if ([self.UIDelegate respondsToSelector:alertSelector]) {
-    [self.UIDelegate webController:self
-        runJavaScriptAlertPanelWithMessage:message
-                                requestURL:net::GURLWithNSURL(frame.request.URL)
-                         completionHandler:completionHandler];
-  } else {
-    [self runJavaScriptDialogOfType:web::JAVASCRIPT_DIALOG_TYPE_ALERT
-                   initiatedByFrame:frame
-                            message:message
-                        defaultText:nil
-                         completion:^(BOOL, NSString*) {
-                           completionHandler();
-                         }];
-  }
+  [self runJavaScriptDialogOfType:web::JAVASCRIPT_DIALOG_TYPE_ALERT
+                 initiatedByFrame:frame
+                          message:message
+                      defaultText:nil
+                       completion:^(BOOL, NSString*) {
+                         completionHandler();
+                       }];
 }
 
 - (void)webView:(WKWebView*)webView
@@ -4978,33 +4968,15 @@
                         initiatedByFrame:(WKFrameInfo*)frame
                        completionHandler:
                            (void (^)(BOOL result))completionHandler {
-  if (self.shouldSuppressDialogs) {
-    [self didSuppressDialog];
-    completionHandler(NO);
-    return;
-  }
-
-  SEL confirmationSelector = @selector(webController:
-                runJavaScriptConfirmPanelWithMessage:
-                                          requestURL:
-                                   completionHandler:);
-  if ([self.UIDelegate respondsToSelector:confirmationSelector]) {
-    [self.UIDelegate webController:self
-        runJavaScriptConfirmPanelWithMessage:message
-                                  requestURL:net::GURLWithNSURL(
-                                                 frame.request.URL)
-                           completionHandler:completionHandler];
-  } else {
-    [self runJavaScriptDialogOfType:web::JAVASCRIPT_DIALOG_TYPE_CONFIRM
-                   initiatedByFrame:frame
-                            message:message
-                        defaultText:nil
-                         completion:^(BOOL success, NSString*) {
-                           if (completionHandler) {
-                             completionHandler(success);
-                           }
-                         }];
-  }
+  [self runJavaScriptDialogOfType:web::JAVASCRIPT_DIALOG_TYPE_CONFIRM
+                 initiatedByFrame:frame
+                          message:message
+                      defaultText:nil
+                       completion:^(BOOL success, NSString*) {
+                         if (completionHandler) {
+                           completionHandler(success);
+                         }
+                       }];
 }
 
 - (void)webView:(WKWebView*)webView
@@ -5021,35 +4993,15 @@
     return;
   }
 
-  if (self.shouldSuppressDialogs) {
-    [self didSuppressDialog];
-    completionHandler(nil);
-    return;
-  }
-
-  SEL textInputSelector = @selector(webController:
-            runJavaScriptTextInputPanelWithPrompt:
-                                      defaultText:
-                                       requestURL:
-                                completionHandler:);
-  if ([self.UIDelegate respondsToSelector:textInputSelector]) {
-    GURL requestURL = net::GURLWithNSURL(frame.request.URL);
-    [self.UIDelegate webController:self
-        runJavaScriptTextInputPanelWithPrompt:prompt
-                                  defaultText:defaultText
-                                   requestURL:requestURL
-                            completionHandler:completionHandler];
-  } else {
-    [self runJavaScriptDialogOfType:web::JAVASCRIPT_DIALOG_TYPE_PROMPT
-                   initiatedByFrame:frame
-                            message:prompt
-                        defaultText:defaultText
-                         completion:^(BOOL, NSString* input) {
-                           if (completionHandler) {
-                             completionHandler(input);
-                           }
-                         }];
-  }
+  [self runJavaScriptDialogOfType:web::JAVASCRIPT_DIALOG_TYPE_PROMPT
+                 initiatedByFrame:frame
+                          message:prompt
+                      defaultText:defaultText
+                       completion:^(BOOL, NSString* input) {
+                         if (completionHandler) {
+                           completionHandler(input);
+                         }
+                       }];
 }
 
 #pragma mark -
@@ -5175,6 +5127,19 @@
       [self registerLoadRequest:webViewURL];
     }
   }
+
+  if (![self currentSessionEntry]) {
+    // In this state CRWWebController will crash in |didCommitNavigation:|
+    // (crbug.com/676458). It's unclear if web controller could get into this
+    // state but it's one of the guesses for crbug.com/676458 root cause. Report
+    // UMA historgam if that happens.
+    // TODO(crbug.com/677552): Remove this historgam.
+    UMA_HISTOGRAM_BOOLEAN(
+        "WebController."
+        "StartProvisionalNavigationExitedWithEmptyNavigationManager",
+        true);
+  }
+
   // Ensure the URL is registered and loadPhase is as expected.
   DCHECK(_lastRegisteredRequestURL == webViewURL);
   DCHECK(self.loadPhase == web::LOAD_REQUESTED);
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 18f93671..836bd30 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -20,6 +20,7 @@
 #import "ios/web/public/test/fakes/test_native_content.h"
 #import "ios/web/public/test/fakes/test_native_content_provider.h"
 #import "ios/web/public/test/fakes/test_web_client.h"
+#import "ios/web/public/test/fakes/test_web_state_delegate.h"
 #import "ios/web/public/test/fakes/test_web_view_content_view.h"
 #import "ios/web/public/web_state/crw_web_controller_observer.h"
 #import "ios/web/public/web_state/ui/crw_content_view.h"
@@ -75,63 +76,6 @@
     : OCMockComplexTypeHelper<CRWWebUserInterfaceDelegate>
 @end
 
-@implementation CRWWebUserInterfaceDelegateStub
-
-- (void)webController:(CRWWebController*)webController
-    runJavaScriptAlertPanelWithMessage:(NSString*)message
-                            requestURL:(const GURL&)requestURL
-                     completionHandler:(void (^)(void))completionHandler {
-  void (^stubBlock)(CRWWebController*, NSString*, const GURL&, id) =
-      [self blockForSelector:_cmd];
-  stubBlock(webController, message, requestURL, completionHandler);
-}
-
-- (void)webController:(CRWWebController*)webController
-    runJavaScriptConfirmPanelWithMessage:(NSString*)message
-                              requestURL:(const GURL&)requestURL
-                       completionHandler:(void (^)(BOOL))completionHandler {
-  void (^stubBlock)(CRWWebController*, NSString*, const GURL&, id) =
-      [self blockForSelector:_cmd];
-  stubBlock(webController, message, requestURL, completionHandler);
-}
-
-- (void)webController:(CRWWebController*)webController
-    runJavaScriptTextInputPanelWithPrompt:(NSString*)message
-                              defaultText:(NSString*)defaultText
-                               requestURL:(const GURL&)requestURL
-                        completionHandler:
-                            (void (^)(NSString* input))completionHandler {
-  void (^stubBlock)(CRWWebController*, NSString*, NSString*, const GURL&, id) =
-      [self blockForSelector:_cmd];
-  stubBlock(webController, message, defaultText, requestURL, completionHandler);
-}
-
-- (BOOL)respondsToSelector:(SEL)selector {
-  // OCMockComplexTypeHelper DCHECKs when respondsToSelector: is called for
-  // expected selector.
-  if (selector == @selector(webController:
-                      runJavaScriptAlertPanelWithMessage:
-                                              requestURL:
-                                       completionHandler:)) {
-    return YES;
-  }
-  if (selector == @selector(webController:
-                      runJavaScriptConfirmPanelWithMessage:
-                                                requestURL:
-                                         completionHandler:)) {
-    return YES;
-  }
-  if (selector == @selector(webController:
-                      runJavaScriptTextInputPanelWithPrompt:
-                                                defaultText:
-                                                 requestURL:
-                                          completionHandler:)) {
-    return YES;
-  }
-  return [super respondsToSelector:selector];
-}
-@end
-
 @implementation MockInteractionLoader {
   // Backs up the property with the same name.
   std::unique_ptr<web::BlockedPopupInfo> _blockedPopupInfo;
@@ -458,39 +402,37 @@
 class CRWWebControllerPageDialogOpenPolicyTest
     : public web::WebTestWithWebController {
  protected:
+  CRWWebControllerPageDialogOpenPolicyTest()
+      : page_url_("https://chromium.test/") {}
   void SetUp() override {
     web::WebTestWithWebController::SetUp();
-    LoadHtml(@"<html><body></body></html>");
+    LoadHtml(@"<html><body></body></html>", page_url_);
     web_delegate_mock_.reset(
         [[OCMockObject mockForProtocol:@protocol(CRWWebDelegate)] retain]);
     [web_controller() setDelegate:web_delegate_mock_];
-    id ui_delegate_oc_mock =
-        [OCMockObject mockForProtocol:@protocol(CRWWebUserInterfaceDelegate)];
-    ui_delegate_mock_.reset([[CRWWebUserInterfaceDelegateStub alloc]
-        initWithRepresentedObject:ui_delegate_oc_mock]);
-    [web_controller() setUIDelegate:ui_delegate_mock_];
-    // Web Controller cancels all dialogs on |close|.
-    [[ui_delegate_mock_ stub] cancelDialogsForWebController:web_controller()];
+    web_state()->SetDelegate(&test_web_delegate_);
   }
   void TearDown() override {
     WaitForBackgroundTasks();
     EXPECT_OCMOCK_VERIFY(web_delegate_mock_);
-    EXPECT_OCMOCK_VERIFY(ui_delegate_mock_);
     [web_controller() setDelegate:nil];
-    [web_controller() setUIDelegate:nil];
+    web_state()->SetDelegate(nullptr);
 
     web::WebTestWithWebController::TearDown();
   }
-  // Returns CRWWebDelegate mock object.
   id web_delegate_mock() { return web_delegate_mock_; };
-  // Returns CRWWebUserInterfaceDelegate mock object.
-  id ui_delegate_mock() { return ui_delegate_mock_; };
+  web::TestJavaScriptDialogPresenter* js_dialog_presenter() {
+    return test_web_delegate_.GetTestJavaScriptDialogPresenter();
+  }
+  const std::vector<web::TestJavaScriptDialog>& requested_dialogs() {
+    return js_dialog_presenter()->requested_dialogs();
+  }
+  const GURL& page_url() { return page_url_; }
 
  private:
-  // Mocks CRWWebDelegate object.
+  web::TestWebStateDelegate test_web_delegate_;
   base::scoped_nsprotocol<id> web_delegate_mock_;
-  // Mocks CRWWebUserInterfaceDelegate object.
-  base::scoped_nsprotocol<id> ui_delegate_mock_;
+  GURL page_url_;
 };
 
 // Tests that window.alert dialog is suppressed for DIALOG_POLICY_SUPPRESS.
@@ -503,109 +445,98 @@
 
 // Tests that window.alert dialog is shown for DIALOG_POLICY_ALLOW.
 TEST_F(CRWWebControllerPageDialogOpenPolicyTest, AllowAlert) {
-  SEL selector = @selector(webController:
-      runJavaScriptAlertPanelWithMessage:
-                              requestURL:
-                       completionHandler:);
-  [ui_delegate_mock() onSelector:selector
-            callBlockExpectation:^(CRWWebController* controller,
-                                   NSString* message, const GURL& url,
-                                   ProceduralBlock completion_handler) {
-              EXPECT_NSEQ(web_controller(), controller);
-              EXPECT_NSEQ(@"test", message);
-              web::URLVerificationTrustLevel unused;
-              EXPECT_EQ([controller currentURLWithTrustLevel:&unused], url);
-              completion_handler();
-            }];
+  ASSERT_TRUE(requested_dialogs().empty());
 
   [web_controller() setShouldSuppressDialogs:NO];
   ExecuteJavaScript(@"alert('test')");
+
+  ASSERT_EQ(1U, requested_dialogs().size());
+  web::TestJavaScriptDialog dialog = requested_dialogs()[0];
+  EXPECT_EQ(web_state(), dialog.web_state);
+  EXPECT_EQ(page_url(), dialog.origin_url);
+  EXPECT_EQ(web::JAVASCRIPT_DIALOG_TYPE_ALERT, dialog.java_script_dialog_type);
+  EXPECT_NSEQ(@"test", dialog.message_text);
+  EXPECT_FALSE(dialog.default_prompt_text);
 };
 
 // Tests that window.confirm dialog is suppressed for DIALOG_POLICY_SUPPRESS.
 TEST_F(CRWWebControllerPageDialogOpenPolicyTest, SuppressConfirm) {
+  ASSERT_TRUE(requested_dialogs().empty());
+
   [[web_delegate_mock() expect]
       webControllerDidSuppressDialog:web_controller()];
   [web_controller() setShouldSuppressDialogs:YES];
   EXPECT_NSEQ(@NO, ExecuteJavaScript(@"confirm('test')"));
+
+  ASSERT_TRUE(requested_dialogs().empty());
 };
 
 // Tests that window.confirm dialog is shown for DIALOG_POLICY_ALLOW and
 // it's result is true.
 TEST_F(CRWWebControllerPageDialogOpenPolicyTest, AllowConfirmWithTrue) {
-  SEL selector = @selector(webController:
-      runJavaScriptConfirmPanelWithMessage:
-                                requestURL:
-                         completionHandler:);
-  [ui_delegate_mock()
-                onSelector:selector
-      callBlockExpectation:^(CRWWebController* controller, NSString* message,
-                             const GURL& url, id completion_handler) {
-        EXPECT_NSEQ(web_controller(), controller);
-        EXPECT_NSEQ(@"test", message);
-        web::URLVerificationTrustLevel unused;
-        EXPECT_EQ([controller currentURLWithTrustLevel:&unused], url);
-        void (^callable_block)(BOOL) = completion_handler;
-        callable_block(YES);
-      }];
+  ASSERT_TRUE(requested_dialogs().empty());
+
+  js_dialog_presenter()->set_callback_success_argument(true);
 
   [web_controller() setShouldSuppressDialogs:NO];
   EXPECT_NSEQ(@YES, ExecuteJavaScript(@"confirm('test')"));
+
+  ASSERT_EQ(1U, requested_dialogs().size());
+  web::TestJavaScriptDialog dialog = requested_dialogs()[0];
+  EXPECT_EQ(web_state(), dialog.web_state);
+  EXPECT_EQ(page_url(), dialog.origin_url);
+  EXPECT_EQ(web::JAVASCRIPT_DIALOG_TYPE_CONFIRM,
+            dialog.java_script_dialog_type);
+  EXPECT_NSEQ(@"test", dialog.message_text);
+  EXPECT_FALSE(dialog.default_prompt_text);
 }
 
 // Tests that window.confirm dialog is shown for DIALOG_POLICY_ALLOW and
 // it's result is false.
 TEST_F(CRWWebControllerPageDialogOpenPolicyTest, AllowConfirmWithFalse) {
-  SEL selector = @selector(webController:
-      runJavaScriptConfirmPanelWithMessage:
-                                requestURL:
-                         completionHandler:);
-  [ui_delegate_mock()
-                onSelector:selector
-      callBlockExpectation:^(CRWWebController* controller, NSString* message,
-                             const GURL& url, id completion_handler) {
-        EXPECT_NSEQ(web_controller(), controller);
-        EXPECT_NSEQ(@"test", message);
-        web::URLVerificationTrustLevel unused;
-        EXPECT_EQ([controller currentURLWithTrustLevel:&unused], url);
-        void (^callable_block)(BOOL) = completion_handler;
-        callable_block(NO);
-      }];
+  ASSERT_TRUE(requested_dialogs().empty());
 
   [web_controller() setShouldSuppressDialogs:NO];
   EXPECT_NSEQ(@NO, ExecuteJavaScript(@"confirm('test')"));
+
+  ASSERT_EQ(1U, requested_dialogs().size());
+  web::TestJavaScriptDialog dialog = requested_dialogs()[0];
+  EXPECT_EQ(web_state(), dialog.web_state);
+  EXPECT_EQ(page_url(), dialog.origin_url);
+  EXPECT_EQ(web::JAVASCRIPT_DIALOG_TYPE_CONFIRM,
+            dialog.java_script_dialog_type);
+  EXPECT_NSEQ(@"test", dialog.message_text);
+  EXPECT_FALSE(dialog.default_prompt_text);
 }
 
 // Tests that window.prompt dialog is suppressed for DIALOG_POLICY_SUPPRESS.
 TEST_F(CRWWebControllerPageDialogOpenPolicyTest, SuppressPrompt) {
+  ASSERT_TRUE(requested_dialogs().empty());
+
   [[web_delegate_mock() expect]
       webControllerDidSuppressDialog:web_controller()];
   [web_controller() setShouldSuppressDialogs:YES];
   EXPECT_EQ([NSNull null], ExecuteJavaScript(@"prompt('Yes?', 'No')"));
+
+  ASSERT_TRUE(requested_dialogs().empty());
 }
 
 // Tests that window.prompt dialog is shown for DIALOG_POLICY_ALLOW.
 TEST_F(CRWWebControllerPageDialogOpenPolicyTest, AllowPrompt) {
-  SEL selector = @selector(webController:
-      runJavaScriptTextInputPanelWithPrompt:
-                                defaultText:
-                                 requestURL:
-                          completionHandler:);
-  [ui_delegate_mock() onSelector:selector
-            callBlockExpectation:^(CRWWebController* controller,
-                                   NSString* message, NSString* default_text,
-                                   const GURL& url, id completion_handler) {
-              EXPECT_NSEQ(web_controller(), controller);
-              EXPECT_NSEQ(@"Yes?", message);
-              EXPECT_NSEQ(@"No", default_text);
-              web::URLVerificationTrustLevel unused;
-              EXPECT_EQ([controller currentURLWithTrustLevel:&unused], url);
-              void (^callable_block)(NSString*) = completion_handler;
-              callable_block(@"Maybe");
-            }];
+  ASSERT_TRUE(requested_dialogs().empty());
+
+  js_dialog_presenter()->set_callback_user_input_argument(@"Maybe");
 
   [web_controller() setShouldSuppressDialogs:NO];
   EXPECT_NSEQ(@"Maybe", ExecuteJavaScript(@"prompt('Yes?', 'No')"));
+
+  ASSERT_EQ(1U, requested_dialogs().size());
+  web::TestJavaScriptDialog dialog = requested_dialogs()[0];
+  EXPECT_EQ(web_state(), dialog.web_state);
+  EXPECT_EQ(page_url(), dialog.origin_url);
+  EXPECT_EQ(web::JAVASCRIPT_DIALOG_TYPE_PROMPT, dialog.java_script_dialog_type);
+  EXPECT_NSEQ(@"Yes?", dialog.message_text);
+  EXPECT_NSEQ(@"No", dialog.default_prompt_text);
 }
 
 // Tests that geolocation dialog is suppressed for DIALOG_POLICY_SUPPRESS.
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index f6180e0..13dd7915 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -603,15 +603,17 @@
 
   // Execute script with callback.
   __block std::unique_ptr<base::Value> execution_result;
+  __block bool execution_complete = false;
   web_state_->ExecuteJavaScript(base::UTF8ToUTF16("window.foo"),
                                 base::BindBlock(^(const base::Value* value) {
-                                  ASSERT_TRUE(value);
                                   execution_result = value->CreateDeepCopy();
+                                  execution_complete = true;
                                 }));
-  base::test::ios::WaitUntilCondition(^bool() {
-    return execution_result.get();
+  base::test::ios::WaitUntilCondition(^{
+    return execution_complete;
   });
 
+  ASSERT_TRUE(execution_result);
   std::string string_result;
   execution_result->GetAsString(&string_result);
   EXPECT_EQ("bar", string_result);
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index 859efbb..07b878a 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -206,12 +206,14 @@
 
     deps = [
       ":ipc",
+      ":test_interfaces",
       ":test_support",
       "//base",
       "//base:i18n",
       "//base/test:test_support",
       "//mojo/edk/system",
       "//mojo/edk/test:test_support",
+      "//mojo/edk/test:test_support_impl",
       "//testing/gtest",
     ]
   }
@@ -219,8 +221,6 @@
   static_library("test_support") {
     testonly = true
     sources = [
-      "ipc_perftest_support.cc",
-      "ipc_perftest_support.h",
       "ipc_security_test_util.cc",
       "ipc_security_test_util.h",
       "ipc_test_base.cc",
diff --git a/ipc/ipc_mojo_perftest.cc b/ipc/ipc_mojo_perftest.cc
index 0fcb8ba81..4c0990a9 100644
--- a/ipc/ipc_mojo_perftest.cc
+++ b/ipc/ipc_mojo_perftest.cc
@@ -8,36 +8,408 @@
 #include "base/memory/ptr_util.h"
 #include "base/process/process_metrics.h"
 #include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/perf_time_logger.h"
+#include "base/test/test_io_thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "ipc/ipc_channel_mojo.h"
-#include "ipc/ipc_perftest_support.h"
+#include "ipc/ipc_test.mojom.h"
+#include "ipc/ipc_test_base.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/test/mojo_test_base.h"
 #include "mojo/edk/test/multiprocess_test_helper.h"
 #include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 
 namespace IPC {
 namespace {
 
-class MojoChannelPerfTest : public test::IPCChannelPerfTestBase {
+// This class simply collects stats about abstract "events" (each of which has a
+// start time and an end time).
+class EventTimeTracker {
  public:
-  void TearDown() override {
-    test::IPCChannelPerfTestBase::TearDown();
+  explicit EventTimeTracker(const char* name)
+      : name_(name),
+        count_(0) {
   }
 
-  mojo::edk::test::MultiprocessTestHelper helper_;
+  void AddEvent(const base::TimeTicks& start, const base::TimeTicks& end) {
+    DCHECK(end >= start);
+    count_++;
+    base::TimeDelta duration = end - start;
+    total_duration_ += duration;
+    max_duration_ = std::max(max_duration_, duration);
+  }
+
+  void ShowResults() const {
+    VLOG(1) << name_ << " count: " << count_;
+    VLOG(1) << name_ << " total duration: "
+            << total_duration_.InMillisecondsF() << " ms";
+    VLOG(1) << name_ << " average duration: "
+            << (total_duration_.InMillisecondsF() / static_cast<double>(count_))
+            << " ms";
+    VLOG(1) << name_ << " maximum duration: "
+            << max_duration_.InMillisecondsF() << " ms";
+  }
+
+  void Reset() {
+    count_ = 0;
+    total_duration_ = base::TimeDelta();
+    max_duration_ = base::TimeDelta();
+  }
+
+ private:
+  const std::string name_;
+
+  uint64_t count_;
+  base::TimeDelta total_duration_;
+  base::TimeDelta max_duration_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventTimeTracker);
+};
+
+class PerformanceChannelListener : public Listener {
+ public:
+  explicit PerformanceChannelListener(const std::string& label)
+      : label_(label),
+        sender_(NULL),
+        msg_count_(0),
+        msg_size_(0),
+        count_down_(0),
+        latency_tracker_("Server messages") {
+    VLOG(1) << "Server listener up";
+  }
+
+  ~PerformanceChannelListener() override {
+    VLOG(1) << "Server listener down";
+  }
+
+  void Init(Sender* sender) {
+    DCHECK(!sender_);
+    sender_ = sender;
+  }
+
+  // Call this before running the message loop.
+  void SetTestParams(int msg_count, size_t msg_size) {
+    DCHECK_EQ(0, count_down_);
+    msg_count_ = msg_count;
+    msg_size_ = msg_size;
+    count_down_ = msg_count_;
+    payload_ = std::string(msg_size_, 'a');
+  }
+
+  bool OnMessageReceived(const Message& message) override {
+    CHECK(sender_);
+
+    base::PickleIterator iter(message);
+    int64_t time_internal;
+    EXPECT_TRUE(iter.ReadInt64(&time_internal));
+    int msgid;
+    EXPECT_TRUE(iter.ReadInt(&msgid));
+    std::string reflected_payload;
+    EXPECT_TRUE(iter.ReadString(&reflected_payload));
+
+    // Include message deserialization in latency.
+    base::TimeTicks now = base::TimeTicks::Now();
+
+    if (reflected_payload == "hello") {
+      // Start timing on hello.
+      latency_tracker_.Reset();
+      DCHECK(!perf_logger_.get());
+      std::string test_name =
+          base::StringPrintf("IPC_%s_Perf_%dx_%u",
+                             label_.c_str(),
+                             msg_count_,
+                             static_cast<unsigned>(msg_size_));
+      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
+    } else {
+      DCHECK_EQ(payload_.size(), reflected_payload.size());
+
+      latency_tracker_.AddEvent(
+          base::TimeTicks::FromInternalValue(time_internal), now);
+
+      CHECK(count_down_ > 0);
+      count_down_--;
+      if (count_down_ == 0) {
+        perf_logger_.reset();  // Stop the perf timer now.
+        latency_tracker_.ShowResults();
+        base::MessageLoop::current()->QuitWhenIdle();
+        return true;
+      }
+    }
+
+    Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
+    msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
+    msg->WriteInt(count_down_);
+    msg->WriteString(payload_);
+    sender_->Send(msg);
+    return true;
+  }
+
+ private:
+  std::string label_;
+  Sender* sender_;
+  int msg_count_;
+  size_t msg_size_;
+
+  int count_down_;
+  std::string payload_;
+  EventTimeTracker latency_tracker_;
+  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
+};
+
+// This channel listener just replies to all messages with the exact same
+// message. It assumes each message has one string parameter. When the string
+// "quit" is sent, it will exit.
+class ChannelReflectorListener : public Listener {
+ public:
+  ChannelReflectorListener()
+      : channel_(NULL),
+        latency_tracker_("Client messages") {
+    VLOG(1) << "Client listener up";
+  }
+
+  ~ChannelReflectorListener() override {
+    VLOG(1) << "Client listener down";
+    latency_tracker_.ShowResults();
+  }
+
+  void Init(Channel* channel) {
+    DCHECK(!channel_);
+    channel_ = channel;
+  }
+
+  bool OnMessageReceived(const Message& message) override {
+    CHECK(channel_);
+
+    base::PickleIterator iter(message);
+    int64_t time_internal;
+    EXPECT_TRUE(iter.ReadInt64(&time_internal));
+    int msgid;
+    EXPECT_TRUE(iter.ReadInt(&msgid));
+    base::StringPiece payload;
+    EXPECT_TRUE(iter.ReadStringPiece(&payload));
+
+    // Include message deserialization in latency.
+    base::TimeTicks now = base::TimeTicks::Now();
+
+    if (payload == "hello") {
+      latency_tracker_.Reset();
+    } else if (payload == "quit") {
+      latency_tracker_.ShowResults();
+      base::MessageLoop::current()->QuitWhenIdle();
+      return true;
+    } else {
+      // Don't track hello and quit messages.
+      latency_tracker_.AddEvent(
+          base::TimeTicks::FromInternalValue(time_internal), now);
+    }
+
+    Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
+    msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
+    msg->WriteInt(msgid);
+    msg->WriteString(payload);
+    channel_->Send(msg);
+    return true;
+  }
+
+ private:
+  Channel* channel_;
+  EventTimeTracker latency_tracker_;
+};
+
+// This class locks the current thread to a particular CPU core. This is
+// important because otherwise the different threads and processes of these
+// tests end up on different CPU cores which means that all of the cores are
+// lightly loaded so the OS (Windows and Linux) fails to ramp up the CPU
+// frequency, leading to unpredictable and often poor performance.
+class LockThreadAffinity {
+ public:
+  explicit LockThreadAffinity(int cpu_number) : affinity_set_ok_(false) {
+#if defined(OS_WIN)
+    const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
+    old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
+    affinity_set_ok_ = old_affinity_ != 0;
+#elif defined(OS_LINUX)
+    cpu_set_t cpuset;
+    CPU_ZERO(&cpuset);
+    CPU_SET(cpu_number, &cpuset);
+    auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
+    DCHECK_EQ(0, get_result);
+    auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
+    // Check for get_result failure, even though it should always succeed.
+    affinity_set_ok_ = (set_result == 0) && (get_result == 0);
+#endif
+    if (!affinity_set_ok_)
+      LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
+  }
+
+  ~LockThreadAffinity() {
+    if (!affinity_set_ok_)
+      return;
+#if defined(OS_WIN)
+    auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
+    DCHECK_NE(0u, set_result);
+#elif defined(OS_LINUX)
+    auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
+    DCHECK_EQ(0, set_result);
+#endif
+  }
+
+ private:
+  bool affinity_set_ok_;
+#if defined(OS_WIN)
+  DWORD_PTR old_affinity_;
+#elif defined(OS_LINUX)
+  cpu_set_t old_cpuset_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(LockThreadAffinity);
+};
+
+class PingPongTestParams {
+ public:
+  PingPongTestParams(size_t size, int count)
+      : message_size_(size), message_count_(count) {
+  }
+
+  size_t message_size() const { return message_size_; }
+  int message_count() const { return message_count_; }
+
+ private:
+  size_t message_size_;
+  int message_count_;
+};
+
+std::vector<PingPongTestParams> GetDefaultTestParams() {
+  // Test several sizes. We use 12^N for message size, and limit the message
+  // count to keep the test duration reasonable.
+#ifdef NDEBUG
+  const int kMultiplier = 100;
+#else
+  // Debug builds on Windows run these tests orders of magnitude more slowly.
+  const int kMultiplier = 1;
+#endif
+  std::vector<PingPongTestParams> list;
+  list.push_back(PingPongTestParams(12, 500 * kMultiplier));
+  list.push_back(PingPongTestParams(144, 500 * kMultiplier));
+  list.push_back(PingPongTestParams(1728, 500 * kMultiplier));
+  list.push_back(PingPongTestParams(20736, 120 * kMultiplier));
+  list.push_back(PingPongTestParams(248832, 10 * kMultiplier));
+  return list;
+}
+
+// Avoid core 0 due to conflicts with Intel's Power Gadget.
+// Setting thread affinity will fail harmlessly on single/dual core machines.
+const int kSharedCore = 2;
+
+class MojoChannelPerfTest : public IPCChannelMojoTestBase {
+ public:
+  MojoChannelPerfTest() = default;
+  ~MojoChannelPerfTest() override = default;
+
+  void RunTestChannelPingPong() {
+    Init("MojoPerfTestClient");
+
+    // Set up IPC channel and start client.
+    PerformanceChannelListener listener("Channel");
+    CreateChannel(&listener);
+    listener.Init(channel());
+    ASSERT_TRUE(ConnectChannel());
+
+    LockThreadAffinity thread_locker(kSharedCore);
+    std::vector<PingPongTestParams> params = GetDefaultTestParams();
+    for (size_t i = 0; i < params.size(); i++) {
+      listener.SetTestParams(params[i].message_count(),
+                             params[i].message_size());
+
+      // This initial message will kick-start the ping-pong of messages.
+      Message* message =
+          new Message(0, 2, Message::PRIORITY_NORMAL);
+      message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
+      message->WriteInt(-1);
+      message->WriteString("hello");
+      sender()->Send(message);
+
+      // Run message loop.
+      base::RunLoop().Run();
+    }
+
+    // Send quit message.
+    Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
+    message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
+    message->WriteInt(-1);
+    message->WriteString("quit");
+    sender()->Send(message);
+
+    EXPECT_TRUE(WaitForClientShutdown());
+    DestroyChannel();
+}
+
+  void RunTestChannelProxyPingPong() {
+    io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));
+
+    Init("MojoPerfTestClient");
+
+    // Set up IPC channel and start client.
+    PerformanceChannelListener listener("ChannelProxy");
+    auto channel_proxy = IPC::ChannelProxy::Create(
+        TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
+        io_thread_->task_runner());
+    listener.Init(channel_proxy.get());
+
+    LockThreadAffinity thread_locker(kSharedCore);
+    std::vector<PingPongTestParams> params = GetDefaultTestParams();
+    for (size_t i = 0; i < params.size(); i++) {
+      listener.SetTestParams(params[i].message_count(),
+                              params[i].message_size());
+
+      // This initial message will kick-start the ping-pong of messages.
+      Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
+      message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
+      message->WriteInt(-1);
+      message->WriteString("hello");
+      channel_proxy->Send(message);
+
+      // Run message loop.
+      base::RunLoop().Run();
+    }
+
+    // Send quit message.
+    Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
+    message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
+    message->WriteInt(-1);
+    message->WriteString("quit");
+    channel_proxy->Send(message);
+
+    EXPECT_TRUE(WaitForClientShutdown());
+    channel_proxy.reset();
+
+    io_thread_.reset();
+  }
+
+  scoped_refptr<base::TaskRunner> io_task_runner() {
+    if (io_thread_)
+      return io_thread_->task_runner();
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+ private:
+  std::unique_ptr<base::TestIOThread> io_thread_;
 };
 
 TEST_F(MojoChannelPerfTest, ChannelPingPong) {
-  RunTestChannelPingPong(GetDefaultTestParams());
+  RunTestChannelPingPong();
 
   base::RunLoop run_loop;
   run_loop.RunUntilIdle();
 }
 
 TEST_F(MojoChannelPerfTest, ChannelProxyPingPong) {
-  RunTestChannelProxyPingPong(GetDefaultTestParams());
+  RunTestChannelProxyPingPong();
 
   base::RunLoop run_loop;
   run_loop.RunUntilIdle();
@@ -57,36 +429,34 @@
   }
 }
 
-class MojoPerfTestClient : public test::PingPongTestClient {
+class MojoPerfTestClient {
  public:
-  typedef test::PingPongTestClient SuperType;
+  MojoPerfTestClient()
+      : listener_(new ChannelReflectorListener()) {
+    mojo::edk::test::MultiprocessTestHelper::ChildSetup();
+  }
 
-  MojoPerfTestClient();
+  ~MojoPerfTestClient() = default;
 
-  std::unique_ptr<Channel> CreateChannel(Listener* listener) override;
+  int Run(MojoHandle handle) {
+    handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle));
+    LockThreadAffinity thread_locker(kSharedCore);
+    std::unique_ptr<Channel> channel = ChannelMojo::Create(
+        std::move(handle_), Channel::MODE_CLIENT, listener_.get());
+    listener_->Init(channel.get());
+    CHECK(channel->Connect());
 
-  int Run(MojoHandle handle);
+    base::RunLoop().Run();
+    return 0;
+  }
 
  private:
-  mojo::edk::test::ScopedIPCSupport ipc_support_;
+  base::MessageLoopForIO main_message_loop_;
+  std::unique_ptr<ChannelReflectorListener> listener_;
+  std::unique_ptr<Channel> channel_;
   mojo::ScopedMessagePipeHandle handle_;
 };
 
-MojoPerfTestClient::MojoPerfTestClient()
-    : ipc_support_(base::ThreadTaskRunnerHandle::Get()) {
-  mojo::edk::test::MultiprocessTestHelper::ChildSetup();
-}
-
-std::unique_ptr<Channel> MojoPerfTestClient::CreateChannel(Listener* listener) {
-  return ChannelMojo::Create(std::move(handle_), Channel::MODE_CLIENT,
-                             listener);
-}
-
-int MojoPerfTestClient::Run(MojoHandle handle) {
-  handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle));
-  return RunMain();
-}
-
 MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
   MojoPerfTestClient client;
   int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(
@@ -98,5 +468,208 @@
   return rv;
 }
 
+class ReflectorImpl : public IPC::mojom::Reflector {
+ public:
+  explicit ReflectorImpl(mojo::ScopedMessagePipeHandle handle)
+      : binding_(this, std::move(handle)) {}
+  ~ReflectorImpl() override {
+    ignore_result(binding_.Unbind().PassMessagePipe().release());
+  }
+
+ private:
+  // IPC::mojom::Reflector:
+  void Ping(const std::string& value, const PingCallback& callback) override {
+    callback.Run(value);
+  }
+
+  void Quit() override {
+    base::MessageLoop::current()->QuitWhenIdle();
+  }
+
+  mojo::Binding<IPC::mojom::Reflector> binding_;
+};
+
+class MojoInterfacePerfTest : public mojo::edk::test::MojoTestBase {
+ public:
+  MojoInterfacePerfTest() : message_count_(0), count_down_(0) {}
+
+ protected:
+  void RunPingPongServer(MojoHandle mp, const std::string& label) {
+    base::MessageLoop main_message_loop;
+    label_ = label;
+
+    mojo::MessagePipeHandle mp_handle(mp);
+    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
+    ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(
+        std::move(scoped_mp), 0u));
+
+    LockThreadAffinity thread_locker(kSharedCore);
+    std::vector<PingPongTestParams> params = GetDefaultTestParams();
+    for (size_t i = 0; i < params.size(); i++) {
+      ping_receiver_->Ping(
+          "hello",
+          base::Bind(&MojoInterfacePerfTest::OnPong, base::Unretained(this)));
+      message_count_ = count_down_ = params[i].message_count();
+      payload_ = std::string(params[i].message_size(), 'a');
+
+      base::RunLoop().Run();
+    }
+
+    ping_receiver_->Quit();
+
+    ignore_result(ping_receiver_.PassInterface().PassHandle().release());
+  }
+
+  void OnPong(const std::string& value) {
+    if (value == "hello") {
+      DCHECK(!perf_logger_.get());
+      std::string test_name =
+          base::StringPrintf("IPC_%s_Perf_%dx_%zu",
+                             label_.c_str(),
+                             message_count_,
+                             payload_.size());
+      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
+    } else {
+      DCHECK_EQ(payload_.size(), value.size());
+
+      CHECK(count_down_ > 0);
+      count_down_--;
+      if (count_down_ == 0) {
+        perf_logger_.reset();
+        base::MessageLoop::current()->QuitWhenIdle();
+        return;
+      }
+    }
+
+    ping_receiver_->Ping(
+        payload_,
+        base::Bind(&MojoInterfacePerfTest::OnPong, base::Unretained(this)));
+  }
+
+  static int RunPingPongClient(MojoHandle mp) {
+    mojo::MessagePipeHandle mp_handle(mp);
+    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
+
+    // In single process mode, this is running in a task and by default other
+    // tasks (in particular, the binding) won't run. To keep the single process
+    // and multi-process code paths the same, enable nestable tasks.
+    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
+        base::MessageLoop::current());
+
+    LockThreadAffinity thread_locker(kSharedCore);
+    ReflectorImpl impl(std::move(scoped_mp));
+    base::RunLoop().Run();
+    return 0;
+  }
+
+ private:
+  int message_count_;
+  int count_down_;
+  std::string label_;
+  std::string payload_;
+  IPC::mojom::ReflectorPtr ping_receiver_;
+  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoInterfacePerfTest);
+};
+
+DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoInterfacePerfTest, h) {
+  base::MessageLoop main_message_loop;
+  return RunPingPongClient(h);
+}
+
+// Similar to MojoChannelPerfTest above, but uses a Mojo interface instead of
+// raw IPC::Messages.
+TEST_F(MojoInterfacePerfTest, MultiprocessPingPong) {
+  RUN_CHILD_ON_PIPE(PingPongClient, h)
+    RunPingPongServer(h, "MultiProcess");
+  END_CHILD()
+}
+
+// A single process version of the above test.
+TEST_F(MojoInterfacePerfTest, SingleProcessPingPong) {
+  MojoHandle server_handle, client_handle;
+  CreateMessagePipe(&server_handle, &client_handle);
+
+  base::Thread client_thread("PingPongClient");
+  client_thread.Start();
+  client_thread.task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(base::IgnoreResult(&RunPingPongClient), client_handle));
+
+  RunPingPongServer(server_handle, "SingleProcess");
+}
+
+class CallbackPerfTest : public testing::Test {
+ public:
+  CallbackPerfTest()
+      : client_thread_("PingPongClient"), message_count_(0), count_down_(0) {}
+
+ protected:
+  void RunPingPongServer() {
+    client_thread_.Start();
+
+    LockThreadAffinity thread_locker(kSharedCore);
+    std::vector<PingPongTestParams> params = GetDefaultTestParams();
+    for (size_t i = 0; i < params.size(); i++) {
+      std::string hello("hello");
+      client_thread_.task_runner()->PostTask(
+          FROM_HERE,
+          base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), hello));
+      message_count_ = count_down_ = params[i].message_count();
+      payload_ = std::string(params[i].message_size(), 'a');
+
+      base::RunLoop().Run();
+    }
+  }
+
+  void Ping(const std::string& value) {
+    main_message_loop.task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&CallbackPerfTest::OnPong, base::Unretained(this),
+        value));
+  }
+
+  void OnPong(const std::string& value) {
+    if (value == "hello") {
+      DCHECK(!perf_logger_.get());
+      std::string test_name =
+          base::StringPrintf("Callback_Perf_%dx_%zu",
+                             message_count_,
+                             payload_.size());
+      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
+    } else {
+      DCHECK_EQ(payload_.size(), value.size());
+
+      CHECK(count_down_ > 0);
+      count_down_--;
+      if (count_down_ == 0) {
+        perf_logger_.reset();
+        base::MessageLoop::current()->QuitWhenIdle();
+        return;
+      }
+    }
+
+    client_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), payload_));
+  }
+
+ private:
+  base::Thread client_thread_;
+  base::MessageLoop main_message_loop;
+  int message_count_;
+  int count_down_;
+  std::string payload_;
+  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallbackPerfTest);
+};
+
+// Sends the same data as above using PostTask instead of IPCs for comparison.
+TEST_F(CallbackPerfTest, PingPong) {
+  RunPingPongServer();
+}
+
 }  // namespace
 }  // namespace IPC
diff --git a/ipc/ipc_perftest_support.cc b/ipc/ipc_perftest_support.cc
deleted file mode 100644
index 10516bc..0000000
--- a/ipc/ipc_perftest_support.cc
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ipc/ipc_perftest_support.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <memory>
-#include <string>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/pickle.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/perf_time_logger.h"
-#include "base/test/test_io_thread.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "ipc/ipc_channel.h"
-#include "ipc/ipc_channel_proxy.h"
-#include "ipc/ipc_descriptors.h"
-#include "ipc/ipc_message_utils.h"
-#include "ipc/ipc_sender.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
-
-namespace IPC {
-namespace test {
-
-// Avoid core 0 due to conflicts with Intel's Power Gadget.
-// Setting thread affinity will fail harmlessly on single/dual core machines.
-const int kSharedCore = 2;
-
-// This class simply collects stats about abstract "events" (each of which has a
-// start time and an end time).
-class EventTimeTracker {
- public:
-  explicit EventTimeTracker(const char* name)
-      : name_(name),
-        count_(0) {
-  }
-
-  void AddEvent(const base::TimeTicks& start, const base::TimeTicks& end) {
-    DCHECK(end >= start);
-    count_++;
-    base::TimeDelta duration = end - start;
-    total_duration_ += duration;
-    max_duration_ = std::max(max_duration_, duration);
-  }
-
-  void ShowResults() const {
-    VLOG(1) << name_ << " count: " << count_;
-    VLOG(1) << name_ << " total duration: "
-            << total_duration_.InMillisecondsF() << " ms";
-    VLOG(1) << name_ << " average duration: "
-            << (total_duration_.InMillisecondsF() / static_cast<double>(count_))
-            << " ms";
-    VLOG(1) << name_ << " maximum duration: "
-            << max_duration_.InMillisecondsF() << " ms";
-  }
-
-  void Reset() {
-    count_ = 0;
-    total_duration_ = base::TimeDelta();
-    max_duration_ = base::TimeDelta();
-  }
-
- private:
-  const std::string name_;
-
-  uint64_t count_;
-  base::TimeDelta total_duration_;
-  base::TimeDelta max_duration_;
-
-  DISALLOW_COPY_AND_ASSIGN(EventTimeTracker);
-};
-
-// This channel listener just replies to all messages with the exact same
-// message. It assumes each message has one string parameter. When the string
-// "quit" is sent, it will exit.
-class ChannelReflectorListener : public Listener {
- public:
-  ChannelReflectorListener()
-      : channel_(NULL),
-        latency_tracker_("Client messages") {
-    VLOG(1) << "Client listener up";
-  }
-
-  ~ChannelReflectorListener() override {
-    VLOG(1) << "Client listener down";
-    latency_tracker_.ShowResults();
-  }
-
-  void Init(Channel* channel) {
-    DCHECK(!channel_);
-    channel_ = channel;
-  }
-
-  bool OnMessageReceived(const Message& message) override {
-    CHECK(channel_);
-
-    base::PickleIterator iter(message);
-    int64_t time_internal;
-    EXPECT_TRUE(iter.ReadInt64(&time_internal));
-    int msgid;
-    EXPECT_TRUE(iter.ReadInt(&msgid));
-    base::StringPiece payload;
-    EXPECT_TRUE(iter.ReadStringPiece(&payload));
-
-    // Include message deserialization in latency.
-    base::TimeTicks now = base::TimeTicks::Now();
-
-    if (payload == "hello") {
-      latency_tracker_.Reset();
-    } else if (payload == "quit") {
-      latency_tracker_.ShowResults();
-      base::MessageLoop::current()->QuitWhenIdle();
-      return true;
-    } else {
-      // Don't track hello and quit messages.
-      latency_tracker_.AddEvent(
-          base::TimeTicks::FromInternalValue(time_internal), now);
-    }
-
-    Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
-    msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-    msg->WriteInt(msgid);
-    msg->WriteString(payload);
-    channel_->Send(msg);
-    return true;
-  }
-
- private:
-  Channel* channel_;
-  EventTimeTracker latency_tracker_;
-};
-
-class PerformanceChannelListener : public Listener {
- public:
-  explicit PerformanceChannelListener(const std::string& label)
-      : label_(label),
-        sender_(NULL),
-        msg_count_(0),
-        msg_size_(0),
-        count_down_(0),
-        latency_tracker_("Server messages") {
-    VLOG(1) << "Server listener up";
-  }
-
-  ~PerformanceChannelListener() override {
-    VLOG(1) << "Server listener down";
-  }
-
-  void Init(Sender* sender) {
-    DCHECK(!sender_);
-    sender_ = sender;
-  }
-
-  // Call this before running the message loop.
-  void SetTestParams(int msg_count, size_t msg_size) {
-    DCHECK_EQ(0, count_down_);
-    msg_count_ = msg_count;
-    msg_size_ = msg_size;
-    count_down_ = msg_count_;
-    payload_ = std::string(msg_size_, 'a');
-  }
-
-  bool OnMessageReceived(const Message& message) override {
-    CHECK(sender_);
-
-    base::PickleIterator iter(message);
-    int64_t time_internal;
-    EXPECT_TRUE(iter.ReadInt64(&time_internal));
-    int msgid;
-    EXPECT_TRUE(iter.ReadInt(&msgid));
-    std::string reflected_payload;
-    EXPECT_TRUE(iter.ReadString(&reflected_payload));
-
-    // Include message deserialization in latency.
-    base::TimeTicks now = base::TimeTicks::Now();
-
-    if (reflected_payload == "hello") {
-      // Start timing on hello.
-      latency_tracker_.Reset();
-      DCHECK(!perf_logger_.get());
-      std::string test_name =
-          base::StringPrintf("IPC_%s_Perf_%dx_%u",
-                             label_.c_str(),
-                             msg_count_,
-                             static_cast<unsigned>(msg_size_));
-      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
-    } else {
-      DCHECK_EQ(payload_.size(), reflected_payload.size());
-
-      latency_tracker_.AddEvent(
-          base::TimeTicks::FromInternalValue(time_internal), now);
-
-      CHECK(count_down_ > 0);
-      count_down_--;
-      if (count_down_ == 0) {
-        perf_logger_.reset();  // Stop the perf timer now.
-        latency_tracker_.ShowResults();
-        base::MessageLoop::current()->QuitWhenIdle();
-        return true;
-      }
-    }
-
-    Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL);
-    msg->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-    msg->WriteInt(count_down_);
-    msg->WriteString(payload_);
-    sender_->Send(msg);
-    return true;
-  }
-
- private:
-  std::string label_;
-  Sender* sender_;
-  int msg_count_;
-  size_t msg_size_;
-
-  int count_down_;
-  std::string payload_;
-  EventTimeTracker latency_tracker_;
-  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
-};
-
-IPCChannelPerfTestBase::IPCChannelPerfTestBase() = default;
-IPCChannelPerfTestBase::~IPCChannelPerfTestBase() = default;
-
-std::vector<PingPongTestParams>
-IPCChannelPerfTestBase::GetDefaultTestParams() {
-  // Test several sizes. We use 12^N for message size, and limit the message
-  // count to keep the test duration reasonable.
-#ifdef NDEBUG
-  const int kMultiplier = 100;
-#else
-  // Debug builds on Windows run these tests orders of magnitude more slowly.
-  const int kMultiplier = 1;
-#endif
-  std::vector<PingPongTestParams> list;
-  list.push_back(PingPongTestParams(12, 500 * kMultiplier));
-  list.push_back(PingPongTestParams(144, 500 * kMultiplier));
-  list.push_back(PingPongTestParams(1728, 500 * kMultiplier));
-  list.push_back(PingPongTestParams(20736, 120 * kMultiplier));
-  list.push_back(PingPongTestParams(248832, 10 * kMultiplier));
-  return list;
-}
-
-void IPCChannelPerfTestBase::RunTestChannelPingPong(
-    const std::vector<PingPongTestParams>& params) {
-  auto message_loop = base::MakeUnique<base::MessageLoopForIO>();
-  mojo::edk::test::ScopedIPCSupport ipc_support(message_loop->task_runner());
-  InitWithCustomMessageLoop("MojoPerfTestClient", std::move(message_loop));
-
-  // Set up IPC channel and start client.
-  PerformanceChannelListener listener("Channel");
-  CreateChannel(&listener);
-  listener.Init(channel());
-  ASSERT_TRUE(ConnectChannel());
-
-  LockThreadAffinity thread_locker(kSharedCore);
-  for (size_t i = 0; i < params.size(); i++) {
-    listener.SetTestParams(params[i].message_count(),
-                           params[i].message_size());
-
-    // This initial message will kick-start the ping-pong of messages.
-    Message* message =
-        new Message(0, 2, Message::PRIORITY_NORMAL);
-    message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-    message->WriteInt(-1);
-    message->WriteString("hello");
-    sender()->Send(message);
-
-    // Run message loop.
-    base::RunLoop().Run();
-  }
-
-  // Send quit message.
-  Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
-  message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-  message->WriteInt(-1);
-  message->WriteString("quit");
-  sender()->Send(message);
-
-  EXPECT_TRUE(WaitForClientShutdown());
-  DestroyChannel();
-}
-
-void IPCChannelPerfTestBase::RunTestChannelProxyPingPong(
-    const std::vector<PingPongTestParams>& params) {
-  io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));
-  {
-    auto message_loop = base::MakeUnique<base::MessageLoopForIO>();
-    mojo::edk::test::ScopedIPCSupport ipc_support(io_thread_->task_runner());
-    InitWithCustomMessageLoop("MojoPerfTestClient", std::move(message_loop));
-
-    // Set up IPC channel and start client.
-    PerformanceChannelListener listener("ChannelProxy");
-    auto channel_proxy = IPC::ChannelProxy::Create(
-        TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
-        io_thread_->task_runner());
-    listener.Init(channel_proxy.get());
-
-    LockThreadAffinity thread_locker(kSharedCore);
-    for (size_t i = 0; i < params.size(); i++) {
-      listener.SetTestParams(params[i].message_count(),
-                             params[i].message_size());
-
-      // This initial message will kick-start the ping-pong of messages.
-      Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
-      message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-      message->WriteInt(-1);
-      message->WriteString("hello");
-      channel_proxy->Send(message);
-
-      // Run message loop.
-      base::RunLoop().Run();
-    }
-
-    // Send quit message.
-    Message* message = new Message(0, 2, Message::PRIORITY_NORMAL);
-    message->WriteInt64(base::TimeTicks::Now().ToInternalValue());
-    message->WriteInt(-1);
-    message->WriteString("quit");
-    channel_proxy->Send(message);
-
-    EXPECT_TRUE(WaitForClientShutdown());
-    channel_proxy.reset();
-  }
-
-  io_thread_.reset();
-}
-
-
-PingPongTestClient::PingPongTestClient()
-    : listener_(new ChannelReflectorListener()) {
-}
-
-PingPongTestClient::~PingPongTestClient() {
-}
-
-int PingPongTestClient::RunMain() {
-  LockThreadAffinity thread_locker(kSharedCore);
-  std::unique_ptr<Channel> channel = CreateChannel(listener_.get());
-  listener_->Init(channel.get());
-  CHECK(channel->Connect());
-
-  base::RunLoop().Run();
-  return 0;
-}
-
-scoped_refptr<base::TaskRunner> PingPongTestClient::task_runner() {
-  return main_message_loop_.task_runner();
-}
-
-LockThreadAffinity::LockThreadAffinity(int cpu_number)
-    : affinity_set_ok_(false) {
-#if defined(OS_WIN)
-  const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
-  old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
-  affinity_set_ok_ = old_affinity_ != 0;
-#elif defined(OS_LINUX)
-  cpu_set_t cpuset;
-  CPU_ZERO(&cpuset);
-  CPU_SET(cpu_number, &cpuset);
-  auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
-  DCHECK_EQ(0, get_result);
-  auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
-  // Check for get_result failure, even though it should always succeed.
-  affinity_set_ok_ = (set_result == 0) && (get_result == 0);
-#endif
-  if (!affinity_set_ok_)
-    LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
-}
-
-LockThreadAffinity::~LockThreadAffinity() {
-  if (!affinity_set_ok_)
-    return;
-#if defined(OS_WIN)
-  auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
-  DCHECK_NE(0u, set_result);
-#elif defined(OS_LINUX)
-  auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
-  DCHECK_EQ(0, set_result);
-#endif
-}
-
-}  // namespace test
-}  // namespace IPC
diff --git a/ipc/ipc_perftest_support.h b/ipc/ipc_perftest_support.h
deleted file mode 100644
index f98496f7..0000000
--- a/ipc/ipc_perftest_support.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IPC_IPC_PERFTEST_SUPPORT_H_
-#define IPC_IPC_PERFTEST_SUPPORT_H_
-
-#include <stddef.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/test/test_io_thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "ipc/ipc_test_base.h"
-
-namespace IPC {
-namespace test {
-
-class ChannelReflectorListener;
-
-class PingPongTestParams {
- public:
-  PingPongTestParams(size_t size, int count)
-      : message_size_(size), message_count_(count) {
-  }
-
-  size_t message_size() const { return message_size_; }
-  int message_count() const { return message_count_; }
-
- private:
-  size_t message_size_;
-  int message_count_;
-};
-
-class IPCChannelPerfTestBase : public IPCChannelMojoTestBase {
- public:
-  IPCChannelPerfTestBase();
-  ~IPCChannelPerfTestBase() override;
-
-  static std::vector<PingPongTestParams> GetDefaultTestParams();
-
-  void RunTestChannelPingPong(
-      const std::vector<PingPongTestParams>& params_list);
-  void RunTestChannelProxyPingPong(
-      const std::vector<PingPongTestParams>& params_list);
-
-  scoped_refptr<base::TaskRunner> io_task_runner() {
-    if (io_thread_)
-      return io_thread_->task_runner();
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
- private:
-  std::unique_ptr<base::TestIOThread> io_thread_;
-};
-
-class PingPongTestClient {
- public:
-  PingPongTestClient();
-  virtual ~PingPongTestClient();
-
-  virtual std::unique_ptr<Channel> CreateChannel(Listener* listener) = 0;
-  int RunMain();
-  scoped_refptr<base::TaskRunner> task_runner();
-
- private:
-  base::MessageLoopForIO main_message_loop_;
-  std::unique_ptr<ChannelReflectorListener> listener_;
-  std::unique_ptr<Channel> channel_;
-};
-
-// This class locks the current thread to a particular CPU core. This is
-// important because otherwise the different threads and processes of these
-// tests end up on different CPU cores which means that all of the cores are
-// lightly loaded so the OS (Windows and Linux) fails to ramp up the CPU
-// frequency, leading to unpredictable and often poor performance.
-class LockThreadAffinity {
- public:
-  explicit LockThreadAffinity(int cpu_number);
-  ~LockThreadAffinity();
-
- private:
-  bool affinity_set_ok_;
-#if defined(OS_WIN)
-  DWORD_PTR old_affinity_;
-#elif defined(OS_LINUX)
-  cpu_set_t old_cpuset_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(LockThreadAffinity);
-};
-
-}
-}
-
-#endif  // IPC_IPC_PERFTEST_SUPPORT_H_
diff --git a/ipc/ipc_test.mojom b/ipc/ipc_test.mojom
index 8c246d567..a4edfc9 100644
--- a/ipc/ipc_test.mojom
+++ b/ipc/ipc_test.mojom
@@ -28,3 +28,8 @@
 interface IndirectTestDriver {
   GetPingReceiver(associated PingReceiver& request);
 };
+
+interface Reflector {
+  Ping(string value) => (string value);
+  Quit();
+};
diff --git a/ipc/run_all_perftests.cc b/ipc/run_all_perftests.cc
index a942b8b5..1e3d91f 100644
--- a/ipc/run_all_perftests.cc
+++ b/ipc/run_all_perftests.cc
@@ -6,12 +6,18 @@
 
 #include "base/command_line.h"
 #include "base/test/perf_test_suite.h"
+#include "base/test/test_io_thread.h"
 #include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/test/test_support_impl.h"
 
 int main(int argc, char** argv) {
   base::PerfTestSuite test(argc, argv);
 
   mojo::edk::Init();
+  base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
+  mojo::edk::test::ScopedIPCSupport ipc_support(test_io_thread.task_runner());
+  mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
 
   return test.Run();
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index 954cc28..892e8d9 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -281,7 +281,7 @@
                 // We copy blacklisting patterns from software_renderin_list_json.cc
                 // although they are broader than the bugs they refer to.
 
-                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+                if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
                     // Samsung Galaxy Note 2, http://crbug.com/308721.
                     if (Build.MODEL.startsWith("GT-")) return false;
 
@@ -290,12 +290,21 @@
 
                     // Samsung Galaxy Tab, http://crbug.com/408353.
                     if (Build.MODEL.startsWith("SM-T")) return false;
+
+                    // http://crbug.com/600454
+                    if (Build.MODEL.startsWith("SM-G")) return false;
                 }
             }
 
             // MediaTek decoders do not work properly on vp8. See http://crbug.com/446974 and
             // http://crbug.com/597836.
             if (Build.HARDWARE.startsWith("mt")) return false;
+
+            // http://crbug.com/600454
+            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT
+                    && Build.MODEL.startsWith("Lenovo A6000")) {
+                return false;
+            }
         } else if (mime.equals("video/x-vnd.on2.vp9")) {
             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return false;
 
diff --git a/media/blink/renderer_media_player_interface.h b/media/blink/renderer_media_player_interface.h
index 0f1bc34..6202613 100644
--- a/media/blink/renderer_media_player_interface.h
+++ b/media/blink/renderer_media_player_interface.h
@@ -61,12 +61,6 @@
   virtual void OnRemoteRouteAvailabilityChanged(
       blink::WebRemotePlaybackAvailability availability) = 0;
 
-  // Getters of playback state.
-  virtual bool paused() const = 0;
-
-  // True if the loaded media has a playable video track.
-  virtual bool hasVideo() const = 0;
-
   // This function is called by the RendererMediaPlayerManager to pause the
   // video and release the media player and surface texture when we switch tabs.
   // However, the actual GlTexture is not released to keep the video screenshot.
diff --git a/media/blink/webmediaplayer_cast_android.cc b/media/blink/webmediaplayer_cast_android.cc
index 3ea8fe01..1d698f57 100644
--- a/media/blink/webmediaplayer_cast_android.cc
+++ b/media/blink/webmediaplayer_cast_android.cc
@@ -339,14 +339,6 @@
 
 void WebMediaPlayerCast::SuspendAndReleaseResources() {}
 
-bool WebMediaPlayerCast::hasVideo() const {
-  return true;
-}
-
-bool WebMediaPlayerCast::paused() const {
-  return paused_;
-}
-
 void WebMediaPlayerCast::SetDeviceScaleFactor(float scale_factor) {
   device_scale_factor_ = scale_factor;
 }
diff --git a/media/blink/webmediaplayer_cast_android.h b/media/blink/webmediaplayer_cast_android.h
index 3a82b94..ff94c16 100644
--- a/media/blink/webmediaplayer_cast_android.h
+++ b/media/blink/webmediaplayer_cast_android.h
@@ -45,6 +45,7 @@
   void SetMediaPlayerManager(
       RendererMediaPlayerManagerInterface* media_player_manager);
   bool isRemote() const { return is_remote_; }
+  bool IsPaused() const { return paused_; }
 
   double currentTime() const;
   void play();
@@ -82,20 +83,11 @@
   void OnRemoteRouteAvailabilityChanged(
       blink::WebRemotePlaybackAvailability availability) override;
 
-  // Getters of playback state.
-  // bool paused() const override;
-
-  // True if the loaded media has a playable video track.
-  // bool hasVideo() const override;
-
   // This function is called by the RendererMediaPlayerManager to pause the
   // video and release the media player and surface texture when we switch tabs.
   // However, the actual GlTexture is not released to keep the video screenshot.
   void SuspendAndReleaseResources() override;
 
-  bool paused() const override;
-  bool hasVideo() const override;
-
   void SetDeviceScaleFactor(float scale_factor);
   scoped_refptr<VideoFrame> GetCastingBanner();
   void setPoster(const blink::WebURL& poster);
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index a1d7bb8e..c9b8f8a 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -689,7 +689,7 @@
 
 #if defined(OS_ANDROID)  // WMPI_CAST
   if (isRemote())
-    return cast_impl_.paused();
+    return cast_impl_.IsPaused();
 #endif
 
   return pipeline_.GetPlaybackRate() == 0.0f;
diff --git a/mojo/edk/test/run_all_perftests.cc b/mojo/edk/test/run_all_perftests.cc
index 50c41d7a..f426a2a 100644
--- a/mojo/edk/test/run_all_perftests.cc
+++ b/mojo/edk/test/run_all_perftests.cc
@@ -21,10 +21,7 @@
 
   mojo::edk::Init();
   base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
-  // Leak this because its destructor calls mojo::edk::ShutdownIPCSupport which
-  // really does nothing in the new EDK but does depend on the current message
-  // loop, which is destructed inside base::LaunchUnitTests.
-  new mojo::edk::test::ScopedIPCSupport(test_io_thread.task_runner());
+  mojo::edk::test::ScopedIPCSupport ipc_support(test_io_thread.task_runner());
   mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
 
   return test.Run();
diff --git a/net/data/ftp/dir-listing-ls-34 b/net/data/ftp/dir-listing-ls-34
new file mode 100644
index 0000000..6286ed1
--- /dev/null
+++ b/net/data/ftp/dir-listing-ls-34
@@ -0,0 +1,2 @@
+-rw-rw-r--   1 ftpuser  ftpusers    1761280 Dec 20  2002 controle_embarqu‚_avec_labview_real_time_et_compactfieldpoint.ppt
+-rw-rw-r--   1 ftpuser  ftpusers     329216 Dec 20  2002 optimisez_l'acquisition_de_donn‚es_sous_labview.ppt
diff --git a/net/data/ftp/dir-listing-ls-34.expected b/net/data/ftp/dir-listing-ls-34.expected
new file mode 100644
index 0000000..77aed17
--- /dev/null
+++ b/net/data/ftp/dir-listing-ls-34.expected
@@ -0,0 +1,17 @@
+-

+controle_embarqu�_avec_labview_real_time_et_compactfieldpoint.ppt

+1761280

+2002

+12

+20

+0

+0

+

+-

+optimisez_l'acquisition_de_donnFs_sous_labview.ppt

+329216

+2002

+12

+20

+0

+0

diff --git a/net/ftp/ftp_directory_listing_parser.cc b/net/ftp/ftp_directory_listing_parser.cc
index 151c1b9..1096ae0 100644
--- a/net/ftp/ftp_directory_listing_parser.cc
+++ b/net/ftp/ftp_directory_listing_parser.cc
@@ -28,7 +28,7 @@
                   std::vector<FtpDirectoryListingEntry>* entries) {
   for (size_t i = 0; i < entries->size(); i++) {
     if (!base::UTF16ToCodepage(entries->at(i).name, encoding.c_str(),
-                               base::OnStringConversionError::FAIL,
+                               base::OnStringConversionError::SUBSTITUTE,
                                &entries->at(i).raw_name)) {
       return ERR_ENCODING_CONVERSION_FAILED;
     }
@@ -91,7 +91,7 @@
 
   base::string16 converted_text;
   if (base::CodepageToUTF16(text, encoding_name,
-                            base::OnStringConversionError::FAIL,
+                            base::OnStringConversionError::SUBSTITUTE,
                             &converted_text)) {
     const char* const kNewlineSeparators[] = {"\n", "\r\n"};
 
diff --git a/net/ftp/ftp_directory_listing_parser_unittest.cc b/net/ftp/ftp_directory_listing_parser_unittest.cc
index dc75ade5..a7f5bcf 100644
--- a/net/ftp/ftp_directory_listing_parser_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_unittest.cc
@@ -158,6 +158,7 @@
     {"dir-listing-ls-31", OK},
     {"dir-listing-ls-32", OK},  // busybox
     {"dir-listing-ls-33", OK},
+    {"dir-listing-ls-34", OK},  // Broken encoding. Should not fail.
 
     {"dir-listing-netware-1", OK},
     {"dir-listing-netware-2", OK},
diff --git a/services/ui/gpu/gpu_service.cc b/services/ui/gpu/gpu_service.cc
index 0be4a879..ac657e8 100644
--- a/services/ui/gpu/gpu_service.cc
+++ b/services/ui/gpu/gpu_service.cc
@@ -144,7 +144,7 @@
 void GpuService::SendAcceleratedSurfaceCreatedChildWindow(
     gpu::SurfaceHandle parent_window,
     gpu::SurfaceHandle child_window) {
-  ::SetParent(child_window, parent_window);
+  gpu_host_->SetChildSurface(parent_window, child_window);
 }
 #endif
 
diff --git a/services/ui/gpu/interfaces/gpu_host.mojom b/services/ui/gpu/interfaces/gpu_host.mojom
index cd84b9e..b4baab5 100644
--- a/services/ui/gpu/interfaces/gpu_host.mojom
+++ b/services/ui/gpu/interfaces/gpu_host.mojom
@@ -5,6 +5,7 @@
 module ui.mojom;
 
 import "gpu/ipc/common/gpu_info.mojom";
+import "gpu/ipc/common/surface_handle.mojom";
 import "services/ui/gpu/interfaces/context_lost_reason.mojom";
 import "url/mojo/url.mojom";
 
@@ -18,5 +19,7 @@
                  ContextLostReason reason,
                  url.mojom.Url active_url);
 
+  SetChildSurface(gpu.mojom.SurfaceHandle parent,
+                  gpu.mojom.SurfaceHandle child);
   StoreShaderToDisk(int32 client_id, string key, string shader);
 };
diff --git a/services/ui/ws/display_manager.cc b/services/ui/ws/display_manager.cc
index 2d9abf3..f8d6271 100644
--- a/services/ui/ws/display_manager.cc
+++ b/services/ui/ws/display_manager.cc
@@ -63,6 +63,7 @@
 
     DCHECK(displays_.count(display));
     displays_.erase(display);
+    window_server_->OnDisplayDestroyed(display);
   }
   delete display;
 
diff --git a/services/ui/ws/gpu_host.cc b/services/ui/ws/gpu_host.cc
index d8b4eb7..ac2c22fa6 100644
--- a/services/ui/ws/gpu_host.cc
+++ b/services/ui/ws/gpu_host.cc
@@ -19,6 +19,10 @@
 #include "services/ui/ws/gpu_host_delegate.h"
 #include "ui/gfx/buffer_format_util.h"
 
+#if defined(OS_WIN)
+#include "ui/gfx/win/rendering_window_manager.h"
+#endif
+
 namespace ui {
 namespace ws {
 
@@ -99,10 +103,10 @@
       next_client_id_(kInternalGpuChannelClientId + 1),
       main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       gpu_host_binding_(this) {
-  gpu_main_impl_ = base::MakeUnique<GpuMain>(MakeRequest(&gpu_main_));
-  gpu_main_impl_->OnStart();
   // TODO(sad): Once GPU process is split, this would look like:
   //   connector->ConnectToInterface("gpu", &gpu_main_);
+  gpu_main_impl_ = base::MakeUnique<GpuMain>(MakeRequest(&gpu_main_));
+  gpu_main_impl_->OnStart();
   gpu_main_->CreateGpuService(MakeRequest(&gpu_service_),
                               gpu_host_binding_.CreateInterfacePtrAndBind());
   gpu_memory_buffer_manager_ = base::MakeUnique<ServerGpuMemoryBufferManager>(
@@ -119,12 +123,30 @@
       std::move(request));
 }
 
+void GpuHost::OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) {
+#if defined(OS_WIN)
+  gfx::RenderingWindowManager::GetInstance()->RegisterParent(widget);
+#endif
+}
+
+void GpuHost::OnAcceleratedWidgetDestroyed(gfx::AcceleratedWidget widget) {
+#if defined(OS_WIN)
+  gfx::RenderingWindowManager::GetInstance()->UnregisterParent(widget);
+#endif
+}
+
 void GpuHost::CreateDisplayCompositor(
     cc::mojom::DisplayCompositorRequest request,
     cc::mojom::DisplayCompositorClientPtr client) {
   gpu_main_->CreateDisplayCompositor(std::move(request), std::move(client));
 }
 
+void GpuHost::OnBadMessageFromGpu() {
+  // TODO(sad): Received some unexpected message from the gpu process. We
+  // should kill the process and restart it.
+  NOTIMPLEMENTED();
+}
+
 void GpuHost::DidInitialize(const gpu::GPUInfo& gpu_info) {
   gpu_info_ = gpu_info;
   delegate_->OnGpuServiceInitialized();
@@ -140,6 +162,28 @@
                              gpu::error::ContextLostReason reason,
                              const GURL& active_url) {}
 
+void GpuHost::SetChildSurface(gpu::SurfaceHandle parent,
+                              gpu::SurfaceHandle child) {
+#if defined(OS_WIN)
+  // Verify that |parent| was created by the window server.
+  DWORD process_id = 0;
+  DWORD thread_id = GetWindowThreadProcessId(parent, &process_id);
+  if (!thread_id || process_id != ::GetCurrentProcessId()) {
+    OnBadMessageFromGpu();
+    return;
+  }
+
+  // TODO(sad): Also verify that |child| was created by the mus-gpu process.
+
+  if (!gfx::RenderingWindowManager::GetInstance()->RegisterChild(parent,
+                                                                 child)) {
+    OnBadMessageFromGpu();
+  }
+#else
+  NOTREACHED();
+#endif
+}
+
 void GpuHost::StoreShaderToDisk(int32_t client_id,
                                 const std::string& key,
                                 const std::string& shader) {}
diff --git a/services/ui/ws/gpu_host.h b/services/ui/ws/gpu_host.h
index a2511c2..b937266 100644
--- a/services/ui/ws/gpu_host.h
+++ b/services/ui/ws/gpu_host.h
@@ -34,11 +34,16 @@
 
   void Add(mojom::GpuRequest request);
 
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget);
+  void OnAcceleratedWidgetDestroyed(gfx::AcceleratedWidget widget);
+
   // Requests a cc::mojom::DisplayCompositor interface from mus-gpu.
   void CreateDisplayCompositor(cc::mojom::DisplayCompositorRequest request,
                                cc::mojom::DisplayCompositorClientPtr client);
 
  private:
+  void OnBadMessageFromGpu();
+
   // mojom::GpuHost:
   void DidInitialize(const gpu::GPUInfo& gpu_info) override;
   void DidCreateOffscreenContext(const GURL& url) override;
@@ -47,6 +52,8 @@
   void DidLoseContext(bool offscreen,
                       gpu::error::ContextLostReason reason,
                       const GURL& active_url) override;
+  void SetChildSurface(gpu::SurfaceHandle parent,
+                       gpu::SurfaceHandle child) override;
   void StoreShaderToDisk(int32_t client_id,
                          const std::string& key,
                          const std::string& shader) override;
diff --git a/services/ui/ws/platform_display.h b/services/ui/ws/platform_display.h
index 96b1ca9..39299ae 100644
--- a/services/ui/ws/platform_display.h
+++ b/services/ui/ws/platform_display.h
@@ -13,6 +13,7 @@
 #include "base/strings/string16.h"
 #include "services/ui/display/viewport_metrics.h"
 #include "services/ui/public/interfaces/cursor.mojom.h"
+#include "ui/gfx/native_widget_types.h"
 
 namespace gfx {
 class Rect;
@@ -63,6 +64,10 @@
 
   virtual const display::ViewportMetrics& GetViewportMetrics() const = 0;
 
+  // Returns the AcceleratedWidget associated with the Display. It can return
+  // kNullAcceleratedWidget if the accelerated widget is not available yet.
+  virtual gfx::AcceleratedWidget GetAcceleratedWidget() const = 0;
+
   virtual FrameGenerator* GetFrameGenerator() = 0;
 
   // Overrides factory for testing. Default (NULL) value indicates regular
diff --git a/services/ui/ws/platform_display_default.cc b/services/ui/ws/platform_display_default.cc
index e69588ed..ba50d30 100644
--- a/services/ui/ws/platform_display_default.cc
+++ b/services/ui/ws/platform_display_default.cc
@@ -36,7 +36,8 @@
       image_cursors_(new ImageCursors),
 #endif
       frame_generator_(new FrameGenerator(this, init_params.root_window)),
-      metrics_(init_params.metrics) {
+      metrics_(init_params.metrics),
+      widget_(gfx::kNullAcceleratedWidget) {
   frame_generator_->set_device_scale_factor(
       init_params.metrics.device_scale_factor);
 }
@@ -157,6 +158,10 @@
   return metrics_;
 }
 
+gfx::AcceleratedWidget PlatformDisplayDefault::GetAcceleratedWidget() const {
+  return widget_;
+}
+
 void PlatformDisplayDefault::UpdateEventRootLocation(ui::LocatedEvent* event) {
   gfx::Point location = event->location();
   location.Offset(metrics_.bounds.x(), metrics_.bounds.y());
@@ -238,6 +243,8 @@
   // This will get called after Init() is called, either synchronously as part
   // of the Init() callstack or async after Init() has returned, depending on
   // the platform.
+  DCHECK_EQ(gfx::kNullAcceleratedWidget, widget_);
+  widget_ = widget;
   delegate_->OnAcceleratedWidgetAvailable();
   frame_generator_->OnAcceleratedWidgetAvailable(widget);
 }
diff --git a/services/ui/ws/platform_display_default.h b/services/ui/ws/platform_display_default.h
index 8636c2b..f0f5326 100644
--- a/services/ui/ws/platform_display_default.h
+++ b/services/ui/ws/platform_display_default.h
@@ -44,6 +44,7 @@
   gfx::Rect GetBounds() const override;
   bool UpdateViewportMetrics(const display::ViewportMetrics& metrics) override;
   const display::ViewportMetrics& GetViewportMetrics() const override;
+  gfx::AcceleratedWidget GetAcceleratedWidget() const override;
   FrameGenerator* GetFrameGenerator() override;
 
  private:
@@ -82,6 +83,7 @@
 
   display::ViewportMetrics metrics_;
   std::unique_ptr<ui::PlatformWindow> platform_window_;
+  gfx::AcceleratedWidget widget_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformDisplayDefault);
 };
diff --git a/services/ui/ws/test_utils.cc b/services/ui/ws/test_utils.cc
index 5c50fae..a1822059 100644
--- a/services/ui/ws/test_utils.cc
+++ b/services/ui/ws/test_utils.cc
@@ -63,6 +63,9 @@
   const display::ViewportMetrics& GetViewportMetrics() const override {
     return display_metrics_;
   }
+  gfx::AcceleratedWidget GetAcceleratedWidget() const override {
+    return gfx::kNullAcceleratedWidget;
+  }
   FrameGenerator* GetFrameGenerator() override { return nullptr; }
 
  private:
diff --git a/services/ui/ws/window_server.cc b/services/ui/ws/window_server.cc
index 651eb15..7e8d355e 100644
--- a/services/ui/ws/window_server.cc
+++ b/services/ui/ws/window_server.cc
@@ -509,6 +509,13 @@
 void WindowServer::OnDisplayReady(Display* display, bool is_first) {
   if (is_first)
     delegate_->OnFirstDisplayReady();
+  gpu_host_->OnAcceleratedWidgetAvailable(
+      display->platform_display()->GetAcceleratedWidget());
+}
+
+void WindowServer::OnDisplayDestroyed(Display* display) {
+  gpu_host_->OnAcceleratedWidgetDestroyed(
+      display->platform_display()->GetAcceleratedWidget());
 }
 
 void WindowServer::OnNoMoreDisplays() {
diff --git a/services/ui/ws/window_server.h b/services/ui/ws/window_server.h
index da21f65..9e8a43cf 100644
--- a/services/ui/ws/window_server.h
+++ b/services/ui/ws/window_server.h
@@ -223,6 +223,7 @@
   bool in_drag_loop() const { return !!current_drag_loop_; }
 
   void OnDisplayReady(Display* display, bool is_first);
+  void OnDisplayDestroyed(Display* display);
   void OnNoMoreDisplays();
   WindowManagerState* GetWindowManagerStateForUser(const UserId& user_id);
 
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 64bc750..db9b5423 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -367,10 +367,6 @@
   # Select the right BitmapPlatformDevice.
   if (is_win) {
     sources += [ "ext/bitmap_platform_device_win.cc" ]
-  } else if (is_mac) {
-    sources += [ "ext/bitmap_platform_device_mac.cc" ]
-  } else if (use_cairo) {
-    sources += [ "ext/bitmap_platform_device_cairo.cc" ]
   } else if (!is_ios) {
     sources += [ "ext/bitmap_platform_device_skia.cc" ]
   }
diff --git a/skia/ext/bitmap_platform_device.h b/skia/ext/bitmap_platform_device.h
deleted file mode 100644
index 1ca5f7e..0000000
--- a/skia/ext/bitmap_platform_device.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_H_
-#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_H_
-
-// This file provides an easy way to include the appropriate
-// BitmapPlatformDevice header file for your platform.
-
-#include <stdint.h>
-
-#if defined(WIN32)
-#include "skia/ext/bitmap_platform_device_win.h"
-#elif defined(__APPLE__)
-#include "skia/ext/bitmap_platform_device_mac.h"
-#elif defined(USE_CAIRO)
-#include "skia/ext/bitmap_platform_device_cairo.h"
-#else
-#include "skia/ext/bitmap_platform_device_skia.h"
-#endif
-
-#endif  // SKIA_EXT_BITMAP_PLATFORM_DEVICE_H_
diff --git a/skia/ext/bitmap_platform_device_cairo.cc b/skia/ext/bitmap_platform_device_cairo.cc
deleted file mode 100644
index 261228b..0000000
--- a/skia/ext/bitmap_platform_device_cairo.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "build/build_config.h"
-#include "skia/ext/bitmap_platform_device_cairo.h"
-#include "skia/ext/platform_canvas.h"
-
-#if defined(OS_OPENBSD)
-#include <cairo.h>
-#else
-#include <cairo/cairo.h>
-#endif
-
-namespace skia {
-
-namespace {
-
-void CairoSurfaceReleaseProc(void*, void* context) {
-  SkASSERT(context);
-  cairo_surface_destroy(static_cast<cairo_surface_t*>(context));
-}
-
-// Back the destination bitmap by a Cairo surface.  The bitmap's
-// pixelRef takes ownership of the passed-in surface and will call
-// cairo_surface_destroy() upon destruction.
-//
-// Note: it may immediately destroy the surface, if it fails to create a bitmap
-// with pixels, thus the caller must either ref() the surface before hand, or
-// it must not refer to the surface after this call.
-bool InstallCairoSurfacePixels(SkBitmap* dst,
-                               cairo_surface_t* surface,
-                               bool is_opaque) {
-  SkASSERT(dst);
-  if (!surface) {
-    return false;
-  }
-  SkImageInfo info
-      = SkImageInfo::MakeN32(cairo_image_surface_get_width(surface),
-                             cairo_image_surface_get_height(surface),
-                             is_opaque ? kOpaque_SkAlphaType
-                                       : kPremul_SkAlphaType);
-  return dst->installPixels(info,
-                            cairo_image_surface_get_data(surface),
-                            cairo_image_surface_get_stride(surface),
-                            NULL,
-                            &CairoSurfaceReleaseProc,
-                            static_cast<void*>(surface));
-}
-
-void LoadMatrixToContext(cairo_t* context, const SkMatrix& matrix) {
-  cairo_matrix_t cairo_matrix;
-  cairo_matrix_init(&cairo_matrix,
-                    SkScalarToFloat(matrix.getScaleX()),
-                    SkScalarToFloat(matrix.getSkewY()),
-                    SkScalarToFloat(matrix.getSkewX()),
-                    SkScalarToFloat(matrix.getScaleY()),
-                    SkScalarToFloat(matrix.getTranslateX()),
-                    SkScalarToFloat(matrix.getTranslateY()));
-  cairo_set_matrix(context, &cairo_matrix);
-}
-
-void LoadClipToContext(cairo_t* context, const SkIRect& clip_bounds) {
-  cairo_reset_clip(context);
-
-  cairo_rectangle(context, clip_bounds.fLeft, clip_bounds.fTop,
-                  clip_bounds.width(), clip_bounds.height());
-  cairo_clip(context);
-}
-
-}  // namespace
-
-void BitmapPlatformDevice::LoadConfig(const SkMatrix& transform,
-                                      const SkIRect& clip_bounds) {
-  if (!cairo_)
-    return;  // Nothing to do.
-
-  LoadClipToContext(cairo_, clip_bounds);
-  LoadMatrixToContext(cairo_, transform);
-}
-
-// We use this static factory function instead of the regular constructor so
-// that we can create the pixel data before calling the constructor. This is
-// required so that we can call the base class' constructor with the pixel
-// data.
-BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
-                                                   bool is_opaque,
-                                                   cairo_surface_t* surface) {
-  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
-    cairo_surface_destroy(surface);
-    return NULL;
-  }
-
-  // must call this before trying to install the surface, since that may result
-  // in the surface being destroyed.
-  cairo_t* cairo = cairo_create(surface);
-
-  SkBitmap bitmap;
-  if (!InstallCairoSurfacePixels(&bitmap, surface, is_opaque)) {
-    cairo_destroy(cairo);
-    return NULL;
-  }
-
-  // The device object will take ownership of the graphics context.
-  return new BitmapPlatformDevice(bitmap, cairo);
-}
-
-BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
-                                                   bool is_opaque) {
-  // This initializes the bitmap to all zeros.
-  cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
-                                                        width, height);
-
-  BitmapPlatformDevice* device = Create(width, height, is_opaque, surface);
-
-#ifndef NDEBUG
-    if (device && is_opaque)  // Fill with bright bluish green
-        SkCanvas(device).drawColor(0xFF00FF80);
-#endif
-
-  return device;
-}
-
-BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
-                                                   bool is_opaque,
-                                                   uint8_t* data) {
-  cairo_surface_t* surface = cairo_image_surface_create_for_data(
-      data, CAIRO_FORMAT_ARGB32, width, height,
-      cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width));
-
-  return Create(width, height, is_opaque, surface);
-}
-
-// Ownership of the cairo object is transferred.
-BitmapPlatformDevice::BitmapPlatformDevice(
-    const SkBitmap& bitmap,
-    cairo_t* cairo)
-    : SkBitmapDevice(bitmap),
-      cairo_(cairo) {
-  SetPlatformDevice(this, this);
-}
-
-BitmapPlatformDevice::~BitmapPlatformDevice() {
-  cairo_destroy(cairo_);
-}
-
-SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const CreateInfo& info,
-                                                   const SkPaint*) {
-  SkASSERT(info.fInfo.colorType() == kN32_SkColorType);
-  return BitmapPlatformDevice::Create(info.fInfo.width(), info.fInfo.height(),
-                                      info.fInfo.isOpaque());
-}
-
-cairo_t* BitmapPlatformDevice::BeginPlatformPaint(
-      const SkMatrix& transform,
-      const SkIRect& clip_bounds) {
-  LoadConfig(transform, clip_bounds);
-  cairo_surface_t* surface = cairo_get_target(cairo_);
-  // Tell cairo to flush anything it has pending.
-  cairo_surface_flush(surface);
-  // Tell Cairo that we (probably) modified (actually, will modify) its pixel
-  // buffer directly.
-  cairo_surface_mark_dirty(surface);
-  return cairo_;
-}
-
-// PlatformCanvas impl
-
-std::unique_ptr<SkCanvas> CreatePlatformCanvasWithPixels(
-    int width,
-    int height,
-    bool is_opaque,
-    uint8_t* data,
-    OnFailureType failureType) {
-  sk_sp<SkBaseDevice> dev(
-      BitmapPlatformDevice::Create(width, height, is_opaque, data));
-  return CreateCanvas(dev, failureType);
-}
-
-}  // namespace skia
diff --git a/skia/ext/bitmap_platform_device_cairo.h b/skia/ext/bitmap_platform_device_cairo.h
deleted file mode 100644
index 5e6a6f3..0000000
--- a/skia/ext/bitmap_platform_device_cairo.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_CAIRO_H_
-#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_CAIRO_H_
-
-#include <stdint.h>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "skia/ext/platform_device.h"
-
-typedef struct _cairo_surface cairo_surface_t;
-
-// -----------------------------------------------------------------------------
-// Image byte ordering on Linux:
-//
-// Pixels are packed into 32-bit words these days. Even for 24-bit images,
-// often 8-bits will be left unused for alignment reasons. Thus, when you see
-// ARGB as the byte order you have to wonder if that's in memory order or
-// little-endian order. Here I'll write A.R.G.B to specifiy the memory order.
-//
-// GdkPixbuf's provide a nice backing store and defaults to R.G.B.A order.
-// They'll do the needed byte swapping to match the X server when drawn.
-//
-// Skia can be controled in skia/include/corecg/SkUserConfig.h (see bits about
-// SK_R32_SHIFT). For Linux we define it to be ARGB in registers. For little
-// endian machines that means B.G.R.A in memory.
-//
-// The image loaders are controlled in
-// webkit/port/platform/image-decoders/ImageDecoder.h (see setRGBA). These are
-// also configured for ARGB in registers.
-//
-// Cairo's only 32-bit mode is ARGB in registers.
-//
-// X servers commonly have a 32-bit visual with xRGB in registers (since they
-// typically don't do alpha blending of drawables at the user level. Composite
-// extensions aside.)
-//
-// We don't use GdkPixbuf because its byte order differs from the rest. Most
-// importantly, it differs from Cairo which, being a system library, is
-// something that we can't easily change.
-// -----------------------------------------------------------------------------
-
-namespace skia {
-
-// -----------------------------------------------------------------------------
-// This is the Linux bitmap backing for Skia. We create a Cairo image surface
-// to store the backing buffer. This buffer is BGRA in memory (on little-endian
-// machines).
-//
-// For now we are also using Cairo to paint to the Drawables so we provide an
-// accessor for getting the surface.
-//
-// This is all quite ok for test_shell. In the future we will want to use
-// shared memory between the renderer and the main process at least. In this
-// case we'll probably create the buffer from a precreated region of memory.
-// -----------------------------------------------------------------------------
-class BitmapPlatformDevice : public SkBitmapDevice, public PlatformDevice {
- public:
-  // Create a BitmapPlatformDeviceLinux from an already constructed bitmap;
-  // you should probably be using Create(). This may become private later if
-  // we ever have to share state between some native drawing UI and Skia, like
-  // the Windows and Mac versions of this class do.
-  //
-  // This object takes ownership of @cairo.
-  BitmapPlatformDevice(const SkBitmap& other, cairo_t* cairo);
-  ~BitmapPlatformDevice() override;
-
-  // Constructs a device with size |width| * |height| with contents initialized
-  // to zero. |is_opaque| should be set if the caller knows the bitmap will be
-  // completely opaque and allows some optimizations.
-  static BitmapPlatformDevice* Create(int width, int height, bool is_opaque);
-
-  // This doesn't take ownership of |data|. If |data| is NULL, the contents
-  // of the device are initialized to 0.
-  static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
-                                      uint8_t* data);
-
- protected:
-  SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
-
- private:
-  // Overridden from PlatformDevice:
-  cairo_t* BeginPlatformPaint(const SkMatrix& transform,
-                              const SkIRect& clip_bounds) override;
-
-  static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
-                                      cairo_surface_t* surface);
-
-  // Loads the current transform and clip into the context.
-  void LoadConfig(const SkMatrix& transform, const SkIRect& clip_bounds);
-
-  // Graphics context used to draw into the surface.
-  cairo_t* cairo_;
-
-  DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
-};
-
-}  // namespace skia
-
-#endif  // SKIA_EXT_BITMAP_PLATFORM_DEVICE_CAIRO_H_
diff --git a/skia/ext/bitmap_platform_device_mac.cc b/skia/ext/bitmap_platform_device_mac.cc
deleted file mode 100644
index 67ad2a9..0000000
--- a/skia/ext/bitmap_platform_device_mac.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "skia/ext/bitmap_platform_device_mac.h"
-
-#import <ApplicationServices/ApplicationServices.h>
-#include <stddef.h>
-#include <time.h>
-
-#include "base/mac/mac_util.h"
-#include "base/memory/ref_counted.h"
-#include "skia/ext/bitmap_platform_device.h"
-#include "skia/ext/platform_canvas.h"
-#include "skia/ext/skia_utils_mac.h"
-#include "third_party/skia/include/core/SkMatrix.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "third_party/skia/include/core/SkRect.h"
-#include "third_party/skia/include/core/SkTypes.h"
-
-namespace skia {
-
-namespace {
-
-// Returns true if it is unsafe to attempt to allocate an offscreen buffer
-// given these dimensions.
-bool RasterDeviceTooBigToAllocate(int width, int height) {
-
-#ifndef SKIA_EXT_RASTER_DEVICE_ALLOCATION_MAX
-#define SKIA_EXT_RASTER_DEVICE_ALLOCATION_MAX    (2 * 256 * 1024 * 1024)
-#endif
-
-    int bytesPerPixel = 4;
-    int64_t bytes = (int64_t)width * height * bytesPerPixel;
-    return bytes > SKIA_EXT_RASTER_DEVICE_ALLOCATION_MAX;
-}
-
-static CGContextRef CGContextForData(void* data, int width, int height) {
-#define HAS_ARGB_SHIFTS(a, r, g, b) \
-            (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
-             && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
-#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
-  // Allocate a bitmap context with 4 components per pixel (BGRA).  Apple
-  // recommends these flags for improved CG performance.
-
-  // CGBitmapContextCreate returns NULL if width/height are 0. However, our
-  // callers expect to get a canvas back (which they later resize/reallocate)
-  // so we pin the dimensions here.
-  width = SkMax32(1, width);
-  height = SkMax32(1, height);
-  CGContextRef context =
-      CGBitmapContextCreate(data, width, height, 8, width * 4,
-                            base::mac::GetSystemColorSpace(),
-                            kCGImageAlphaPremultipliedFirst |
-                                kCGBitmapByteOrder32Host);
-#else
-#error We require that Skia's and CoreGraphics's recommended \
-       image memory layout match.
-#endif
-#undef HAS_ARGB_SHIFTS
-
-  if (!context)
-    return NULL;
-
-  // Change the coordinate system to match WebCore's
-  CGContextTranslateCTM(context, 0, height);
-  CGContextScaleCTM(context, 1.0, -1.0);
-
-  return context;
-}
-
-}  // namespace
-
-void BitmapPlatformDevice::ReleaseBitmapContext() {
-  SkASSERT(bitmap_context_);
-  CGContextRelease(bitmap_context_);
-  bitmap_context_ = NULL;
-}
-
-// Loads the specified Skia transform into the device context
-static void LoadTransformToCGContext(CGContextRef context,
-                                     const SkMatrix& matrix) {
-  // CoreGraphics can concatenate transforms, but not reset the current one.
-  // So in order to get the required behavior here, we need to first make
-  // the current transformation matrix identity and only then load the new one.
-
-  // Reset matrix to identity.
-  CGAffineTransform orig_cg_matrix = CGContextGetCTM(context);
-  CGAffineTransform orig_cg_matrix_inv =
-      CGAffineTransformInvert(orig_cg_matrix);
-  CGContextConcatCTM(context, orig_cg_matrix_inv);
-
-  // assert that we have indeed returned to the identity Matrix.
-  SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context)));
-
-  // Convert xform to CG-land.
-  // Our coordinate system is flipped to match WebKit's so we need to modify
-  // the xform to match that.
-  SkMatrix transformed_matrix = matrix;
-  SkScalar sy = -matrix.getScaleY();
-  transformed_matrix.setScaleY(sy);
-  size_t height = CGBitmapContextGetHeight(context);
-  SkScalar ty = -matrix.getTranslateY();  // y axis is flipped.
-  transformed_matrix.setTranslateY(ty + (SkScalar)height);
-
-  CGAffineTransform cg_matrix =
-      skia::SkMatrixToCGAffineTransform(transformed_matrix);
-
-  // Load final transform into context.
-  CGContextConcatCTM(context, cg_matrix);
-}
-
-static void LoadClippingRegionToCGContext(CGContextRef context,
-                                          const SkIRect& clip_bounds,
-                                          const SkMatrix& transformation) {
-  // CoreGraphics applies the current transform to clip rects, which is
-  // unwanted. Inverse-transform the rect before sending it to CG. This only
-  // works for translations and scaling, but not for rotations (but the
-  // viewport is never rotated anyway).
-  SkMatrix t;
-  bool did_invert = transformation.invert(&t);
-  if (!did_invert)
-    t.reset();
-
-  SkRect rect = SkRect::Make(clip_bounds);
-  t.mapRect(&rect);
-  SkIRect irect;
-  rect.round(&irect);
-  CGContextClipToRect(context, skia::SkIRectToCGRect(irect));
-}
-
-void BitmapPlatformDevice::LoadConfig(const SkMatrix& transform,
-                                      const SkIRect& clip_bounds) {
-  if (!bitmap_context_)
-    return;  // Nothing to do.
-
-  // We must restore and then save the state of the graphics context since the
-  // calls to Load the clipping region to the context are strictly cummulative,
-  // i.e., you can't replace a clip rect, other than with a save/restore.
-  // But this implies that no other changes to the state are done elsewhere.
-  // If we ever get to need to change this, then we must replace the clip rect
-  // calls in LoadClippingRegionToCGContext() with an image mask instead.
-  CGContextRestoreGState(bitmap_context_);
-  CGContextSaveGState(bitmap_context_);
-  LoadTransformToCGContext(bitmap_context_, transform);
-  LoadClippingRegionToCGContext(bitmap_context_, clip_bounds, transform);
-}
-
-
-// We use this static factory function instead of the regular constructor so
-// that we can create the pixel data before calling the constructor. This is
-// required so that we can call the base class' constructor with the pixel
-// data.
-BitmapPlatformDevice* BitmapPlatformDevice::Create(CGContextRef context,
-                                                   int width,
-                                                   int height,
-                                                   bool is_opaque,
-                                                   bool do_clear) {
-  if (RasterDeviceTooBigToAllocate(width, height))
-    return NULL;
-
-  SkBitmap bitmap;
-  // TODO: verify that the CG Context's pixels will have tight rowbytes or pass in the correct
-  // rowbytes for the case when context != NULL.
-  bitmap.setInfo(SkImageInfo::MakeN32(width, height, is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType));
-
-  void* data;
-  if (context) {
-    data = CGBitmapContextGetData(context);
-    bitmap.setPixels(data);
-  } else {
-    if (!bitmap.tryAllocPixels())
-      return NULL;
-    data = bitmap.getPixels();
-  }
-  if (do_clear)
-    memset(data, 0, bitmap.getSafeSize());
-
-  // If we were given data, then don't clobber it!
-#ifndef NDEBUG
-  if (!context && is_opaque) {
-    // To aid in finding bugs, we set the background color to something
-    // obviously wrong so it will be noticable when it is not cleared
-    bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
-  }
-#endif
-
-  if (!context) {
-    context = CGContextForData(data, width, height);
-    if (!context)
-      return NULL;
-  } else
-    CGContextRetain(context);
-
-  BitmapPlatformDevice* rv = new BitmapPlatformDevice(context, bitmap);
-
-  // The device object took ownership of the graphics context with its own
-  // CGContextRetain call.
-  CGContextRelease(context);
-
-  return rv;
-}
-
-BitmapPlatformDevice* BitmapPlatformDevice::CreateWithData(uint8_t* data,
-                                                           int width,
-                                                           int height,
-                                                           bool is_opaque) {
-  CGContextRef context = NULL;
-  if (data)
-    context = CGContextForData(data, width, height);
-
-  BitmapPlatformDevice* rv = Create(context, width, height, is_opaque, false);
-
-  // The device object took ownership of the graphics context with its own
-  // CGContextRetain call.
-  if (context)
-    CGContextRelease(context);
-
-  return rv;
-}
-
-// The device will own the bitmap, which corresponds to also owning the pixel
-// data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
-BitmapPlatformDevice::BitmapPlatformDevice(
-    CGContextRef context, const SkBitmap& bitmap)
-    : SkBitmapDevice(bitmap),
-      bitmap_context_(context) {
-  SetPlatformDevice(this, this);
-  SkASSERT(bitmap_context_);
-  // Initialize the clip region to the entire bitmap.
-
-  SkIRect rect;
-  rect.set(0, 0,
-           CGBitmapContextGetWidth(bitmap_context_),
-           CGBitmapContextGetHeight(bitmap_context_));
-  CGContextRetain(bitmap_context_);
-  // We must save the state once so that we can use the restore/save trick
-  // in LoadConfig().
-  CGContextSaveGState(bitmap_context_);
-}
-
-BitmapPlatformDevice::~BitmapPlatformDevice() {
-  if (bitmap_context_)
-    CGContextRelease(bitmap_context_);
-}
-
-NativeDrawingContext BitmapPlatformDevice::BeginPlatformPaint(
-    const SkMatrix& transform,
-    const SkIRect& clip_bounds) {
-  LoadConfig(transform, clip_bounds);
-  return bitmap_context_;
-}
-
-SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const CreateInfo& cinfo,
-                                                   const SkPaint*) {
-  const SkImageInfo& info = cinfo.fInfo;
-  const bool do_clear = !info.isOpaque();
-  SkASSERT(info.colorType() == kN32_SkColorType);
-  return Create(NULL, info.width(), info.height(), info.isOpaque(), do_clear);
-}
-
-// PlatformCanvas impl
-
-std::unique_ptr<SkCanvas> CreatePlatformCanvasWithPixels(
-    int width,
-    int height,
-    bool is_opaque,
-    uint8_t* data,
-    OnFailureType failureType) {
-  sk_sp<SkBaseDevice> dev(
-      BitmapPlatformDevice::CreateWithData(data, width, height, is_opaque));
-  return CreateCanvas(dev, failureType);
-}
-
-}  // namespace skia
diff --git a/skia/ext/bitmap_platform_device_mac.h b/skia/ext/bitmap_platform_device_mac.h
deleted file mode 100644
index 10dce7fe..0000000
--- a/skia/ext/bitmap_platform_device_mac.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_MAC_H_
-#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_MAC_H_
-
-#include <stdint.h>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "skia/ext/platform_device.h"
-
-namespace skia {
-
-// A device is basically a wrapper around SkBitmap that provides a surface for
-// SkCanvas to draw into. Our device provides a surface CoreGraphics can also
-// write to. BitmapPlatformDevice creates a bitmap using
-// CGCreateBitmapContext() in a format that Skia supports and can then use this
-// to draw text into, etc. This pixel data is provided to the bitmap that the
-// device contains so that it can be shared.
-//
-// The device owns the pixel data, when the device goes away, the pixel data
-// also becomes invalid. THIS IS DIFFERENT THAN NORMAL SKIA which uses
-// reference counting for the pixel data. In normal Skia, you could assign
-// another bitmap to this device's bitmap and everything will work properly.
-// For us, that other bitmap will become invalid as soon as the device becomes
-// invalid, which may lead to subtle bugs. Therefore, DO NOT ASSIGN THE
-// DEVICE'S PIXEL DATA TO ANOTHER BITMAP, make sure you copy instead.
-class SK_API BitmapPlatformDevice : public SkBitmapDevice, public PlatformDevice {
- public:
-  // Creates a BitmapPlatformDevice instance. |is_opaque| should be set if the
-  // caller knows the bitmap will be completely opaque and allows some
-  // optimizations.
-  // |context| may be NULL. If |context| is NULL, then the bitmap backing store
-  // is not initialized.
-  static BitmapPlatformDevice* Create(CGContextRef context,
-                                      int width, int height,
-                                      bool is_opaque, bool do_clear = false);
-
-  // Creates a context for |data| and calls Create.
-  // If |data| is NULL, then the bitmap backing store is not initialized.
-  static BitmapPlatformDevice* CreateWithData(uint8_t* data,
-                                              int width, int height,
-                                              bool is_opaque);
-
-  ~BitmapPlatformDevice() override;
-
- protected:
-  BitmapPlatformDevice(CGContextRef context,
-                       const SkBitmap& bitmap);
-
-  SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
-
- private:
-  NativeDrawingContext BeginPlatformPaint(const SkMatrix& transform,
-                                          const SkIRect& clip_bounds) override;
-
-  void ReleaseBitmapContext();
-
-  // Loads the current transform and clip into the context. Can be called even
-  // when |bitmap_context_| is NULL (will be a NOP).
-  void LoadConfig(const SkMatrix& transform, const SkIRect& clip_bounds);
-
-  // Lazily-created graphics context used to draw into the bitmap.
-  CGContextRef bitmap_context_;
-
-  DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
-};
-
-}  // namespace skia
-
-#endif  // SKIA_EXT_BITMAP_PLATFORM_DEVICE_MAC_H_
diff --git a/skia/ext/bitmap_platform_device_skia.cc b/skia/ext/bitmap_platform_device_skia.cc
index a39e5d71..5904d6f2 100644
--- a/skia/ext/bitmap_platform_device_skia.cc
+++ b/skia/ext/bitmap_platform_device_skia.cc
@@ -9,15 +9,7 @@
 
 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
                                                    bool is_opaque) {
-  SkBitmap bitmap;
-  if (bitmap.tryAllocN32Pixels(width, height, is_opaque)) {
-    // Follow the logic in SkCanvas::createDevice(), initialize the bitmap if it
-    // is not opaque.
-    if (!is_opaque)
-      bitmap.eraseARGB(0, 0, 0, 0);
-    return new BitmapPlatformDevice(bitmap);
-  }
-  return NULL;
+  return Create(width, height, is_opaque, nullptr);
 }
 
 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
@@ -26,10 +18,17 @@
   SkBitmap bitmap;
   bitmap.setInfo(SkImageInfo::MakeN32(width, height,
       is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType));
-  if (data)
+
+  if (data) {
     bitmap.setPixels(data);
-  else if (!bitmap.tryAllocPixels())
-    return NULL;
+  } else {
+      if (!bitmap.tryAllocPixels())
+        return nullptr;
+      // Follow the logic in SkCanvas::createDevice(), initialize the bitmap if
+      // it is not opaque.
+      if (!is_opaque)
+        bitmap.eraseARGB(0, 0, 0, 0);
+  }
 
   return new BitmapPlatformDevice(bitmap);
 }
@@ -49,15 +48,6 @@
                                       info.fInfo.isOpaque());
 }
 
-NativeDrawingContext BitmapPlatformDevice::BeginPlatformPaint(
-    const SkMatrix& transform,
-    const SkIRect& clip_bounds) {
-  // TODO(zhenghao): What should we return? The ptr to the address of the
-  // pixels? Maybe this won't be called at all.
-  SkPixmap pixmap;
-  return accessPixels(&pixmap) ? pixmap.writable_addr() : nullptr;
-}
-
 // PlatformCanvas impl
 
 std::unique_ptr<SkCanvas> CreatePlatformCanvasWithPixels(
diff --git a/skia/ext/bitmap_platform_device_skia.h b/skia/ext/bitmap_platform_device_skia.h
index 024064f..42a3be1 100644
--- a/skia/ext/bitmap_platform_device_skia.h
+++ b/skia/ext/bitmap_platform_device_skia.h
@@ -21,15 +21,21 @@
 // shared memory between the renderer and the main process at least. In this
 // case we'll probably create the buffer from a precreated region of memory.
 // -----------------------------------------------------------------------------
-class BitmapPlatformDevice : public SkBitmapDevice, public PlatformDevice {
+class BitmapPlatformDevice final : public SkBitmapDevice,
+                                   public PlatformDevice {
  public:
   // Construct a BitmapPlatformDevice. |is_opaque| should be set if the caller
-  // knows the bitmap will be completely opaque and allows some optimizations.
-  // The bitmap is not initialized.
+  // knows the bitmap will be completely opaque and allows some optimizations
+  // (the bitmap is not initialized to 0 when is_opaque == true).
   static BitmapPlatformDevice* Create(int width, int height, bool is_opaque);
 
-  // This doesn't take ownership of |data|. If |data| is null, the bitmap
-  // is not initialized to 0.
+  // This doesn't take ownership of |data|. If |data| is null and |is_opaque|
+  // is false, the bitmap is initialized to 0.
+  //
+  // Note: historicaly, BitmapPlatformDevice impls have had diverging
+  // initialization behavior for null |data| (Cairo used to initialize, while
+  // the others did not).  For now we stick to the more conservative Cairo
+  // behavior.
   static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
                                       uint8_t* data);
 
@@ -44,9 +50,6 @@
   SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
 
  private:
-  NativeDrawingContext BeginPlatformPaint(const SkMatrix& transform,
-                                          const SkIRect& clip_bounds) override;
-
   DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
 };
 
diff --git a/skia/ext/bitmap_platform_device_win.cc b/skia/ext/bitmap_platform_device_win.cc
index d537ca06..34b7e2f 100644
--- a/skia/ext/bitmap_platform_device_win.cc
+++ b/skia/ext/bitmap_platform_device_win.cc
@@ -33,21 +33,6 @@
 
 namespace skia {
 
-void DrawToNativeContext(SkCanvas* canvas, HDC destination_hdc, int x, int y,
-                         const RECT* src_rect) {
-  RECT temp_rect;
-  if (!src_rect) {
-    temp_rect.left = 0;
-    temp_rect.right = canvas->imageInfo().width();
-    temp_rect.top = 0;
-    temp_rect.bottom = canvas->imageInfo().height();
-    src_rect = &temp_rect;
-  }
-  skia::CopyHDC(skia::GetNativeDrawingContext(canvas), destination_hdc, x, y,
-                canvas->imageInfo().isOpaque(), *src_rect,
-                canvas->getTotalMatrix());
-}
-
 HDC GetNativeDrawingContext(SkCanvas* canvas) {
   PlatformDevice* platform_device = GetPlatformDevice(canvas->getTopDevice(true));
   if (!platform_device)
diff --git a/skia/ext/bitmap_platform_device_win.h b/skia/ext/bitmap_platform_device_win.h
index 82f17ff3..6f359e2 100644
--- a/skia/ext/bitmap_platform_device_win.h
+++ b/skia/ext/bitmap_platform_device_win.h
@@ -17,8 +17,8 @@
 // format that Skia supports and can then use this to draw ClearType into, etc.
 // This pixel data is provided to the bitmap that the device contains so that it
 // can be shared.
-class SK_API BitmapPlatformDevice : public SkBitmapDevice,
-                                    public PlatformDevice {
+class SK_API BitmapPlatformDevice final : public SkBitmapDevice,
+                                          public PlatformDevice {
  public:
   // Factory function. is_opaque should be set if the caller knows the bitmap
   // will be completely opaque and allows some optimizations.
diff --git a/skia/ext/platform_canvas.h b/skia/ext/platform_canvas.h
index 1436ec1..dea8ae82 100644
--- a/skia/ext/platform_canvas.h
+++ b/skia/ext/platform_canvas.h
@@ -52,16 +52,6 @@
     HANDLE shared_section,
     OnFailureType failure_type);
 
-// Draws the top layer of the canvas into the specified HDC. Only works
-// with a SkCanvas with a BitmapPlatformDevice. Will create a temporary
-// HDC to back the canvas if one doesn't already exist, tearing it down
-// before returning. If |src_rect| is null, copies the entire canvas.
-SK_API void DrawToNativeContext(SkCanvas* canvas,
-                                HDC hdc,
-                                int x,
-                                int y,
-                                const RECT* src_rect);
-
 // Returns the NativeDrawingContext to use for native platform drawing calls.
 SK_API HDC GetNativeDrawingContext(SkCanvas* canvas);
 
diff --git a/skia/ext/platform_device.h b/skia/ext/platform_device.h
index b756aa6..f349a49 100644
--- a/skia/ext/platform_device.h
+++ b/skia/ext/platform_device.h
@@ -46,11 +46,14 @@
  public:
   virtual ~PlatformDevice();
 
+// Only implemented in bitmap_platform_device_win.
+#if defined(WIN32)
   // The DC that corresponds to the bitmap, used for GDI operations drawing
   // into the bitmap. This is possibly heavyweight, so it should be existant
   // only during one pass of rendering.
   virtual NativeDrawingContext BeginPlatformPaint(const SkMatrix& transform,
                                                   const SkIRect& clip_bounds) = 0;
+#endif
 };
 
 }  // namespace skia
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 19709af..d32b89e2d 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -214,6 +214,7 @@
                 {
                     "name": "Default",
                     "params": {
+                        "RedirectHistoryService": "true",
                         "RedirectSequencedWorkerPools": "true"
                     }
                 },
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-convertToBlob-noIdleTask-worker.html b/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-convertToBlob-noIdleTask-worker.html
new file mode 100644
index 0000000..e03af52
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-convertToBlob-noIdleTask-worker.html
@@ -0,0 +1,58 @@
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script id="myWorker"  type="text/worker">
+self.onmessage = function(e) {
+  var offscreenCanvas = new OffscreenCanvas(4, 4);
+  var ctx = offscreenCanvas.getContext("2d");
+  ctx.fillStyle = "#FF0102";
+  ctx.fillRect(0, 0, 4, 4);
+
+  offscreenCanvas.convertToBlob().then(function(blob) {
+    self.postMessage(blob);
+  });
+
+};
+</script>
+<script type = 'text/javascript'>
+// In blink Layout Tests, flag "--enable-threaded-compositing", which is
+// essential for idle task running, is not turned on by default. This makes
+// it convenient to test the case when CanvasAsyncBlobCreator is idling for
+// too long and switching to forcing encoding in a normal task.
+// Passing this test means that the enforcing mechanism in
+// CanvasAsyncBlobCreator for OffscreenCanvas case is working as expected.
+var w = 4;
+var h = 4;
+
+var newImg = new Image();
+function imageLoaded() {
+    var canvas2 = document.createElement("canvas");
+    canvas2.width = w;
+    canvas2.height = h;
+    var ctx2 = canvas2.getContext("2d");
+    ctx2.drawImage(newImg, 0, 0, w, h);
+
+    var imageData = ctx2.getImageData(0, 0, w, h).data;
+    assert_equals(imageData[0], 255);
+    assert_equals(imageData[1], 1);
+    assert_equals(imageData[2], 2);
+    assert_equals(imageData[3], 255);
+    testImageFromOffscreen.done();
+}
+
+var workerBlob = new Blob([document.getElementById('myWorker').textContent]);
+var worker = new Worker(URL.createObjectURL(workerBlob));
+worker.addEventListener("message", function(msg) {
+    var blob = msg.data;
+    newImg.src = URL.createObjectURL(blob);
+});
+
+var testImageFromOffscreen = async_test(
+    "Check if the image loaded from blob returned by " +
+    "OffscreenCanvas.convertToBlob() have expected image data values.");
+
+testImageFromOffscreen.step(function() {
+  newImg.onload = testImageFromOffscreen.step_func(imageLoaded);
+  worker.postMessage("");
+});
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-convertToBlob-noIdleTask.html b/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-convertToBlob-noIdleTask.html
new file mode 100644
index 0000000..ce48f18
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/OffscreenCanvas-convertToBlob-noIdleTask.html
@@ -0,0 +1,46 @@
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script type = 'text/javascript'>
+// In blink Layout Tests, flag "--enable-threaded-compositing", which is
+// essential for idle task running, is not turned on by default. This makes
+// it convenient to test the case when CanvasAsyncBlobCreator is idling for
+// too long and switching to forcing encoding in a normal task.
+// Passing this test means that the enforcing mechanism in
+// CanvasAsyncBlobCreator for OffscreenCanvas case is working as expected.
+var w = 4;
+var h = 4;
+
+var newImg = new Image();
+function imageLoaded() {
+    var canvas2 = document.createElement("canvas");
+    canvas2.width = w;
+    canvas2.height = h;
+    var ctx2 = canvas2.getContext("2d");
+    ctx2.drawImage(newImg, 0, 0, w, h);
+
+    var imageData = ctx2.getImageData(0, 0, w, h).data;
+    assert_equals(imageData[0], 255);
+    assert_equals(imageData[1], 1);
+    assert_equals(imageData[2], 2);
+    assert_equals(imageData[3], 255);
+    testImageFromOffscreen.done();
+}
+
+var testImageFromOffscreen = async_test(
+    "Check if the image loaded from blob returned by " +
+    "OffscreenCanvas.convertToBlob() have expected image data values.");
+
+testImageFromOffscreen.step(function() {
+  newImg.onload = testImageFromOffscreen.step_func(imageLoaded);
+
+  var offscreenCanvas = new OffscreenCanvas(w, h);
+  var ctx = offscreenCanvas.getContext("2d");
+  ctx.fillStyle = "#FF0102";
+  ctx.fillRect(0, 0, w, h);
+
+  offscreenCanvas.convertToBlob().then(function(blob) {
+      newImg.src = URL.createObjectURL(blob);
+  });
+});
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/shadow/move-shadow-host-to-child-iframe-crash.html b/third_party/WebKit/LayoutTests/fast/dom/shadow/move-shadow-host-to-child-iframe-crash.html
new file mode 100644
index 0000000..7cdc19df
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/dom/shadow/move-shadow-host-to-child-iframe-crash.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<div id="host">
+    <iframe id="iframe"></iframe>
+</div>
+<script>
+    test(() => {
+        host.attachShadow({mode:"open"}).innerHTML = "<style></style>";
+        iframe.contentDocument.documentElement.appendChild(host);
+    }, "Inserting a shadow host with an iframe child into that iframes document should not crash or assert.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline-expected.html
new file mode 100644
index 0000000..03f5276
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline-expected.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline-expected.txt
new file mode 100644
index 0000000..00574e1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (floating) DIV id='float'",
+          "rect": [8, 8, 100, 100],
+          "reason": "style change"
+        }
+      ]
+    },
+    {
+      "name": "LayoutInline (relative positioned) SPAN",
+      "position": [108, 108],
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='float'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline.html b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline.html
new file mode 100644
index 0000000..2cee1533
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/float-under-composited-inline.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<span style="position: relative; top: 100px; left: 100px; will-change: transform">
+  <div id="float" style="float: left; width: 100px; height: 100px; background-color: red"></div>
+</span>
+<script src="../resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  float.style.backgroundColor = 'green';
+}
+onload = runRepaintAndPixelTest;
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/newly-composited-repaint-rect.html b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/newly-composited-repaint-rect.html
index 29dcf6deec..51598ba 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/newly-composited-repaint-rect.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/newly-composited-repaint-rect.html
@@ -60,7 +60,7 @@
 
     function repaintTest() {
         runAfterLayoutAndPaint(function() {
-          // Changing the position will cause the scrolldiv to become composited becuase it overlaps another compostied element.
+          // Changing the position will cause the scrolldiv to become composited because it overlaps another compostied element.
           changeDivPosition();
 
           // Force DumpRenderTree to do a layout and repaint here, this is where the repaintRect
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/invalidation-after-opacity-change-subtree.html b/third_party/WebKit/LayoutTests/paint/invalidation/invalidation-after-opacity-change-subtree.html
index cb19e441..8adf8bbf 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/invalidation-after-opacity-change-subtree.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/invalidation-after-opacity-change-subtree.html
@@ -8,7 +8,7 @@
 #absolute {
     position: absolute;
     top: 2000px;
-    font: 50px/1 Sherif;
+    font: 50px/1 Sheriff;
 }
 </style>
 <div id="container">
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/opacity-change-on-overflow-float-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/opacity-change-on-overflow-float-expected.html
index abcd81ec..da9e2ce 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/opacity-change-on-overflow-float-expected.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/opacity-change-on-overflow-float-expected.html
@@ -15,7 +15,7 @@
 </head>
 <body>
 <p>Repaint test for <rdar><!-- 
-An explaination as to why this is here.  In the beginning, there was an
+An explanation as to why this is here.  In the beginning, there was an
 <rdar:://problem/6869687> link to rdar.  However, that link was invisible
 because it was enclosed in angle brackets.
 
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/opacity-change-on-overflow-float.html b/third_party/WebKit/LayoutTests/paint/invalidation/opacity-change-on-overflow-float.html
index 540942ab..8eceebaa 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/opacity-change-on-overflow-float.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/opacity-change-on-overflow-float.html
@@ -28,7 +28,7 @@
 </head>
 <body onload="runRepaintAndPixelTest()">
 <p>Repaint test for <rdar><!-- 
-An explaination as to why this is here.  In the beginning, there was an
+An explanation as to why this is here.  In the beginning, there was an
 <rdar:://problem/6869687> link to rdar.  However, that link was invisible
 because it was enclosed in angle brackets.
 
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-selection-text-05-t.svg b/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-selection-text-05-t.svg
index 4d377036..e9c3b7e 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-selection-text-05-t.svg
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/text-selection-text-05-t.svg
@@ -35,8 +35,8 @@
     <!--======================================================================-->
     <g id="test-body-content" font-size="16">
         <defs>
-            <font id="embeded" horiz-adv-x="224">
-<font-face font-family="embeded" units-per-em="1000" panose-1="0 0 0 0 0 0 0 0 0 0" ascent="917" descent="-250" alphabetic="0"/>
+            <font id="embedded" horiz-adv-x="224">
+<font-face font-family="embedded" units-per-em="1000" panose-1="0 0 0 0 0 0 0 0 0 0" ascent="917" descent="-250" alphabetic="0"/>
                 <missing-glyph horiz-adv-x="800" d="M50 0V800H750V0H50ZM700 50V750H100V50H700Z"/>
                 <glyph unicode="1" glyph-name="gl_1" horiz-adv-x="1500" d="M 0 0 L 250 0 L 250 250 L 0 250 Z"/>
                 <glyph unicode="2" glyph-name="gl_2" horiz-adv-x="1500" d="M 0 0 L 500 0 L 500 500 L 0 500 Z"/>
@@ -73,19 +73,19 @@
                     <g id="textContent">
                         <!-- 1.x 1.y -->
                         <use y="30" xlink:href="#marker" fill="#8888ff"/>
-                        <text x="0" y="30" font-family="embeded" font-size="10">1234</text>
+                        <text x="0" y="30" font-family="embedded" font-size="10">1234</text>
 
                         <!-- 4.x 1.y : four text chunks -->
                         <use x="10" y="60" xlink:href="#marker" fill="#8888ff"/>
                         <use x="20" y="60" xlink:href="#marker" fill="#8888ff"/>
                         <use x="30" y="60" xlink:href="#marker" fill="#8888ff"/>
                         <use x="40" y="60" xlink:href="#marker" fill="#8888ff"/>
-                        <text x="10 20 30 40" y="60" font-family="embeded" font-size="10">1234</text>
+                        <text x="10 20 30 40" y="60" font-family="embedded" font-size="10">1234</text>
 
                         <!-- 2.x 1.y : two text chunks -->
                         <use x="10" y="90" xlink:href="#marker" fill="#8888ff"/>
                         <use x="60" y="90" xlink:href="#marker" fill="#8888ff"/>
-                        <text x="10 60" y="90" font-family="embeded" font-size="10">1234</text>
+                        <text x="10 60" y="90" font-family="embedded" font-size="10">1234</text>
 
                         <!-- 1.x 4.y : four text chunks -->
                         <g transform="translate(0, 120)">
@@ -93,7 +93,7 @@
                             <use x="15" y="-5" xlink:href="#marker" fill="#8888ff"/>
                             <use x="30" y="5" xlink:href="#marker" fill="#8888ff"/>
                             <use x="45" y="10" xlink:href="#marker" fill="#8888ff"/>
-                            <text x="0" y="-10 -5 5 10" font-family="embeded" font-size="10">1234</text>
+                            <text x="0" y="-10 -5 5 10" font-family="embedded" font-size="10">1234</text>
                         </g>
 
                         <!-- 4.x 4.y : four text chunks -->
@@ -102,7 +102,7 @@
                             <use x="20" y="-5" xlink:href="#marker" fill="#8888ff"/>
                             <use x="30" y="5" xlink:href="#marker" fill="#8888ff"/>
                             <use x="40" y="10" xlink:href="#marker" fill="#8888ff"/>
-                            <text x="10 20 30 40" y="-10 -5 5 10" font-family="embeded" font-size="10">1234</text>
+                            <text x="10 20 30 40" y="-10 -5 5 10" font-family="embedded" font-size="10">1234</text>
                         </g>
 
                         <!-- 2.x 4.y : four text chunks -->
@@ -111,14 +111,14 @@
                             <use x="20" y="-5" xlink:href="#marker" fill="#8888ff"/>
                             <use x="35" y="5" xlink:href="#marker" fill="#8888ff"/>
                             <use x="50" y="10" xlink:href="#marker" fill="#8888ff"/>
-                            <text x="10 20" y="-10 -5 5 10" font-family="embeded" font-size="10">1234</text>
+                            <text x="10 20" y="-10 -5 5 10" font-family="embedded" font-size="10">1234</text>
                         </g>
 
                         <!-- 1.x 2.y : two text chunks -->
                         <g transform="translate(0, 210)">
                             <use x="0" y="-10" xlink:href="#marker" fill="#8888ff"/>
                             <use x="15" y="5" xlink:href="#marker" fill="#8888ff"/>
-                            <text x="0" y="-10 5" font-family="embeded" font-size="10">1234</text>
+                            <text x="0" y="-10 5" font-family="embedded" font-size="10">1234</text>
                         </g>
 
                         <!-- 4.x 2.y : four text chunks -->
@@ -127,14 +127,14 @@
                             <use x="20" y="-5" xlink:href="#marker" fill="#8888ff"/>
                             <use x="30" y="-5" xlink:href="#marker" fill="#8888ff"/>
                             <use x="40" y="-5" xlink:href="#marker" fill="#8888ff"/>
-                            <text x="10 20 30 40" y="-10 -5" font-family="embeded" font-size="10">1234</text>
+                            <text x="10 20 30 40" y="-10 -5" font-family="embedded" font-size="10">1234</text>
                         </g>
 
                         <!-- 2.x 2.y : two text chunks -->
                         <g transform="translate(0, 270)">
                             <use x="10" y="-10" xlink:href="#marker" fill="#8888ff"/>
                             <use x="60" y="-5" xlink:href="#marker" fill="#8888ff"/>
-                            <text x="10 60" y="-10 -5" font-family="embeded" font-size="10">1234</text>
+                            <text x="10 60" y="-10 -5" font-family="embedded" font-size="10">1234</text>
                         </g>
 
                     </g>
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
index 6726705f..57e5ed0 100644
--- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -169,7 +169,7 @@
 void StyleEngine::removePendingSheet(Node& styleSheetCandidateNode,
                                      const StyleEngineContext& context) {
   if (styleSheetCandidateNode.isConnected())
-    markTreeScopeDirty(styleSheetCandidateNode.treeScope());
+    setNeedsActiveStyleUpdate(styleSheetCandidateNode.treeScope());
 
   if (context.addedPendingSheetBeforeBody()) {
     DCHECK_GT(m_pendingRenderBlockingStylesheets, 0);
@@ -203,7 +203,7 @@
   DCHECK(collection);
   collection->addStyleSheetCandidateNode(node);
 
-  markTreeScopeDirty(treeScope);
+  setNeedsActiveStyleUpdate(treeScope);
   if (treeScope != m_document)
     m_activeTreeScopes.add(&treeScope);
 }
@@ -227,12 +227,12 @@
     return;
   collection->removeStyleSheetCandidateNode(node);
 
-  markTreeScopeDirty(treeScope);
+  setNeedsActiveStyleUpdate(treeScope);
 }
 
 void StyleEngine::modifiedStyleSheetCandidateNode(Node& node) {
   if (node.isConnected())
-    markTreeScopeDirty(node.treeScope());
+    setNeedsActiveStyleUpdate(node.treeScope());
 }
 
 void StyleEngine::mediaQueriesChangedInScope(TreeScope& treeScope) {
diff --git a/third_party/WebKit/Source/core/frame/BrowserControls.h b/third_party/WebKit/Source/core/frame/BrowserControls.h
index 10c1f86a..cbabd91 100644
--- a/third_party/WebKit/Source/core/frame/BrowserControls.h
+++ b/third_party/WebKit/Source/core/frame/BrowserControls.h
@@ -49,6 +49,8 @@
   // scroll amount.
   FloatSize scrollBy(FloatSize scrollDelta);
 
+  WebBrowserControlsState permittedState() const { return m_permittedState; }
+
  private:
   explicit BrowserControls(const FrameHost&);
   void resetBaseline();
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 9435eef..d1c191d 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -1378,9 +1378,9 @@
   // height, compensating for page scale as well, since we want to use the
   // viewport with browser controls hidden for vh (to match Safari).
   BrowserControls& browserControls = m_frame->host()->browserControls();
-  if (m_frame->isMainFrame() && size.width()) {
-    float pageScaleAtLayoutWidth =
-        m_frame->host()->visualViewport().size().width() / size.width();
+  int viewportWidth = m_frame->host()->visualViewport().size().width();
+  if (m_frame->isMainFrame() && size.width() && viewportWidth) {
+    float pageScaleAtLayoutWidth = viewportWidth / size.width();
     size.expand(0, browserControls.height() / pageScaleAtLayoutWidth);
   }
 
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index 7f72739..9e40e7b1 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -94,6 +94,7 @@
 #include "public/platform/WebScreenInfo.h"
 #include "public/platform/WebViewScheduler.h"
 #include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkSurface.h"
 #include "wtf/PtrUtil.h"
 #include "wtf/StdLibExtras.h"
 #include <memory>
@@ -143,10 +144,15 @@
     // TODO(fmalita): endRecording() should return a non-const SKP.
     sk_sp<SkPicture> recording(
         const_cast<SkPicture*>(m_pictureBuilder->endRecording().release()));
-    sk_sp<SkImage> skImage = SkImage::MakeFromPicture(
-        std::move(recording),
-        SkISize::Make(m_bounds.width(), m_bounds.height()), nullptr, nullptr);
-    RefPtr<Image> image = StaticBitmapImage::create(std::move(skImage));
+
+    // Rasterize upfront, since DragImage::create() is going to do it anyway
+    // (SkImage::asLegacyBitmap).
+    sk_sp<SkSurface> surface =
+        SkSurface::MakeRasterN32Premul(m_bounds.width(), m_bounds.height());
+    surface->getCanvas()->drawPicture(recording);
+    RefPtr<Image> image =
+        StaticBitmapImage::create(surface->makeImageSnapshot());
+
     float screenDeviceScaleFactor =
         m_localFrame->page()->chromeClient().screenInfo().deviceScaleFactor;
 
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
index 562b718..6d1eff20 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
@@ -275,17 +275,13 @@
       NOTREACHED();
     }
 
-    // TODO: Enforce OffscreenCanvas.convertToBlob to finish within deadline.
-    // See crbug.com/657102.
-    if (m_functionType == HTMLCanvasToBlobCallback) {
-      // We post the below task to check if the above idle task isn't late.
-      // There's no risk of concurrency as both tasks are on main thread.
-      this->postDelayedTaskToMainThread(
-          BLINK_FROM_HERE,
-          WTF::bind(&CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent,
-                    wrapPersistent(this), quality),
-          IdleTaskStartTimeoutDelay);
-    }
+    // We post the below task to check if the above idle task isn't late.
+    // There's no risk of concurrency as both tasks are on the same thread.
+    this->postDelayedTaskToCurrentThread(
+        BLINK_FROM_HERE,
+        WTF::bind(&CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent,
+                  wrapPersistent(this), quality),
+        IdleTaskStartTimeoutDelay);
   }
 }
 
@@ -412,7 +408,7 @@
   }
 }
 
-void CanvasAsyncBlobCreator::encodeRowsPngOnMainThread() {
+void CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread() {
   DCHECK(m_idleTaskStatus == IdleTaskSwitchedToImmediateTask);
 
   // Continue encoding from the last completed row
@@ -423,20 +419,39 @@
     inputPixels += m_pixelRowStride;
   }
   PNGImageEncoder::finalizePng(m_pngEncoderState.get());
-  this->createBlobAndReturnResult();
+
+  if (isMainThread()) {
+    this->createBlobAndReturnResult();
+  } else {
+    TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
+        ->postTask(
+            BLINK_FROM_HERE,
+            crossThreadBind(&CanvasAsyncBlobCreator::createBlobAndReturnResult,
+                            wrapCrossThreadPersistent(this)));
+  }
 
   this->signalAlternativeCodePathFinishedForTesting();
 }
 
-void CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread() {
+void CanvasAsyncBlobCreator::forceEncodeRowsJpegOnCurrentThread() {
   DCHECK(m_idleTaskStatus == IdleTaskSwitchedToImmediateTask);
 
   // Continue encoding from the last completed row
+  void (CanvasAsyncBlobCreator::*functionToBeCalled)(void);
   if (JPEGImageEncoder::encodeWithPreInitializedState(
           std::move(m_jpegEncoderState), m_data->data(), m_numRowsCompleted)) {
-    this->createBlobAndReturnResult();
+    functionToBeCalled = &CanvasAsyncBlobCreator::createBlobAndReturnResult;
   } else {
-    this->createNullAndReturnResult();
+    functionToBeCalled = &CanvasAsyncBlobCreator::createNullAndReturnResult;
+  }
+
+  if (isMainThread()) {
+    (this->*functionToBeCalled)();
+  } else {
+    TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
+        ->postTask(BLINK_FROM_HERE,
+                   crossThreadBind(functionToBeCalled,
+                                   wrapCrossThreadPersistent(this)));
   }
 
   this->signalAlternativeCodePathFinishedForTesting();
@@ -523,7 +538,7 @@
 void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality) {
   if (m_idleTaskStatus == IdleTaskStarted) {
     // Even if the task started quickly, we still want to ensure completion
-    this->postDelayedTaskToMainThread(
+    this->postDelayedTaskToCurrentThread(
         BLINK_FROM_HERE,
         WTF::bind(&CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent,
                   wrapPersistent(this)),
@@ -540,8 +555,9 @@
         TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
             ->postTask(
                 BLINK_FROM_HERE,
-                WTF::bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread,
-                          wrapPersistent(this)));
+                WTF::bind(
+                    &CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread,
+                    wrapPersistent(this)));
       } else {
         // Failing in initialization of png struct
         this->signalAlternativeCodePathFinishedForTesting();
@@ -552,8 +568,9 @@
         TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
             ->postTask(
                 BLINK_FROM_HERE,
-                WTF::bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread,
-                          wrapPersistent(this)));
+                WTF::bind(
+                    &CanvasAsyncBlobCreator::forceEncodeRowsJpegOnCurrentThread,
+                    wrapPersistent(this)));
       } else {
         // Failing in initialization of jpeg struct
         this->signalAlternativeCodePathFinishedForTesting();
@@ -578,15 +595,17 @@
       TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
           ->postTask(
               BLINK_FROM_HERE,
-              WTF::bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread,
-                        wrapPersistent(this)));
+              WTF::bind(
+                  &CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread,
+                  wrapPersistent(this)));
     } else {
       DCHECK(m_mimeType == MimeTypeJpeg);
       TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
           ->postTask(
               BLINK_FROM_HERE,
-              WTF::bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread,
-                        wrapPersistent(this)));
+              WTF::bind(
+                  &CanvasAsyncBlobCreator::forceEncodeRowsJpegOnCurrentThread,
+                  wrapPersistent(this)));
     }
   } else {
     DCHECK(m_idleTaskStatus == IdleTaskFailed ||
@@ -595,11 +614,10 @@
   }
 }
 
-void CanvasAsyncBlobCreator::postDelayedTaskToMainThread(
+void CanvasAsyncBlobCreator::postDelayedTaskToCurrentThread(
     const WebTraceLocation& location,
     std::unique_ptr<WTF::Closure> task,
     double delayMs) {
-  DCHECK(isMainThread());
   TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
       ->postDelayedTask(location, std::move(task), delayMs);
 }
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.h b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.h
index ca2fff6..ae4caa2 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.h
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.h
@@ -83,9 +83,9 @@
   virtual void scheduleInitiateJpegEncoding(const double&);
   virtual void idleEncodeRowsPng(double deadlineSeconds);
   virtual void idleEncodeRowsJpeg(double deadlineSeconds);
-  virtual void postDelayedTaskToMainThread(const WebTraceLocation&,
-                                           std::unique_ptr<WTF::Closure>,
-                                           double delayMs);
+  virtual void postDelayedTaskToCurrentThread(const WebTraceLocation&,
+                                              std::unique_ptr<WTF::Closure>,
+                                              double delayMs);
   virtual void signalAlternativeCodePathFinishedForTesting() {}
   virtual void createBlobAndReturnResult();
   virtual void createNullAndReturnResult();
@@ -126,13 +126,14 @@
 
   // PNG
   bool initializePngStruct();
-  void
-  encodeRowsPngOnMainThread();  // Similar to idleEncodeRowsPng without deadline
+  void forceEncodeRowsPngOnCurrentThread();  // Similar to idleEncodeRowsPng
+                                             // without deadline
 
   // JPEG
   bool initializeJpegStruct(double quality);
-  void encodeRowsJpegOnMainThread();  // Similar to idleEncodeRowsJpeg without
-                                      // deadline
+  void forceEncodeRowsJpegOnCurrentThread();  // Similar to idleEncodeRowsJpeg
+                                              // without
+                                              // deadline
 
   // WEBP
   void encodeImageOnEncoderThread(double quality);
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreatorTest.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreatorTest.cpp
index f803460..6a771c1 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreatorTest.cpp
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreatorTest.cpp
@@ -41,16 +41,16 @@
   void createBlobAndReturnResult() override{};
   void createNullAndReturnResult() override{};
   void signalAlternativeCodePathFinishedForTesting() override;
-  void postDelayedTaskToMainThread(const WebTraceLocation&,
-                                   std::unique_ptr<WTF::Closure>,
-                                   double delayMs) override;
+  void postDelayedTaskToCurrentThread(const WebTraceLocation&,
+                                      std::unique_ptr<WTF::Closure>,
+                                      double delayMs) override;
 };
 
 void MockCanvasAsyncBlobCreator::signalAlternativeCodePathFinishedForTesting() {
   testing::exitRunLoop();
 }
 
-void MockCanvasAsyncBlobCreator::postDelayedTaskToMainThread(
+void MockCanvasAsyncBlobCreator::postDelayedTaskToCurrentThread(
     const WebTraceLocation& location,
     std::unique_ptr<WTF::Closure> task,
     double delayMs) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutGeometryMap.cpp b/third_party/WebKit/Source/core/layout/LayoutGeometryMap.cpp
index a088fbd..ee0f643b 100644
--- a/third_party/WebKit/Source/core/layout/LayoutGeometryMap.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutGeometryMap.cpp
@@ -204,6 +204,10 @@
 
     if (current == ancestor)
       break;
+
+    if (current->isFloating() && current->parent() &&
+        !current->parent()->isLayoutBlock())
+      return false;
   }
 
   return true;
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index 96512719..d2a508f42 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -613,7 +613,11 @@
 
 PaintLayer* LayoutObject::paintingLayer() const {
   for (const LayoutObject* current = this; current;
-       current = current->paintInvalidationParent()) {
+       // Use containingBlock instead of paintInvalidationParent for floating
+       // object to omit any self-painting layers of inline objects that don't
+       // paint the floating object.
+       current = current->isFloating() ? current->containingBlock()
+                                       : current->paintInvalidationParent()) {
     if (current->hasLayer() &&
         toLayoutBoxModelObject(current)->layer()->isSelfPaintingLayer())
       return toLayoutBoxModelObject(current)->layer();
@@ -893,53 +897,69 @@
   return toLayoutBlock(object);
 }
 
-LayoutBlock* LayoutObject::containingBlockForAbsolutePosition() const {
-  LayoutObject* o = containerForAbsolutePosition();
+LayoutBlock* LayoutObject::containingBlockForAbsolutePosition(
+    const LayoutBoxModelObject* ancestor,
+    bool* ancestorSkipped,
+    bool* filterSkipped) const {
+  LayoutObject* object =
+      containerForAbsolutePosition(ancestor, ancestorSkipped, filterSkipped);
 
   // For relpositioned inlines, we return the nearest non-anonymous enclosing
   // block. We don't try to return the inline itself. This allows us to avoid
   // having a positioned objects list in all LayoutInlines and lets us return a
   // strongly-typed LayoutBlock* result from this method. The container() method
   // can actually be used to obtain the inline directly.
-  if (o && o->isInline() && !o->isAtomicInlineLevel()) {
-    ASSERT(o->style()->hasInFlowPosition());
-    o = o->containingBlock();
+  if (object && object->isInline() && !object->isAtomicInlineLevel()) {
+    DCHECK(object->style()->hasInFlowPosition());
+    object = object->containingBlock(ancestor, ancestorSkipped, filterSkipped);
   }
 
-  if (o && !o->isLayoutBlock())
-    o = o->containingBlock();
+  if (object && !object->isLayoutBlock())
+    object = object->containingBlock(ancestor, ancestorSkipped, filterSkipped);
 
-  while (o && o->isAnonymousBlock())
-    o = o->containingBlock();
+  while (object && object->isAnonymousBlock())
+    object = object->containingBlock(ancestor, ancestorSkipped, filterSkipped);
 
-  if (!o || !o->isLayoutBlock())
+  if (!object || !object->isLayoutBlock())
     return nullptr;  // This can still happen in case of an orphaned tree
 
-  return toLayoutBlock(o);
+  return toLayoutBlock(object);
 }
 
-LayoutBlock* LayoutObject::containingBlock() const {
-  LayoutObject* o = parent();
-  if (!o && isLayoutScrollbarPart())
-    o = toLayoutScrollbarPart(this)->layoutObjectOwningScrollbar();
+LayoutBlock* LayoutObject::containingBlock(const LayoutBoxModelObject* ancestor,
+                                           bool* ancestorSkipped,
+                                           bool* filterSkipped) const {
+  LayoutObject* object = parent();
+  if (!object && isLayoutScrollbarPart())
+    object = toLayoutScrollbarPart(this)->layoutObjectOwningScrollbar();
   if (!isTextOrSVGChild()) {
-    if (m_style->position() == FixedPosition)
-      return containerForFixedPosition();
-    if (m_style->position() == AbsolutePosition)
-      return containingBlockForAbsolutePosition();
+    if (m_style->position() == FixedPosition) {
+      return containerForFixedPosition(ancestor, ancestorSkipped,
+                                       filterSkipped);
+    }
+    if (m_style->position() == AbsolutePosition) {
+      return containingBlockForAbsolutePosition(ancestor, ancestorSkipped,
+                                                filterSkipped);
+    }
   }
   if (isColumnSpanAll()) {
-    o = spannerPlaceholder()->containingBlock();
+    object = spannerPlaceholder()->containingBlock();
   } else {
-    while (o && ((o->isInline() && !o->isAtomicInlineLevel()) ||
-                 !o->isLayoutBlock()))
-      o = o->parent();
+    while (object && ((object->isInline() && !object->isAtomicInlineLevel()) ||
+                      !object->isLayoutBlock())) {
+      if (ancestorSkipped && object == ancestor)
+        *ancestorSkipped = true;
+
+      if (filterSkipped && object->hasFilterInducingProperty())
+        *filterSkipped = true;
+      object = object->parent();
+    }
   }
 
-  if (!o || !o->isLayoutBlock())
+  if (!object || !object->isLayoutBlock())
     return nullptr;  // This can still happen in case of an orphaned tree
 
-  return toLayoutBlock(o);
+  return toLayoutBlock(object);
 }
 
 FloatRect LayoutObject::absoluteBoundingBoxFloatRect() const {
@@ -1030,7 +1050,7 @@
 
 const LayoutBoxModelObject& LayoutObject::containerForPaintInvalidation()
     const {
-  RELEASE_ASSERT(isRooted());
+  CHECK(isRooted());
 
   if (const LayoutBoxModelObject* paintInvalidationContainer =
           enclosingCompositedContainer())
@@ -1039,12 +1059,11 @@
   // If the current frame is not composited, we send just return the main
   // frame's LayoutView so that we generate invalidations on the window.
   const LayoutView* layoutView = view();
-  while (
-      LayoutAPIShim::layoutObjectFrom(layoutView->frame()->ownerLayoutItem()))
-    layoutView =
-        LayoutAPIShim::layoutObjectFrom(layoutView->frame()->ownerLayoutItem())
-            ->view();
-  ASSERT(layoutView);
+  while (const LayoutObject* ownerObject = LayoutAPIShim::constLayoutObjectFrom(
+             layoutView->frame()->ownerLayoutItem()))
+    layoutView = ownerObject->view();
+
+  DCHECK(layoutView);
   return *layoutView;
 }
 
@@ -2498,18 +2517,17 @@
   if (filterSkipped)
     *filterSkipped = false;
 
-  LayoutObject* o = parent();
-
   if (isTextOrSVGChild())
-    return o;
+    return parent();
 
   EPosition pos = m_style->position();
   if (pos == FixedPosition)
     return containerForFixedPosition(ancestor, ancestorSkipped, filterSkipped);
 
-  if (pos == AbsolutePosition)
+  if (pos == AbsolutePosition) {
     return containerForAbsolutePosition(ancestor, ancestorSkipped,
                                         filterSkipped);
+  }
 
   if (isColumnSpanAll()) {
     LayoutObject* multicolContainer = spannerPlaceholder()->container();
@@ -2527,7 +2545,10 @@
     return multicolContainer;
   }
 
-  return o;
+  if (isFloating())
+    return containingBlock(ancestor, ancestorSkipped, filterSkipped);
+
+  return parent();
 }
 
 inline LayoutObject* LayoutObject::paintInvalidationParent() const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index 9cd19a3..cda782a6 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -901,15 +901,15 @@
   //
   // This method is extremely similar to containingBlock(), but with a few
   // notable exceptions.
-  // (1) It can be used on orphaned subtrees, i.e., it can be called safely
-  //     even when the object is not part of the primary document subtree yet.
-  // (2) For normal flow elements, it just returns the parent.
-  // (3) For absolute positioned elements, it will return a relative
+  // (1) For normal flow elements, it just returns the parent.
+  // (2) For absolute positioned elements, it will return a relative
   //     positioned inline. containingBlock() simply skips relpositioned inlines
   //     and lets an enclosing block handle the layout of the positioned object.
   //     This does mean that computePositionedLogicalWidth and
   //     computePositionedLogicalHeight have to use container().
   //
+  // Note that floating objects don't belong to either of the above exceptions.
+  //
   // This function should be used for any invalidation as it would correctly
   // walk the containing block chain. See e.g. markContainerChainForLayout.
   // It is also used for correctly sizing absolutely positioned elements
@@ -927,7 +927,10 @@
       bool* ancestorSkipped = nullptr,
       bool* filterSkipped = nullptr) const;
   // Finds the containing block as if this object is absolute-position.
-  LayoutBlock* containingBlockForAbsolutePosition() const;
+  LayoutBlock* containingBlockForAbsolutePosition(
+      const LayoutBoxModelObject* ancestor = nullptr,
+      bool* ancestorSkipped = nullptr,
+      bool* filterSkipped = nullptr) const;
 
   virtual LayoutObject* hoverAncestor() const { return parent(); }
 
@@ -1116,7 +1119,9 @@
   //
   // See container() for the function that returns the containing block.
   // See LayoutBlock.h for some extra explanations on containing blocks.
-  LayoutBlock* containingBlock() const;
+  LayoutBlock* containingBlock(const LayoutBoxModelObject* ancestor = nullptr,
+                               bool* ancestorSkipped = nullptr,
+                               bool* filterSkipped = nullptr) const;
 
   bool canContainAbsolutePositionObjects() const {
     return m_style->canContainAbsolutePositionObjects() ||
diff --git a/third_party/WebKit/Source/core/layout/LayoutObjectTest.cpp b/third_party/WebKit/Source/core/layout/LayoutObjectTest.cpp
index ca872f5b..e870cd1 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObjectTest.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObjectTest.cpp
@@ -125,6 +125,59 @@
   EXPECT_EQ(columns->layer(), overflowClipObject->paintingLayer());
 }
 
+TEST_F(LayoutObjectTest, FloatUnderBlock) {
+  setBodyInnerHTML(
+      "<div id='layered-div' style='position: absolute'>"
+      "  <div id='container'>"
+      "    <div id='floating' style='float: left'>FLOAT</div>"
+      "  </div>"
+      "</div>");
+
+  LayoutBoxModelObject* layeredDiv =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("layered-div"));
+  LayoutBoxModelObject* container =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("container"));
+  LayoutObject* floating = getLayoutObjectByElementId("floating");
+
+  EXPECT_EQ(layeredDiv->layer(), layeredDiv->paintingLayer());
+  EXPECT_EQ(layeredDiv->layer(), floating->paintingLayer());
+  EXPECT_EQ(container, floating->container());
+  EXPECT_EQ(container, floating->containingBlock());
+}
+
+TEST_F(LayoutObjectTest, FloatUnderInline) {
+  setBodyInnerHTML(
+      "<div id='layered-div' style='position: absolute'>"
+      "  <div id='container'>"
+      "    <span id='layered-span' style='position: relative'>"
+      "      <div id='floating' style='float: left'>FLOAT</div>"
+      "    </span>"
+      "  </div>"
+      "</div>");
+
+  LayoutBoxModelObject* layeredDiv =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("layered-div"));
+  LayoutBoxModelObject* container =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("container"));
+  LayoutBoxModelObject* layeredSpan =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("layered-span"));
+  LayoutObject* floating = getLayoutObjectByElementId("floating");
+
+  EXPECT_EQ(layeredDiv->layer(), layeredDiv->paintingLayer());
+  EXPECT_EQ(layeredSpan->layer(), layeredSpan->paintingLayer());
+  EXPECT_EQ(layeredDiv->layer(), floating->paintingLayer());
+  EXPECT_EQ(container, floating->container());
+  EXPECT_EQ(container, floating->containingBlock());
+
+  bool ancestorSkipped = false;
+  EXPECT_EQ(container, floating->container(layeredSpan, &ancestorSkipped));
+  EXPECT_TRUE(ancestorSkipped);
+
+  ancestorSkipped = false;
+  EXPECT_EQ(container, floating->container(container, &ancestorSkipped));
+  EXPECT_FALSE(ancestorSkipped);
+}
+
 TEST_F(LayoutObjectTest, MutableForPaintingClearPaintFlags) {
   LayoutObject* object = document().body()->layoutObject();
   object->setShouldDoFullPaintInvalidation();
diff --git a/third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp b/third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp
index 421e3ed..fe434f3 100644
--- a/third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp
+++ b/third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp
@@ -98,11 +98,7 @@
       m_svgTransform(parentState.m_svgTransform),
       m_pendingDelayedPaintInvalidations(
           parentState.m_pendingDelayedPaintInvalidations),
-      m_paintingLayer(
-          currentObject.hasLayer() &&
-                  toLayoutBoxModelObject(currentObject).hasSelfPaintingLayer()
-              ? *toLayoutBoxModelObject(currentObject).layer()
-              : parentState.m_paintingLayer)
+      m_paintingLayer(parentState.childPaintingLayer(currentObject))
 #if ENABLE(ASSERT)
       ,
       m_didUpdateForChildren(false)
@@ -145,6 +141,12 @@
     // paintInvalidationContainer.
     m_paintInvalidationContainerForStackedContents =
         m_paintInvalidationContainer;
+  } else if (currentObject.isFloating() &&
+             !currentObject.parent()->isLayoutBlock()) {
+    // See LayoutObject::paintingLayer() for specialty of floating objects.
+    m_paintInvalidationContainer =
+        &currentObject.containerForPaintInvalidation();
+    m_cachedOffsetsEnabled = false;
   } else if (currentObject.styleRef().isStacked() &&
              // This is to exclude some objects (e.g. LayoutText) inheriting
              // stacked style from parent but aren't actually stacked.
@@ -230,6 +232,16 @@
   updateForCurrentObject(parentState);
 }
 
+PaintLayer& PaintInvalidationState::childPaintingLayer(
+    const LayoutObject& child) const {
+  if (child.hasLayer() && toLayoutBoxModelObject(child).hasSelfPaintingLayer())
+    return *toLayoutBoxModelObject(child).layer();
+  // See LayoutObject::paintingLayer() for specialty of floating objects.
+  if (child.isFloating() && !m_currentObject.isLayoutBlock())
+    return *child.paintingLayer();
+  return m_paintingLayer;
+}
+
 void PaintInvalidationState::updateForCurrentObject(
     const PaintInvalidationState& parentState) {
   if (!m_cachedOffsetsEnabled)
diff --git a/third_party/WebKit/Source/core/layout/PaintInvalidationState.h b/third_party/WebKit/Source/core/layout/PaintInvalidationState.h
index cded1752..c887b12 100644
--- a/third_party/WebKit/Source/core/layout/PaintInvalidationState.h
+++ b/third_party/WebKit/Source/core/layout/PaintInvalidationState.h
@@ -108,6 +108,8 @@
   friend class VisualRectMappingTest;
   friend class PaintInvalidatorContextAdapter;
 
+  inline PaintLayer& childPaintingLayer(const LayoutObject& child) const;
+
   void mapLocalRectToPaintInvalidationContainer(LayoutRect&) const;
 
   void updateForCurrentObject(const PaintInvalidationState& parentState);
diff --git a/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp b/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
index d305494..0ff8052 100644
--- a/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
+++ b/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
@@ -674,4 +674,30 @@
   EXPECT_EQ(rect, target->visualRect());
 }
 
+TEST_F(VisualRectMappingTest, FloatUnderInline) {
+  setBodyInnerHTML(
+      "<div style='position: absolute; top: 55px; left: 66px'>"
+      "  <span id='span' style='position: relative; top: 100px; left: 200px'>"
+      "    <div id='target' style='float: left; width: 33px; height: 44px'>"
+      "    </div>"
+      "  </span>"
+      "</div>");
+
+  LayoutBoxModelObject* span =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("span"));
+  LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
+
+  LayoutRect targetVisualRect = target->localVisualRect();
+  EXPECT_EQ(LayoutRect(0, 0, 33, 44), targetVisualRect);
+
+  LayoutRect rect = targetVisualRect;
+  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect));
+  EXPECT_EQ(LayoutRect(66, 55, 33, 44), rect);
+  EXPECT_EQ(rect, target->visualRect());
+
+  rect = targetVisualRect;
+  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(span, rect));
+  EXPECT_EQ(LayoutRect(-200, -100, 33, 44), rect);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.cpp
index eb4b6f3..8510203 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.cpp
@@ -70,6 +70,11 @@
   LayoutBlockFlow::willBeDestroyed();
 }
 
+void LayoutSVGBlock::updateFromStyle() {
+  LayoutBlockFlow::updateFromStyle();
+  setFloating(false);
+}
+
 void LayoutSVGBlock::styleDidChange(StyleDifference diff,
                                     const ComputedStyle* oldStyle) {
   if (diff.needsFullLayout()) {
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
index 56077911..9ac0a9d 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
@@ -74,6 +74,7 @@
   void absoluteRects(Vector<IntRect>&,
                      const LayoutPoint& accumulatedOffset) const final;
 
+  void updateFromStyle() final;
   void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) final;
 
   bool nodeAtPoint(HitTestResult&,
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
index 64ef222..53bb1b95 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
@@ -198,8 +198,12 @@
 void PaintInvalidator::updatePaintingLayer(const LayoutObject& object,
                                            PaintInvalidatorContext& context) {
   if (object.hasLayer() &&
-      toLayoutBoxModelObject(object).hasSelfPaintingLayer())
+      toLayoutBoxModelObject(object).hasSelfPaintingLayer()) {
     context.paintingLayer = toLayoutBoxModelObject(object).layer();
+  } else if (object.isFloating() && !object.parent()->isLayoutBlock()) {
+    // See LayoutObject::paintingLayer() for specialty of floating objects.
+    context.paintingLayer = object.paintingLayer();
+  }
 
   if (object.isLayoutBlockFlow() && toLayoutBlockFlow(object).containsFloats())
     context.paintingLayer->setNeedsPaintPhaseFloat();
@@ -281,6 +285,10 @@
     if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled())
       undoFrameViewContentClipAndScroll.emplace(
           *toLayoutView(object).frameView(), context);
+  } else if (object.isFloating() && !object.parent()->isLayoutBlock()) {
+    // See LayoutObject::paintingLayer() for specialty of floating objects.
+    context.paintInvalidationContainer =
+        &object.containerForPaintInvalidation();
   } else if (object.styleRef().isStacked() &&
              // This is to exclude some objects (e.g. LayoutText) inheriting
              // stacked style from parent but aren't actually stacked.
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
index 3f8a6de6..05403b5 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
@@ -177,28 +177,32 @@
   return storeClipRectsInCache(context, parentClipRects, *clipRects);
 }
 
-void PaintLayerClipper::clearClipRectsIncludingDescendants() {
+void PaintLayerClipper::clearCache(ClipRectsCacheSlot cacheSlot) {
+  if (cacheSlot == NumberOfClipRectsCacheSlots)
+    m_layer.clearClipRectsCache();
+  else if (ClipRectsCache* cache = m_layer.clipRectsCache())
+    cache->clear(cacheSlot);
+
   if (m_geometryMapper)
     m_geometryMapper.reset(new GeometryMapper);
-  m_layer.clearClipRectsCache();
+}
 
-  for (PaintLayer* layer = m_layer.firstChild(); layer;
-       layer = layer->nextSibling()) {
-    layer->clipper().clearClipRectsIncludingDescendants();
-  }
+void PaintLayerClipper::clearClipRectsIncludingDescendants() {
+  clearClipRectsIncludingDescendants(NumberOfClipRectsCacheSlots);
 }
 
 void PaintLayerClipper::clearClipRectsIncludingDescendants(
     ClipRectsCacheSlot cacheSlot) {
-  if (m_geometryMapper)
-    m_geometryMapper.reset(new GeometryMapper);
+  std::stack<const PaintLayer*> layers;
+  layers.push(&m_layer);
 
-  if (ClipRectsCache* cache = m_layer.clipRectsCache())
-    cache->clear(cacheSlot);
-
-  for (PaintLayer* layer = m_layer.firstChild(); layer;
-       layer = layer->nextSibling()) {
-    layer->clipper().clearClipRectsIncludingDescendants(cacheSlot);
+  while (!layers.empty()) {
+    const PaintLayer* currentLayer = layers.top();
+    layers.pop();
+    currentLayer->clipper().clearCache(cacheSlot);
+    for (const PaintLayer* layer = currentLayer->firstChild(); layer;
+         layer = layer->nextSibling())
+      layers.push(layer);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.h b/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
index df71e64..f3dd97d 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.h
@@ -200,6 +200,7 @@
                                const LayoutSize& subpixelAccumulation) const;
 
  private:
+  void clearCache(ClipRectsCacheSlot);
   ClipRects& getClipRects(const ClipRectsContext&) const;
 
   void calculateClipRects(const ClipRectsContext&, ClipRects&) const;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
index 8cacaeb8a2..b773345 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipperTest.cpp
@@ -144,4 +144,92 @@
             fixed->clipper().localClipRect(transformed));
 }
 
+TEST_F(PaintLayerClipperTest, ClearClipRectsRecursive) {
+  setBodyInnerHTML(
+      "<style>"
+      "div { "
+      "  width: 5px; height: 5px; background: blue;"
+      "  position: relative;"
+      "}"
+      "</style>"
+      "<div id='parent'>"
+      "  <div id='child'>"
+      "    <div id='grandchild'></div>"
+      "  </div>"
+      "</div>");
+
+  PaintLayer* parent =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("parent"))->layer();
+  PaintLayer* child =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("child"))->layer();
+
+  EXPECT_TRUE(parent->clipRectsCache());
+  EXPECT_TRUE(child->clipRectsCache());
+
+  parent->clipper().clearClipRectsIncludingDescendants();
+
+  EXPECT_FALSE(parent->clipRectsCache());
+  EXPECT_FALSE(child->clipRectsCache());
+}
+
+TEST_F(PaintLayerClipperTest, ClearClipRectsRecursiveChild) {
+  setBodyInnerHTML(
+      "<style>"
+      "div { "
+      "  width: 5px; height: 5px; background: blue;"
+      "  position: relative;"
+      "}"
+      "</style>"
+      "<div id='parent'>"
+      "  <div id='child'>"
+      "    <div id='grandchild'></div>"
+      "  </div>"
+      "</div>");
+
+  PaintLayer* parent =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("parent"))->layer();
+  PaintLayer* child =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("child"))->layer();
+
+  EXPECT_TRUE(parent->clipRectsCache());
+  EXPECT_TRUE(child->clipRectsCache());
+
+  child->clipper().clearClipRectsIncludingDescendants();
+
+  EXPECT_TRUE(parent->clipRectsCache());
+  EXPECT_FALSE(child->clipRectsCache());
+}
+
+TEST_F(PaintLayerClipperTest, ClearClipRectsRecursiveOneType) {
+  setBodyInnerHTML(
+      "<style>"
+      "div { "
+      "  width: 5px; height: 5px; background: blue;"
+      "  position: relative;"
+      "}"
+      "</style>"
+      "<div id='parent'>"
+      "  <div id='child'>"
+      "    <div id='grandchild'></div>"
+      "  </div>"
+      "</div>");
+
+  PaintLayer* parent =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("parent"))->layer();
+  PaintLayer* child =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("child"))->layer();
+
+  EXPECT_TRUE(parent->clipRectsCache());
+  EXPECT_TRUE(child->clipRectsCache());
+  EXPECT_TRUE(parent->clipRectsCache()->get(AbsoluteClipRects).root);
+  EXPECT_TRUE(child->clipRectsCache()->get(AbsoluteClipRects).root);
+
+  parent->clipper().clearClipRectsIncludingDescendants(AbsoluteClipRects);
+
+  EXPECT_TRUE(parent->clipRectsCache());
+  EXPECT_TRUE(child->clipRectsCache());
+  EXPECT_FALSE(parent->clipRectsCache()->get(AbsoluteClipRects).root);
+  EXPECT_FALSE(parent->clipRectsCache()->get(AbsoluteClipRects).root);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index 57fc3e2..4ebf106 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -803,6 +803,9 @@
 void PaintPropertyTreeBuilder::updateOutOfFlowContext(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
+  if (object.isLayoutBlock())
+    context.paintOffsetForFloat = context.current.paintOffset;
+
   if (object.canContainAbsolutePositionObjects()) {
     context.absolutePosition = context.current;
     context.containerForAbsolutePosition = &object;
@@ -897,6 +900,10 @@
     return;
 
   const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object);
+
+  if (boxModelObject.isFloating())
+    context.current.paintOffset = context.paintOffsetForFloat;
+
   switch (object.styleRef().position()) {
     case StaticPosition:
       break;
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
index 3f2f6c9..f3a9112b8 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
@@ -65,6 +65,11 @@
 
   ContainingBlockContext fixedPosition;
 
+  // This is the same as current.paintOffset except when a floating object has
+  // non-block ancestors under its containing block. Paint offsets of the
+  // non-block ancestors should not be accumulated for the floating object.
+  LayoutPoint paintOffsetForFloat;
+
   // The effect hierarchy is applied by the stacking context tree. It is
   // guaranteed that every DOM descendant is also a stacking context descendant.
   // Therefore, we don't need extra bookkeeping for effect nodes and can
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index e7480ff..573872eb 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -3196,4 +3196,27 @@
   EXPECT_NE(CompositorElementId(), properties->effect()->compositorElementId());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, FloatUnderInline) {
+  setBodyInnerHTML(
+      "<div style='position: absolute; top: 55px; left: 66px'>"
+      "  <span id='span'"
+      "      style='position: relative; top: 100px; left: 200px; opacity: 0.5'>"
+      "    <div id='target' style='float: left; width: 33px; height: 44px'>"
+      "    </div>"
+      "  </span"
+      "</div>");
+
+  LayoutObject* span = getLayoutObjectByElementId("span");
+  const auto* effect = span->paintProperties()->effect();
+  ASSERT_TRUE(effect);
+  EXPECT_EQ(0.5f, effect->opacity());
+
+  LayoutObject* target = getLayoutObjectByElementId("target");
+  const auto* localBorderBoxProperties =
+      target->paintProperties()->localBorderBoxProperties();
+  ASSERT_TRUE(localBorderBoxProperties);
+  EXPECT_EQ(LayoutPoint(66, 55), localBorderBoxProperties->paintOffset);
+  EXPECT_EQ(effect, localBorderBoxProperties->propertyTreeState.effect());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/front_end/platform/utilities.js b/third_party/WebKit/Source/devtools/front_end/platform/utilities.js
index 1edf9b9..9c1413b 100644
--- a/third_party/WebKit/Source/devtools/front_end/platform/utilities.js
+++ b/third_party/WebKit/Source/devtools/front_end/platform/utilities.js
@@ -1367,7 +1367,8 @@
  * @return {number}
  */
 self.setImmediate = function(callback) {
-  Promise.resolve().then(callback);
+  const args = [...arguments].slice(1);
+  Promise.resolve().then(() => callback(...args));
   return 0;
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/CookieItemsView.js b/third_party/WebKit/Source/devtools/front_end/resources/CookieItemsView.js
index 42c424c..8344131 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/CookieItemsView.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/CookieItemsView.js
@@ -36,17 +36,23 @@
 
     this.element.classList.add('storage-view');
 
-    this._deleteButton = new UI.ToolbarButton(Common.UIString('Delete'), 'largeicon-delete');
-    this._deleteButton.setVisible(false);
+    this._deleteButton = new UI.ToolbarButton(Common.UIString('Delete Selected'), 'largeicon-delete');
     this._deleteButton.addEventListener(UI.ToolbarButton.Events.Click, this._deleteButtonClicked, this);
 
-    this._clearButton = new UI.ToolbarButton(Common.UIString('Clear'), 'largeicon-clear');
-    this._clearButton.setVisible(false);
+    this._clearButton = new UI.ToolbarButton(Common.UIString('Clear All'), 'largeicon-clear');
     this._clearButton.addEventListener(UI.ToolbarButton.Events.Click, this._clearButtonClicked, this);
 
     this._refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh');
     this._refreshButton.addEventListener(UI.ToolbarButton.Events.Click, this._refreshButtonClicked, this);
 
+    this._filterBar = new UI.FilterBar('cookiesPanel', true);
+    this._textFilterUI = new UI.TextFilterUI(true);
+    this._textFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._filterChanged, this);
+    this._filterBar.addFilter(this._textFilterUI);
+
+    this._filterSeparator = new UI.ToolbarSeparator();
+    this._filterButton = this._filterBar.filterButton();
+
     this._treeElement = treeElement;
     this._cookieDomain = cookieDomain;
 
@@ -65,7 +71,10 @@
    * @return {!Array.<!UI.ToolbarItem>}
    */
   syncToolbarItems() {
-    return [this._refreshButton, this._clearButton, this._deleteButton];
+    return [
+      this._refreshButton, this._clearButton, this._deleteButton,
+      this._filterSeparator, this._filterButton
+    ];
   }
 
   /**
@@ -79,7 +88,16 @@
    * @override
    */
   willHide() {
-    this._deleteButton.setVisible(false);
+    this._deleteButton.setEnabled(false);
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _filterChanged(event) {
+    var text = this._textFilterUI.value();
+    this._filterRegex = text && new RegExp(text.escapeForRegExp(), 'i');
+    this._update();
   }
 
   _update() {
@@ -95,8 +113,9 @@
     if (!this._cookies.length) {
       // Nothing to show.
       this._emptyWidget.show(this.element);
-      this._clearButton.setVisible(false);
-      this._deleteButton.setVisible(false);
+      this._filterButton.setEnabled(false);
+      this._clearButton.setEnabled(false);
+      this._deleteButton.setEnabled(false);
       if (this._cookiesTable)
         this._cookiesTable.detach();
       return;
@@ -104,19 +123,35 @@
 
     if (!this._cookiesTable) {
       this._cookiesTable =
-          new Components.CookiesTable(false, this._update.bind(this), this._showDeleteButton.bind(this));
+          new Components.CookiesTable(false, this._update.bind(this), this._enableDeleteButton.bind(this));
     }
 
-    this._cookiesTable.setCookies(this._cookies);
+    var shownCookies = this._filterCookiesForFilters(this._cookies);
+    this._cookiesTable.setCookies(shownCookies);
     this._emptyWidget.detach();
     this._cookiesTable.show(this.element);
+    this._filterBar.show(this.element);
     this._treeElement.subtitle =
         String.sprintf(Common.UIString('%d cookies (%s)'), this._cookies.length, Number.bytesToString(this._totalSize));
-    this._clearButton.setVisible(true);
-    this._deleteButton.setVisible(!!this._cookiesTable.selectedCookie());
+    this._filterButton.setEnabled(true);
+    this._clearButton.setEnabled(true);
+    this._deleteButton.setEnabled(!!this._cookiesTable.selectedCookie());
   }
 
   /**
+   * @param {!Array.<!SDK.Cookie>} cookies
+   */
+  _filterCookiesForFilters(cookies) {
+    if (!this._filterRegex)
+      return cookies;
+
+    return cookies.filter(cookie => {
+      const candidate = `${cookie.name()} ${cookie.value()} ${cookie.domain()}`;
+      return this._filterRegex.test(candidate);
+    });
+   }
+
+  /**
    * @param {!Array.<!SDK.Cookie>} allCookies
    */
   _filterCookiesForDomain(allCookies) {
@@ -163,8 +198,8 @@
     this.clear();
   }
 
-  _showDeleteButton() {
-    this._deleteButton.setVisible(true);
+  _enableDeleteButton() {
+    this._deleteButton.setEnabled(true);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/resourcesPanel.css b/third_party/WebKit/Source/devtools/front_end/resources/resourcesPanel.css
index b620be29d..70e82bd1 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/resourcesPanel.css
+++ b/third_party/WebKit/Source/devtools/front_end/resources/resourcesPanel.css
@@ -80,6 +80,11 @@
     overflow-x: hidden;
 }
 
+.storage-view .filter-bar {
+    border-bottom: none;
+    border-top: 1px solid #dadada;
+}
+
 .database-query-prompt {
     position: relative;
     padding: 1px 22px 1px 24px;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
index 862dd38d..5afcdec 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
@@ -199,7 +199,10 @@
    * @param {string} placeholder
    */
   setPlaceholder(placeholder) {
-    this._element.setAttribute('data-placeholder', placeholder);
+    if (placeholder)
+      this._element.setAttribute('data-placeholder', placeholder);
+    else
+      this._element.removeAttribute('data-placeholder');
   }
 
   _removeFromElement() {
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/textPrompt.css b/third_party/WebKit/Source/devtools/front_end/ui/textPrompt.css
index 25602a2..a39597a 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/textPrompt.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/textPrompt.css
@@ -35,7 +35,7 @@
     -webkit-user-modify: read-only;
 }
 
-::content .text-prompt:empty::before {
+::content .text-prompt[data-placeholder]:empty::before {
     content: attr(data-placeholder);
     color: rgb(128, 128, 128);
 }
diff --git a/third_party/WebKit/Source/web/WebFrameSerializer.cpp b/third_party/WebKit/Source/web/WebFrameSerializer.cpp
index 38008c2..8e977263 100644
--- a/third_party/WebKit/Source/web/WebFrameSerializer.cpp
+++ b/third_party/WebKit/Source/web/WebFrameSerializer.cpp
@@ -294,6 +294,11 @@
     FrameSerializer serializer(resources, coreDelegate);
     serializer.serializeFrame(*frame);
   }
+
+  // There was an error serializing the frame (e.g. of an image resource).
+  if (resources.isEmpty())
+    return WebThreadSafeData();
+
   TRACE_EVENT_END1("page-serialization",
                    "WebFrameSerializer::generateMHTMLParts serializing",
                    "resource count",
@@ -302,7 +307,6 @@
   // Encode serialized resources as MHTML.
   RefPtr<RawData> output = RawData::create();
   {
-    DCHECK(!resources.isEmpty());
     SCOPED_BLINK_UMA_HISTOGRAM_TIMER(
         "PageSerialization.MhtmlGeneration.EncodingTime.SingleFrame");
     // Frame is the 1st resource (see FrameSerializer::serializeFrame doc
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index 25834de..ec409dc 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -1776,6 +1776,7 @@
   // viewport with the browser controls shown.
   IntSize ICBSize = m_size;
   if (RuntimeEnabledFeatures::inertTopControlsEnabled() &&
+      browserControls().permittedState() == WebBrowserControlsBoth &&
       !browserControls().shrinkViewport())
     ICBSize.expand(0, -browserControls().height());
 
@@ -1797,8 +1798,20 @@
 void WebViewImpl::updateBrowserControlsState(WebBrowserControlsState constraint,
                                              WebBrowserControlsState current,
                                              bool animate) {
+  WebBrowserControlsState oldPermittedState =
+      browserControls().permittedState();
+
   browserControls().updateConstraintsAndState(constraint, current, animate);
 
+  // If the controls are going from a locked to an unlocked state, or
+  // vice-versa, then we need to force a recompute of the ICB size since that
+  // depends on the permitted browser controls state.
+  if (oldPermittedState != constraint &&
+      (oldPermittedState == WebBrowserControlsBoth ||
+       constraint == WebBrowserControlsBoth)) {
+    performResize();
+  }
+
   if (m_layerTreeView)
     m_layerTreeView->updateBrowserControlsState(constraint, current, animate);
 }
@@ -1845,8 +1858,7 @@
   return page()->frameHost().browserControls();
 }
 
-void WebViewImpl::resizeViewWhileAnchored(FrameView* view,
-                                          float browserControlsHeight,
+void WebViewImpl::resizeViewWhileAnchored(float browserControlsHeight,
                                           bool browserControlsShrinkLayout) {
   DCHECK(mainFrameImpl());
 
@@ -1910,12 +1922,10 @@
   if (isRotation) {
     RotationViewportAnchor anchor(*view, visualViewport, viewportAnchorCoords,
                                   pageScaleConstraintsSet());
-    resizeViewWhileAnchored(view, browserControlsHeight,
-                            browserControlsShrinkLayout);
+    resizeViewWhileAnchored(browserControlsHeight, browserControlsShrinkLayout);
   } else {
     ResizeViewportAnchor::ResizeScope resizeScope(*m_resizeViewportAnchor);
-    resizeViewWhileAnchored(view, browserControlsHeight,
-                            browserControlsShrinkLayout);
+    resizeViewWhileAnchored(browserControlsHeight, browserControlsShrinkLayout);
   }
   sendResizeEventAndRepaint();
 }
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index b7bbfed..bcf5884 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -519,8 +519,7 @@
   IntSize contentsSize() const;
 
   void performResize();
-  void resizeViewWhileAnchored(FrameView*,
-                               float browserControlsHeight,
+  void resizeViewWhileAnchored(float browserControlsHeight,
                                bool browserControlsShrinkLayout);
 
   // Overrides the compositor visibility. See the description of
diff --git a/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp b/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
index bf75054..1e81b49 100644
--- a/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
+++ b/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
@@ -726,6 +726,86 @@
   EXPECT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
 }
 
+// Ensure that browser controls do not affect the layout by showing and hiding
+// except for position: fixed elements.
+TEST_F(BrowserControlsTest, MAYBE(AffectLayoutHeightWhenConstrained)) {
+  // Initialize with the browser controls showing.
+  WebViewImpl* webView = initialize("percent-height.html");
+  webView->resizeWithBrowserControls(WebSize(400, 300), 100.f, true);
+  webView->updateBrowserControlsState(WebBrowserControlsBoth,
+                                      WebBrowserControlsShown, false);
+  webView->browserControls().setShownRatio(1);
+  webView->updateAllLifecyclePhases();
+
+  Element* absPos = getElementById(WebString::fromUTF8("abs"));
+  Element* fixedPos = getElementById(WebString::fromUTF8("fixed"));
+
+  ASSERT_EQ(100.f, webView->browserControls().contentOffset());
+
+  // Hide the browser controls.
+  verticalScroll(-100.f);
+  webView->resizeWithBrowserControls(WebSize(400, 400), 100.f, false);
+  webView->updateAllLifecyclePhases();
+  ASSERT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
+
+  // Now lock the controls in a hidden state. The layout and elements should
+  // resize without a WebView::resize.
+  webView->updateBrowserControlsState(WebBrowserControlsHidden,
+                                      WebBrowserControlsBoth, false);
+
+  EXPECT_FLOAT_EQ(200.f, absPos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height());
+
+  EXPECT_EQ(400, frame()->view()->layoutSize(IncludeScrollbars).height());
+
+  // Unlock the controls, the sizes should change even though the controls are
+  // still hidden.
+  webView->updateBrowserControlsState(WebBrowserControlsBoth,
+                                      WebBrowserControlsBoth, false);
+
+  EXPECT_FLOAT_EQ(150.f, absPos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height());
+
+  EXPECT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
+
+  // Now lock the controls in a shown state.
+  webView->updateBrowserControlsState(WebBrowserControlsShown,
+                                      WebBrowserControlsBoth, false);
+  webView->resizeWithBrowserControls(WebSize(400, 300), 100.f, true);
+
+  EXPECT_FLOAT_EQ(150.f, absPos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(150.f, fixedPos->getBoundingClientRect()->height());
+
+  EXPECT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
+
+  // Shown -> Hidden
+  webView->resizeWithBrowserControls(WebSize(400, 400), 100.f, false);
+  webView->updateBrowserControlsState(WebBrowserControlsHidden,
+                                      WebBrowserControlsBoth, false);
+
+  EXPECT_FLOAT_EQ(200.f, absPos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height());
+
+  EXPECT_EQ(400, frame()->view()->layoutSize(IncludeScrollbars).height());
+
+  // Go from Unlocked and showing, to locked and hidden but issue the resize
+  // before the constraint update to check for race issues.
+  webView->updateBrowserControlsState(WebBrowserControlsBoth,
+                                      WebBrowserControlsShown, false);
+  webView->resizeWithBrowserControls(WebSize(400, 300), 100.f, true);
+  ASSERT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
+  webView->updateAllLifecyclePhases();
+
+  webView->resizeWithBrowserControls(WebSize(400, 400), 100.f, false);
+  webView->updateBrowserControlsState(WebBrowserControlsHidden,
+                                      WebBrowserControlsHidden, false);
+
+  EXPECT_FLOAT_EQ(200.f, absPos->getBoundingClientRect()->height());
+  EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height());
+
+  EXPECT_EQ(400, frame()->view()->layoutSize(IncludeScrollbars).height());
+}
+
 // Ensure that browser controls do not affect vh units.
 TEST_F(BrowserControlsTest, MAYBE(DontAffectVHUnits)) {
   // Initialize with the browser controls showing.
diff --git a/third_party/WebKit/Source/web/tests/LayoutGeometryMapTest.cpp b/third_party/WebKit/Source/web/tests/LayoutGeometryMapTest.cpp
index d4d2dcff..9d6bfa83 100644
--- a/third_party/WebKit/Source/web/tests/LayoutGeometryMapTest.cpp
+++ b/third_party/WebKit/Source/web/tests/LayoutGeometryMapTest.cpp
@@ -77,14 +77,19 @@
     return element->layoutBox();
   }
 
-  static LayoutBox* getLayoutBox(WebView* webView,
-                                 const WTF::AtomicString& elementId) {
+  static Element* getElement(WebView* webView,
+                             const WTF::AtomicString& elementId) {
     WebViewImpl* webViewImpl = toWebViewImpl(webView);
     if (!webViewImpl)
       return nullptr;
     LocalFrame* frame = webViewImpl->mainFrameImpl()->frame();
     Document* doc = frame->document();
-    Element* element = doc->getElementById(elementId);
+    return doc->getElementById(elementId);
+  }
+
+  static LayoutBox* getLayoutBox(WebView* webView,
+                                 const WTF::AtomicString& elementId) {
+    Element* element = getElement(webView, elementId);
     if (!element)
       return nullptr;
     return element->layoutBox();
@@ -494,4 +499,38 @@
   EXPECT_EQ(3.0f, rectFromQuad(rgm.mapToAncestor(rect, nullptr)).height());
 }
 
+TEST_P(LayoutGeometryMapTest, FloatUnderInlineLayer) {
+  registerMockedHttpURLLoad("rgm_float_under_inline.html");
+  FrameTestHelpers::WebViewHelper webViewHelper;
+  WebView* webView = webViewHelper.initializeAndLoad(
+      m_baseURL + "rgm_float_under_inline.html", true, 0, 0);
+  webView->resize(WebSize(1000, 1000));
+  webView->updateAllLifecyclePhases();
+
+  LayoutGeometryMap rgm;
+  auto* layerUnderFloat = getLayoutBox(webView, "layer-under-float");
+  auto* span = getElement(webView, "span")->layoutBoxModelObject();
+  auto* floating = getLayoutBox(webView, "float");
+  auto* container = getLayoutBox(webView, "container");
+  FloatRect rect(3.0f, 4.0f, 10.0f, 8.0f);
+
+  rgm.pushMappingsToAncestor(container->layer(), nullptr);
+  rgm.pushMappingsToAncestor(span->layer(), container->layer());
+  rgm.pushMappingsToAncestor(layerUnderFloat->layer(), span->layer());
+  EXPECT_EQ(rect, rectFromQuad(rgm.mapToAncestor(rect, container)));
+  EXPECT_EQ(FloatRect(63.0f, 54.0f, 10.0f, 8.0f),
+            rectFromQuad(rgm.mapToAncestor(rect, nullptr)));
+
+  rgm.popMappingsToAncestor(span->layer());
+  EXPECT_EQ(FloatRect(203.0f, 104.0f, 10.0f, 8.0f),
+            rectFromQuad(rgm.mapToAncestor(rect, container)));
+  EXPECT_EQ(FloatRect(263.0f, 154.0f, 10.0f, 8.0f),
+            rectFromQuad(rgm.mapToAncestor(rect, nullptr)));
+
+  rgm.pushMappingsToAncestor(floating, span);
+  EXPECT_EQ(rect, rectFromQuad(rgm.mapToAncestor(rect, container)));
+  EXPECT_EQ(FloatRect(63.0f, 54.0f, 10.0f, 8.0f),
+            rectFromQuad(rgm.mapToAncestor(rect, nullptr)));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
index 626598fb..7548471 100644
--- a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
+++ b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
@@ -2456,4 +2456,24 @@
   RuntimeEnabledFeatures::setInertTopControlsEnabled(originalInertTopControls);
 }
 
+// Make sure we don't crash when the visual viewport's height is 0. This can
+// happen transiently in autoresize mode and cause a crash. This test passes if
+// it doesn't crash.
+TEST_P(VisualViewportTest, AutoResizeNoHeightUsesMinimumHeight) {
+  initializeWithDesktopSettings();
+  webViewImpl()->resizeWithBrowserControls(WebSize(0, 0), 0, false);
+  webViewImpl()->enableAutoResizeMode(WebSize(25, 25), WebSize(100, 100));
+  WebURL baseURL = URLTestHelpers::toKURL("http://example.com/");
+  FrameTestHelpers::loadHTMLString(webViewImpl()->mainFrame(),
+                                   "<!DOCTYPE html>"
+                                   "<style>"
+                                   "  body {"
+                                   "    margin: 0px;"
+                                   "  }"
+                                   "  div { height:110vh; width: 110vw; }"
+                                   "</style>"
+                                   "<div></div>",
+                                   baseURL);
+}
+
 }  // namespace
diff --git a/third_party/WebKit/Source/web/tests/WebFrameSerializerTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameSerializerTest.cpp
index d196b2b..3379845a 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameSerializerTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameSerializerTest.cpp
@@ -198,10 +198,12 @@
 
   ~WebFrameSerializerSanitizationTest() override {}
 
-  String generateMHTMLParts(const String& url, const String& fileName) {
+  String generateMHTMLParts(const String& url,
+                            const String& fileName,
+                            const String& mimeType = "text/html") {
     KURL parsedURL(ParsedURLString, url);
     URLTestHelpers::registerMockedURLLoad(parsedURL, fileName,
-                                          "frameserialization/", "text/html");
+                                          "frameserialization/", mimeType);
     FrameTestHelpers::loadFrame(mainFrameImpl(), url.utf8().data());
     WebThreadSafeData result = WebFrameSerializer::generateMHTMLParts(
         WebString("boundary"), mainFrameImpl(), &m_mhtmlDelegate);
@@ -274,4 +276,12 @@
   EXPECT_NE(WTF::kNotFound, mhtml.find("<div"));
 }
 
+// Regression test for crbug.com/678893, where in some cases serializing an
+// image document could cause code to pick an element from an empty container.
+TEST_F(WebFrameSerializerSanitizationTest, FromBrokenImageDocument) {
+  String mhtml = generateMHTMLParts("http://www.test.com", "broken-image.png",
+                                    "image/png");
+  EXPECT_TRUE(mhtml.isEmpty());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/data/frameserialization/broken-image.png b/third_party/WebKit/Source/web/tests/data/frameserialization/broken-image.png
new file mode 100644
index 0000000..96696a17
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/data/frameserialization/broken-image.png
@@ -0,0 +1 @@
+broken image / this is not a png file
diff --git a/third_party/WebKit/Source/web/tests/data/rgm_float_under_inline.html b/third_party/WebKit/Source/web/tests/data/rgm_float_under_inline.html
new file mode 100644
index 0000000..519e7e2
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/data/rgm_float_under_inline.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<div id="container" style="position: absolute; top: 50px; left: 60px">
+  <span id="span" style="position: relative; top: 100px; left: 200px">
+    <div id="float" style="float: left">
+      <div id="layer-under-float" style="position: relative"></div>
+    </div>
+  </span>
+</div>
diff --git a/third_party/closure_compiler/compiled_resources2.gyp b/third_party/closure_compiler/compiled_resources2.gyp
index 7f30d1fe..12978d6 100644
--- a/third_party/closure_compiler/compiled_resources2.gyp
+++ b/third_party/closure_compiler/compiled_resources2.gyp
@@ -30,6 +30,7 @@
         '<(DEPTH)/chrome/browser/resources/offline_pages/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/settings/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/uber/compiled_resources2.gyp:*',
+        '<(DEPTH)/chrome/browser/resources/vr_shell/compiled_resources2.gyp:*',
         '<(DEPTH)/ui/file_manager/compiled_resources2.gyp:*',
         '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:*',
         '<(DEPTH)/ui/webui/resources/js/chromeos/compiled_resources2.gyp:*',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b6b4d92..5741715 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -44896,6 +44896,14 @@
   </summary>
 </histogram>
 
+<histogram name="PhysicalWeb.InitialState.IosChrome"
+    enum="PhysicalWebInitialStateIosChrome">
+  <owner>cco3@chromium.org</owner>
+  <owner>mattreynolds@chromium.org</owner>
+  <owner>mmocny@chromium.org</owner>
+  <summary>Initial state of the Physical Web in Chrome for iOS.</summary>
+</histogram>
+
 <histogram name="PhysicalWeb.ReferralDelay.OptInNotification" units="ms">
   <owner>cco3@chromium.org</owner>
   <owner>mattreynolds@chromium.org</owner>
@@ -72106,6 +72114,19 @@
   </summary>
 </histogram>
 
+<histogram
+    name="WebController.StartProvisionalNavigationExitedWithEmptyNavigationManager"
+    enum="BooleanHit">
+  <owner>eugenebut@chromium.org</owner>
+  <summary>
+    Chrome for iOS crashes in |didCommitNavigation:| if Navigation Manager is
+    empty. It's unclear how the app can get into this state and one assumption
+    is that didStartProvisionalNavigation: does not add a pending navigation.
+    This metric is logged when didStartProvisionalNavigation: fails to add a
+    pending item.
+  </summary>
+</histogram>
+
 <histogram name="WebCore.Animation.CSSProperties" enum="MappedCSSProperties">
   <owner>ajuma@chromium.org</owner>
   <summary>
@@ -93816,6 +93837,7 @@
   <int value="-1821058653" label="enable-delay-agnostic-aec"/>
   <int value="-1812579951" label="ContentSuggestionsCategoryRanker:enabled"/>
   <int value="-1811394154" label="disable-webrtc-hw-vp8-encoding"/>
+  <int value="-1810294310" label="AndroidPaymentApps:enabled"/>
   <int value="-1804485171" label="disable-fullscreen-tab-detaching"/>
   <int value="-1802502753" label="enable-manual-password-generation:enabled"/>
   <int value="-1798337879" label="enable-md-downloads"/>
@@ -94320,6 +94342,7 @@
   <int value="550378029" label="reset-app-list-install-state"/>
   <int value="550387510" label="NTPAssetDownloadSuggestions:disabled"/>
   <int value="567368307" label="enable-experimental-canvas-features"/>
+  <int value="575394365" label="AndroidPaymentApps:disabled"/>
   <int value="581118445" label="enable-eol-notification"/>
   <int value="581355159" label="ContentSuggestionsCategoryRanker:disabled"/>
   <int value="584541349" label="ContextualSearchSingleActions:disabled"/>
@@ -99473,6 +99496,33 @@
   <int value="4" label="REFERER_DIAGNOSTICS"/>
 </enum>
 
+<enum name="PhysicalWebInitialStateIosChrome" type="int">
+  <int value="0" label="OPTOUT_BTOFF_LOCOFF_UNAUTH"/>
+  <int value="1" label="OPTOUT_BTOFF_LOCOFF_AUTH"/>
+  <int value="2" label="OPTOUT_BTOFF_LOCON_UNAUTH"/>
+  <int value="3" label="OPTOUT_BTOFF_LOCON_AUTH"/>
+  <int value="4" label="OPTOUT_BTON_LOCOFF_UNAUTH"/>
+  <int value="5" label="OPTOUT_BTON_LOCOFF_AUTH"/>
+  <int value="6" label="OPTOUT_BTON_LOCON_UNAUTH"/>
+  <int value="7" label="OPTOUT_BTON_LOCON_AUTH"/>
+  <int value="8" label="OPTIN_BTOFF_LOCOFF_UNAUTH"/>
+  <int value="9" label="OPTIN_BTOFF_LOCOFF_AUTH"/>
+  <int value="10" label="OPTIN_BTOFF_LOCON_UNAUTH"/>
+  <int value="11" label="OPTIN_BTOFF_LOCON_AUTH"/>
+  <int value="12" label="OPTIN_BTON_LOCOFF_UNAUTH"/>
+  <int value="13" label="OPTIN_BTON_LOCOFF_AUTH"/>
+  <int value="14" label="OPTIN_BTON_LOCON_UNAUTH"/>
+  <int value="15" label="OPTIN_BTON_LOCON_AUTH"/>
+  <int value="16" label="ONBOARDING_BTOFF_LOCOFF_UNAUTH"/>
+  <int value="17" label="ONBOARDING_BTOFF_LOCOFF_AUTH"/>
+  <int value="18" label="ONBOARDING_BTOFF_LOCON_UNAUTH"/>
+  <int value="19" label="ONBOARDING_BTOFF_LOCON_AUTH"/>
+  <int value="20" label="ONBOARDING_BTON_LOCOFF_UNAUTH"/>
+  <int value="21" label="ONBOARDING_BTON_LOCOFF_AUTH"/>
+  <int value="22" label="ONBOARDING_BTON_LOCON_UNAUTH"/>
+  <int value="23" label="ONBOARDING_BTON_LOCON_AUTH"/>
+</enum>
+
 <enum name="PhysicalWebPreferenceStatus" type="int">
   <int value="0" label="OFF"/>
   <int value="1" label="ON"/>
@@ -115943,6 +115993,16 @@
   <affected-histogram name="Tabs.SwitchFromUserLatency"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="TaskSchedulerMayBlock" separator=".">
+  <suffix name="MayBlock"
+      label="Applies to tasks posted with MayBlock() or
+             WithBaseSyncPrimitives()."/>
+  <affected-histogram name="TaskScheduler.TaskLatency.BackgroundTaskPriority"/>
+  <affected-histogram
+      name="TaskScheduler.TaskLatency.UserBlockingTaskPriority"/>
+  <affected-histogram name="TaskScheduler.TaskLatency.UserVisibleTaskPriority"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="TaskSchedulerTaskPriority" separator=".">
   <suffix name="BackgroundTaskPriority"
       label="Applies to tasks posted with a BACKGROUND priority."/>
@@ -115950,10 +116010,27 @@
       label="Applies to tasks posted with a USER_VISIBLE priority."/>
   <suffix name="UserBlockingTaskPriority"
       label="Applies to tasks posted with a USER_BLOCKING priority."/>
-  <affected-histogram name="TaskScheduler.TaskLatency.BackgroundFileIOPool"/>
-  <affected-histogram name="TaskScheduler.TaskLatency.BackgroundPool"/>
-  <affected-histogram name="TaskScheduler.TaskLatency.ForegroundFileIOPool"/>
-  <affected-histogram name="TaskScheduler.TaskLatency.ForegroundPool"/>
+  <affected-histogram name="TaskScheduler.TaskLatency"/>
+  <affected-histogram name="TaskScheduler.TaskLatency.BackgroundFileIOPool">
+    <obsolete>
+      Deprecated 12/2016. Pool name removed from task latency histogram name.
+    </obsolete>
+  </affected-histogram>
+  <affected-histogram name="TaskScheduler.TaskLatency.BackgroundPool">
+    <obsolete>
+      Deprecated 12/2016. Pool name removed from task latency histogram name.
+    </obsolete>
+  </affected-histogram>
+  <affected-histogram name="TaskScheduler.TaskLatency.ForegroundFileIOPool">
+    <obsolete>
+      Deprecated 12/2016. Pool name removed from task latency histogram name.
+    </obsolete>
+  </affected-histogram>
+  <affected-histogram name="TaskScheduler.TaskLatency.ForegroundPool">
+    <obsolete>
+      Deprecated 12/2016. Pool name removed from task latency histogram name.
+    </obsolete>
+  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="TaskSchedulerWorkerPool" separator=".">
@@ -115966,7 +116043,11 @@
   <affected-histogram name="TaskScheduler.DetachDuration"/>
   <affected-histogram name="TaskScheduler.NumTasksBeforeDetach"/>
   <affected-histogram name="TaskScheduler.NumTasksBetweenWaits"/>
-  <affected-histogram name="TaskScheduler.TaskLatency"/>
+  <affected-histogram name="TaskScheduler.TaskLatency">
+    <obsolete>
+      Deprecated 12/2016. Pool name removed from task latency histogram name.
+    </obsolete>
+  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="ThreadWatcher" separator=".">
diff --git a/ui/events/blink/blink_features.cc b/ui/events/blink/blink_features.cc
index 9c06c865..427ccf2 100644
--- a/ui/events/blink/blink_features.cc
+++ b/ui/events/blink/blink_features.cc
@@ -11,4 +11,14 @@
 const base::Feature kVsyncAlignedInputEvents{"VsyncAlignedInput",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSendMouseLeaveEvents {
+  "SendMouseLeaveEvents",
+// TODO(chaopeng) this fix only for chromeos now, should convert ET_MOUSE_EXITED
+// to MouseLeave when crbug.com/450631 fixed.
+#if defined(OS_CHROMEOS)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
 }
diff --git a/ui/events/blink/blink_features.h b/ui/events/blink/blink_features.h
index 5f1d848..3d1473d 100644
--- a/ui/events/blink/blink_features.h
+++ b/ui/events/blink/blink_features.h
@@ -11,6 +11,11 @@
 
 extern const base::Feature kVsyncAlignedInputEvents;
 
+// This feature allows native ET_MOUSE_EXIT events to be passed
+// through to blink as mouse leave events. Traditionally these events were
+// converted to mouse move events due to a number of incosistencies on
+// the native platforms. Enabled by default on ChromeOS. crbug.com/450631
+extern const base::Feature kSendMouseLeaveEvents;
 }
 
 #endif  // UI_EVENTS_BLINK_BLINK_FEATURES_H_
diff --git a/ui/events/blink/web_input_event.cc b/ui/events/blink/web_input_event.cc
index 10b13b5..3476f00 100644
--- a/ui/events/blink/web_input_event.cc
+++ b/ui/events/blink/web_input_event.cc
@@ -6,6 +6,7 @@
 
 #include "ui/events/base_event_utils.h"
 #include "ui/events/blink/blink_event_util.h"
+#include "ui/events/blink/blink_features.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
@@ -385,13 +386,13 @@
       type = blink::WebInputEvent::MouseUp;
       click_count = event.GetClickCount();
       break;
-    case ET_MOUSE_EXITED:
-// TODO(chaopeng) this fix only for chromeos now, should convert ET_MOUSE_EXITED
-// to MouseLeave when crbug.com/450631 fixed.
-#if defined(OS_CHROMEOS)
-      type = blink::WebInputEvent::MouseLeave;
+    case ET_MOUSE_EXITED: {
+      static bool s_send_leave =
+          base::FeatureList::IsEnabled(features::kSendMouseLeaveEvents);
+      type = s_send_leave ? blink::WebInputEvent::MouseLeave
+                          : blink::WebInputEvent::MouseMove;
       break;
-#endif
+    }
     case ET_MOUSE_ENTERED:
     case ET_MOUSE_MOVED:
     case ET_MOUSE_DRAGGED:
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index 5f2bff97..6acf55a 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -12,6 +12,7 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/geometry/insets_f.h"
@@ -26,12 +27,25 @@
 
 namespace gfx {
 
+namespace {
+
+sk_sp<SkSurface> CreateSurface(const Size& size, bool is_opaque) {
+  // SkSurface cannot be zero-sized, but clients of Canvas sometimes request
+  // that (and then later resize).
+  int width = std::max(size.width(), 1);
+  int height = std::max(size.height(), 1);
+  SkAlphaType alpha = is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+  SkImageInfo info = SkImageInfo::MakeN32(width, height, alpha);
+  return SkSurface::MakeRaster(info);
+}
+
+}  // namespace
+
 Canvas::Canvas(const Size& size, float image_scale, bool is_opaque)
     : image_scale_(image_scale) {
   Size pixel_size = ScaleToCeiledSize(size, image_scale);
-  canvas_owner_ = skia::CreatePlatformCanvas(pixel_size.width(),
-                                             pixel_size.height(), is_opaque);
-  canvas_ = canvas_owner_.get();
+  surface_ = CreateSurface(pixel_size, is_opaque);
+  canvas_ = surface_->getCanvas();
 
 #if !defined(USE_CAIRO)
   // skia::PlatformCanvas instances are initialized to 0 by Cairo, but
@@ -46,8 +60,8 @@
 
 Canvas::Canvas()
     : image_scale_(1.f),
-      canvas_owner_(skia::CreatePlatformCanvas(0, 0, false)),
-      canvas_(canvas_owner_.get()) {}
+      surface_(CreateSurface({0, 0}, false)),
+      canvas_(surface_->getCanvas()) {}
 
 Canvas::Canvas(SkCanvas* canvas, float image_scale)
     : image_scale_(image_scale), canvas_(canvas) {
@@ -62,9 +76,8 @@
                                    bool is_opaque) {
   image_scale_ = image_scale;
   Size pixel_size = ScaleToFlooredSize(size, image_scale);
-  canvas_owner_ = skia::CreatePlatformCanvas(pixel_size.width(),
-                                             pixel_size.height(), is_opaque);
-  canvas_ = canvas_owner_.get();
+  surface_ = CreateSurface(pixel_size, is_opaque);
+  canvas_ = surface_->getCanvas();
 
   SkScalar scale_scalar = SkFloatToScalar(image_scale);
   canvas_->scale(scale_scalar, scale_scalar);
diff --git a/ui/gfx/canvas.h b/ui/gfx/canvas.h
index adf1286..c81c0f25 100644
--- a/ui/gfx/canvas.h
+++ b/ui/gfx/canvas.h
@@ -13,11 +13,11 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "skia/ext/platform_canvas.h"
-#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/text_constants.h"
-
 namespace gfx {
 
 class Rect;
@@ -495,10 +495,10 @@
   float image_scale_;
 
   // canvas_ is our active canvas object. Sometimes we are also the owner,
-  // in which case canvas_owner_ will be set. Other times we are just
+  // in which case surface_ will be set. Other times we are just
   // borrowing someone else's canvas, in which case canvas_ will point there
-  // but canvas_owner_ will be null.
-  std::unique_ptr<SkCanvas> canvas_owner_;
+  // but surface_ will be null.
+  sk_sp<SkSurface> surface_;
   SkCanvas* canvas_;
 
   DISALLOW_COPY_AND_ASSIGN(Canvas);
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index af86e79..24eaae3 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -17,7 +17,6 @@
 #include "base/win/scoped_select_object.h"
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
-#include "skia/ext/bitmap_platform_device.h"
 #include "skia/ext/platform_canvas.h"
 #include "skia/ext/skia_utils_win.h"
 #include "third_party/skia/include/core/SkCanvas.h"