diff --git a/AUTHORS b/AUTHORS
index 05de8dc9..f773b6d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -58,6 +58,7 @@
 Andra Paraschiv <andra.paraschiv@intel.com>
 Andrei Parvu <andrei.prv@gmail.com>
 Andrei Parvu <parvu@adobe.com>
+Andrew Boyarshin <andrew.boyarshin@gmail.com>
 Andrew Brampton <me@bramp.net>
 Andrew Hung <andrhung@amazon.com>
 Andrew MacPherson <andrew.macpherson@soundtrap.com>
diff --git a/DEPS b/DEPS
index 3207e8b3..db2158c 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '6ecc911070add386dc9ffeb8b131d21035672fb6',
+  'skia_revision': '1b0126b01511fe23d6e8f7b5be402d4d81921245',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '326a8fdf23e2ba9faf35a5e200ffb50f847a06d9',
+  'v8_revision': '175a595935ba451d675245238d3c2f9ba7075cbc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '77e3fc5cebfb19aed09f920536200e017cff9a0b',
+  'pdfium_revision': '2bf942d8c21b653efdfdcae681769cffbfaa0663',
   # 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': 'e9dc4c57fb003d9b289f6bf8bac98f820e1fe8b4',
+  'catapult_revision': '57e600c76c9f2f6ab3a5b82d3cc21ca738a62a7e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -366,7 +366,7 @@
       Var('chromium_git') + '/external/github.com/swisspol/GCDWebServer.git' + '@' + '43555c66627f6ed44817855a0f6d465f559d30e0',
 
     'src/ios/third_party/material_components_ios/src':
-      Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '6d5844642b004594ba1607916032616f85c7c045',
+      Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'b452b1e7fb85cc9aeef2df2b6a44cbe0e8594560',
 
     'src/ios/third_party/material_font_disk_loader_ios/src':
       Var('chromium_git') + '/external/github.com/material-foundation/material-font-disk-loader-ios.git' + '@' + '8e30188777b016182658fbaa0a4a020a48183224',
@@ -1229,7 +1229,7 @@
       'action': [
         'python',
         'src/build/fuchsia/update_sdk.py',
-        'f79f55be4e69ebd90ea84f79d7322525853256c3',
+        '9c7191fae2233b5688d34f9d66717ee5c16ee2c7',
       ],
     },
   ],
diff --git a/android_webview/browser/net/aw_cookie_store_wrapper_unittest.cc b/android_webview/browser/net/aw_cookie_store_wrapper_unittest.cc
index 171bfb7..d856d24 100644
--- a/android_webview/browser/net/aw_cookie_store_wrapper_unittest.cc
+++ b/android_webview/browser/net/aw_cookie_store_wrapper_unittest.cc
@@ -34,6 +34,7 @@
   static const bool preserves_trailing_dots = true;
   static const bool filters_schemes = true;
   static const bool has_path_prefix_bug = false;
+  static const bool forbids_setting_empty_name = false;
   static const int creation_time_granularity_in_ms = 0;
 };
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
index 56d09a9..50edaf6 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
@@ -100,6 +100,8 @@
 
         mCommittedNavigation = true;
 
+        if (!isInMainFrame) return;
+
         AwContentsClient client = mAwContentsClient.get();
         if (hasCommitted && client != null) {
             boolean isReload = pageTransition != null
@@ -107,8 +109,6 @@
             client.getCallbackHelper().postDoUpdateVisitedHistory(url, isReload);
         }
 
-        if (!isInMainFrame) return;
-
         // Only invoke the onPageCommitVisible callback when navigating to a different document,
         // but not when navigating to a different fragment within the same document.
         if (!isSameDocument) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientVisitedHistoryTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientVisitedHistoryTest.java
index 04b8906..0434d53 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientVisitedHistoryTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientVisitedHistoryTest.java
@@ -67,8 +67,9 @@
         AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
         AwContents awContents = testView.getAwContents();
 
+        // Load a page with an iframe to make sure the callback only happens for the main frame URL.
         final String path = "/testUpdateVisitedHistoryCallback.html";
-        final String html = "testUpdateVisitedHistoryCallback";
+        final String html = "<iframe src=\"about:blank\"></iframe>";
 
         TestWebServer webServer = TestWebServer.start();
         try {
@@ -80,13 +81,14 @@
             doUpdateVisitedHistoryHelper.waitForCallback(callCount);
             assertEquals(pageUrl, doUpdateVisitedHistoryHelper.getUrl());
             assertEquals(false, doUpdateVisitedHistoryHelper.getIsReload());
+            assertEquals(callCount + 1, doUpdateVisitedHistoryHelper.getCallCount());
 
             // Reload
-            callCount = doUpdateVisitedHistoryHelper.getCallCount();
             loadUrlAsync(awContents, pageUrl);
-            doUpdateVisitedHistoryHelper.waitForCallback(callCount);
+            doUpdateVisitedHistoryHelper.waitForCallback(callCount + 1);
             assertEquals(pageUrl, doUpdateVisitedHistoryHelper.getUrl());
             assertEquals(true, doUpdateVisitedHistoryHelper.getIsReload());
+            assertEquals(callCount + 2, doUpdateVisitedHistoryHelper.getCallCount());
         } finally {
             webServer.shutdown();
         }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
index 0db88d6..0718fa1a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
@@ -160,6 +160,20 @@
         assertEquals(false, doUpdateVisitedHistoryHelper.getIsReload());
 
         callCount = doUpdateVisitedHistoryHelper.getCallCount();
+        mWebContentsObserver.didFinishNavigation(nullUrl, isInMainFrame, isErrorPage, hasCommitted,
+                !isSameDocument, fragmentNavigation, PageTransition.TYPED, errorCode,
+                errorDescription, httpStatusCode);
+        mWebContentsObserver.didFinishNavigation(EXAMPLE_URL, !isInMainFrame, isErrorPage,
+                hasCommitted, !isSameDocument, fragmentNavigation, PageTransition.TYPED, errorCode,
+                errorDescription, httpStatusCode);
+        doUpdateVisitedHistoryHelper.waitForCallback(callCount);
+        assertEquals("doUpdateVisitedHistory should only be called for the main frame.",
+                callCount + 1, doUpdateVisitedHistoryHelper.getCallCount());
+        assertEquals("doUpdateVisitedHistory should only be called for the main frame.", nullUrl,
+                doUpdateVisitedHistoryHelper.getUrl());
+        assertEquals(false, doUpdateVisitedHistoryHelper.getIsReload());
+
+        callCount = doUpdateVisitedHistoryHelper.getCallCount();
         mWebContentsObserver.didFinishNavigation(EXAMPLE_URL, isInMainFrame, isErrorPage,
                 hasCommitted, isSameDocument, !fragmentNavigation, PageTransition.RELOAD, errorCode,
                 errorDescription, httpStatusCode);
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
index f29be24..dcee1c8 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
@@ -5166,8 +5166,7 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix : DOMMatrixReadOnly
-    attribute @@toStringTag
+interface WebKitCSSMatrix
     getter a
     getter b
     getter c
@@ -5191,18 +5190,16 @@
     getter m43
     getter m44
     method constructor
-    method invertSelf
-    method multiplySelf
-    method preMultiplySelf
-    method rotateAxisAngleSelf
-    method rotateFromVectorSelf
-    method rotateSelf
-    method scale3dSelf
-    method scaleSelf
+    method inverse
+    method multiply
+    method rotate
+    method rotateAxisAngle
+    method scale
     method setMatrixValue
-    method skewXSelf
-    method skewYSelf
-    method translateSelf
+    method skewX
+    method skewY
+    method toString
+    method translate
     setter a
     setter b
     setter c
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 3db6f13..30fc6c4 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -30,9 +30,6 @@
 // Enable keyboard shortcuts used by developers only.
 const char kAshDeveloperShortcuts[] = "ash-dev-shortcuts";
 
-// Disable the Night Light feature.
-const char kAshDisableNightLight[] = "ash-disable-night-light";
-
 // Disable the Touch Exploration Mode. Touch Exploration Mode will no longer be
 // turned on automatically when spoken feedback is enabled when this flag is
 // set.
@@ -43,6 +40,9 @@
 const char kAshEnableMagnifierKeyScroller[] =
     "ash-enable-magnifier-key-scroller";
 
+// Enables the NightLight feature.
+const char kAshEnableNightLight[] = "ash-enable-night-light";
+
 // Enables the palette on every display, instead of only the internal one.
 const char kAshEnablePaletteOnAllDisplays[] =
     "ash-enable-palette-on-all-displays";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index e3e7337..5aecb7b5 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -20,10 +20,10 @@
 ASH_EXPORT extern const char kAshCopyHostBackgroundAtBoot[];
 ASH_EXPORT extern const char kAshDebugShortcuts[];
 ASH_EXPORT extern const char kAshDeveloperShortcuts[];
-ASH_EXPORT extern const char kAshDisableNightLight[];
 ASH_EXPORT extern const char kAshDisableSmoothScreenRotation[];
 ASH_EXPORT extern const char kAshDisableTouchExplorationMode[];
 ASH_EXPORT extern const char kAshEnableMagnifierKeyScroller[];
+ASH_EXPORT extern const char kAshEnableNightLight[];
 ASH_EXPORT extern const char kAshEnablePaletteOnAllDisplays[];
 ASH_EXPORT extern const char kAshEnableScaleSettingsTray[];
 ASH_EXPORT extern const char kAshEnableTouchView[];
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index 10aec6ab..b11a3f8 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -27,7 +27,6 @@
 #include "base/bind.h"
 #include "base/single_thread_task_runner.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/app_list/presenter/app_list.h"
 
 namespace ash {
diff --git a/ash/mojo_interface_factory.h b/ash/mojo_interface_factory.h
index ea3b95e3..49edce2 100644
--- a/ash/mojo_interface_factory.h
+++ b/ash/mojo_interface_factory.h
@@ -8,15 +8,12 @@
 #include "ash/ash_export.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace base {
 class SingleThreadTaskRunner;
 }
 
-namespace service_manager {
-class BinderRegistry;
-}
-
 namespace ash {
 
 namespace mojo_interface_factory {
diff --git a/ash/system/night_light/night_light_controller.cc b/ash/system/night_light/night_light_controller.cc
index e84b5ce..e5f68eaa 100644
--- a/ash/system/night_light/night_light_controller.cc
+++ b/ash/system/night_light/night_light_controller.cc
@@ -110,8 +110,8 @@
 
 // static
 bool NightLightController::IsFeatureEnabled() {
-  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      ash::switches::kAshDisableNightLight);
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      ash::switches::kAshEnableNightLight);
 }
 
 // static
diff --git a/ash/system/night_light/night_light_controller_unittest.cc b/ash/system/night_light/night_light_controller_unittest.cc
index c3427a1f..3745641 100644
--- a/ash/system/night_light/night_light_controller_unittest.cc
+++ b/ash/system/night_light/night_light_controller_unittest.cc
@@ -7,6 +7,7 @@
 #include <cmath>
 #include <limits>
 
+#include "ash/ash_switches.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/session_types.h"
@@ -17,6 +18,7 @@
 #include "ash/test/test_shell_delegate.h"
 #include "base/bind.h"
 #include "base/callback_forward.h"
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "components/prefs/testing_pref_service.h"
 #include "ui/compositor/layer.h"
@@ -125,6 +127,10 @@
 
   // ash::test::AshTestBase:
   void SetUp() override {
+    // Explicitly enable the NightLight feature for the tests.
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        ash::switches::kAshEnableNightLight);
+
     test::AshTestBase::SetUp();
     CreateTestUserSessions();
     Shell::RegisterPrefs(user1_pref_service_.registry());
diff --git a/ash/system/night_light/tray_night_light_unittest.cc b/ash/system/night_light/tray_night_light_unittest.cc
index 3a8ca56..1491771c 100644
--- a/ash/system/night_light/tray_night_light_unittest.cc
+++ b/ash/system/night_light/tray_night_light_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/night_light/tray_night_light.h"
 
+#include "ash/ash_switches.h"
 #include "ash/public/cpp/config.h"
 #include "ash/shell.h"
 #include "ash/system/night_light/night_light_controller.h"
@@ -11,6 +12,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/test_shell_delegate.h"
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "components/prefs/testing_pref_service.h"
 
@@ -27,6 +29,10 @@
 
   // ash::test::AshTestBase:
   void SetUp() override {
+    // Explicitly enable the NightLight feature for the tests.
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        ash::switches::kAshEnableNightLight);
+
     test::AshTestBase::SetUp();
     GetSessionControllerClient()->Reset();
     GetSessionControllerClient()->AddUserSession(kFakeUserEmail);
diff --git a/ash/system/tiles/tray_tiles_unittest.cc b/ash/system/tiles/tray_tiles_unittest.cc
index 50a4920..6599f48 100644
--- a/ash/system/tiles/tray_tiles_unittest.cc
+++ b/ash/system/tiles/tray_tiles_unittest.cc
@@ -4,12 +4,14 @@
 
 #include "ash/system/tiles/tray_tiles.h"
 
+#include "ash/ash_switches.h"
 #include "ash/system/night_light/night_light_controller.h"
 #include "ash/system/night_light/night_light_toggle_button.h"
 #include "ash/system/tiles/tiles_default_view.h"
 #include "ash/system/tray/system_menu_button.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_session_controller_client.h"
+#include "base/command_line.h"
 #include "components/user_manager/user_type.h"
 #include "ui/views/view.h"
 
@@ -24,6 +26,10 @@
   ~TrayTilesTest() override {}
 
   void SetUp() override {
+    // Explicitly enable the NightLight feature for the tests.
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        ash::switches::kAshEnableNightLight);
+
     test::NoSessionAshTestBase::SetUp();
     tray_tiles_.reset(new TrayTiles(GetPrimarySystemTray()));
   }
diff --git a/base/cpu.cc b/base/cpu.cc
index 848208f7..136501c 100644
--- a/base/cpu.cc
+++ b/base/cpu.cc
@@ -19,7 +19,7 @@
 #endif
 
 #if defined(ARCH_CPU_X86_FAMILY)
-#if defined(_MSC_VER)
+#if defined(COMPILER_MSVC)
 #include <intrin.h>
 #include <immintrin.h>  // For _xgetbv()
 #endif
@@ -54,7 +54,7 @@
 namespace {
 
 #if defined(ARCH_CPU_X86_FAMILY)
-#ifndef _MSC_VER
+#if !defined(COMPILER_MSVC)
 
 #if defined(__pic__) && defined(__i386__)
 
@@ -89,7 +89,7 @@
   return (static_cast<uint64_t>(edx) << 32) | eax;
 }
 
-#endif  // !_MSC_VER
+#endif  // !defined(COMPILER_MSVC)
 #endif  // ARCH_CPU_X86_FAMILY
 
 #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
diff --git a/base/cpu.h b/base/cpu.h
index 0e24df6..2c6caeaf 100644
--- a/base/cpu.h
+++ b/base/cpu.h
@@ -12,9 +12,8 @@
 namespace base {
 
 // Query information about the processor.
-class BASE_EXPORT CPU {
+class BASE_EXPORT CPU final {
  public:
-  // Constructor
   CPU();
 
   enum IntelMicroArchitecture {
diff --git a/base/process/memory_unittest.cc b/base/process/memory_unittest.cc
index ca6a112..03704c4 100644
--- a/base/process/memory_unittest.cc
+++ b/base/process/memory_unittest.cc
@@ -38,7 +38,7 @@
 
 #if defined(OS_WIN)
 
-#if defined(_MSC_VER)
+#if defined(COMPILER_MSVC)
 // ssize_t needed for OutOfMemoryTest.
 #if defined(_WIN64)
 typedef __int64 ssize_t;
diff --git a/base/strings/safe_sprintf.h b/base/strings/safe_sprintf.h
index 65524a50..d6886db 100644
--- a/base/strings/safe_sprintf.h
+++ b/base/strings/safe_sprintf.h
@@ -21,7 +21,7 @@
 namespace base {
 namespace strings {
 
-#if defined(_MSC_VER)
+#if defined(COMPILER_MSVC)
 // Define ssize_t inside of our namespace.
 #if defined(_WIN64)
 typedef __int64 ssize_t;
diff --git a/build/check_gn_headers.py b/build/check_gn_headers.py
index 1db587a7..f6ae8f5 100755
--- a/build/check_gn_headers.py
+++ b/build/check_gn_headers.py
@@ -148,9 +148,12 @@
 
 def IsBuildClean(out_dir):
   cmd = [os.path.join(DEPOT_TOOLS_DIR, 'ninja'), '-C', out_dir, '-n']
-  out = subprocess.check_output(cmd)
-  return 'no work to do.' in out
-
+  try:
+    out = subprocess.check_output(cmd)
+    return 'no work to do.' in out
+  except Exception as e:
+    print e
+    return False
 
 def ParseWhiteList(whitelist):
   out = set()
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 0e191073..bb48708b 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -23,7 +23,7 @@
 
 
 # Use MSVS2015 as the default toolchain.
-CURRENT_DEFAULT_TOOLCHAIN_VERSION = '2015'
+CURRENT_DEFAULT_TOOLCHAIN_VERSION = '2017'
 
 
 def SetEnvironmentAndGetRuntimeDllDirs():
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index bfb71501..c17e76d 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -87,7 +87,6 @@
       property_tree_sequence_number_(-1),
       should_flatten_transform_from_property_tree_(false),
       draws_content_(false),
-      use_local_transform_for_backface_visibility_(false),
       should_check_backface_visibility_(false),
       force_render_surface_for_testing_(false),
       subtree_property_changed_(false),
@@ -1029,13 +1028,6 @@
   SetNeedsPushProperties();
 }
 
-void Layer::SetUseLocalTransformForBackfaceVisibility(bool use_local) {
-  if (use_local_transform_for_backface_visibility_ == use_local)
-    return;
-  use_local_transform_for_backface_visibility_ = use_local;
-  SetNeedsPushProperties();
-}
-
 void Layer::SetShouldCheckBackfaceVisibility(
     bool should_check_backface_visibility) {
   if (should_check_backface_visibility_ == should_check_backface_visibility)
@@ -1144,6 +1136,7 @@
   // The ElementId should be set first because other setters depend on it such
   // as LayerImpl::SetScrollClipLayer.
   layer->SetElementId(inputs_.element_id);
+  layer->SetHasTransformNode(has_transform_node_);
   layer->SetBackgroundColor(inputs_.background_color);
   layer->SetSafeOpaqueBackgroundColor(safe_opaque_background_color_);
   layer->SetBounds(inputs_.bounds);
@@ -1176,8 +1169,6 @@
   layer->set_should_flatten_transform_from_property_tree(
       should_flatten_transform_from_property_tree_);
   layer->SetUseParentBackfaceVisibility(inputs_.use_parent_backface_visibility);
-  layer->SetUseLocalTransformForBackfaceVisibility(
-      use_local_transform_for_backface_visibility_);
   layer->SetShouldCheckBackfaceVisibility(should_check_backface_visibility_);
 
   layer->SetScrollClipLayer(inputs_.scroll_clip_layer_id);
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 1c21419..e77765e 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -281,11 +281,6 @@
     return inputs_.use_parent_backface_visibility;
   }
 
-  void SetUseLocalTransformForBackfaceVisibility(bool use_local);
-  bool use_local_transform_for_backface_visibility() const {
-    return use_local_transform_for_backface_visibility_;
-  }
-
   void SetShouldCheckBackfaceVisibility(bool should_check_backface_visibility);
   bool should_check_backface_visibility() const {
     return should_check_backface_visibility_;
@@ -629,7 +624,6 @@
   gfx::Vector2dF offset_to_transform_parent_;
   bool should_flatten_transform_from_property_tree_ : 1;
   bool draws_content_ : 1;
-  bool use_local_transform_for_backface_visibility_ : 1;
   bool should_check_backface_visibility_ : 1;
   bool force_render_surface_for_testing_ : 1;
   bool subtree_property_changed_ : 1;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index d46326c..ede6881a 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -62,7 +62,6 @@
       masks_to_bounds_(false),
       contents_opaque_(false),
       use_parent_backface_visibility_(false),
-      use_local_transform_for_backface_visibility_(false),
       should_check_backface_visibility_(false),
       draws_content_(false),
       contributes_to_drawn_render_surface_(false),
@@ -80,7 +79,8 @@
       needs_push_properties_(false),
       scrollbars_hidden_(false),
       needs_show_scrollbars_(false),
-      raster_even_if_not_drawn_(false) {
+      raster_even_if_not_drawn_(false),
+      has_transform_node_(false) {
   DCHECK_GT(layer_id_, 0);
 
   DCHECK(layer_tree_impl_);
@@ -312,6 +312,7 @@
   // as LayerImpl::SetScrollClipLayer.
   layer->SetElementId(element_id_);
 
+  layer->has_transform_node_ = has_transform_node_;
   layer->offset_to_transform_parent_ = offset_to_transform_parent_;
   layer->main_thread_scrolling_reasons_ = main_thread_scrolling_reasons_;
   layer->should_flatten_transform_from_property_tree_ =
@@ -320,8 +321,6 @@
   layer->contents_opaque_ = contents_opaque_;
   layer->may_contain_video_ = may_contain_video_;
   layer->use_parent_backface_visibility_ = use_parent_backface_visibility_;
-  layer->use_local_transform_for_backface_visibility_ =
-      use_local_transform_for_backface_visibility_;
   layer->should_check_backface_visibility_ = should_check_backface_visibility_;
   layer->draws_content_ = draws_content_;
   layer->non_fast_scrollable_region_ = non_fast_scrollable_region_;
@@ -674,11 +673,6 @@
   return GetMutatorHost()->HasFilterAnimationThatInflatesBounds(element_id());
 }
 
-bool LayerImpl::HasTransformAnimationThatInflatesBounds() const {
-  return GetMutatorHost()->HasTransformAnimationThatInflatesBounds(
-      element_id());
-}
-
 bool LayerImpl::HasAnimationThatInflatesBounds() const {
   return GetMutatorHost()->HasAnimationThatInflatesBounds(element_id());
 }
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 245c777..9c2e758 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -87,6 +87,9 @@
   gfx::ScrollOffset ScrollOffsetForAnimation() const;
   bool IsActive() const;
 
+  void SetHasTransformNode(bool val) { has_transform_node_ = val; }
+  bool has_transform_node() { return has_transform_node_; }
+
   void set_property_tree_sequence_number(int sequence_number) {}
 
   void SetTransformTreeIndex(int index);
@@ -202,13 +205,6 @@
     return use_parent_backface_visibility_;
   }
 
-  void SetUseLocalTransformForBackfaceVisibility(bool use_local) {
-    use_local_transform_for_backface_visibility_ = use_local;
-  }
-  bool use_local_transform_for_backface_visibility() const {
-    return use_local_transform_for_backface_visibility_;
-  }
-
   void SetShouldCheckBackfaceVisibility(bool should_check_backface_visibility) {
     should_check_backface_visibility_ = should_check_backface_visibility;
   }
@@ -331,7 +327,6 @@
   bool HasPotentiallyRunningTransformAnimation() const;
 
   bool HasFilterAnimationThatInflatesBounds() const;
-  bool HasTransformAnimationThatInflatesBounds() const;
   bool HasAnimationThatInflatesBounds() const;
 
   bool FilterAnimationBoundsForBox(const gfx::BoxF& box,
@@ -496,7 +491,6 @@
   bool masks_to_bounds_ : 1;
   bool contents_opaque_ : 1;
   bool use_parent_backface_visibility_ : 1;
-  bool use_local_transform_for_backface_visibility_ : 1;
   bool should_check_backface_visibility_ : 1;
   bool draws_content_ : 1;
   bool contributes_to_drawn_render_surface_ : 1;
@@ -565,6 +559,8 @@
   // are still rasterized.
   bool raster_even_if_not_drawn_ : 1;
 
+  bool has_transform_node_ : 1;
+
   DISALLOW_COPY_AND_ASSIGN(LayerImpl);
 };
 
diff --git a/cc/layers/layer_utils.cc b/cc/layers/layer_utils.cc
index 6dbaecd..715fd06f 100644
--- a/cc/layers/layer_utils.cc
+++ b/cc/layers/layer_utils.cc
@@ -14,10 +14,6 @@
 
 namespace {
 
-bool HasTransformAnimationThatInflatesBounds(const LayerImpl& layer) {
-  return layer.HasTransformAnimationThatInflatesBounds();
-}
-
 inline bool HasAncestorTransformAnimation(const TransformNode* transform_node) {
   return transform_node->to_screen_is_potentially_animated;
 }
@@ -86,19 +82,19 @@
 
   for (; transform_tree.parent(transform_node);
        transform_node = transform_tree.parent(transform_node)) {
-    LayerImpl* layer =
-        layer_in.layer_tree_impl()->LayerById(transform_node->owning_layer_id);
-
     // Filter animation bounds are unimplemented, see function
     // HasAncestorFilterAnimation() for reference.
 
-    if (HasTransformAnimationThatInflatesBounds(*layer)) {
+    if (transform_node->element_id != ElementId() &&
+        layer_in.GetMutatorHost()->HasTransformAnimationThatInflatesBounds(
+            transform_node->element_id)) {
       coalesced_transform.ConcatTransform(transform_node->pre_local);
       coalesced_transform.TransformBox(&box);
       coalesced_transform.MakeIdentity();
 
       gfx::BoxF inflated;
-      if (!layer->TransformAnimationBoundsForBox(box, &inflated))
+      if (!layer_in.GetMutatorHost()->TransformAnimationBoundsForBox(
+              transform_node->element_id, box, &inflated))
         return false;
       box = inflated;
 
diff --git a/cc/trees/clip_node.cc b/cc/trees/clip_node.cc
index a0da843..76272e39 100644
--- a/cc/trees/clip_node.cc
+++ b/cc/trees/clip_node.cc
@@ -14,7 +14,6 @@
 ClipNode::ClipNode()
     : id(ClipTree::kInvalidNodeId),
       parent_id(ClipTree::kInvalidNodeId),
-      owning_layer_id(Layer::INVALID_ID),
       clip_type(ClipType::APPLIES_LOCAL_CLIP),
       transform_id(TransformTree::kInvalidNodeId) {
 }
@@ -22,7 +21,6 @@
 ClipNode::ClipNode(const ClipNode& other)
     : id(other.id),
       parent_id(other.parent_id),
-      owning_layer_id(other.owning_layer_id),
       clip_type(other.clip_type),
       clip(other.clip),
       transform_id(other.transform_id) {
@@ -38,7 +36,6 @@
 ClipNode& ClipNode::operator=(const ClipNode& other) {
   id = other.id;
   parent_id = other.parent_id;
-  owning_layer_id = other.owning_layer_id;
   clip_type = other.clip_type;
   clip = other.clip;
   transform_id = other.transform_id;
@@ -65,7 +62,6 @@
       (!clip_expander && other.clip_expander))
     return false;
   return id == other.id && parent_id == other.parent_id &&
-         owning_layer_id == other.owning_layer_id &&
          clip_type == other.clip_type && clip == other.clip &&
          transform_id == other.transform_id;
 }
@@ -73,7 +69,6 @@
 void ClipNode::AsValueInto(base::trace_event::TracedValue* value) const {
   value->SetInteger("id", id);
   value->SetInteger("parent_id", parent_id);
-  value->SetInteger("owning_layer_id", owning_layer_id);
   value->SetInteger("clip_type", static_cast<int>(clip_type));
   MathUtil::AddToTracedValue("clip", clip, value);
   value->SetInteger("transform_id", transform_id);
diff --git a/cc/trees/clip_node.h b/cc/trees/clip_node.h
index dcad9cb..506b493 100644
--- a/cc/trees/clip_node.h
+++ b/cc/trees/clip_node.h
@@ -32,8 +32,6 @@
   int id;
   // The node index of the parent node in the clip tree node vector.
   int parent_id;
-  // The layer id of the layer that owns this node.
-  int owning_layer_id;
 
   enum class ClipType {
     // The node contributes a new clip (that is, |clip| needs to be applied).
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index c9c729d..7578582 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -355,11 +355,9 @@
 template <typename LayerType>
 static int TransformTreeIndexForBackfaceVisibility(LayerType* layer,
                                                    const TransformTree& tree) {
-  if (!layer->use_parent_backface_visibility())
+  if (!layer->use_parent_backface_visibility() || !layer->has_transform_node())
     return layer->transform_tree_index();
-  const TransformNode* node = tree.Node(layer->transform_tree_index());
-  return layer->id() == node->owning_layer_id ? tree.parent(node)->id
-                                              : node->id;
+  return tree.Node(layer->transform_tree_index())->parent_id;
 }
 
 static bool IsTargetSpaceTransformBackFaceVisible(
@@ -386,12 +384,8 @@
 static bool IsLayerBackFaceVisible(LayerType* layer,
                                    int transform_tree_index,
                                    const PropertyTrees* property_trees) {
-  const TransformNode* node =
-      property_trees->transform_tree.Node(transform_tree_index);
-  return layer->use_local_transform_for_backface_visibility()
-             ? node->local.IsBackFaceVisible()
-             : IsTargetSpaceTransformBackFaceVisible(
-                   layer, transform_tree_index, property_trees);
+  return IsTargetSpaceTransformBackFaceVisible(layer, transform_tree_index,
+                                               property_trees);
 }
 
 static inline bool TransformToScreenIsKnown(Layer* layer,
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 89e907e2..f5b9502 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -5606,16 +5606,10 @@
   EXPECT_EQ(1u, render_surface_list_impl()->size());
   EXPECT_TRUE(grand_child->contributes_to_drawn_render_surface());
 
-  // As all layers have identity transform, we shouldn't check for backface
-  // visibility.
+  // A ll layers with invisible backfgaces should be checked.
   EXPECT_FALSE(root->should_check_backface_visibility());
-  EXPECT_FALSE(child->should_check_backface_visibility());
-  EXPECT_FALSE(grand_child->should_check_backface_visibility());
-  // As there are no 3d rendering contexts, all layers should use their local
-  // transform for backface visibility.
-  EXPECT_TRUE(root->use_local_transform_for_backface_visibility());
-  EXPECT_TRUE(child->use_local_transform_for_backface_visibility());
-  EXPECT_TRUE(grand_child->use_local_transform_for_backface_visibility());
+  EXPECT_TRUE(child->should_check_backface_visibility());
+  EXPECT_TRUE(grand_child->should_check_backface_visibility());
 
   gfx::Transform rotation_transform;
   rotation_transform.RotateAboutXAxis(180.0);
@@ -5635,13 +5629,6 @@
   EXPECT_FALSE(root->should_check_backface_visibility());
   EXPECT_TRUE(child->should_check_backface_visibility());
   EXPECT_TRUE(grand_child->should_check_backface_visibility());
-  // child uses its local transform for backface visibility as it is the root of
-  // a 3d rendering context. grand_child is in a 3d rendering context and is not
-  // the root, but it derives its backface visibility from its parent which uses
-  // its local transform.
-  EXPECT_TRUE(root->use_local_transform_for_backface_visibility());
-  EXPECT_TRUE(child->use_local_transform_for_backface_visibility());
-  EXPECT_TRUE(grand_child->use_local_transform_for_backface_visibility());
 
   grand_child->SetUseParentBackfaceVisibility(false);
   grand_child->test_properties()->double_sided = false;
@@ -5657,11 +5644,6 @@
   EXPECT_FALSE(root->should_check_backface_visibility());
   EXPECT_TRUE(child->should_check_backface_visibility());
   EXPECT_TRUE(grand_child->should_check_backface_visibility());
-  // grand_child is in an existing 3d rendering context, so it should not use
-  // local transform for backface visibility.
-  EXPECT_TRUE(root->use_local_transform_for_backface_visibility());
-  EXPECT_TRUE(child->use_local_transform_for_backface_visibility());
-  EXPECT_FALSE(grand_child->use_local_transform_for_backface_visibility());
 }
 
 TEST_F(LayerTreeHostCommonTest, TransformAnimationUpdatesBackfaceVisibility) {
@@ -9207,12 +9189,6 @@
                                  page_scale_layer, inner_viewport_scroll_layer,
                                  outer_viewport_scroll_layer);
 
-  TransformTree& transform_tree =
-      root->layer_tree_impl()->property_trees()->transform_tree;
-  TransformNode* transform_node =
-      transform_tree.Node(significant_transform->transform_tree_index());
-  EXPECT_EQ(transform_node->owning_layer_id, significant_transform->id());
-
   EXPECT_TRUE(GetRenderSurface(root));
   EXPECT_EQ(GetRenderSurface(significant_transform), GetRenderSurface(root));
   EXPECT_TRUE(GetRenderSurface(layer_clips_subtree));
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 1d8c66c..8f83f803 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -207,17 +207,12 @@
   for (auto& pair : element_id_to_scrollbar_layer_ids_) {
     ElementId scrolling_element_id = pair.first;
 
-    // TODO(pdr): Remove this LayerImpl access and just use scroll nodes. This
-    // is blocked on scroll offset's dependency on LayerImpl.
-    LayerImpl* scrolling_layer = LayerByElementId(scrolling_element_id);
-    if (!scrolling_layer)
-      continue;
-    gfx::ScrollOffset current_offset = scrolling_layer->CurrentScrollOffset();
-
     auto& scroll_tree = property_trees()->scroll_tree;
     auto* scroll_node = scroll_tree.FindNodeFromElementId(scrolling_element_id);
     if (!scroll_node)
       continue;
+    gfx::ScrollOffset current_offset =
+        scroll_tree.current_scroll_offset(scrolling_element_id);
     gfx::SizeF scrolling_size(scroll_node->bounds);
     gfx::SizeF bounds_size(
         scroll_tree.scroll_clip_layer_bounds(scroll_node->id));
@@ -246,14 +241,13 @@
       bounds_size.Scale(1 / current_page_scale_factor());
     }
 
-    bool y_offset_did_change = false;
     for (auto* scrollbar : ScrollbarsFor(scrolling_element_id)) {
       if (scrollbar->orientation() == HORIZONTAL) {
         scrollbar->SetCurrentPos(current_offset.x());
         scrollbar->SetClipLayerLength(bounds_size.width());
         scrollbar->SetScrollLayerLength(scrolling_size.width());
       } else {
-        y_offset_did_change = scrollbar->SetCurrentPos(current_offset.y());
+        scrollbar->SetCurrentPos(current_offset.y());
         scrollbar->SetClipLayerLength(bounds_size.height());
         scrollbar->SetScrollLayerLength(scrolling_size.height());
       }
@@ -262,10 +256,6 @@
             InnerViewportContainerLayer()->ViewportBoundsDelta().y());
       }
     }
-
-    if (y_offset_did_change && is_viewport_scrollbar)
-      TRACE_COUNTER_ID1("cc", "scroll_offset_y", scrolling_layer->id(),
-                        current_offset.y());
   }
 
   scrollbar_geometries_need_update_ = false;
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 32ffa3ec..6f2fcdd 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1949,15 +1949,16 @@
           .combined_starting_animation_scale =
           max_local_scale * ancestor_starting_animation_scale;
     } else {
-      // TODO(sunxd): make LayerTreeImpl::MaximumTargetScale take layer id as
-      // parameter.
-      LayerImpl* layer_impl = layer_tree_impl->LayerById(node->owning_layer_id);
-      layer_impl->GetMutatorHost()->MaximumTargetScale(
-          layer_impl->element_id(), layer_impl->GetElementTypeForAnimation(),
+      ElementListType list_type = layer_tree_impl->IsActiveTree()
+                                      ? ElementListType::ACTIVE
+                                      : ElementListType::PENDING;
+
+      layer_tree_impl->mutator_host()->MaximumTargetScale(
+          node->element_id, list_type,
           &cached_data_.animation_scales[transform_node_id]
                .local_maximum_animation_target_scale);
-      layer_impl->GetMutatorHost()->AnimationStartScale(
-          layer_impl->element_id(), layer_impl->GetElementTypeForAnimation(),
+      layer_tree_impl->mutator_host()->AnimationStartScale(
+          node->element_id, list_type,
           &cached_data_.animation_scales[transform_node_id]
                .local_starting_animation_scale);
       gfx::Vector2dF local_scales =
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 490a055..2f287ca 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -274,7 +274,6 @@
     node.clip = gfx::RectF(gfx::PointF() + layer->offset_to_transform_parent(),
                            gfx::SizeF(layer->bounds()));
     node.transform_id = transform_parent->transform_tree_index();
-    node.owning_layer_id = layer->id();
     if (layer_clips_subtree) {
       node.clip_type = ClipNode::ClipType::APPLIES_LOCAL_CLIP;
     } else {
@@ -435,6 +434,7 @@
   if (layer->element_id()) {
     data_for_children->property_trees
         ->element_id_to_transform_node_index[layer->element_id()] = node->id;
+    node->element_id = layer->element_id();
   }
 
   node->scrolls = is_scrollable;
@@ -575,8 +575,6 @@
   // Flattening (if needed) will be handled by |node|.
   layer->set_should_flatten_transform_from_property_tree(false);
 
-  node->owning_layer_id = layer->id();
-
   return true;
 }
 
@@ -998,8 +996,8 @@
   return layer->test_properties()->user_scrollable_vertical;
 }
 
-void SetHasTransformNode(LayerImpl* layer, bool val) {}
-void SetHasTransformNode(Layer* layer, bool val) {
+template <typename LayerType>
+void SetHasTransformNode(LayerType* layer, bool val) {
   layer->SetHasTransformNode(val);
 }
 
@@ -1098,37 +1096,19 @@
 template <typename LayerType>
 void SetBackfaceVisibilityTransform(LayerType* layer,
                                     bool created_transform_node) {
-  const bool is_at_boundary_of_3d_rendering_context =
-      IsAtBoundaryOf3dRenderingContext(layer);
   if (layer->use_parent_backface_visibility()) {
-    DCHECK(!is_at_boundary_of_3d_rendering_context);
     DCHECK(Parent(layer));
     DCHECK(!Parent(layer)->use_parent_backface_visibility());
-    layer->SetUseLocalTransformForBackfaceVisibility(
-        Parent(layer)->use_local_transform_for_backface_visibility());
     layer->SetShouldCheckBackfaceVisibility(
         Parent(layer)->should_check_backface_visibility());
   } else {
-    // The current W3C spec on CSS transforms says that backface visibility
-    // should be determined differently depending on whether the layer is in a
-    // "3d rendering context" or not. For Chromium code, we can determine
-    // whether we are in a 3d rendering context by checking if the parent
-    // preserves 3d.
-    const bool use_local_transform =
-        !Is3dSorted(layer) ||
-        (Is3dSorted(layer) && is_at_boundary_of_3d_rendering_context);
-    layer->SetUseLocalTransformForBackfaceVisibility(use_local_transform);
-
     // A double-sided layer's backface can been shown when its visible.
-    if (DoubleSided(layer))
-      layer->SetShouldCheckBackfaceVisibility(false);
-    // The backface of a layer that uses local transform for backface visibility
-    // is not visible when it does not create a transform node as its local
-    // transform is identity or 2d translation and is not animating.
-    else if (use_local_transform && !created_transform_node)
-      layer->SetShouldCheckBackfaceVisibility(false);
-    else
-      layer->SetShouldCheckBackfaceVisibility(true);
+    // In addition, we need to check if (1) there might be a local 3D transform
+    // on the layer that might turn it to the backface, or (2) it is not drawn
+    // into a flattened space.
+    layer->SetShouldCheckBackfaceVisibility(
+        !DoubleSided(layer) &&
+        (created_transform_node || !ShouldFlattenTransform(Parent(layer))));
   }
 }
 
diff --git a/cc/trees/transform_node.cc b/cc/trees/transform_node.cc
index 6f89261d..044975f 100644
--- a/cc/trees/transform_node.cc
+++ b/cc/trees/transform_node.cc
@@ -14,7 +14,6 @@
 TransformNode::TransformNode()
     : id(TransformTree::kInvalidNodeId),
       parent_id(TransformTree::kInvalidNodeId),
-      owning_layer_id(Layer::INVALID_ID),
       sticky_position_constraint_id(-1),
       source_node_id(TransformTree::kInvalidNodeId),
       sorting_context_id(0),
@@ -43,9 +42,9 @@
 
 bool TransformNode::operator==(const TransformNode& other) const {
   return id == other.id && parent_id == other.parent_id &&
-         owning_layer_id == other.owning_layer_id &&
-         pre_local == other.pre_local && local == other.local &&
-         post_local == other.post_local && to_parent == other.to_parent &&
+         element_id == other.element_id && pre_local == other.pre_local &&
+         local == other.local && post_local == other.post_local &&
+         to_parent == other.to_parent &&
          source_node_id == other.source_node_id &&
          sorting_context_id == other.sorting_context_id &&
          needs_local_transform_update == other.needs_local_transform_update &&
@@ -104,7 +103,7 @@
 void TransformNode::AsValueInto(base::trace_event::TracedValue* value) const {
   value->SetInteger("id", id);
   value->SetInteger("parent_id", parent_id);
-  value->SetInteger("owning_layer_id", owning_layer_id);
+  value->SetInteger("element_id", element_id.id_);
   MathUtil::AddToTracedValue("pre_local", pre_local, value);
   MathUtil::AddToTracedValue("local", local, value);
   MathUtil::AddToTracedValue("post_local", post_local, value);
diff --git a/cc/trees/transform_node.h b/cc/trees/transform_node.h
index 2abfb1c..9318675 100644
--- a/cc/trees/transform_node.h
+++ b/cc/trees/transform_node.h
@@ -26,8 +26,8 @@
   int id;
   // The node index of the parent node in the transform tree node vector.
   int parent_id;
-  // The layer id of the layer that owns this node.
-  int owning_layer_id;
+
+  ElementId element_id;
 
   // The local transform information is combined to form to_parent (ignoring
   // snapping) as follows:
diff --git a/chrome/VERSION b/chrome/VERSION
index 59a6461..78ffc81c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=61
 MINOR=0
-BUILD=3133
+BUILD=3135
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java
index 97a68d33..1836678 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java
@@ -142,6 +142,19 @@
             layout.findViewById(R.id.js_modal_dialog_scroll_view).setVisibility(View.GONE);
         } else {
             ((TextView) layout.findViewById(R.id.js_modal_dialog_message)).setText(mMessage);
+
+            layout.findViewById(R.id.js_modal_dialog_scroll_view)
+                    .addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                        @Override
+                        public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                                int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                            boolean isScrollable =
+                                    v.getMeasuredHeight() - v.getPaddingTop() - v.getPaddingBottom()
+                                    < ((ViewGroup) v).getChildAt(0).getMeasuredHeight();
+
+                            v.setFocusable(isScrollable);
+                        }
+                    });
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 01dbb160..05c0aa6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -65,6 +65,7 @@
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
+import org.chromium.chrome.browser.tabmodel.AsyncTabParams;
 import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
 import org.chromium.chrome.browser.tabmodel.ChromeTabCreator;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
@@ -98,7 +99,8 @@
     private static final int WEBCONTENTS_STATE_NO_WEBCONTENTS = 0;
     private static final int WEBCONTENTS_STATE_PRERENDERED_WEBCONTENTS = 1;
     private static final int WEBCONTENTS_STATE_SPARE_WEBCONTENTS = 2;
-    private static final int WEBCONTENTS_STATE_MAX = 3;
+    private static final int WEBCONTENTS_STATE_TRANSFERRED_WEBCONTENTS = 3;
+    private static final int WEBCONTENTS_STATE_MAX = 4;
 
     private static CustomTabContentHandler sActiveContentHandler;
 
@@ -574,28 +576,15 @@
 
     private Tab createMainTab() {
         CustomTabsConnection connection = CustomTabsConnection.getInstance(getApplication());
-        String url = getUrlToLoad();
-        String referrerUrl = connection.getReferrer(mSession, getIntent());
-        Tab tab = new Tab(Tab.INVALID_TAB_ID, Tab.INVALID_TAB_ID, false, this, getWindowAndroid(),
+        WebContents webContents = takeWebContents(connection);
+
+        int assignedTabId = IntentUtils.safeGetIntExtra(
+                getIntent(), IntentHandler.EXTRA_TAB_ID, Tab.INVALID_TAB_ID);
+        int parentTabId = IntentUtils.safeGetIntExtra(
+                getIntent(), IntentHandler.EXTRA_PARENT_TAB_ID, Tab.INVALID_TAB_ID);
+        Tab tab = new Tab(assignedTabId, parentTabId, false, this, getWindowAndroid(),
                 TabLaunchType.FROM_EXTERNAL_APP, null, null);
         tab.setAppAssociatedWith(connection.getClientPackageNameForSession(mSession));
-
-        int webContentsStateOnLaunch = WEBCONTENTS_STATE_NO_WEBCONTENTS;
-        WebContents webContents = connection.takePrerenderedUrl(mSession, url, referrerUrl);
-        mUsingPrerender = webContents != null;
-        if (mUsingPrerender) webContentsStateOnLaunch = WEBCONTENTS_STATE_PRERENDERED_WEBCONTENTS;
-        if (!mUsingPrerender) {
-            webContents = WarmupManager.getInstance().takeSpareWebContents(false, false);
-            if (webContents != null) webContentsStateOnLaunch = WEBCONTENTS_STATE_SPARE_WEBCONTENTS;
-        }
-        RecordHistogram.recordEnumeratedHistogram("CustomTabs.WebContentsStateOnLaunch",
-                webContentsStateOnLaunch, WEBCONTENTS_STATE_MAX);
-        if (webContents == null) {
-            webContents = WebContentsFactory.createWebContentsWithWarmRenderer(false, false);
-        }
-        if (!mUsingPrerender) {
-            connection.resetPostMessageHandlerForSession(mSession, webContents);
-        }
         tab.initialize(
                 webContents, getTabContentManager(),
                 new CustomTabDelegateFactory(
@@ -612,6 +601,52 @@
         return tab;
     }
 
+    private WebContents takeWebContents(CustomTabsConnection connection) {
+        mUsingPrerender = true;
+        int webContentsStateOnLaunch = WEBCONTENTS_STATE_PRERENDERED_WEBCONTENTS;
+        WebContents webContents = takePrerenderedWebContents(connection);
+
+        if (webContents == null) {
+            mUsingPrerender = false;
+            webContents = takeAsyncWebContents();
+            if (webContents != null) {
+                webContentsStateOnLaunch = WEBCONTENTS_STATE_TRANSFERRED_WEBCONTENTS;
+            } else {
+                webContents = WarmupManager.getInstance().takeSpareWebContents(false, false);
+                if (webContents != null) {
+                    webContentsStateOnLaunch = WEBCONTENTS_STATE_SPARE_WEBCONTENTS;
+                } else {
+                    webContents =
+                            WebContentsFactory.createWebContentsWithWarmRenderer(false, false);
+                    webContentsStateOnLaunch = WEBCONTENTS_STATE_NO_WEBCONTENTS;
+                }
+            }
+        }
+
+        RecordHistogram.recordEnumeratedHistogram("CustomTabs.WebContentsStateOnLaunch",
+                webContentsStateOnLaunch, WEBCONTENTS_STATE_MAX);
+
+        if (!mUsingPrerender) {
+            connection.resetPostMessageHandlerForSession(mSession, webContents);
+        }
+
+        return webContents;
+    }
+
+    private WebContents takePrerenderedWebContents(CustomTabsConnection connection) {
+        String url = getUrlToLoad();
+        String referrerUrl = connection.getReferrer(mSession, getIntent());
+        return connection.takePrerenderedUrl(mSession, url, referrerUrl);
+    }
+
+    private WebContents takeAsyncWebContents() {
+        int assignedTabId = IntentUtils.safeGetIntExtra(
+                getIntent(), IntentHandler.EXTRA_TAB_ID, Tab.INVALID_TAB_ID);
+        AsyncTabParams asyncParams = AsyncTabParamsManager.remove(assignedTabId);
+        if (asyncParams == null) return null;
+        return asyncParams.getWebContents();
+    }
+
     private void initializeMainTab(Tab tab) {
         tab.getTabRedirectHandler().updateIntent(getIntent());
         tab.getView().requestFocus();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 83ecd149..299aaa5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -301,7 +301,9 @@
                 if (mShouldRemovePreviousNavigation) {
                     mShouldRemovePreviousNavigation = false;
                     NavigationController controller = webContents.getNavigationController();
-                    controller.removeEntryAtIndex(mLastDistillerPageIndex);
+                    if (controller.getEntryAtIndex(mLastDistillerPageIndex) != null) {
+                        controller.removeEntryAtIndex(mLastDistillerPageIndex);
+                    }
                 }
 
                 // Make sure the tab was not destroyed.
@@ -376,7 +378,7 @@
             return;
         }
 
-        ReaderModeInfoBar.showReaderModeInfoBar(mTabModelSelector.getCurrentTab(), this);
+        ReaderModeInfoBar.showReaderModeInfoBar(mTabModelSelector.getCurrentTab());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
index 8d7749e..38ce0d86 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
@@ -22,9 +22,6 @@
  * {@link OverlayPanel} implementation when Chrome Home is enabled.
  */
 public class ReaderModeInfoBar extends InfoBar {
-    /** A handle to the {@link ReaderModeManager} to trigger page navigations. */
-    private static ReaderModeManager sManager;
-
     /**
      * Default constructor.
      */
@@ -51,7 +48,7 @@
         prompt.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                sManager.navigateToReaderMode();
+                if (getReaderModeManager() != null) getReaderModeManager().navigateToReaderMode();
             }
         });
 
@@ -61,20 +58,31 @@
     @Override
     public void onCloseButtonClicked() {
         super.onCloseButtonClicked();
-        sManager.onClosed(StateChangeReason.CLOSE_BUTTON);
+        if (getReaderModeManager() != null) {
+            getReaderModeManager().onClosed(StateChangeReason.CLOSE_BUTTON);
+        }
     }
 
     /**
      * Create and show the Reader Mode {@link InfoBar}.
      * @param tab The tab that the {@link InfoBar} should be shown in.
-     * @param manager The {@link ReaderModeManager} for this instance of Chrome.
      */
-    public static void showReaderModeInfoBar(Tab tab, ReaderModeManager manager) {
-        sManager = manager;
+    public static void showReaderModeInfoBar(Tab tab) {
         nativeCreate(tab);
     }
 
     /**
+     * @return The {@link ReaderModeManager} for this infobar.
+     */
+    private ReaderModeManager getReaderModeManager() {
+        if (getNativeInfoBarPtr() == 0) return null;
+        Tab tab = nativeGetTab(getNativeInfoBarPtr());
+
+        if (tab == null || tab.getActivity() == null) return null;
+        return tab.getActivity().getReaderModeManager();
+    }
+
+    /**
      * @return An instance of the {@link ReaderModeInfoBar}.
      */
     @CalledByNative
@@ -83,4 +91,5 @@
     }
 
     private static native void nativeCreate(Tab tab);
+    private native Tab nativeGetTab(long nativeReaderModeInfoBar);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java
index 6ec9d02..4642fac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java
@@ -8,18 +8,14 @@
 import android.graphics.Rect;
 import android.os.StrictMode;
 import android.text.Editable;
-import android.text.Selection;
-import android.text.Spanned;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionWrapper;
 import android.widget.EditText;
 
 import org.chromium.base.Log;
@@ -29,40 +25,38 @@
 /**
  * An {@link EditText} that shows autocomplete text at the end.
  */
-public class AutocompleteEditText extends VerticallyFixedEditText {
+public class AutocompleteEditText
+        extends VerticallyFixedEditText implements AutocompleteEditTextModelBase.Delegate {
     private static final String TAG = "cr_AutocompleteEdit";
 
     private static final boolean DEBUG = false;
 
-    private final AutocompleteSpan mAutocompleteSpan;
     private final AccessibilityManager mAccessibilityManager;
 
+    // This contains most of the logic. Lazily initialized in ensureModel() because View constructor
+    // may call its methods, so always call ensureModel() before accessing it.
+    private AutocompleteEditTextModelBase mModel;
+
     /**
      * Whether default TextView scrolling should be disabled because autocomplete has been added.
      * This allows the user entered text to be shown instead of the end of the autocomplete.
      */
     private boolean mDisableTextScrollingFromAutocomplete;
 
-    private int mBatchEditNestCount;
-    private int mBeforeBatchEditAutocompleteIndex = -1;
-    private String mBeforeBatchEditFullText;
-    private boolean mSelectionChangedInBatchMode;
-    private boolean mTextDeletedInBatchMode;
-
-    // Set to true when the text is modified programmatically. Initially set to true until the old
-    // state has been loaded.
-    private boolean mIgnoreTextChangeFromAutocomplete = true;
-    private boolean mLastEditWasDelete;
-
     private boolean mIgnoreImeForTest;
 
     public AutocompleteEditText(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mAutocompleteSpan = new AutocompleteSpan();
         mAccessibilityManager =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
     }
 
+    private void ensureModel() {
+        // Lazy initialization here to ensure that model methods get called even in View's
+        // constructor.
+        if (mModel == null) mModel = new AutocompleteEditTextModel(this);
+    }
+
     /**
      * Sets whether text changes should trigger autocomplete.
      *
@@ -70,51 +64,29 @@
      *                           triggered.
      */
     public void setIgnoreTextChangesForAutocomplete(boolean ignoreAutocomplete) {
-        if (DEBUG) Log.i(TAG, "setIgnoreTextChangesForAutocomplete: " + ignoreAutocomplete);
-        mIgnoreTextChangeFromAutocomplete = ignoreAutocomplete;
-    }
-
-    /** @return Text that includes autocomplete. */
-    public String getTextWithAutocomplete() {
-        return getEditableText() != null ? getEditableText().toString() : "";
-    }
-
-    /**
-     * @return Whether the current cursor position is at the end of the user typed text (i.e.
-     *         at the beginning of the inline autocomplete text if present otherwise the very
-     *         end of the current text).
-     */
-    private boolean isCursorAtEndOfTypedText() {
-        final int selectionStart = getSelectionStart();
-        final int selectionEnd = getSelectionEnd();
-
-        int expectedSelectionStart = getText().getSpanStart(mAutocompleteSpan);
-        int expectedSelectionEnd = getText().length();
-        if (expectedSelectionStart < 0) {
-            expectedSelectionStart = expectedSelectionEnd;
-        }
-
-        return selectionStart == expectedSelectionStart && selectionEnd == expectedSelectionEnd;
+        ensureModel();
+        mModel.setIgnoreTextChangeFromAutocomplete(ignoreAutocomplete);
     }
 
     /**
      * @return The user text without the autocomplete text.
      */
     public String getTextWithoutAutocomplete() {
-        int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan);
-        if (autoCompleteIndex < 0) {
-            return getTextWithAutocomplete();
-        } else {
-            return getTextWithAutocomplete().substring(0, autoCompleteIndex);
-        }
+        ensureModel();
+        return mModel.getTextWithoutAutocomplete();
+    }
+
+    /** @return Text that includes autocomplete. */
+    public String getTextWithAutocomplete() {
+        ensureModel();
+        return mModel.getTextWithAutocomplete();
     }
 
     /** @return Whether any autocomplete information is specified on the current text. */
     @VisibleForTesting
     public boolean hasAutocomplete() {
-        return getText().getSpanStart(mAutocompleteSpan) >= 0
-                || mAutocompleteSpan.mAutocompleteText != null
-                || mAutocompleteSpan.mUserText != null;
+        ensureModel();
+        return mModel.hasAutocomplete();
     }
 
     /**
@@ -124,100 +96,21 @@
      * @return Whether we want to be showing inline autocomplete results.
      */
     public boolean shouldAutocomplete() {
-        if (mLastEditWasDelete) return false;
-        Editable text = getText();
-
-        return isCursorAtEndOfTypedText() && mBatchEditNestCount == 0
-                && BaseInputConnection.getComposingSpanEnd(text)
-                == BaseInputConnection.getComposingSpanStart(text);
-    }
-
-    private void onPostEndBatchEdit() {
-        if (mSelectionChangedInBatchMode) {
-            validateSelection(getSelectionStart(), getSelectionEnd());
-            mSelectionChangedInBatchMode = false;
-        }
-
-        String newText = getText().toString();
-        if (!TextUtils.equals(mBeforeBatchEditFullText, newText)
-                || getText().getSpanStart(mAutocompleteSpan) != mBeforeBatchEditAutocompleteIndex) {
-            // If the text being typed is a single character that matches the next character in the
-            // previously visible autocomplete text, we reapply the autocomplete text to prevent
-            // a visual flickering when the autocomplete text is cleared and then quickly reapplied
-            // when the next round of suggestions is received.
-            if (shouldAutocomplete() && mBeforeBatchEditAutocompleteIndex != -1
-                    && mBeforeBatchEditFullText != null
-                    && mBeforeBatchEditFullText.startsWith(newText) && !mTextDeletedInBatchMode
-                    && newText.length() - mBeforeBatchEditAutocompleteIndex == 1) {
-                setAutocompleteText(newText, mBeforeBatchEditFullText.substring(newText.length()));
-            }
-            notifyAutocompleteTextStateChanged(mTextDeletedInBatchMode, true);
-        }
-
-        mTextDeletedInBatchMode = false;
-        mBeforeBatchEditAutocompleteIndex = -1;
-        mBeforeBatchEditFullText = null;
+        ensureModel();
+        return mModel.shouldAutocomplete();
     }
 
     @Override
     protected void onSelectionChanged(int selStart, int selEnd) {
-        if (DEBUG) Log.i(TAG, "onSelectionChanged -- selStart: %d, selEnd: %d", selStart, selEnd);
-        if (mBatchEditNestCount == 0) {
-            int beforeTextLength = getText().length();
-            if (validateSelection(selStart, selEnd)) {
-                boolean textDeleted = getText().length() < beforeTextLength;
-                notifyAutocompleteTextStateChanged(textDeleted, false);
-            }
-        } else {
-            mSelectionChangedInBatchMode = true;
-        }
+        ensureModel();
+        mModel.onSelectionChanged(selStart, selEnd);
         super.onSelectionChanged(selStart, selEnd);
     }
 
-    /**
-     * Validates the selection and clears the autocomplete span if needed.  The autocomplete text
-     * will be deleted if the selection occurs entirely before the autocomplete region.
-     *
-     * @param selStart The start of the selection.
-     * @param selEnd The end of the selection.
-     * @return Whether the autocomplete span was removed as a result of this validation.
-     */
-    private boolean validateSelection(int selStart, int selEnd) {
-        int spanStart = getText().getSpanStart(mAutocompleteSpan);
-        int spanEnd = getText().getSpanEnd(mAutocompleteSpan);
-
-        if (DEBUG) {
-            Log.i(TAG, "validateSelection -- selStart: %d, selEnd: %d, spanStart: %d, spanEnd: %d",
-                    selStart, selEnd, spanStart, spanEnd);
-        }
-
-        if (spanStart >= 0 && (spanStart != selStart || spanEnd != selEnd)) {
-            CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompleteText;
-
-            // On selection changes, the autocomplete text has been accepted by the user or needs
-            // to be deleted below.
-            mAutocompleteSpan.clearSpan();
-
-            // The autocomplete text will be deleted any time the selection occurs entirely before
-            // the start of the autocomplete text.  This is required because certain keyboards will
-            // insert characters temporarily when starting a key entry gesture (whether it be
-            // swyping a word or long pressing to get a special character).  When this temporary
-            // character appears, Chrome may decide to append some autocomplete, but the keyboard
-            // will then remove this temporary character only while leaving the autocomplete text
-            // alone.  See crbug/273763 for more details.
-            if (selEnd <= spanStart
-                    && TextUtils.equals(previousAutocompleteText,
-                               getText().subSequence(spanStart, getText().length()))) {
-                getText().delete(spanStart, getText().length());
-            }
-            return true;
-        }
-        return false;
-    }
-
     @Override
     protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-        if (!focused) mAutocompleteSpan.clearSpan();
+        ensureModel();
+        mModel.onFocusChanged(focused);
         super.onFocusChanged(focused, direction, previouslyFocusedRect);
     }
 
@@ -241,6 +134,12 @@
         return retVal;
     }
 
+    /** Call this when text is pasted. */
+    public void onPaste() {
+        ensureModel();
+        mModel.onPaste();
+    }
+
     /**
      * Autocompletes the text and selects the text that was not entered by the user. Using append()
      * instead of setText() to preserve the soft-keyboard layout.
@@ -248,50 +147,10 @@
      * @param inlineAutocompleteText The suggested autocompletion for the user's text.
      */
     public void setAutocompleteText(CharSequence userText, CharSequence inlineAutocompleteText) {
-        if (DEBUG) {
-            Log.i(TAG, "setAutocompleteText -- userText: %s, inlineAutocompleteText: %s", userText,
-                    inlineAutocompleteText);
-        }
         boolean emptyAutocomplete = TextUtils.isEmpty(inlineAutocompleteText);
-
         if (!emptyAutocomplete) mDisableTextScrollingFromAutocomplete = true;
-
-        int autocompleteIndex = userText.length();
-
-        String previousText = getTextWithAutocomplete();
-        CharSequence newText = TextUtils.concat(userText, inlineAutocompleteText);
-
-        setIgnoreTextChangesForAutocomplete(true);
-
-        if (!TextUtils.equals(previousText, newText)) {
-            // The previous text may also have included autocomplete text, so we only
-            // append the new autocomplete text that has changed.
-            if (TextUtils.indexOf(newText, previousText) == 0) {
-                append(newText.subSequence(previousText.length(), newText.length()));
-            } else {
-                replaceAllTextFromAutocomplete(newText.toString());
-            }
-        }
-
-        if (getSelectionStart() != autocompleteIndex || getSelectionEnd() != getText().length()) {
-            setSelection(autocompleteIndex, getText().length());
-
-            if (inlineAutocompleteText.length() != 0) {
-                // Sending a TYPE_VIEW_TEXT_SELECTION_CHANGED accessibility event causes the
-                // previous TYPE_VIEW_TEXT_CHANGED event to be swallowed. As a result the user
-                // hears the autocomplete text but *not* the text they typed. Instead we send a
-                // TYPE_ANNOUNCEMENT event, which doesn't swallow the text-changed event.
-                announceForAccessibility(inlineAutocompleteText);
-            }
-        }
-
-        if (emptyAutocomplete) {
-            mAutocompleteSpan.clearSpan();
-        } else {
-            mAutocompleteSpan.setSpan(userText, inlineAutocompleteText);
-        }
-
-        setIgnoreTextChangesForAutocomplete(false);
+        ensureModel();
+        mModel.setAutocompleteText(userText, inlineAutocompleteText);
     }
 
     /**
@@ -299,31 +158,20 @@
      * currently displayed.
      */
     public int getAutocompleteLength() {
-        int autoCompleteIndex = getText().getSpanStart(mAutocompleteSpan);
-        if (autoCompleteIndex < 0) return 0;
-        return getText().length() - autoCompleteIndex;
+        ensureModel();
+        return mModel.getAutocompleteText().length();
     }
 
     @Override
     protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
-        if (DEBUG) {
-            Log.i(TAG, "onTextChanged -- text: %s, start: %d, lengthBefore: %d, lengthAfter: %d",
-                    text, start, lengthBefore, lengthAfter);
-        }
-
         super.onTextChanged(text, start, lengthBefore, lengthAfter);
-        boolean textDeleted = lengthAfter == 0;
-        if (mBatchEditNestCount == 0) {
-            notifyAutocompleteTextStateChanged(textDeleted, true);
-        } else {
-            mTextDeletedInBatchMode = textDeleted;
-        }
+        ensureModel();
+        mModel.onTextChanged(text, start, lengthBefore, lengthAfter);
     }
 
     @Override
     public void setText(CharSequence text, BufferType type) {
         if (DEBUG) Log.i(TAG, "setText -- text: %s", text);
-
         mDisableTextScrollingFromAutocomplete = false;
 
         // Avoid setting the same text as it will mess up the scroll/cursor position.
@@ -339,39 +187,17 @@
                 StrictMode.setThreadPolicy(oldPolicy);
             }
         }
-
-        // Verify the autocomplete is still valid after the text change.
-        // Note: mAutocompleteSpan may be still null here if setText() is called in View
-        // constructor.
-        if (mAutocompleteSpan != null && mAutocompleteSpan.mUserText != null
-                && mAutocompleteSpan.mAutocompleteText != null) {
-            if (getText().getSpanStart(mAutocompleteSpan) < 0) {
-                mAutocompleteSpan.clearSpan();
-            } else {
-                clearAutocompleteSpanIfInvalid();
-            }
-        }
-    }
-
-    private void clearAutocompleteSpanIfInvalid() {
-        Editable editableText = getEditableText();
-        CharSequence previousUserText = mAutocompleteSpan.mUserText;
-        CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompleteText;
-        if (editableText.length()
-                != (previousUserText.length() + previousAutocompleteText.length())) {
-            mAutocompleteSpan.clearSpan();
-        } else if (TextUtils.indexOf(getText(), previousUserText) != 0
-                || TextUtils.indexOf(getText(), previousAutocompleteText, previousUserText.length())
-                        != 0) {
-            mAutocompleteSpan.clearSpan();
-        }
+        ensureModel();
+        mModel.onSetText(text);
     }
 
     @Override
     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
-        if (mIgnoreTextChangeFromAutocomplete) {
+        ensureModel();
+        if (mModel.shouldIgnoreTextChangeFromAutocomplete()) {
             if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
                     || event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) {
+                if (DEBUG) Log.i(TAG, "Ignoring accessibility event from autocomplete.");
                 return;
             }
         }
@@ -391,8 +217,9 @@
     }
 
     @VisibleForTesting
-    public InputConnectionWrapper getInputConnection() {
-        return mInputConnection;
+    public InputConnection getInputConnection() {
+        ensureModel();
+        return mModel.getInputConnection();
     }
 
     @VisibleForTesting
@@ -400,145 +227,17 @@
         mIgnoreImeForTest = ignore;
     }
 
-    private InputConnectionWrapper mInputConnection = new InputConnectionWrapper(null, true) {
-        private final char[] mTempSelectionChar = new char[1];
-
-        @Override
-        public boolean beginBatchEdit() {
-            ++mBatchEditNestCount;
-            if (mBatchEditNestCount == 1) {
-                if (DEBUG) Log.i(TAG, "beginBatchEdit");
-                mBeforeBatchEditAutocompleteIndex = getText().getSpanStart(mAutocompleteSpan);
-                mBeforeBatchEditFullText = getText().toString();
-
-                boolean retVal = super.beginBatchEdit();
-                mTextDeletedInBatchMode = false;
-                return retVal;
-            }
-            return super.beginBatchEdit();
-        }
-
-        @Override
-        public boolean endBatchEdit() {
-            mBatchEditNestCount = Math.max(mBatchEditNestCount - 1, 0);
-            if (mBatchEditNestCount == 0) {
-                if (DEBUG) Log.i(TAG, "endBatchEdit");
-                boolean retVal = super.endBatchEdit();
-                onPostEndBatchEdit();
-                return retVal;
-            }
-            return super.endBatchEdit();
-        }
-
-        @Override
-        public boolean commitText(CharSequence text, int newCursorPosition) {
-            if (DEBUG) Log.i(TAG, "commitText: [%s]", text);
-            Editable currentText = getText();
-            if (currentText == null) return super.commitText(text, newCursorPosition);
-
-            int selectionStart = Selection.getSelectionStart(currentText);
-            int selectionEnd = Selection.getSelectionEnd(currentText);
-            int autocompleteIndex = currentText.getSpanStart(mAutocompleteSpan);
-            // If the text being committed is a single character that matches the next character
-            // in the selection (assumed to be the autocomplete text), we only move the text
-            // selection instead clearing the autocomplete text causing flickering as the
-            // autocomplete text will appear once the next suggestions are received.
-            //
-            // To be confident that the selection is an autocomplete, we ensure the selection
-            // is at least one character and the end of the selection is the end of the
-            // currently entered text.
-            if (newCursorPosition == 1 && selectionStart > 0 && selectionStart != selectionEnd
-                    && selectionEnd >= currentText.length() && autocompleteIndex == selectionStart
-                    && text.length() == 1) {
-                currentText.getChars(selectionStart, selectionStart + 1, mTempSelectionChar, 0);
-                if (mTempSelectionChar[0] == text.charAt(0)) {
-                    // Since the text isn't changing, TalkBack won't read out the typed characters.
-                    // To work around this, explicitly send an accessibility event. crbug.com/416595
-                    if (mAccessibilityManager != null && mAccessibilityManager.isEnabled()) {
-                        AccessibilityEvent event = AccessibilityEvent.obtain(
-                                AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
-                        event.setFromIndex(selectionStart);
-                        event.setRemovedCount(0);
-                        event.setAddedCount(1);
-                        event.setBeforeText(currentText.toString().substring(0, selectionStart));
-                        sendAccessibilityEventUnchecked(event);
-                    }
-
-                    setAutocompleteText(currentText.subSequence(0, selectionStart + 1),
-                            currentText.subSequence(selectionStart + 1, selectionEnd));
-                    if (mBatchEditNestCount == 0) {
-                        notifyAutocompleteTextStateChanged(false, false);
-                    }
-                    return true;
-                }
-            }
-
-            boolean retVal = super.commitText(text, newCursorPosition);
-
-            // Ensure the autocomplete span is removed if it is no longer valid after committing the
-            // text.
-            if (getText().getSpanStart(mAutocompleteSpan) >= 0) clearAutocompleteSpanIfInvalid();
-
-            return retVal;
-        }
-
-        @Override
-        public boolean setComposingText(CharSequence text, int newCursorPosition) {
-            if (DEBUG) Log.i(TAG, "setComposingText: [%s]", text);
-            Editable currentText = getText();
-            int autoCompleteSpanStart = currentText.getSpanStart(mAutocompleteSpan);
-            if (autoCompleteSpanStart >= 0) {
-                int composingEnd = BaseInputConnection.getComposingSpanEnd(currentText);
-
-                // On certain device/keyboard combinations, the composing regions are specified
-                // with a noticeable delay after the initial character is typed, and in certain
-                // circumstances it does not check that the current state of the text matches the
-                // expectations of it's composing region.
-                // For example, you can be typing:
-                //   chrome://f
-                // Chrome will autocomplete to:
-                //   chrome://f[lags]
-                // And after the autocomplete has been set, the keyboard will set the composing
-                // region to the last character and it assumes it is 'f' as it was the last
-                // character the keyboard sent.  If we commit this composition, the text will
-                // look like:
-                //   chrome://flag[f]
-                // And if we use the autocomplete clearing logic below, it will look like:
-                //   chrome://f[f]
-                // To work around this, we see if the composition matches all the characters prior
-                // to the autocomplete and just readjust the composing region to be that subset.
-                //
-                // See crbug.com/366732
-                if (composingEnd == currentText.length() && autoCompleteSpanStart >= text.length()
-                        && TextUtils.equals(
-                                   currentText.subSequence(autoCompleteSpanStart - text.length(),
-                                           autoCompleteSpanStart),
-                                   text)) {
-                    setComposingRegion(
-                            autoCompleteSpanStart - text.length(), autoCompleteSpanStart);
-                }
-
-                // Once composing text is being modified, the autocomplete text has been accepted
-                // or has to be deleted.
-                mAutocompleteSpan.clearSpan();
-                Selection.setSelection(currentText, autoCompleteSpanStart);
-                currentText.delete(autoCompleteSpanStart, currentText.length());
-            }
-            return super.setComposingText(text, newCursorPosition);
-        }
-    };
-
     @Override
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        if (DEBUG) Log.i(TAG, "onCreateInputConnection");
         return createInputConnection(super.onCreateInputConnection(outAttrs));
     }
 
     @VisibleForTesting
     public InputConnection createInputConnection(InputConnection target) {
-        mInputConnection.setTarget(target);
+        ensureModel();
+        InputConnection retVal = mModel.onCreateInputConnection(target);
         if (mIgnoreImeForTest) return null;
-        return mInputConnection;
+        return retVal;
     }
 
     @Override
@@ -547,58 +246,38 @@
         return super.dispatchKeyEvent(event);
     }
 
-    private void notifyAutocompleteTextStateChanged(boolean textDeleted, boolean updateDisplay) {
-        if (DEBUG) {
-            Log.i(TAG, "notifyAutocompleteTextStateChanged: DEL[%b] DIS[%b] IGN[%b]", textDeleted,
-                    updateDisplay, mIgnoreTextChangeFromAutocomplete);
-        }
-        if (mIgnoreTextChangeFromAutocomplete) return;
-        if (!hasFocus()) return;
-        mLastEditWasDelete = textDeleted;
-        onAutocompleteTextStateChanged(textDeleted, updateDisplay);
+    /**
+     * @return Whether the current UrlBar input has been pasted from the clipboard.
+     */
+    public boolean isPastedText() {
+        ensureModel();
+        return mModel.isPastedText();
     }
 
-    /**
-     * This is called when autocomplete replaces the whole text.
-     *
-     * @param text The text.
-     */
-    protected void replaceAllTextFromAutocomplete(String text) {
-        setText(text);
+    @Override
+    public void replaceAllTextFromAutocomplete(String text) {
+        assert false; // make sure that this method is properly overridden.
     }
 
-    /**
-     * This is called when autocomplete text state changes.
-     * @param textDeleted True if text is just deleted.
-     * @param updateDisplay True if string is changed.
-     */
-    public void onAutocompleteTextStateChanged(boolean textDeleted, boolean updateDisplay) {}
+    @Override
+    public void onAutocompleteTextStateChanged(boolean textDeleted, boolean updateDisplay) {
+        assert false; // make sure that this method is properly overridden.
+    }
 
-    /**
-     * Simple span used for tracking the current autocomplete state.
-     */
-    private class AutocompleteSpan {
-        private CharSequence mUserText;
-        private CharSequence mAutocompleteText;
-
-        /**
-         * Adds the span to the current text.
-         * @param userText The user entered text.
-         * @param autocompleteText The autocomplete text being appended.
-         */
-        public void setSpan(CharSequence userText, CharSequence autocompleteText) {
-            Editable text = getText();
-            text.removeSpan(this);
-            mAutocompleteText = autocompleteText;
-            mUserText = userText;
-            text.setSpan(this, userText.length(), text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-        }
-
-        /** Removes this span from the current text and clears the internal state. */
-        public void clearSpan() {
-            getText().removeSpan(this);
-            mAutocompleteText = null;
-            mUserText = null;
+    @Override
+    public void onNoChangeTypingAccessibilityEvent(int selectionStart) {
+        if (DEBUG) Log.i(TAG, "onNoChangeTypingAccessibilityEvent: " + selectionStart);
+        // Since the text isn't changing, TalkBack won't read out the typed characters.
+        // To work around this, explicitly send an accessibility event. crbug.com/416595
+        Editable currentText = getText();
+        if (mAccessibilityManager != null && mAccessibilityManager.isEnabled()) {
+            AccessibilityEvent event =
+                    AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+            event.setFromIndex(selectionStart);
+            event.setRemovedCount(0);
+            event.setAddedCount(1);
+            event.setBeforeText(currentText.toString().substring(0, selectionStart));
+            sendAccessibilityEventUnchecked(event);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModel.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModel.java
new file mode 100644
index 0000000..2bbcc5f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModel.java
@@ -0,0 +1,471 @@
+// 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.omnibox;
+
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+
+import org.chromium.base.Log;
+
+/**
+ * An autocomplete model that appends autocomplete text at the end of query/URL text and selects it.
+ */
+public class AutocompleteEditTextModel implements AutocompleteEditTextModelBase {
+    private static final String TAG = "cr_AutocompleteEdit";
+
+    private static final boolean DEBUG = false;
+
+    private final Delegate mDelegate;
+    private final AutocompleteSpan mAutocompleteSpan;
+
+    private int mBatchEditNestCount;
+    private int mBeforeBatchEditAutocompleteIndex = -1;
+    private String mBeforeBatchEditFullText;
+    private boolean mSelectionChangedInBatchMode;
+    private boolean mTextDeletedInBatchMode;
+    // Set to true when the text is modified programmatically. Initially set to true until the old
+    // state has been loaded.
+    private boolean mIgnoreTextChangeFromAutocomplete = true;
+
+    private boolean mLastEditWasDelete;
+    private boolean mIsPastedText;
+
+    public AutocompleteEditTextModel(AutocompleteEditTextModel.Delegate delegate) {
+        mDelegate = delegate;
+        mAutocompleteSpan = new AutocompleteSpan();
+    }
+
+    @Override
+    public void setIgnoreTextChangeFromAutocomplete(boolean ignore) {
+        if (DEBUG) Log.i(TAG, "setIgnoreTextChangesForAutocomplete: " + ignore);
+        mIgnoreTextChangeFromAutocomplete = ignore;
+    }
+
+    @Override
+    public String getTextWithAutocomplete() {
+        return mDelegate.getText().toString();
+    }
+
+    /**
+     * @return Whether the current cursor position is at the end of the user typed text (i.e.
+     *         at the beginning of the inline autocomplete text if present otherwise the very
+     *         end of the current text).
+     */
+    private boolean isCursorAtEndOfTypedText() {
+        final int selectionStart = mDelegate.getSelectionStart();
+        final int selectionEnd = mDelegate.getSelectionEnd();
+
+        int expectedSelectionStart = mDelegate.getText().getSpanStart(mAutocompleteSpan);
+        int expectedSelectionEnd = mDelegate.getText().length();
+        if (expectedSelectionStart < 0) {
+            expectedSelectionStart = expectedSelectionEnd;
+        }
+
+        return selectionStart == expectedSelectionStart && selectionEnd == expectedSelectionEnd;
+    }
+
+    @Override
+    public String getTextWithoutAutocomplete() {
+        int autoCompleteIndex = mDelegate.getText().getSpanStart(mAutocompleteSpan);
+        if (autoCompleteIndex < 0) {
+            return getTextWithAutocomplete();
+        } else {
+            return getTextWithAutocomplete().substring(0, autoCompleteIndex);
+        }
+    }
+
+    @Override
+    public boolean hasAutocomplete() {
+        return mDelegate.getText().getSpanStart(mAutocompleteSpan) >= 0
+                || mAutocompleteSpan.mAutocompleteText != null
+                || mAutocompleteSpan.mUserText != null;
+    }
+
+    @Override
+    public boolean shouldAutocomplete() {
+        if (mLastEditWasDelete) return false;
+        Editable text = mDelegate.getText();
+        return isCursorAtEndOfTypedText() && !isPastedText() && mBatchEditNestCount == 0
+                && BaseInputConnection.getComposingSpanEnd(text)
+                == BaseInputConnection.getComposingSpanStart(text);
+    }
+
+    private void onPostEndBatchEdit() {
+        if (mSelectionChangedInBatchMode) {
+            validateSelection(mDelegate.getSelectionStart(), mDelegate.getSelectionEnd());
+            mSelectionChangedInBatchMode = false;
+        }
+
+        String newText = mDelegate.getText().toString();
+        if (!TextUtils.equals(mBeforeBatchEditFullText, newText)
+                || mDelegate.getText().getSpanStart(mAutocompleteSpan)
+                        != mBeforeBatchEditAutocompleteIndex) {
+            // If the text being typed is a single character that matches the next character in the
+            // previously visible autocomplete text, we reapply the autocomplete text to prevent
+            // a visual flickering when the autocomplete text is cleared and then quickly reapplied
+            // when the next round of suggestions is received.
+            if (shouldAutocomplete() && mBeforeBatchEditAutocompleteIndex != -1
+                    && mBeforeBatchEditFullText != null
+                    && mBeforeBatchEditFullText.startsWith(newText) && !mTextDeletedInBatchMode
+                    && newText.length() - mBeforeBatchEditAutocompleteIndex == 1) {
+                setAutocompleteText(newText, mBeforeBatchEditFullText.substring(newText.length()));
+            }
+            notifyAutocompleteTextStateChanged(mTextDeletedInBatchMode, true);
+        }
+
+        mTextDeletedInBatchMode = false;
+        mBeforeBatchEditAutocompleteIndex = -1;
+        mBeforeBatchEditFullText = null;
+    }
+
+    @Override
+    public void onSelectionChanged(int selStart, int selEnd) {
+        if (DEBUG) Log.i(TAG, "onSelectionChanged -- selStart: %d, selEnd: %d", selStart, selEnd);
+        if (mBatchEditNestCount == 0) {
+            int beforeTextLength = mDelegate.getText().length();
+            if (validateSelection(selStart, selEnd)) {
+                boolean textDeleted = mDelegate.getText().length() < beforeTextLength;
+                notifyAutocompleteTextStateChanged(textDeleted, false);
+            }
+        } else {
+            mSelectionChangedInBatchMode = true;
+        }
+    }
+
+    /**
+     * Validates the selection and clears the autocomplete span if needed.  The autocomplete text
+     * will be deleted if the selection occurs entirely before the autocomplete region.
+     *
+     * @param selStart The start of the selection.
+     * @param selEnd The end of the selection.
+     * @return Whether the autocomplete span was removed as a result of this validation.
+     */
+    private boolean validateSelection(int selStart, int selEnd) {
+        Editable text = mDelegate.getText();
+        int spanStart = text.getSpanStart(mAutocompleteSpan);
+        int spanEnd = text.getSpanEnd(mAutocompleteSpan);
+
+        if (DEBUG) {
+            Log.i(TAG, "validateSelection -- selStart: %d, selEnd: %d, spanStart: %d, spanEnd: %d",
+                    selStart, selEnd, spanStart, spanEnd);
+        }
+
+        if (spanStart >= 0 && (spanStart != selStart || spanEnd != selEnd)) {
+            CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompleteText;
+
+            // On selection changes, the autocomplete text has been accepted by the user or needs
+            // to be deleted below.
+            mAutocompleteSpan.clearSpan();
+
+            // The autocomplete text will be deleted any time the selection occurs entirely before
+            // the start of the autocomplete text.  This is required because certain keyboards will
+            // insert characters temporarily when starting a key entry gesture (whether it be
+            // swyping a word or long pressing to get a special character).  When this temporary
+            // character appears, Chrome may decide to append some autocomplete, but the keyboard
+            // will then remove this temporary character only while leaving the autocomplete text
+            // alone.  See crbug/273763 for more details.
+            if (selEnd <= spanStart
+                    && TextUtils.equals(previousAutocompleteText,
+                               text.subSequence(spanStart, text.length()))) {
+                text.delete(spanStart, text.length());
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onFocusChanged(boolean focused) {
+        if (!focused) mAutocompleteSpan.clearSpan();
+    }
+
+    @Override
+    public void setAutocompleteText(CharSequence userText, CharSequence inlineAutocompleteText) {
+        if (DEBUG) {
+            Log.i(TAG, "setAutocompleteText -- userText: %s, inlineAutocompleteText: %s", userText,
+                    inlineAutocompleteText);
+        }
+
+        int autocompleteIndex = userText.length();
+        String previousText = getTextWithAutocomplete();
+        CharSequence newText = TextUtils.concat(userText, inlineAutocompleteText);
+
+        setIgnoreTextChangeFromAutocomplete(true);
+
+        if (!TextUtils.equals(previousText, newText)) {
+            // The previous text may also have included autocomplete text, so we only
+            // append the new autocomplete text that has changed.
+            if (TextUtils.indexOf(newText, previousText) == 0) {
+                mDelegate.append(newText.subSequence(previousText.length(), newText.length()));
+            } else {
+                mDelegate.replaceAllTextFromAutocomplete(newText.toString());
+            }
+        }
+
+        if (mDelegate.getSelectionStart() != autocompleteIndex
+                || mDelegate.getSelectionEnd() != mDelegate.getText().length()) {
+            mDelegate.setSelection(autocompleteIndex, mDelegate.getText().length());
+
+            if (inlineAutocompleteText.length() != 0) {
+                // Sending a TYPE_VIEW_TEXT_SELECTION_CHANGED accessibility event causes the
+                // previous TYPE_VIEW_TEXT_CHANGED event to be swallowed. As a result the user
+                // hears the autocomplete text but *not* the text they typed. Instead we send a
+                // TYPE_ANNOUNCEMENT event, which doesn't swallow the text-changed event.
+                mDelegate.announceForAccessibility(inlineAutocompleteText);
+            }
+        }
+
+        boolean emptyAutocomplete = TextUtils.isEmpty(inlineAutocompleteText);
+        if (emptyAutocomplete) {
+            mAutocompleteSpan.clearSpan();
+        } else {
+            mAutocompleteSpan.setSpan(userText, inlineAutocompleteText);
+        }
+
+        setIgnoreTextChangeFromAutocomplete(false);
+        if (DEBUG) Log.i(TAG, "setAutocompleteText finished");
+    }
+
+    @Override
+    public String getAutocompleteText() {
+        if (!hasAutocomplete()) return "";
+        return mAutocompleteSpan.mAutocompleteText.toString();
+    }
+
+    @Override
+    public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        if (DEBUG) {
+            Log.i(TAG, "onTextChanged: [%s], start: %d, bef: %d, aft: %d", text, start,
+                    lengthBefore, lengthAfter);
+        }
+        boolean textDeleted = lengthAfter == 0;
+        if (mBatchEditNestCount == 0) {
+            notifyAutocompleteTextStateChanged(textDeleted, true);
+        } else {
+            mTextDeletedInBatchMode = textDeleted;
+        }
+        mIsPastedText = false;
+    }
+
+    @Override
+    public void onSetText(CharSequence text) {
+        if (DEBUG) Log.i(TAG, "onSetText: [%s]", text);
+        // Verify the autocomplete is still valid after the text change.
+        if (mAutocompleteSpan.mUserText != null && mAutocompleteSpan.mAutocompleteText != null) {
+            if (mDelegate.getText().getSpanStart(mAutocompleteSpan) < 0) {
+                mAutocompleteSpan.clearSpan();
+            } else {
+                clearAutocompleteSpanIfInvalid();
+            }
+        }
+    }
+
+    private void clearAutocompleteSpanIfInvalid() {
+        Editable editableText = mDelegate.getEditableText();
+        CharSequence previousUserText = mAutocompleteSpan.mUserText;
+        CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompleteText;
+        if (editableText.length()
+                != (previousUserText.length() + previousAutocompleteText.length())) {
+            mAutocompleteSpan.clearSpan();
+        } else if (TextUtils.indexOf(mDelegate.getText(), previousUserText) != 0
+                || TextUtils.indexOf(
+                           mDelegate.getText(), previousAutocompleteText, previousUserText.length())
+                        != 0) {
+            mAutocompleteSpan.clearSpan();
+        }
+    }
+
+    @Override
+    public InputConnection getInputConnection() {
+        return mInputConnection;
+    }
+
+    private final InputConnectionWrapper mInputConnection = new InputConnectionWrapper(null, true) {
+        private final char[] mTempSelectionChar = new char[1];
+
+        @Override
+        public boolean beginBatchEdit() {
+            ++mBatchEditNestCount;
+            if (mBatchEditNestCount == 1) {
+                if (DEBUG) Log.i(TAG, "beginBatchEdit");
+                mBeforeBatchEditAutocompleteIndex =
+                        mDelegate.getText().getSpanStart(mAutocompleteSpan);
+                mBeforeBatchEditFullText = mDelegate.getText().toString();
+                boolean retVal = super.beginBatchEdit();
+                mTextDeletedInBatchMode = false;
+                return retVal;
+            }
+            return super.beginBatchEdit();
+        }
+
+        @Override
+        public boolean endBatchEdit() {
+            mBatchEditNestCount = Math.max(mBatchEditNestCount - 1, 0);
+            if (mBatchEditNestCount == 0) {
+                if (DEBUG) Log.i(TAG, "endBatchEdit");
+                boolean retVal = super.endBatchEdit();
+                onPostEndBatchEdit();
+                return retVal;
+            }
+            return super.endBatchEdit();
+        }
+
+        @Override
+        public boolean commitText(CharSequence text, int newCursorPosition) {
+            if (DEBUG) Log.i(TAG, "commitText: [%s]", text);
+            Editable currentText = mDelegate.getText();
+            if (currentText == null) return super.commitText(text, newCursorPosition);
+
+            int selectionStart = Selection.getSelectionStart(currentText);
+            int selectionEnd = Selection.getSelectionEnd(currentText);
+            int autocompleteIndex = currentText.getSpanStart(mAutocompleteSpan);
+            // If the text being committed is a single character that matches the next character
+            // in the selection (assumed to be the autocomplete text), we only move the text
+            // selection instead clearing the autocomplete text causing flickering as the
+            // autocomplete text will appear once the next suggestions are received.
+            //
+            // To be confident that the selection is an autocomplete, we ensure the selection
+            // is at least one character and the end of the selection is the end of the
+            // currently entered text.
+            if (newCursorPosition == 1 && selectionStart > 0 && selectionStart != selectionEnd
+                    && selectionEnd >= currentText.length() && autocompleteIndex == selectionStart
+                    && text.length() == 1) {
+                currentText.getChars(selectionStart, selectionStart + 1, mTempSelectionChar, 0);
+                if (mTempSelectionChar[0] == text.charAt(0)) {
+                    mDelegate.onNoChangeTypingAccessibilityEvent(selectionStart);
+
+                    setAutocompleteText(currentText.subSequence(0, selectionStart + 1),
+                            currentText.subSequence(selectionStart + 1, selectionEnd));
+                    if (mBatchEditNestCount == 0) {
+                        notifyAutocompleteTextStateChanged(false, false);
+                    }
+                    return true;
+                }
+            }
+
+            boolean retVal = super.commitText(text, newCursorPosition);
+
+            // Ensure the autocomplete span is removed if it is no longer valid after committing
+            // the text.
+            if (currentText.getSpanStart(mAutocompleteSpan) >= 0) {
+                clearAutocompleteSpanIfInvalid();
+            }
+
+            return retVal;
+        }
+
+        @Override
+        public boolean setComposingText(CharSequence text, int newCursorPosition) {
+            if (DEBUG) Log.i(TAG, "setComposingText: [%s]", text);
+            Editable currentText = mDelegate.getText();
+            int autoCompleteSpanStart = currentText.getSpanStart(mAutocompleteSpan);
+            if (autoCompleteSpanStart >= 0) {
+                int composingEnd = BaseInputConnection.getComposingSpanEnd(currentText);
+
+                // On certain device/keyboard combinations, the composing regions are specified
+                // with a noticeable delay after the initial character is typed, and in certain
+                // circumstances it does not check that the current state of the text matches
+                // the expectations of it's composing region. For example, you can be typing:
+                //   chrome://f
+                // Chrome will autocomplete to:
+                //   chrome://f[lags]
+                // And after the autocomplete has been set, the keyboard will set the composing
+                // region to the last character and it assumes it is 'f' as it was the last
+                // character the keyboard sent.  If we commit this composition, the text will
+                // look like:
+                //   chrome://flag[f]
+                // And if we use the autocomplete clearing logic below, it will look like:
+                //   chrome://f[f]
+                // To work around this, we see if the composition matches all the characters
+                // prior to the autocomplete and just readjust the composing region to be that
+                // subset.
+                //
+                // See crbug.com/366732
+                if (composingEnd == currentText.length()
+                        && autoCompleteSpanStart >= text.length()) {
+                    CharSequence trailingText = currentText.subSequence(
+                            autoCompleteSpanStart - text.length(), autoCompleteSpanStart);
+                    if (TextUtils.equals(trailingText, text)) {
+                        setComposingRegion(
+                                autoCompleteSpanStart - text.length(), autoCompleteSpanStart);
+                    }
+                }
+
+                // Once composing text is being modified, the autocomplete text has been
+                // accepted or has to be deleted.
+                mAutocompleteSpan.clearSpan();
+                Selection.setSelection(currentText, autoCompleteSpanStart);
+                currentText.delete(autoCompleteSpanStart, currentText.length());
+            }
+            return super.setComposingText(text, newCursorPosition);
+        }
+    };
+
+    @Override
+    public InputConnection onCreateInputConnection(InputConnection superInputConnection) {
+        if (DEBUG) Log.i(TAG, "onCreateInputConnection");
+        mInputConnection.setTarget(superInputConnection);
+        return mInputConnection;
+    }
+
+    private void notifyAutocompleteTextStateChanged(boolean textDeleted, boolean updateDisplay) {
+        if (DEBUG) {
+            Log.i(TAG, "notifyAutocompleteTextStateChanged: DEL[%b] DIS[%b] IGN[%b]", textDeleted,
+                    updateDisplay, mIgnoreTextChangeFromAutocomplete);
+        }
+        if (mIgnoreTextChangeFromAutocomplete) return;
+        mLastEditWasDelete = textDeleted;
+        mDelegate.onAutocompleteTextStateChanged(textDeleted, updateDisplay);
+    }
+
+    @Override
+    public boolean shouldIgnoreTextChangeFromAutocomplete() {
+        return mIgnoreTextChangeFromAutocomplete;
+    }
+
+    @Override
+    public void onPaste() {
+        if (DEBUG) Log.i(TAG, "onPaste");
+        mIsPastedText = true;
+    }
+
+    @Override
+    public boolean isPastedText() {
+        return mIsPastedText;
+    }
+
+    /**
+     * Simple span used for tracking the current autocomplete state.
+     */
+    private class AutocompleteSpan {
+        private CharSequence mUserText;
+        private CharSequence mAutocompleteText;
+
+        /**
+         * Adds the span to the current text.
+         * @param userText The user entered text.
+         * @param autocompleteText The autocomplete text being appended.
+         */
+        public void setSpan(CharSequence userText, CharSequence autocompleteText) {
+            Editable text = mDelegate.getText();
+            text.removeSpan(this);
+            mAutocompleteText = autocompleteText;
+            mUserText = userText;
+            text.setSpan(this, userText.length(), text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+
+        /** Removes this span from the current text and clears the internal state. */
+        public void clearSpan() {
+            mDelegate.getText().removeSpan(this);
+            mAutocompleteText = null;
+            mUserText = null;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModelBase.java
new file mode 100644
index 0000000..acc4024e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModelBase.java
@@ -0,0 +1,148 @@
+// 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.omnibox;
+
+import android.text.Editable;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import org.chromium.base.VisibleForTesting;
+
+/**
+ * An abstraction of the text model to show, keep track of, and update autocomplete.
+ */
+public interface AutocompleteEditTextModelBase {
+    /**
+     * An embedder should implement this.
+     */
+    public interface Delegate {
+        /** @see TextView#getText() */
+        Editable getText();
+        /** @see TextView#getEditableText() */
+        Editable getEditableText();
+        /** @see TextView#append(CharSequence) */
+        void append(CharSequence subSequence);
+        /** @see TextView#getSelectionStart() */
+        int getSelectionStart();
+        /** @see TextView#getSelectionEnd() */
+        int getSelectionEnd();
+        /** @see EditText#setSelection(int, int) */
+        void setSelection(int autocompleteIndex, int length);
+        /** @see TextView#announceForAccessibility(CharSequence) */
+        void announceForAccessibility(CharSequence inlineAutocompleteText);
+
+        /**
+         * This is called when autocomplete replaces the whole text.
+         * @param text The text.
+         */
+        void replaceAllTextFromAutocomplete(String text);
+
+        /**
+         * This is called when there is a typing accessibility event that actually causes no change.
+         * @param selectionStart The selection start.
+         */
+        void onNoChangeTypingAccessibilityEvent(int selectionStart);
+
+        /**
+         * This is called when autocomplete text state changes.
+         * @param textDeleted True if text is just deleted.
+         * @param updateDisplay True if string is changed.
+         */
+        void onAutocompleteTextStateChanged(boolean textDeleted, boolean updateDisplay);
+    }
+
+    /**
+     * Called when creating an input connection.
+     * @param inputConnection An {@link InputConnection} created by EditText.
+     * @return A wrapper @{link InputConnection} created by the model.
+     */
+    InputConnection onCreateInputConnection(InputConnection inputConnection);
+
+    /**
+     * Called when TextView#setText(CharSequence, BufferType) is called.
+     * @param text The new text.
+     */
+    void onSetText(CharSequence text);
+
+    /**
+     * Called when TextView#onSelectionChanged(int, int) is called.
+     * @param selStart The selection start.
+     * @param selEnd The selection end.
+     */
+    void onSelectionChanged(int selStart, int selEnd);
+
+    /**
+     * Called when View#onFocusChanged(boolean, int, Rect) is called.
+     * @param focused True if the View has focus; false otherwise.
+     */
+    void onFocusChanged(boolean focused);
+
+    /**
+     * Called when TextView#onTextChanged(CharSequence, int, int, int) is called.
+     * @param text The text the TextView is displaying.
+     * @param start The offset of the start of the range of the text that was modified.
+     * @param beforeLength The length of the former text that has been replaced.
+     * @param afterLength The length of the replacement modified text.
+     */
+    void onTextChanged(CharSequence text, int start, int beforeLength, int afterLength);
+
+    /** Called when text gets pasted. */
+    void onPaste();
+
+    /**
+     * @return Whether or not the user just pasted text.
+     */
+    boolean isPastedText();
+
+    /**
+     * @return The whole text including both user text and autocomplete text.
+     */
+    String getTextWithAutocomplete();
+
+    /**
+     * @return The user text without the autocomplete text.
+     */
+    String getTextWithoutAutocomplete();
+
+    /**
+     * Returns the length of the autocomplete text currently displayed, zero if none is
+     * currently displayed.
+     */
+    String getAutocompleteText();
+
+    /**
+     * Sets whether text changes should trigger autocomplete.
+     * @param ignore Whether text changes should be ignored and no auto complete.
+     */
+    void setIgnoreTextChangeFromAutocomplete(boolean ignore);
+
+    /** @return Whether we should ignore text change from autocomplete. */
+    boolean shouldIgnoreTextChangeFromAutocomplete();
+
+    /**
+     * Autocompletes the text and selects the text that was not entered by the user. Using append()
+     * instead of setText() to preserve the soft-keyboard layout.
+     * @param userText user The text entered by the user.
+     * @param inlineAutocompleteText The suggested autocompletion for the user's text.
+     */
+    void setAutocompleteText(CharSequence userText, CharSequence inlineAutocompleteText);
+
+    /**
+     * Whether we want to be showing inline autocomplete results. We don't want to show them as the
+     * user deletes input. Also if there is a composition (e.g. while using the Japanese IME),
+     * we must not autocomplete or we'll destroy the composition.
+     * @return Whether we want to be showing inline autocomplete results.
+     */
+    boolean shouldAutocomplete();
+
+    /** @return Whether any autocomplete information is specified on the current text. */
+    @VisibleForTesting
+    boolean hasAutocomplete();
+
+    /** @return The current {@link InputConnection} object. */
+    @VisibleForTesting
+    InputConnection getInputConnection();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index 7d8be3d3..04daa63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -114,8 +114,6 @@
 
     private long mFirstFocusTimeMs;
 
-    private boolean mIsPastedText;
-
     // Used as a hint to indicate the text may contain an ellipsize span.  This will be true if an
     // ellispize span was applied the last time the text changed.  A true value here does not
     // guarantee that the text does contain the span currently as newly set text may have cleared
@@ -287,12 +285,6 @@
         return super.onKeyPreIme(keyCode, event);
     }
 
-    @Override
-    public boolean shouldAutocomplete() {
-        if (isPastedText()) return false;
-        return super.shouldAutocomplete();
-    }
-
     /**
      * See {@link AutocompleteEditText#setIgnoreTextChangesForAutocomplete(boolean)}.
      * <p>
@@ -538,7 +530,7 @@
 
                 Selection.setSelection(getText(), max);
                 getText().replace(min, max, pasteString);
-                mIsPastedText = true;
+                onPaste();
                 return true;
             }
         }
@@ -665,12 +657,6 @@
     }
 
     @Override
-    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
-        super.onTextChanged(text, start, lengthBefore, lengthAfter);
-        mIsPastedText = false;
-    }
-
-    @Override
     public void setText(CharSequence text, BufferType type) {
         if (DEBUG) Log.i(TAG, "setText -- text: %s", text);
         super.setText(text, type);
@@ -780,13 +766,6 @@
         OmniboxUrlEmphasizer.deEmphasizeUrl(getText());
     }
 
-    /**
-     * @return Whether the current UrlBar input has been pasted from the clipboard.
-     */
-    public boolean isPastedText() {
-        return mIsPastedText;
-    }
-
     @Override
     public CharSequence getAccessibilityClassName() {
         // When UrlBar is used as a read-only TextView, force Talkback to pronounce it like
@@ -799,12 +778,17 @@
     }
 
     @Override
-    protected void replaceAllTextFromAutocomplete(String text) {
+    public void replaceAllTextFromAutocomplete(String text) {
+        if (DEBUG) Log.i(TAG, "replaceAllTextFromAutocomplete: " + text);
         setUrl(text, null);
     }
 
     @Override
     public void onAutocompleteTextStateChanged(boolean textDeleted, boolean updateDisplay) {
+        if (DEBUG) {
+            Log.i(TAG, "onAutocompleteTextStateChanged: DEL[%b], DIS[%b]", textDeleted,
+                    updateDisplay);
+        }
         if (mUrlBarDelegate == null) return;
         if (updateDisplay) limitDisplayableLength();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
index fa894a6..3735ad3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
@@ -22,6 +22,7 @@
 import org.chromium.chrome.browser.preferences.password.SavePasswordsPreferences;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener;
+import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
@@ -161,10 +162,14 @@
     private void updateSummary() {
         ChromeBasePreference searchEnginePref =
                 (ChromeBasePreference) findPreference(PREF_SEARCH_ENGINE);
+        searchEnginePref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
         searchEnginePref.setEnabled(true);
-        searchEnginePref.setSummary(TemplateUrlService.getInstance()
-                                            .getDefaultSearchEngineTemplateUrl()
-                                            .getShortName());
+
+        String defaultSearchEngineName = null;
+        TemplateUrl dseTemplateUrl =
+                TemplateUrlService.getInstance().getDefaultSearchEngineTemplateUrl();
+        if (dseTemplateUrl != null) defaultSearchEngineName = dseTemplateUrl.getShortName();
+        searchEnginePref.setSummary(defaultSearchEngineName);
     }
 
     private void setOnOffSummary(Preference pref, boolean isOn) {
@@ -215,6 +220,9 @@
                 if (PREF_DATA_REDUCTION.equals(preference.getKey())) {
                     return DataReductionProxySettings.getInstance().isDataReductionProxyManaged();
                 }
+                if (PREF_SEARCH_ENGINE.equals(preference.getKey())) {
+                    return TemplateUrlService.getInstance().isDefaultSearchManaged();
+                }
                 return false;
             }
 
@@ -234,6 +242,9 @@
                     return settings.isDataReductionProxyManaged()
                             && !settings.isDataReductionProxyEnabled();
                 }
+                if (PREF_SEARCH_ENGINE.equals(preference.getKey())) {
+                    return TemplateUrlService.getInstance().isDefaultSearchManaged();
+                }
                 return super.isPreferenceClickDisabledByPolicy(preference);
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
index 0678a0f8..a3e338a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
@@ -18,6 +18,8 @@
 import java.util.List;
 import java.util.Locale;
 
+import javax.annotation.Nullable;
+
 /**
  * Android wrapper of the TemplateUrlService which provides access from the Java
  * layer.
@@ -250,9 +252,10 @@
     }
 
     /**
-     * @return {@link TemplateUrlService.TemplateUrl} for the default search engine.
+     * @return {@link TemplateUrlService.TemplateUrl} for the default search engine.  This can
+     *         be null if DSEs are disabled entirely by administrators.
      */
-    public TemplateUrl getDefaultSearchEngineTemplateUrl() {
+    public @Nullable TemplateUrl getDefaultSearchEngineTemplateUrl() {
         if (!isLoaded()) return null;
 
         int defaultSearchEngineIndex = getDefaultSearchEngineIndex();
@@ -271,8 +274,12 @@
                 mNativeTemplateUrlServiceAndroid, selectedKeyword);
     }
 
-    public boolean isSearchProviderManaged() {
-        return nativeIsSearchProviderManaged(mNativeTemplateUrlServiceAndroid);
+    /**
+     * @return Whether the default search engine is managed and controlled by policy.  If true, the
+     *         DSE can not be modified by the user.
+     */
+    public boolean isDefaultSearchManaged() {
+        return nativeIsDefaultSearchManaged(mNativeTemplateUrlServiceAndroid);
     }
 
     /**
@@ -431,7 +438,7 @@
     private native void nativeSetUserSelectedDefaultSearchProvider(
             long nativeTemplateUrlServiceAndroid, String selectedKeyword);
     private native int nativeGetDefaultSearchProviderIndex(long nativeTemplateUrlServiceAndroid);
-    private native boolean nativeIsSearchProviderManaged(long nativeTemplateUrlServiceAndroid);
+    private native boolean nativeIsDefaultSearchManaged(long nativeTemplateUrlServiceAndroid);
     private native boolean nativeIsSearchResultsPageFromDefaultSearchProvider(
             long nativeTemplateUrlServiceAndroid, String url);
     private native boolean nativeIsSearchByImageAvailable(long nativeTemplateUrlServiceAndroid);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
index 427aba5..2e410a19 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
@@ -32,6 +32,7 @@
 import org.chromium.chrome.browser.omnibox.LocationBarLayout;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener;
+import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver;
 import org.chromium.chrome.browser.util.IntentUtils;
 
@@ -303,12 +304,17 @@
         if (!service.isLoaded()) return;
 
         // Update the URL that we show for zero-suggest.
-        String searchEngineUrl = service.getSearchEngineUrlFromTemplateUrl(
-                service.getDefaultSearchEngineTemplateUrl().getKeyword());
-        sDefaultSearchEngineUrl =
-                LocationBarLayout.splitPathFromUrlDisplayText(searchEngineUrl).first;
+        TemplateUrl dseTemplateUrl = service.getDefaultSearchEngineTemplateUrl();
+        String engineName = null;
+        if (dseTemplateUrl != null) {
+            String searchEngineUrl =
+                    service.getSearchEngineUrlFromTemplateUrl(dseTemplateUrl.getKeyword());
+            sDefaultSearchEngineUrl =
+                    LocationBarLayout.splitPathFromUrlDisplayText(searchEngineUrl).first;
+            engineName = dseTemplateUrl.getShortName();
+        }
 
-        updateCachedEngineName(service.getDefaultSearchEngineTemplateUrl().getShortName());
+        updateCachedEngineName(engineName);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
index 26e30c3..a64514d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tab;
 
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
@@ -24,6 +25,10 @@
  * Class that controls navigations and allows to intercept them. It is used on Android to 'convert'
  * certain navigations to Intents to 3rd party applications and to "pause" navigations when data use
  * tracking has ended.
+ * Note the Intent is often created together with a new empty tab which then shoud be closed
+ * immediately. This task of closing tab should be done in an asynchrnous fashion by posting
+ * the task onto UI thread again to avoid accessing the WebContents and the associated objects
+ * afterwards since they will have been destroyed as well. see https://crbug.com/732260.
  */
 public class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate {
     private final Tab mTab;
@@ -243,7 +248,14 @@
                 // crbug.com/487938.
                 mTab.getActivity().moveTaskToBack(false);
             }
-            mTab.getTabModelSelector().closeTab(mTab);
+            // Defer closing a tab (and the associated WebContents) till the navigation
+            // request and the throttle finishes the job with it.
+            ThreadUtils.postOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mTab.getTabModelSelector().closeTab(mTab);
+                }
+            });
         } else if (mTab.getTabRedirectHandler().isOnNavigation()) {
             int lastCommittedEntryIndexBeforeNavigation = mTab.getTabRedirectHandler()
                     .getLastCommittedEntryIndexBeforeStartingNavigation();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java
index c011fcd..6d995d7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java
@@ -140,6 +140,13 @@
             intent.setComponent(componentName);
         }
 
+        addAsyncTabExtras(asyncParams, parentId, isChromeUI, assignedTabId, intent);
+
+        return intent;
+    }
+
+    protected final void addAsyncTabExtras(AsyncTabCreationParams asyncParams, int parentId,
+            boolean isChromeUI, int assignedTabId, Intent intent) {
         Map<String, String> extraHeaders = asyncParams.getLoadUrlParams().getExtraHeaders();
         if (extraHeaders != null && !extraHeaders.isEmpty()) {
             Bundle bundle = new Bundle();
@@ -171,7 +178,6 @@
         }
 
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return intent;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index 3067695..3d20118 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -267,6 +267,19 @@
             } finally {
                 StrictMode.setThreadPolicy(oldPolicy);
             }
+
+            // If the browser has been initialized by this point, check the experiment as well to
+            // avoid the restart logic in cacheChromeHomeEnabled.
+            if (ChromeFeatureList.isInitialized()) {
+                boolean chromeHomeExperimentEnabled =
+                        ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_HOME);
+
+                if (chromeHomeExperimentEnabled != sChromeHomeEnabled) {
+                    sChromeHomeEnabled = chromeHomeExperimentEnabled;
+                    ChromePreferenceManager.getInstance().setChromeHomeEnabled(
+                            chromeHomeExperimentEnabled);
+                }
+            }
         }
 
         return sChromeHomeEnabled;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index 470a58d..1632c42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -13,6 +13,7 @@
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.Callback;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -64,19 +65,20 @@
      */
     private static class PendingUpdate {
         public WebApkInfo mUpdateInfo;
-        public String mPrimaryIconUrl;
-        public String mBadgeIconUrl;
-        public boolean mIsManifestStale;
+        byte[] mSerializedProto;
 
-        public PendingUpdate(WebApkInfo info, String primaryIconUrl, String badgeIconUrl,
-                boolean isManifestStale) {
+        public PendingUpdate(WebApkInfo info, byte[] serializedProto) {
             mUpdateInfo = info;
-            mPrimaryIconUrl = primaryIconUrl;
-            mBadgeIconUrl = badgeIconUrl;
-            mIsManifestStale = isManifestStale;
+            mSerializedProto = serializedProto;
         }
     }
 
+    /** Called with update result. */
+    private static interface WebApkUpdateCallback {
+        @CalledByNative("WebApkUpdateCallback")
+        public void onResultFromNative(@WebApkInstallResult int result, boolean relaxUpdates);
+    }
+
     public WebApkUpdateManager(WebApkActivity activity, WebappDataStorage storage) {
         mActivity = activity;
         mStorage = storage;
@@ -103,8 +105,7 @@
      */
     public boolean requestPendingUpdate() {
         if (mPendingUpdate != null) {
-            updateAsync(mPendingUpdate.mUpdateInfo, mPendingUpdate.mPrimaryIconUrl,
-                    mPendingUpdate.mBadgeIconUrl, mPendingUpdate.mIsManifestStale);
+            updateAsync(mPendingUpdate.mUpdateInfo, mPendingUpdate.mSerializedProto);
             return true;
         }
         return false;
@@ -156,25 +157,26 @@
 
         if (!needsUpgrade) {
             if (!mStorage.didPreviousUpdateSucceed()) {
-                recordUpdate(mStorage, WebApkInstallResult.SUCCESS, false /* relaxUpdates */);
+                recordUpdate(WebApkInstallResult.SUCCESS, false /* relaxUpdates */);
             }
             return;
         }
 
         // Set WebAPK update as having failed in case that Chrome is killed prior to
         // {@link onBuiltWebApk} being called.
-        recordUpdate(mStorage, WebApkInstallResult.FAILURE, false /* relaxUpdates*/);
+        recordUpdate(WebApkInstallResult.FAILURE, false /* relaxUpdates*/);
 
         if (fetchedInfo != null) {
-            scheduleUpdate(fetchedInfo, primaryIconUrl, badgeIconUrl, false /* isManifestStale */);
+            buildProtoAndScheduleUpdate(
+                    fetchedInfo, primaryIconUrl, badgeIconUrl, false /* isManifestStale */);
             return;
         }
 
         // Tell the server that the our version of the Web Manifest might be stale and to ignore
         // our Web Manifest data if the server's Web Manifest data is newer. This scenario can
         // occur if the Web Manifest is temporarily unreachable.
-        scheduleUpdate(mInfo, "" /* primaryIconUrl */, "" /* badgeIconUrl */,
-                true /* isManifestStale */);
+        buildProtoAndScheduleUpdate(
+                mInfo, "" /* primaryIconUrl */, "" /* badgeIconUrl */, true /* isManifestStale */);
     }
 
     /**
@@ -184,16 +186,43 @@
         return new WebApkUpdateDataFetcher();
     }
 
+    /** Builds proto to send to the WebAPK server. */
+    protected void buildProtoAndScheduleUpdate(final WebApkInfo info, String primaryIconUrl,
+            String badgeIconUrl, boolean isManifestStale) {
+        int versionCode = readVersionCodeFromAndroidManifest(info.webApkPackageName());
+        int size = info.iconUrlToMurmur2HashMap().size();
+        String[] iconUrls = new String[size];
+        String[] iconHashes = new String[size];
+        int i = 0;
+        for (Map.Entry<String, String> entry : info.iconUrlToMurmur2HashMap().entrySet()) {
+            iconUrls[i] = entry.getKey();
+            String iconHash = entry.getValue();
+            iconHashes[i] = (iconHash != null) ? iconHash : "";
+            i++;
+        }
+
+        Callback<byte[]> callback = new Callback<byte[]>() {
+            @Override
+            public void onResult(byte[] result) {
+                scheduleUpdate(info, result);
+            }
+        };
+        nativeBuildUpdateWebApkProto(info.manifestStartUrl(), info.scopeUri().toString(),
+                info.name(), info.shortName(), primaryIconUrl, info.icon(), badgeIconUrl,
+                info.badgeIcon(), iconUrls, iconHashes, info.displayMode(), info.orientation(),
+                info.themeColor(), info.backgroundColor(), info.manifestUrl(),
+                info.webApkPackageName(), versionCode, isManifestStale, callback);
+    }
+
     /**
      * Sends update request to WebAPK Server if the WebAPK is running in the background; caches the
      * fetched WebApkInfo otherwise.
      */
-    protected void scheduleUpdate(WebApkInfo info, String primaryIconUrl, String badgeIconUrl,
-            boolean isManifestStale) {
+    protected void scheduleUpdate(WebApkInfo info, byte[] serializedProto) {
         int numberOfUpdateRequests = mStorage.getUpdateRequests();
         boolean forceUpdateNow =  numberOfUpdateRequests >= MAX_UPDATE_ATTEMPTS;
         if (!isInForeground() || forceUpdateNow) {
-            updateAsync(info, primaryIconUrl, badgeIconUrl, isManifestStale);
+            updateAsync(info, serializedProto);
             WebApkUma.recordUpdateRequestSent(WebApkUma.UPDATE_REQUEST_SENT_FIRST_TRY);
             return;
         }
@@ -202,7 +231,7 @@
         // The {@link numberOfUpdateRequests} can never exceed 2 here (otherwise we'll have taken
         // the branch above and have returned before reaching this statement).
         WebApkUma.recordUpdateRequestQueued(numberOfUpdateRequests);
-        mPendingUpdate = new PendingUpdate(info, primaryIconUrl, badgeIconUrl, isManifestStale);
+        mPendingUpdate = new PendingUpdate(info, serializedProto);
     }
 
     /** Returns whether the associated WebApkActivity is running in foreground. */
@@ -214,9 +243,8 @@
     /**
      * Sends update request to the WebAPK Server and cleanup.
      */
-    private void updateAsync(WebApkInfo info, String primaryIconUrl, String badgeIconUrl,
-            boolean isManifestStale) {
-        updateAsyncImpl(info, primaryIconUrl, badgeIconUrl, isManifestStale);
+    private void updateAsync(WebApkInfo info, byte[] serializedProto) {
+        updateAsyncImpl(info, serializedProto);
         mStorage.resetUpdateRequests();
         mPendingUpdate = null;
     }
@@ -224,28 +252,21 @@
     /**
      * Sends update request to the WebAPK Server.
      */
-    protected void updateAsyncImpl(WebApkInfo info, String primaryIconUrl, String badgeIconUrl,
-            boolean isManifestStale) {
-        if (info == null) {
+    protected void updateAsyncImpl(WebApkInfo info, byte[] serializedProto) {
+        if (info == null || serializedProto == null) {
             return;
         }
 
-        int versionCode = readVersionCodeFromAndroidManifest(info.webApkPackageName());
-        int size = info.iconUrlToMurmur2HashMap().size();
-        String[] iconUrls = new String[size];
-        String[] iconHashes = new String[size];
-        int i = 0;
-        for (Map.Entry<String, String> entry : info.iconUrlToMurmur2HashMap().entrySet()) {
-            iconUrls[i] = entry.getKey();
-            String iconHash = entry.getValue();
-            iconHashes[i] = iconHash != null ? iconHash : "";
-            i++;
-        }
-        nativeUpdateAsync(info.id(), info.manifestStartUrl(), info.scopeUri().toString(),
-                info.name(), info.shortName(), primaryIconUrl, info.icon(), badgeIconUrl,
-                info.badgeIcon(), iconUrls, iconHashes, info.displayMode(), info.orientation(),
-                info.themeColor(), info.backgroundColor(), info.manifestUrl(),
-                info.webApkPackageName(), versionCode, isManifestStale);
+        WebApkUpdateCallback callback = new WebApkUpdateCallback() {
+            @Override
+            public void onResultFromNative(@WebApkInstallResult int result, boolean relaxUpdates) {
+                recordUpdate(result, relaxUpdates);
+                mStorage.updateLastRequestedShellApkVersion(
+                        WebApkVersion.CURRENT_SHELL_APK_VERSION);
+            }
+        };
+        nativeUpdateWebApk(info.webApkPackageName(), info.manifestStartUrl(), info.shortName(),
+                serializedProto, callback);
     }
 
     /**
@@ -313,13 +334,12 @@
      * Updates {@link WebappDataStorage} with the time of the latest WebAPK update and whether the
      * WebAPK update succeeded.
      */
-    private static void recordUpdate(
-            WebappDataStorage storage, @WebApkInstallResult int result, boolean relaxUpdates) {
+    private void recordUpdate(@WebApkInstallResult int result, boolean relaxUpdates) {
         // Update the request time and result together. It prevents getting a correct request time
         // but a result from the previous request.
-        storage.updateTimeOfLastWebApkUpdateRequestCompletion();
-        storage.updateDidLastWebApkUpdateRequestSucceed(result == WebApkInstallResult.SUCCESS);
-        storage.setRelaxedUpdates(relaxUpdates);
+        mStorage.updateTimeOfLastWebApkUpdateRequestCompletion();
+        mStorage.updateDidLastWebApkUpdateRequestSucceed(result == WebApkInstallResult.SUCCESS);
+        mStorage.setRelaxedUpdates(relaxUpdates);
     }
 
     /**
@@ -379,23 +399,12 @@
         return UrlUtilities.urlsMatchIgnoringFragments(url1, url2);
     }
 
-    /**
-     * Called after either a request to update the WebAPK has been sent or the update process
-     * fails.
-     */
-    @CalledByNative
-    private static void onBuiltWebApk(
-            String id, @WebApkInstallResult int result, boolean relaxUpdates) {
-        WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(id);
-        if (storage == null) return;
-
-        recordUpdate(storage, result, relaxUpdates);
-        storage.updateLastRequestedShellApkVersion(WebApkVersion.CURRENT_SHELL_APK_VERSION);
-    }
-
-    private static native void nativeUpdateAsync(String id, String startUrl, String scope,
+    private static native void nativeBuildUpdateWebApkProto(String startUrl, String scope,
             String name, String shortName, String primaryIconUrl, Bitmap primaryIcon,
             String badgeIconUrl, Bitmap badgeIcon, String[] iconUrls, String[] iconHashes,
             int displayMode, int orientation, long themeColor, long backgroundColor,
-            String manifestUrl, String webApkPackage, int webApkVersion, boolean isManifestStale);
+            String manifestUrl, String webApkPackage, int webApkVersion, boolean isManifestStale,
+            Callback<byte[]> callback);
+    private static native void nativeUpdateWebApk(String webApkPackage, String startUrl,
+            String shortName, byte[] serializedProto, WebApkUpdateCallback callback);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 5ca8630..a98fb2e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -39,6 +39,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
 import org.chromium.chrome.browser.tab.TabObserver;
+import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.UrlUtilities;
 import org.chromium.content.browser.ScreenOrientationProvider;
@@ -714,6 +715,11 @@
         return new WebappDelegateFactory(this);
     }
 
+    @Override
+    protected TabDelegate createTabDelegate(boolean incognito) {
+        return new WebappTabDelegate(this, incognito);
+    }
+
     // We're temporarily disable CS on webapp since there are some issues. (http://crbug.com/471950)
     // TODO(changwan): re-enable it once the issues are resolved.
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
new file mode 100644
index 0000000..e3e1e9e9
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.chrome.browser.webapps;
+
+import android.net.Uri;
+import android.support.customtabs.CustomTabsIntent;
+
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabIdManager;
+import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.document.AsyncTabCreationParams;
+import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
+
+/**
+ * Asynchronously creates Tabs for navigation originating from an installed PWA.
+ *
+ * This is the same as the parent class with exception of opening a Custom Tab for
+ * {@code _blank} links and {@code window.open(url)} calls instead of creating a new tab in Chrome.
+ */
+public class WebappTabDelegate extends TabDelegate {
+    private final WebappActivity mActivity;
+
+    public WebappTabDelegate(WebappActivity activity, boolean incognito) {
+        super(incognito);
+        this.mActivity = activity;
+    }
+
+    @Override
+    public void createNewTab(AsyncTabCreationParams asyncParams, TabLaunchType type, int parentId) {
+        int assignedTabId = TabIdManager.getInstance().generateValidId(Tab.INVALID_TAB_ID);
+        AsyncTabParamsManager.add(assignedTabId, asyncParams);
+
+        CustomTabsIntent customTabIntent =
+                new CustomTabsIntent.Builder().setShowTitle(true).build();
+
+        customTabIntent.intent.setPackage(mActivity.getPackageName());
+        addAsyncTabExtras(asyncParams, parentId, true, assignedTabId, customTabIntent.intent);
+        customTabIntent.launchUrl(mActivity, Uri.parse(asyncParams.getLoadUrlParams().getUrl()));
+    }
+}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index a61c550..9f958ec 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -713,6 +713,8 @@
   "java/src/org/chromium/chrome/browser/omnibox/AnswersImage.java",
   "java/src/org/chromium/chrome/browser/omnibox/AutocompleteController.java",
   "java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java",
+  "java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModel.java",
+  "java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModelBase.java",
   "java/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelper.java",
   "java/src/org/chromium/chrome/browser/omnibox/LocationBar.java",
   "java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java",
@@ -1210,6 +1212,7 @@
   "java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java",
   "java/src/org/chromium/chrome/browser/webapps/WebappManagedActivity.java",
   "java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java",
+  "java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java",
   "java/src/org/chromium/chrome/browser/webapps/WebappUrlBar.java",
   "java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java",
   "java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java
index db3903b..12bcd77 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java
@@ -186,6 +186,48 @@
         Assert.assertEquals("Invalid return value.", '"' + promptText + '"', resultString);
     }
 
+    /**
+     * Verifies that message content in a dialog is only focusable if the message itself is long
+     * enough to require scrolling.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Browser", "Main"})
+    public void testAlertModalDialogMessageFocus()
+            throws InterruptedException, TimeoutException, ExecutionException {
+        assertScrollViewFocusabilityInAlertDialog("alert('Short message!');", false);
+
+        assertScrollViewFocusabilityInAlertDialog(
+                "alert(new Array(200).join('Long message!'));", true);
+    }
+
+    private void assertScrollViewFocusabilityInAlertDialog(
+            final String jsAlertScript, final boolean expectedFocusability)
+            throws InterruptedException, TimeoutException, ExecutionException {
+        final OnEvaluateJavaScriptResultHelper scriptEvent =
+                executeJavaScriptAndWaitForDialog(jsAlertScript);
+
+        final JavascriptAppModalDialog jsDialog = getCurrentDialog();
+        Assert.assertNotNull("No dialog showing.", jsDialog);
+
+        final String errorMessage =
+                "Scroll view focusability was incorrect. Expected: " + expectedFocusability;
+
+        CriteriaHelper.pollUiThread(new Criteria(errorMessage) {
+            @Override
+            public boolean isSatisfied() {
+                return jsDialog.getDialogForTest()
+                               .findViewById(R.id.js_modal_dialog_scroll_view)
+                               .isFocusable()
+                        == expectedFocusability;
+            }
+        });
+
+        clickOk(jsDialog);
+        Assert.assertTrue("JavaScript execution should continue after closing prompt.",
+                scriptEvent.waitUntilHasValue());
+    }
+
     private static class TapGestureStateListener extends GestureStateListener {
         private CallbackHelper mCallbackHelper = new CallbackHelper();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index ba24728..e33d26f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -93,7 +93,7 @@
         }
 
         @Override
-        protected void scheduleUpdate(WebApkInfo fetchedInfo, String primaryIconUrl,
+        protected void buildProtoAndScheduleUpdate(final WebApkInfo info, String primaryIconUrl,
                 String badgeIconUrl, boolean isManifestStale) {
             mNeedsUpdate = true;
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
index a105310..5845ad5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
@@ -21,7 +21,6 @@
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -36,13 +35,9 @@
 import org.chromium.chrome.browser.tab.TabIdManager;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.MultiActivityTestRule;
-import org.chromium.chrome.test.util.ActivityUtils;
 import org.chromium.chrome.test.util.ApplicationTestUtils;
-import org.chromium.chrome.test.util.browser.TabLoadObserver;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
-import org.chromium.content.browser.test.util.JavaScriptUtils;
-import org.chromium.content.browser.test.util.TouchCommon;
 import org.chromium.content_public.common.ScreenOrientationValues;
 import org.chromium.net.test.EmbeddedTestServer;
 
@@ -273,77 +268,6 @@
     }
 
     /**
-     * Tests that WebappActivities handle window.open() properly in tabbed mode.
-     */
-    @Test
-    @MediumTest
-    @Feature({"Webapps"})
-    public void testWebappHandlesWindowOpenInTabbedMode() throws Exception {
-        triggerWindowOpenAndWaitForLoad(ChromeTabbedActivity.class, getOnClickLinkUrl(), true);
-    }
-
-    /**
-     * Tests that WebappActivities handle suppressed window.open() properly in tabbed mode.
-     */
-    @Test
-    @MediumTest
-    @Feature({"Webapps"})
-    public void testWebappHandlesSuppressedWindowOpenInTabbedMode() throws Exception {
-        triggerWindowOpenAndWaitForLoad(
-                ChromeTabbedActivity.class, getHrefNoReferrerLinkUrl(), false);
-    }
-
-    private <T extends ChromeActivity> void triggerWindowOpenAndWaitForLoad(
-            Class<T> classToWaitFor, String linkHtml, boolean checkContents) throws Exception {
-        final WebappActivity firstActivity =
-                startWebappActivity(WEBAPP_1_ID, WEBAPP_1_URL, WEBAPP_1_TITLE, WEBAPP_ICON);
-        final int firstWebappId = firstActivity.getActivityTab().getId();
-
-        // Load up the test page.
-        new TabLoadObserver(firstActivity.getActivityTab()).fullyLoadUrl(linkHtml);
-
-        // Do a plain click to make the link open in the main browser via a window.open().
-        // If the window is opened successfully, javascript on the first page triggers and changes
-        // its URL as a signal for this test.
-        Runnable fgTrigger = new Runnable() {
-            @Override
-            public void run() {
-                ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-                    @Override
-                    public void run() {
-                        View view = firstActivity.findViewById(android.R.id.content).getRootView();
-                        TouchCommon.singleClickView(view);
-                    }
-                });
-            }
-        };
-        ChromeActivity secondActivity = ActivityUtils.waitForActivity(
-                InstrumentationRegistry.getInstrumentation(), classToWaitFor, fgTrigger);
-        mTestRule.waitForFullLoad(secondActivity, "The Google");
-        if (checkContents) {
-            Assert.assertEquals("New WebContents was not created", "SUCCESS",
-                    firstActivity.getActivityTab().getTitle());
-        }
-        Assert.assertNotSame("Wrong Activity in foreground", firstActivity,
-                ApplicationStatus.getLastTrackedFocusedActivity());
-
-        // Close the child window to kick the user back to the WebappActivity.
-        JavaScriptUtils.executeJavaScript(
-                secondActivity.getActivityTab().getWebContents(), "window.close()");
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                Activity lastActivity = ApplicationStatus.getLastTrackedFocusedActivity();
-                if (!isWebappActivityReady(lastActivity)) return false;
-
-                WebappActivity webappActivity = (WebappActivity) lastActivity;
-                return webappActivity.getActivityTab().getId() == firstWebappId;
-            }
-        });
-        ApplicationTestUtils.waitUntilChromeInForeground();
-    }
-
-    /**
      * Starts a WebappActivity for the given data and waits for it to be initialized.  We can't use
      * ActivityUtils.waitForActivity() because of the way WebappActivity is instanced on pre-L
      * devices.
@@ -372,49 +296,4 @@
 
         return true;
     }
-
-    /** Defines one gigantic link spanning the whole page that creates a new
-     *  window with chrome/test/data/android/google.html. Disallowing a referrer from being
-     *  sent triggers another codepath.
-     */
-    private String getHrefNoReferrerLinkUrl() {
-        return UrlUtils.encodeHtmlDataUri("<html>"
-                + "  <head>"
-                + "    <title>href no referrer link page</title>"
-                + "    <meta name='viewport'"
-                + "        content='width=device-width initial-scale=0.5, maximum-scale=0.5'>"
-                + "    <style>"
-                + "      body {margin: 0em;} div {width: 100%; height: 100%; background: #011684;}"
-                + "    </style>"
-                + "  </head>"
-                + "  <body>"
-                + "    <a href='" + mTestServer.getURL("/chrome/test/data/android/google.html")
-                + "' target='_blank' rel='noreferrer'><div></div></a>"
-                + "  </body>");
-    }
-
-    /** Returns a URL where clicking the body triggers a window.open() call to open
-     * chrome/test/data/android/google.html. */
-    private String getOnClickLinkUrl() {
-        return UrlUtils.encodeHtmlDataUri("<html>"
-                + "  <head>"
-                + "    <title>window.open page</title>"
-                + "    <meta name='viewport'"
-                + "        content='width=device-width initial-scale=0.5, maximum-scale=0.5'>"
-                + "    <style>"
-                + "      body {margin: 0em;} div {width: 100%; height: 100%; background: #011684;}"
-                + "    </style>"
-                + "    <script>"
-                + "      function openNewWindow() {"
-                + "        var site = window.open('"
-                + mTestServer.getURL("/chrome/test/data/android/google.html") + "');"
-                + "        document.title = site ? 'SUCCESS' : 'FAILURE';"
-                + "      }"
-                + "    </script>"
-                + "  </head>"
-                + "  <body id='body'>"
-                + "    <div onclick='openNewWindow()'></div>"
-                + "  </body>"
-                + "</html>");
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
index 25c476d3..6d234b8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
@@ -4,8 +4,12 @@
 
 package org.chromium.chrome.browser.webapps;
 
+import android.app.ActivityManager;
+import android.app.ActivityManager.AppTask;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Color;
+import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -17,7 +21,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
@@ -28,7 +31,7 @@
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.contextmenu.ContextMenuUtils;
-import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content.browser.test.util.DOMUtils;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.ui.base.PageTransition;
 
@@ -64,18 +67,12 @@
     @Test
     @SmallTest
     @Feature({"Webapps"})
-    public void testOffOriginNavigationUsingLinkAndNoWebappThemeColor() throws Exception {
+    public void testRegularLinkOffOriginInCctNoWebappThemeColor() throws Exception {
         runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent());
-
-        // Not using #loadUrl, as it expects the URL to load in the activity under test,
-        // which is not happening here.
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mActivityTestRule.getActivity().getActivityTab().loadUrl(
-                        new LoadUrlParams(OFF_ORIGIN_URL, PageTransition.LINK));
-            }
-        });
+        assertNumberOfTasksInRecents("We should start with one Android task", 1);
+        addAnchor("testId", OFF_ORIGIN_URL, "_self");
+        DOMUtils.clickNode(
+                mActivityTestRule.getActivity().getActivityTab().getContentViewCore(), "testId");
 
         CustomTabActivity customTab = assertCustomTabActivityLaunchedForOffOriginUrl();
 
@@ -84,14 +81,17 @@
                 ApiCompatibilityUtils.getColor(
                         customTab.getResources(), R.color.default_primary_color),
                 customTab.getToolbarManager().getPrimaryColor());
+        assertNumberOfTasksInRecents(
+                "Link with target=\"_self\" should stay in the same Android task.", 1);
     }
 
     @Test
     @SmallTest
     @Feature({"Webapps"})
-    public void testOffOriginNavigationUsingJavaScriptAndWebappThemeColor() throws Exception {
+    public void testWindowTopLocationOffOriginInCctAndWebappThemeColor() throws Exception {
         runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent().putExtra(
                 ShortcutHelper.EXTRA_THEME_COLOR, (long) Color.CYAN));
+        assertNumberOfTasksInRecents("We should start with one Android task", 1);
 
         mActivityTestRule.runJavaScriptCodeInCurrentTab(
                 String.format("window.top.location = '%s'", OFF_ORIGIN_URL));
@@ -100,16 +100,58 @@
 
         Assert.assertEquals("CCT Toolbar should use the theme color of a webapp", Color.CYAN,
                 customTab.getToolbarManager().getPrimaryColor());
+        assertNumberOfTasksInRecents(
+                "Executing window.top.location = url; should stay in the same Android task.", 1);
     }
 
-    private CustomTabActivity assertCustomTabActivityLaunchedForOffOriginUrl() {
-        CustomTabActivity customTab = activityListener.waitFor(CustomTabActivity.class);
+    @Test
+    @SmallTest
+    @Feature({"Webapps"})
+    public void testNewTabLinkOpensInCct() throws Exception {
+        runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent().putExtra(
+                ShortcutHelper.EXTRA_THEME_COLOR, (long) Color.CYAN));
+        assertNumberOfTasksInRecents("We should start with one Android task", 1);
+        addAnchor("testId", OFF_ORIGIN_URL, "_blank");
+        DOMUtils.clickNode(
+                mActivityTestRule.getActivity().getActivityTab().getContentViewCore(), "testId");
+        CustomTabActivity customTab = assertCustomTabActivityLaunchedForOffOriginUrl();
+        Assert.assertEquals(
+                "CCT Toolbar should use default primary color even if webapp has theme color",
+                ApiCompatibilityUtils.getColor(
+                        customTab.getResources(), R.color.default_primary_color),
+                customTab.getToolbarManager().getPrimaryColor());
+        assertNumberOfTasksInRecents(
+                "Link with target=\"_blank\" should open in a new Android task.", 2);
+    }
 
-        mActivityTestRule.waitUntilIdle(customTab);
-        // Dropping the TLD as Google can redirect to a local site, so this could fail outside US.
-        Assert.assertTrue(customTab.getActivityTab().getUrl().startsWith("https://www.google."));
+    @Test
+    @SmallTest
+    @Feature({"Webapps"})
+    public void testWindowOpenInCct() throws Exception {
+        runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent());
+        assertNumberOfTasksInRecents("We should start with one Android task", 1);
+        // Executing window.open() through a click on a link,
+        // as it needs user gesture to avoid Chrome blocking it as a popup.
+        mActivityTestRule.runJavaScriptCodeInCurrentTab(
+                String.format("var aTag = document.createElement('testId');"
+                                + "aTag.id = 'testId';"
+                                + "aTag.innerHTML = 'Click Me!';"
+                                + "aTag.onclick = function() {"
+                                + "  window.open('%s');"
+                                + "  return false;"
+                                + "};"
+                                + "document.body.appendChild(aTag);",
+                        OFF_ORIGIN_URL));
+        DOMUtils.clickNode(
+                mActivityTestRule.getActivity().getActivityTab().getContentViewCore(), "testId");
 
-        return customTab;
+        CustomTabActivity customTab = assertCustomTabActivityLaunchedForOffOriginUrl();
+        Assert.assertEquals("CCT Toolbar should use default primary color",
+                ApiCompatibilityUtils.getColor(
+                        customTab.getResources(), R.color.default_primary_color),
+                customTab.getToolbarManager().getPrimaryColor());
+        assertNumberOfTasksInRecents(
+                "Executing window.open(url) should open a new Android task.", 2);
     }
 
     @Test
@@ -138,11 +180,7 @@
         FirstRunStatus.setFirstRunFlowComplete(true);
         runWebappActivityAndWaitForIdle(mActivityTestRule.createIntent());
 
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("var aTag = document.createElement('a');"
-                + "aTag.id = 'myTestAnchorId';"
-                + "aTag.setAttribute('href','https://www.google.com/');"
-                + "aTag.innerHTML = 'Click Me!';"
-                + "document.body.appendChild(aTag);");
+        addAnchor("myTestAnchorId", OFF_ORIGIN_URL, "_self");
 
         ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(),
                 null /* activity to check for focus after click */,
@@ -163,4 +201,45 @@
         mActivityTestRule.waitUntilSplashscreenHides();
         mActivityTestRule.waitUntilIdle();
     }
+
+    private CustomTabActivity assertCustomTabActivityLaunchedForOffOriginUrl() {
+        CustomTabActivity customTab = activityListener.waitFor(CustomTabActivity.class);
+
+        mActivityTestRule.waitUntilIdle(customTab);
+        // Dropping the TLD as Google can redirect to a local site, so this could fail outside US.
+        Assert.assertTrue(customTab.getActivityTab().getUrl().contains("https://www.google."));
+
+        return customTab;
+    }
+
+    private void addAnchor(String id, String url, String target) throws Exception {
+        mActivityTestRule.runJavaScriptCodeInCurrentTab(
+                String.format("var aTag = document.createElement('a');"
+                                + "aTag.id = '%s';"
+                                + "aTag.setAttribute('href','%s');"
+                                + "aTag.setAttribute('target','%s');"
+                                + "aTag.innerHTML = 'Click Me!';"
+                                + "document.body.appendChild(aTag);",
+                        id, url, target));
+    }
+
+    private void assertNumberOfTasksInRecents(String message, int expectedNumberOfTasks) {
+        // We only have API to check this since Lollipop.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
+
+        ActivityManager activityManager =
+                (ActivityManager) mActivityTestRule.getActivity().getSystemService(
+                        Context.ACTIVITY_SERVICE);
+
+        int count = 0;
+        for (AppTask task : activityManager.getAppTasks()) {
+            if ((task.getTaskInfo().baseIntent.getFlags()
+                        & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                    == 0) {
+                count++;
+            }
+        }
+
+        Assert.assertEquals(message, expectedNumberOfTasks, count);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
index d15846f..27284cc 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
@@ -107,8 +107,10 @@
         mAutocomplete.setIgnoreTextChangesForAutocomplete(false);
         // User types "h".
         assertTrue(mInputConnection.commitText("h", 1));
+        mInOrder.verify(mEmbedder).onAutocompleteTextStateChanged(false, true);
         // User types "hello".
         assertTrue(mInputConnection.commitText("ello", 1));
+        mInOrder.verify(mEmbedder).onAutocompleteTextStateChanged(false, true);
         mInOrder.verifyNoMoreInteractions();
         // The controller kicks in.
         mAutocomplete.setAutocompleteText("hello", " world");
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 3dbb28f..b3717f5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -144,15 +144,14 @@
         }
 
         @Override
-        protected void scheduleUpdate(WebApkInfo info, String primaryIconUrl, String badgeIconUrl,
-                boolean isManifestStale) {
+        protected void buildProtoAndScheduleUpdate(WebApkInfo info, String primaryIconUrl,
+                String badgeIconUrl, boolean isManifestStale) {
             mUpdateName = info.name();
-            super.scheduleUpdate(info, primaryIconUrl, badgeIconUrl, isManifestStale);
+            scheduleUpdate(info, new byte[0]);
         }
 
         @Override
-        protected void updateAsyncImpl(WebApkInfo info, String primaryIconUrl, String badgeIconUrl,
-                boolean isManifestStale) {
+        protected void updateAsyncImpl(WebApkInfo info, byte[] serializedProto) {
             mUpdateRequested = true;
         }
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b1747d4..f094008 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1162,6 +1162,8 @@
     "resource_delegate_mac.mm",
     "resources_util.cc",
     "resources_util.h",
+    "safe_browsing/browser_url_loader_throttle.cc",
+    "safe_browsing/browser_url_loader_throttle.h",
     "safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc",
     "safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h",
     "safe_browsing/chrome_cleaner/chrome_cleaner_fetcher_win.cc",
@@ -1182,6 +1184,8 @@
     "safe_browsing/mojo_safe_browsing_impl.h",
     "safe_browsing/safe_browsing_tab_observer.cc",
     "safe_browsing/safe_browsing_tab_observer.h",
+    "safe_browsing/safe_browsing_url_checker_impl.cc",
+    "safe_browsing/safe_browsing_url_checker_impl.h",
     "safe_search_api/safe_search_url_checker.cc",
     "safe_search_api/safe_search_url_checker.h",
     "search/iframe_source.cc",
@@ -2208,12 +2212,15 @@
       "android/offline_pages/request_coordinator_factory.h",
       "offline_pages/background_loader_offliner.cc",
       "offline_pages/background_loader_offliner.h",
+      "offline_pages/offliner_user_data.cc",
+      "offline_pages/offliner_user_data.h",
       "offline_pages/prefetch/offline_metrics_collector_impl.cc",
       "offline_pages/prefetch/offline_metrics_collector_impl.h",
       "offline_pages/prefetch/prefetch_instance_id_proxy.cc",
       "offline_pages/prefetch/prefetch_instance_id_proxy.h",
       "offline_pages/prefetch/prefetch_service_factory.cc",
       "offline_pages/prefetch/prefetch_service_factory.h",
+      "offline_pages/resource_loading_observer.h",
     ]
     if (is_android) {
       sources += [
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b376a1c..d302cd55 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -389,13 +389,6 @@
      switches::kForceGpuRasterization, ""},
 };
 
-const FeatureEntry::Choice kColorCorrectRenderingChoices[] = {
-    {flags_ui::kGenericExperimentChoiceDefault, "", ""},
-    {flags_ui::kGenericExperimentChoiceEnabled,
-     switches::kEnableColorCorrectRendering, ""},
-    {flags_ui::kGenericExperimentChoiceDisabled, "", ""},
-};
-
 const FeatureEntry::Choice kEnableWebGL2Choices[] = {
     {flags_ui::kGenericExperimentChoiceDefault, "", ""},
     {flags_ui::kGenericExperimentChoiceEnabled, switches::kEnableES3APIs, ""},
@@ -1397,9 +1390,9 @@
          proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery)},
 #endif  // OS_CHROMEOS
 #if defined(USE_ASH)
-    {"ash-disable-night-light", flag_descriptions::kDisableNightLightName,
-     flag_descriptions::kDisableNightLightDescription, kOsAll,
-     SINGLE_DISABLE_VALUE_TYPE(ash::switches::kAshDisableNightLight)},
+    {"ash-enable-night-light", flag_descriptions::kEnableNightLightName,
+     flag_descriptions::kEnableNightLightDescription, kOsAll,
+     SINGLE_VALUE_TYPE(ash::switches::kAshEnableNightLight)},
     {"show-touch-hud", flag_descriptions::kShowTouchHudName,
      flag_descriptions::kShowTouchHudDescription, kOsAll,
      SINGLE_VALUE_TYPE(ash::switches::kAshTouchHud)},
@@ -2944,7 +2937,7 @@
     {"enable-color-correct-rendering",
      flag_descriptions::kColorCorrectRenderingName,
      flag_descriptions::kColorCorrectRenderingDescription, kOsAll,
-     MULTI_VALUE_TYPE(kColorCorrectRenderingChoices)},
+     FEATURE_VALUE_TYPE(features::kColorCorrectRendering)},
 
 #if defined(OS_CHROMEOS)
     {"quick-unlock-pin-signin", flag_descriptions::kQuickUnlockPinSignin,
diff --git a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
index c46efc1..c81115e2 100644
--- a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
+++ b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
@@ -12,6 +12,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/strings/string16.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "components/security_state/core/security_state.h"
@@ -27,10 +28,10 @@
 
 void DeleteFileOnFileThread(const base::FilePath& file_path,
                             const base::Closure& callback) {
-  content::BrowserThread::PostTaskAndReply(
-      content::BrowserThread::FILE, FROM_HERE,
-      base::Bind(base::IgnoreResult(&base::DeleteFile), file_path,
-                 false /* recursive */),
+  base::PostTaskWithTraitsAndReply(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+      base::BindOnce(base::IgnoreResult(&base::DeleteFile), file_path,
+                     false /* recursive */),
       callback);
 }
 }  // namespace
diff --git a/chrome/browser/android/usb/web_usb_chooser_service_android.cc b/chrome/browser/android/usb/web_usb_chooser_service_android.cc
index d4618a0..88148a82 100644
--- a/chrome/browser/android/usb/web_usb_chooser_service_android.cc
+++ b/chrome/browser/android/usb/web_usb_chooser_service_android.cc
@@ -20,10 +20,10 @@
 WebUsbChooserServiceAndroid::~WebUsbChooserServiceAndroid() {}
 
 void WebUsbChooserServiceAndroid::GetPermission(
-    const std::vector<device::UsbDeviceFilter>& device_filters,
+    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
     const GetPermissionCallback& callback) {
   usb_chooser_dialog_android_.push_back(
-      base::MakeUnique<UsbChooserDialogAndroid>(device_filters,
+      base::MakeUnique<UsbChooserDialogAndroid>(std::move(device_filters),
                                                 render_frame_host_, callback));
 }
 
diff --git a/chrome/browser/android/usb/web_usb_chooser_service_android.h b/chrome/browser/android/usb/web_usb_chooser_service_android.h
index db2df4d9..d6919ba 100644
--- a/chrome/browser/android/usb/web_usb_chooser_service_android.h
+++ b/chrome/browser/android/usb/web_usb_chooser_service_android.h
@@ -30,8 +30,9 @@
   ~WebUsbChooserServiceAndroid() override;
 
   // device::usb::ChooserService:
-  void GetPermission(const std::vector<device::UsbDeviceFilter>& device_filters,
-                     const GetPermissionCallback& callback) override;
+  void GetPermission(
+      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
+      const GetPermissionCallback& callback) override;
 
   void Bind(mojo::InterfaceRequest<device::mojom::UsbChooserService> request);
 
diff --git a/chrome/browser/android/webapk/webapk_install_service.cc b/chrome/browser/android/webapk/webapk_install_service.cc
index 2c027b2..22dcbbeae 100644
--- a/chrome/browser/android/webapk/webapk_install_service.cc
+++ b/chrome/browser/android/webapk/webapk_install_service.cc
@@ -42,24 +42,14 @@
 }
 
 void WebApkInstallService::UpdateAsync(
-    const ShortcutInfo& shortcut_info,
-    const SkBitmap& primary_icon,
-    const SkBitmap& badge_icon,
     const std::string& webapk_package,
-    int webapk_version,
-    const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-    bool is_manifest_stale,
+    const GURL& start_url,
+    const base::string16& short_name,
+    std::unique_ptr<std::vector<uint8_t>> serialized_proto,
     const FinishCallback& finish_callback) {
-  DCHECK(!IsInstallInProgress(shortcut_info.manifest_url));
-
-  installs_.insert(shortcut_info.manifest_url);
-
-  WebApkInstaller::UpdateAsync(
-      browser_context_, shortcut_info, primary_icon, badge_icon, webapk_package,
-      webapk_version, icon_url_to_murmur2_hash, is_manifest_stale,
-      base::Bind(&WebApkInstallService::OnFinishedInstall,
-                 weak_ptr_factory_.GetWeakPtr(), shortcut_info.manifest_url,
-                 finish_callback));
+  WebApkInstaller::UpdateAsync(browser_context_, webapk_package, start_url,
+                               short_name, std::move(serialized_proto),
+                               finish_callback);
 }
 
 void WebApkInstallService::OnFinishedInstall(
diff --git a/chrome/browser/android/webapk/webapk_install_service.h b/chrome/browser/android/webapk/webapk_install_service.h
index 476e9c7..6134f08b 100644
--- a/chrome/browser/android/webapk/webapk_install_service.h
+++ b/chrome/browser/android/webapk/webapk_install_service.h
@@ -6,12 +6,15 @@
 #define CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_INSTALL_SERVICE_H_
 
 #include <map>
+#include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "url/gurl.h"
 
@@ -63,17 +66,13 @@
                     const FinishCallback& finish_callback);
 
   // Talks to the Chrome WebAPK server to update a WebAPK on the server and to
-  // the Google Play server to install the downloaded WebAPK. Calls |callback|
-  // after the request to install the WebAPK is sent to Google Play.
-  void UpdateAsync(
-      const ShortcutInfo& shortcut_info,
-      const SkBitmap& primary_icon,
-      const SkBitmap& badge_icon,
-      const std::string& webapk_package,
-      int webapk_version,
-      const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-      bool is_manifest_stale,
-      const FinishCallback& finish_callback);
+  // the Google Play server to install the downloaded WebAPK. Calls
+  // |finish_callback| once the update completed or failed.
+  void UpdateAsync(const std::string& webapk_package,
+                   const GURL& start_url,
+                   const base::string16& short_name,
+                   std::unique_ptr<std::vector<uint8_t>> serialized_proto,
+                   const FinishCallback& finish_callback);
 
  private:
   // Called once the install/update completed or failed.
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index 31966bd..663093d 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -4,12 +4,15 @@
 
 #include "chrome/browser/android/webapk/webapk_installer.h"
 
+#include <utility>
+
 #include "base/android/build_info.h"
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/android/path_utils.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -108,7 +111,7 @@
 
 // Populates webapk::WebApk and returns it.
 // Must be called on a worker thread because it encodes an SkBitmap.
-std::unique_ptr<webapk::WebApk> BuildProtoInBackground(
+std::unique_ptr<std::vector<uint8_t>> BuildProtoInBackground(
     const ShortcutInfo& shortcut_info,
     const SkBitmap& primary_icon,
     const SkBitmap& badge_icon,
@@ -171,7 +174,12 @@
     image->set_hash(entry.second);
   }
 
-  return webapk;
+  size_t serialized_size = webapk->ByteSize();
+  std::unique_ptr<std::vector<uint8_t>> serialized_proto =
+      base::MakeUnique<std::vector<uint8_t>>();
+  serialized_proto->resize(serialized_size);
+  webapk->SerializeToArray(serialized_proto->data(), serialized_size);
+  return serialized_proto;
 }
 
 // Returns task runner for running background tasks.
@@ -196,48 +204,44 @@
                                    const SkBitmap& badge_icon,
                                    const FinishCallback& finish_callback) {
   // The installer will delete itself when it is done.
-  WebApkInstaller* installer =
-      new WebApkInstaller(context, shortcut_info, primary_icon, badge_icon);
-  installer->InstallAsync(finish_callback);
+  WebApkInstaller* installer = new WebApkInstaller(context);
+  installer->InstallAsync(shortcut_info, primary_icon, badge_icon,
+                          finish_callback);
 }
 
 // static
 void WebApkInstaller::UpdateAsync(
     content::BrowserContext* context,
-    const ShortcutInfo& shortcut_info,
-    const SkBitmap& primary_icon,
-    const SkBitmap& badge_icon,
     const std::string& webapk_package,
-    int webapk_version,
-    const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-    bool is_manifest_stale,
+    const GURL& start_url,
+    const base::string16& short_name,
+    std::unique_ptr<std::vector<uint8_t>> serialized_proto,
     const FinishCallback& finish_callback) {
   // The installer will delete itself when it is done.
-  WebApkInstaller* installer = new WebApkInstaller(
-      context, shortcut_info, primary_icon, badge_icon);
-  installer->UpdateAsync(webapk_package, webapk_version,
-                         icon_url_to_murmur2_hash, is_manifest_stale,
-                         finish_callback);
+  WebApkInstaller* installer = new WebApkInstaller(context);
+  installer->UpdateAsync(webapk_package, start_url, short_name,
+                         std::move(serialized_proto), finish_callback);
 }
 
-// staic
-void WebApkInstaller::InstallAsyncForTesting(
-    WebApkInstaller* installer,
-    const FinishCallback& finish_callback) {
-  installer->InstallAsync(finish_callback);
+// static
+void WebApkInstaller::InstallAsyncForTesting(WebApkInstaller* installer,
+                                             const ShortcutInfo& shortcut_info,
+                                             const SkBitmap& primary_icon,
+                                             const SkBitmap& badge_icon,
+                                             const FinishCallback& callback) {
+  installer->InstallAsync(shortcut_info, primary_icon, badge_icon, callback);
 }
 
 // static
 void WebApkInstaller::UpdateAsyncForTesting(
     WebApkInstaller* installer,
     const std::string& webapk_package,
-    int webapk_version,
-    const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-    bool is_manifest_stale,
+    const GURL& start_url,
+    const base::string16& short_name,
+    std::unique_ptr<std::vector<uint8_t>> serialized_proto,
     const FinishCallback& finish_callback) {
-  installer->UpdateAsync(webapk_package, webapk_version,
-                         icon_url_to_murmur2_hash, is_manifest_stale,
-                         finish_callback);
+  installer->UpdateAsync(webapk_package, start_url, short_name,
+                         std::move(serialized_proto), finish_callback);
 }
 
 void WebApkInstaller::SetTimeoutMs(int timeout_ms) {
@@ -260,7 +264,8 @@
     const std::string& version,
     const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
     bool is_manifest_stale,
-    const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback) {
+    const base::Callback<void(std::unique_ptr<std::vector<uint8_t>>)>&
+        callback) {
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
       base::Bind(&BuildProtoInBackground, shortcut_info, primary_icon,
@@ -283,17 +288,17 @@
   base::android::ScopedJavaLocalRef<jstring> java_webapk_package =
       base::android::ConvertUTF8ToJavaString(env, webapk_package_);
   base::android::ScopedJavaLocalRef<jstring> java_title =
-      base::android::ConvertUTF16ToJavaString(env, shortcut_info_.user_title);
+      base::android::ConvertUTF16ToJavaString(env, short_name_);
   base::android::ScopedJavaLocalRef<jstring> java_token =
       base::android::ConvertUTF8ToJavaString(env, token);
   base::android::ScopedJavaLocalRef<jstring> java_url =
-      base::android::ConvertUTF8ToJavaString(env, shortcut_info_.url.spec());
+      base::android::ConvertUTF8ToJavaString(env, start_url_.spec());
 
   if (task_type_ == WebApkInstaller::INSTALL) {
     webapk::TrackRequestTokenDuration(install_duration_timer_->Elapsed());
-    Java_WebApkInstaller_installWebApkAsync(env, java_ref_, java_webapk_package,
-                                            version, java_title, java_token,
-                                            java_url, shortcut_info_.source);
+    Java_WebApkInstaller_installWebApkAsync(
+        env, java_ref_, java_webapk_package, version, java_title, java_token,
+        java_url, install_shortcut_info_->source);
   } else {
     Java_WebApkInstaller_updateAsync(env, java_ref_, java_webapk_package,
                                      version, java_title, java_token, java_url);
@@ -317,15 +322,9 @@
   delete this;
 }
 
-WebApkInstaller::WebApkInstaller(content::BrowserContext* browser_context,
-                                 const ShortcutInfo& shortcut_info,
-                                 const SkBitmap& primary_icon,
-                                 const SkBitmap& badge_icon)
+WebApkInstaller::WebApkInstaller(content::BrowserContext* browser_context)
     : request_context_getter_(
           Profile::FromBrowserContext(browser_context)->GetRequestContext()),
-      shortcut_info_(shortcut_info),
-      primary_icon_(primary_icon),
-      badge_icon_(badge_icon),
       server_url_(GetServerUrl()),
       webapk_server_timeout_ms_(kWebApkDownloadUrlTimeoutMs),
       relax_updates_(false),
@@ -340,9 +339,17 @@
       Java_WebApkInstaller_create(env, reinterpret_cast<intptr_t>(this)));
 }
 
-void WebApkInstaller::InstallAsync(const FinishCallback& finish_callback) {
+void WebApkInstaller::InstallAsync(const ShortcutInfo& shortcut_info,
+                                   const SkBitmap& primary_icon,
+                                   const SkBitmap& badge_icon,
+                                   const FinishCallback& finish_callback) {
   install_duration_timer_.reset(new base::ElapsedTimer());
 
+  install_shortcut_info_.reset(new ShortcutInfo(shortcut_info));
+  install_primary_icon_ = primary_icon;
+  install_badge_icon_ = badge_icon;
+  start_url_ = shortcut_info.url;
+  short_name_ = shortcut_info.short_name;
   finish_callback_ = finish_callback;
   task_type_ = INSTALL;
 
@@ -355,26 +362,29 @@
   // We redownload the icon in order to take the Murmur2 hash. The redownload
   // should be fast because the icon should be in the HTTP cache.
   WebApkIconHasher::DownloadAndComputeMurmur2Hash(
-      request_context_getter_, shortcut_info_.best_primary_icon_url,
+      request_context_getter_, install_shortcut_info_->best_primary_icon_url,
       base::Bind(&WebApkInstaller::OnGotPrimaryIconMurmur2Hash,
                  weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WebApkInstaller::UpdateAsync(
     const std::string& webapk_package,
-    int webapk_version,
-    const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-    bool is_manifest_stale,
+    const GURL& start_url,
+    const base::string16& short_name,
+    std::unique_ptr<std::vector<uint8_t>> serialized_proto,
     const FinishCallback& finish_callback) {
   webapk_package_ = webapk_package;
+  start_url_ = start_url;
+  short_name_ = short_name;
   finish_callback_ = finish_callback;
   task_type_ = UPDATE;
 
-  BuildProto(shortcut_info_, primary_icon_, badge_icon_, webapk_package_,
-             std::to_string(webapk_version), icon_url_to_murmur2_hash,
-             is_manifest_stale,
-             base::Bind(&WebApkInstaller::SendRequest,
-                        weak_ptr_factory_.GetWeakPtr()));
+  if (!serialized_proto || serialized_proto->empty()) {
+    OnResult(WebApkInstallResult::FAILURE);
+    return;
+  }
+
+  SendRequest(std::move(serialized_proto));
 }
 
 void WebApkInstaller::OnURLFetchComplete(const net::URLFetcher* source) {
@@ -426,11 +436,11 @@
     return;
   }
 
-  if (!shortcut_info_.best_badge_icon_url.is_empty() &&
-      shortcut_info_.best_badge_icon_url !=
-          shortcut_info_.best_primary_icon_url) {
+  if (!install_shortcut_info_->best_badge_icon_url.is_empty() &&
+      install_shortcut_info_->best_badge_icon_url !=
+          install_shortcut_info_->best_primary_icon_url) {
     WebApkIconHasher::DownloadAndComputeMurmur2Hash(
-        request_context_getter_, shortcut_info_.best_badge_icon_url,
+        request_context_getter_, install_shortcut_info_->best_badge_icon_url,
         base::Bind(&WebApkInstaller::OnGotBadgeIconMurmur2Hash,
                    weak_ptr_factory_.GetWeakPtr(), true, primary_icon_hash));
   } else {
@@ -450,35 +460,36 @@
 
   // Maps icon URLs to Murmur2 hashes.
   std::map<std::string, std::string> icon_url_to_murmur2_hash;
-  for (const std::string& icon_url : shortcut_info_.icon_urls) {
-    if (icon_url == shortcut_info_.best_primary_icon_url.spec())
+  for (const std::string& icon_url : install_shortcut_info_->icon_urls) {
+    if (icon_url == install_shortcut_info_->best_primary_icon_url.spec())
       icon_url_to_murmur2_hash[icon_url] = primary_icon_hash;
-    else if (icon_url == shortcut_info_.best_badge_icon_url.spec())
+    else if (icon_url == install_shortcut_info_->best_badge_icon_url.spec())
       icon_url_to_murmur2_hash[icon_url] = badge_icon_hash;
     else
       icon_url_to_murmur2_hash[icon_url] = "";
   }
 
-  BuildProto(shortcut_info_, primary_icon_, badge_icon_, "" /* package_name */,
-             "" /* version */, icon_url_to_murmur2_hash,
-             false /* is_manifest_stale */,
+  BuildProto(*install_shortcut_info_, install_primary_icon_,
+             install_badge_icon_, "" /* package_name */, "" /* version */,
+             icon_url_to_murmur2_hash, false /* is_manifest_stale */,
              base::Bind(&WebApkInstaller::SendRequest,
                         weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WebApkInstaller::SendRequest(
-    std::unique_ptr<webapk::WebApk> request_proto) {
+    std::unique_ptr<std::vector<uint8_t>> serialized_proto) {
   timer_.Start(
       FROM_HERE, base::TimeDelta::FromMilliseconds(webapk_server_timeout_ms_),
       base::Bind(&WebApkInstaller::OnResult, weak_ptr_factory_.GetWeakPtr(),
                  WebApkInstallResult::FAILURE));
 
+  std::string serialized_proto_string(serialized_proto->begin(),
+                                      serialized_proto->end());
+
   url_fetcher_ =
       net::URLFetcher::Create(server_url_, net::URLFetcher::POST, this);
   url_fetcher_->SetRequestContext(request_context_getter_);
-  std::string serialized_request;
-  request_proto->SerializeToString(&serialized_request);
-  url_fetcher_->SetUploadData(kProtoMimeType, serialized_request);
+  url_fetcher_->SetUploadData(kProtoMimeType, serialized_proto_string);
   url_fetcher_->SetLoadFlags(
       net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SEND_COOKIES |
       net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA);
diff --git a/chrome/browser/android/webapk/webapk_installer.h b/chrome/browser/android/webapk/webapk_installer.h
index 807b24b4..2c21121a 100644
--- a/chrome/browser/android/webapk/webapk_installer.h
+++ b/chrome/browser/android/webapk/webapk_installer.h
@@ -8,11 +8,13 @@
 #include <jni.h>
 #include <map>
 #include <memory>
+#include <vector>
 
 #include "base/android/scoped_java_ref.h"
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/android/shortcut_info.h"
 #include "chrome/browser/android/webapk/webapk_install_service.h"
@@ -28,11 +30,7 @@
 class BrowserContext;
 }
 
-namespace webapk {
-class WebApk;
-}
-
-// Talks to Chrome WebAPK server to download metadata about a WebApk and issue
+// Talks to Chrome WebAPK server to download metadata about a WebAPK and issue
 // a request for it to be installed. The native WebApkInstaller owns the Java
 // WebApkInstaller counterpart.
 class WebApkInstaller : public net::URLFetcherDelegate {
@@ -55,18 +53,18 @@
   // APK to be installed. Calls |callback| once the install completed or failed.
   static void UpdateAsync(
       content::BrowserContext* context,
-      const ShortcutInfo& shortcut_info,
-      const SkBitmap& primary_icon,
-      const SkBitmap& badge_icon,
       const std::string& webapk_package,
-      int webapk_version,
-      const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-      bool is_manifest_stale,
+      const GURL& start_url,
+      const base::string16& short_name,
+      std::unique_ptr<std::vector<uint8_t>> serialized_proto,
       const FinishCallback& callback);
 
   // Calls the private function |InstallAsync| for testing.
   // Should be used only for testing.
   static void InstallAsyncForTesting(WebApkInstaller* installer,
+                                     const ShortcutInfo& shortcut_info,
+                                     const SkBitmap& primary_icon,
+                                     const SkBitmap& badge_icon,
                                      const FinishCallback& finish_callback);
 
   // Calls the private function |UpdateAsync| for testing.
@@ -74,9 +72,9 @@
   static void UpdateAsyncForTesting(
       WebApkInstaller* installer,
       const std::string& webapk_package,
-      int webapk_version,
-      const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-      bool is_manifest_stale,
+      const GURL& start_url,
+      const base::string16& short_name,
+      std::unique_ptr<std::vector<uint8_t>> serialized_proto,
       const FinishCallback& callback);
 
   // Sets the timeout for the server requests.
@@ -97,16 +95,14 @@
       const std::string& version,
       const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
       bool is_manifest_stale,
-      const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback);
+      const base::Callback<void(std::unique_ptr<std::vector<uint8_t>>)>&
+          callback);
 
   // Registers JNI hooks.
   static bool Register(JNIEnv* env);
 
  protected:
-  WebApkInstaller(content::BrowserContext* browser_context,
-                  const ShortcutInfo& shortcut_info,
-                  const SkBitmap& primary_icon,
-                  const SkBitmap& badge_icon);
+  explicit WebApkInstaller(content::BrowserContext* browser_context);
 
   // Called when the package name of the WebAPK is available and the install
   // or update request should be issued.
@@ -130,17 +126,19 @@
   // Talks to the Chrome WebAPK server to generate a WebAPK on the server and to
   // Google Play to install the downloaded WebAPK. Calls |callback| once the
   // install completed or failed.
-  void InstallAsync(const FinishCallback& finish_callback);
+  void InstallAsync(const ShortcutInfo& shortcut_info,
+                    const SkBitmap& primary_icon,
+                    const SkBitmap& badge_icon,
+                    const FinishCallback& finish_callback);
 
   // Talks to the Chrome WebAPK server to update a WebAPK on the server and to
-  // the Google Play server to install the downloaded WebAPK. Calls |callback|
-  // after the request to install the WebAPK is sent to the Google Play server.
-  void UpdateAsync(
-      const std::string& webapk_package,
-      int webapk_version,
-      const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-      bool is_manifest_stale,
-      const FinishCallback& callback);
+  // the Google Play server to install the downloaded WebAPK. Calls
+  // |finish_callback| once the update completed or failed.
+  void UpdateAsync(const std::string& webapk_package,
+                   const GURL& start_url,
+                   const base::string16& short_name,
+                   const std::unique_ptr<std::vector<uint8_t>> serialized_proto,
+                   const FinishCallback& callback);
 
   // net::URLFetcherDelegate:
   void OnURLFetchComplete(const net::URLFetcher* source) override;
@@ -156,9 +154,9 @@
                                  const std::string& badge_icon_hash);
 
   // Sends a request to WebAPK server to create/update WebAPK. During a
-  // successful request the WebAPK server responds with the URL of the generated
-  // WebAPK.
-  void SendRequest(std::unique_ptr<webapk::WebApk> request_proto);
+  // successful request the WebAPK server responds with a token to send to
+  // Google Play.
+  void SendRequest(std::unique_ptr<std::vector<uint8_t>> serialized_proto);
 
   net::URLRequestContextGetter* request_context_getter_;
 
@@ -175,14 +173,13 @@
   // Callback to call once WebApkInstaller succeeds or fails.
   FinishCallback finish_callback_;
 
-  // Web Manifest info.
-  const ShortcutInfo shortcut_info_;
+  // Data for installs.
+  std::unique_ptr<ShortcutInfo> install_shortcut_info_;
+  SkBitmap install_primary_icon_;
+  SkBitmap install_badge_icon_;
 
-  // WebAPK primary icon.
-  const SkBitmap primary_icon_;
-
-  // WebAPK badge icon.
-  const SkBitmap badge_icon_;
+  GURL start_url_;
+  base::string16 short_name_;
 
   // WebAPK server URL.
   GURL server_url_;
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index 6b209d35..341c5ec 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/callback_forward.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
@@ -63,14 +64,8 @@
 // WebApkInstaller::InstallOrUpdateWebApkFromGooglePlay() are stubbed out.
 class TestWebApkInstaller : public WebApkInstaller {
  public:
-  TestWebApkInstaller(content::BrowserContext* browser_context,
-                      const ShortcutInfo& shortcut_info,
-                      const SkBitmap& primary_icon,
-                      const SkBitmap& badge_icon)
-      : WebApkInstaller(browser_context,
-                        shortcut_info,
-                        primary_icon,
-                        badge_icon) {}
+  explicit TestWebApkInstaller(content::BrowserContext* browser_context)
+      : WebApkInstaller(browser_context) {}
 
   void InstallOrUpdateWebApk(const std::string& package_name,
                              int version,
@@ -102,45 +97,48 @@
   ~WebApkInstallerRunner() {}
 
   void RunInstallWebApk() {
+    base::RunLoop run_loop;
+    on_completed_callback_ = run_loop.QuitClosure();
+
+    ShortcutInfo info((GURL()));
+    info.best_primary_icon_url = best_primary_icon_url_;
+    info.best_badge_icon_url = best_badge_icon_url_;
     WebApkInstaller::InstallAsyncForTesting(
-        CreateWebApkInstaller(), base::Bind(&WebApkInstallerRunner::OnCompleted,
-                                            base::Unretained(this)));
-    Run();
+        CreateWebApkInstaller(), info, SkBitmap(), SkBitmap(),
+        base::Bind(&WebApkInstallerRunner::OnCompleted,
+                   base::Unretained(this)));
+
+    run_loop.Run();
   }
 
-  void RunUpdateWebApk() {
-    const int kWebApkVersion = 1;
+  void RunUpdateWebApk(const std::string& serialized_proto) {
+    base::RunLoop run_loop;
+    on_completed_callback_ = run_loop.QuitClosure();
 
     std::map<std::string, std::string> icon_url_to_murmur2_hash{
         {best_primary_icon_url_.spec(), "0"},
         {best_badge_icon_url_.spec(), "0"}};
+    std::unique_ptr<std::vector<uint8_t>> serialized_proto_vector =
+        base::MakeUnique<std::vector<uint8_t>>(serialized_proto.begin(),
+                                               serialized_proto.end());
 
     WebApkInstaller::UpdateAsyncForTesting(
-        CreateWebApkInstaller(), kDownloadedWebApkPackageName, kWebApkVersion,
-        icon_url_to_murmur2_hash, false /* is_manifest_stale */,
+        CreateWebApkInstaller(), kDownloadedWebApkPackageName,
+        GURL() /* start_url */, base::string16() /* short_name */,
+        std::move(serialized_proto_vector),
         base::Bind(&WebApkInstallerRunner::OnCompleted,
                    base::Unretained(this)));
-    Run();
+
+    run_loop.Run();
   }
 
   WebApkInstaller* CreateWebApkInstaller() {
-    ShortcutInfo info(GURL::EmptyGURL());
-    info.best_primary_icon_url = best_primary_icon_url_;
-    info.best_badge_icon_url = best_badge_icon_url_;
-
     // WebApkInstaller owns itself.
-    WebApkInstaller* installer =
-        new TestWebApkInstaller(browser_context_, info, SkBitmap(), SkBitmap());
+    WebApkInstaller* installer = new TestWebApkInstaller(browser_context_);
     installer->SetTimeoutMs(100);
     return installer;
   }
 
-  void Run() {
-    base::RunLoop run_loop;
-    on_completed_callback_ = run_loop.QuitClosure();
-    run_loop.Run();
-  }
-
   WebApkInstallResult result() { return result_; }
 
  private:
@@ -216,8 +214,11 @@
 
  private:
   // Called when the |webapk_request_| is populated.
-  void OnBuiltWebApkProto(std::unique_ptr<webapk::WebApk> webapk) {
-    webapk_request_ = std::move(webapk);
+  void OnBuiltWebApkProto(
+      std::unique_ptr<std::vector<uint8_t>> serialized_proto) {
+    webapk_request_ = base::MakeUnique<webapk::WebApk>();
+    webapk_request_->ParseFromArray(serialized_proto->data(),
+                                    serialized_proto->size());
     on_completed_callback_.Run();
   }
 
@@ -386,7 +387,7 @@
 // Test update succeeding.
 TEST_F(WebApkInstallerTest, UpdateSuccess) {
   std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunUpdateWebApk();
+  runner->RunUpdateWebApk("non-empty");
   EXPECT_EQ(WebApkInstallResult::SUCCESS, runner->result());
 }
 
@@ -401,10 +402,17 @@
   SetWebApkResponseBuilder(base::Bind(&BuildValidWebApkResponse, ""));
 
   std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
-  runner->RunUpdateWebApk();
+  runner->RunUpdateWebApk("non-empty");
   EXPECT_EQ(WebApkInstallResult::SUCCESS, runner->result());
 }
 
+// Test that an update fails if an empty proto is passed to UpdateAsync().
+TEST_F(WebApkInstallerTest, UpdateFailsEmptyProto) {
+  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
+  runner->RunUpdateWebApk("");
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+}
+
 // When there is no Web Manifest available for a site, an empty
 // |best_primary_icon_url| and an empty |best_badge_icon_url| is used to build a
 // WebApk update request. Tests the request can be built properly.
diff --git a/chrome/browser/android/webapk/webapk_update_manager.cc b/chrome/browser/android/webapk/webapk_update_manager.cc
index 177d915..6728b2a 100644
--- a/chrome/browser/android/webapk/webapk_update_manager.cc
+++ b/chrome/browser/android/webapk/webapk_update_manager.cc
@@ -5,13 +5,20 @@
 #include "chrome/browser/android/webapk/webapk_update_manager.h"
 
 #include <jni.h>
+#include <memory>
+#include <vector>
 
+#include "base/android/callback_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
 #include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/android/shortcut_info.h"
 #include "chrome/browser/android/webapk/webapk_install_service.h"
+#include "chrome/browser/android/webapk/webapk_installer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "content/public/browser/browser_thread.h"
@@ -20,31 +27,39 @@
 #include "ui/gfx/android/java_bitmap.h"
 #include "url/gurl.h"
 
+using base::android::JavaRef;
 using base::android::JavaParamRef;
+using base::android::ScopedJavaGlobalRef;
+
+namespace {
+
+// Called with the serialized proto to send to the WebAPK server.
+void OnBuiltProto(const JavaRef<jobject>& java_callback,
+                  std::unique_ptr<std::vector<uint8_t>> proto) {
+  base::android::RunCallbackAndroid(java_callback, *proto);
+}
+
+// Called after the update either succeeds or fails.
+void OnUpdated(const JavaRef<jobject>& java_callback,
+               WebApkInstallResult result,
+               bool relax_updates,
+               const std::string& webapk_package) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_WebApkUpdateCallback_onResultFromNative(
+      env, java_callback, static_cast<int>(result), relax_updates);
+}
+
+}  // anonymous namespace
 
 // static
 bool WebApkUpdateManager::Register(JNIEnv* env) {
   return RegisterNativesImpl(env);
 }
 
-// static
-void WebApkUpdateManager::OnBuiltWebApk(const std::string& id,
-                                        WebApkInstallResult result,
-                                        bool relax_updates,
-                                        const std::string& webapk_package) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-
-  base::android::ScopedJavaLocalRef<jstring> java_id =
-      base::android::ConvertUTF8ToJavaString(env, id);
-  Java_WebApkUpdateManager_onBuiltWebApk(
-      env, java_id.obj(), static_cast<int>(result), relax_updates);
-}
-
 // static JNI method.
-static void UpdateAsync(
+static void BuildUpdateWebApkProto(
     JNIEnv* env,
     const JavaParamRef<jclass>& clazz,
-    const JavaParamRef<jstring>& java_id,
     const JavaParamRef<jstring>& java_start_url,
     const JavaParamRef<jstring>& java_scope,
     const JavaParamRef<jstring>& java_name,
@@ -62,16 +77,10 @@
     const JavaParamRef<jstring>& java_web_manifest_url,
     const JavaParamRef<jstring>& java_webapk_package,
     jint java_webapk_version,
-    jboolean java_is_manifest_stale) {
+    jboolean java_is_manifest_stale,
+    const JavaParamRef<jobject>& java_callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  Profile* profile = ProfileManager::GetLastUsedProfile();
-  if (profile == nullptr) {
-    NOTREACHED() << "Profile not found.";
-    return;
-  }
-
-  std::string id(ConvertJavaStringToUTF8(env, java_id));
   ShortcutInfo info(GURL(ConvertJavaStringToUTF8(env, java_start_url)));
   info.scope = GURL(ConvertJavaStringToUTF8(env, java_scope));
   info.name = ConvertJavaStringToUTF16(env, java_name);
@@ -112,17 +121,43 @@
   std::string webapk_package;
   ConvertJavaStringToUTF8(env, java_webapk_package, &webapk_package);
 
-  WebApkInstallService* install_service = WebApkInstallService::Get(profile);
-  if (install_service->IsInstallInProgress(info.manifest_url)) {
+  WebApkInstaller::BuildProto(
+      info, primary_icon, badge_icon, webapk_package,
+      std::to_string(java_webapk_version), icon_url_to_murmur2_hash,
+      java_is_manifest_stale,
+      base::Bind(&OnBuiltProto, ScopedJavaGlobalRef<jobject>(java_callback)));
+}
+
+// static JNI method.
+static void UpdateWebApk(JNIEnv* env,
+                         const JavaParamRef<jclass>& clazz,
+                         const JavaParamRef<jstring>& java_webapk_package,
+                         const JavaParamRef<jstring>& java_start_url,
+                         const JavaParamRef<jstring>& java_short_name,
+                         const JavaParamRef<jbyteArray>& java_serialized_proto,
+                         const JavaParamRef<jobject>& java_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  ScopedJavaGlobalRef<jobject> callback_ref(java_callback);
+
+  Profile* profile = ProfileManager::GetLastUsedProfile();
+  if (profile == nullptr) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::Bind(&WebApkUpdateManager::OnBuiltWebApk, id,
-                   WebApkInstallResult::FAILURE, false /* relax_updates */,
-                   "" /* webapk_package */));
+        base::Bind(&OnUpdated, callback_ref, WebApkInstallResult::FAILURE,
+                   false /* relax_updates */, "" /* webapk_package */));
     return;
   }
-  install_service->UpdateAsync(
-      info, primary_icon, badge_icon, webapk_package, java_webapk_version,
-      icon_url_to_murmur2_hash, java_is_manifest_stale,
-      base::Bind(&WebApkUpdateManager::OnBuiltWebApk, id));
+
+  std::string webapk_package =
+      ConvertJavaStringToUTF8(env, java_webapk_package);
+  GURL start_url = GURL(ConvertJavaStringToUTF8(env, java_start_url));
+  base::string16 short_name = ConvertJavaStringToUTF16(env, java_short_name);
+  std::unique_ptr<std::vector<uint8_t>> serialized_proto =
+      base::MakeUnique<std::vector<uint8_t>>();
+  JavaByteArrayToByteVector(env, java_serialized_proto, serialized_proto.get());
+
+  WebApkInstallService::Get(profile)->UpdateAsync(
+      webapk_package, start_url, short_name, std::move(serialized_proto),
+      base::Bind(&OnUpdated, callback_ref));
 }
diff --git a/chrome/browser/android/webapk/webapk_update_manager.h b/chrome/browser/android/webapk/webapk_update_manager.h
index db196233..6040726 100644
--- a/chrome/browser/android/webapk/webapk_update_manager.h
+++ b/chrome/browser/android/webapk/webapk_update_manager.h
@@ -18,15 +18,6 @@
   // Registers JNI hooks.
   static bool Register(JNIEnv* env);
 
-  // Called after either a request to update the WebAPK has been sent, or the
-  // update process fails. |success| indicates whether the request was issued
-  // to the server. A "true" value of |success| does not guarantee that the
-  // WebAPK will be successfully updated.
-  static void OnBuiltWebApk(const std::string& id,
-                            WebApkInstallResult result,
-                            bool relax_updates,
-                            const std::string& webapk_package);
-
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(WebApkUpdateManager);
 };
diff --git a/chrome/browser/apps/app_url_redirector.cc b/chrome/browser/apps/app_url_redirector.cc
index 46321f2e..e5ec867 100644
--- a/chrome/browser/apps/app_url_redirector.cc
+++ b/chrome/browser/apps/app_url_redirector.cc
@@ -113,8 +113,7 @@
           new navigation_interception::InterceptNavigationThrottle(
               handle,
               base::Bind(&LaunchAppWithUrl,
-                         scoped_refptr<const Extension>(*iter), handler->id),
-              true));
+                         scoped_refptr<const Extension>(*iter), handler->id)));
     }
   }
 
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index b6c80d45..e6061e9 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -3935,7 +3935,13 @@
                         WebViewScrollGuestContentTest,
                         testing::Values(false));
 
-IN_PROC_BROWSER_TEST_P(WebViewScrollGuestContentTest, ScrollGuestContent) {
+#if defined(OS_WIN)
+#define MAYBE_ScrollGuestContent DISABLED_ScrollGuestContent
+#else
+#define MAYBE_ScrollGuestContent ScrollGuestContent
+#endif
+IN_PROC_BROWSER_TEST_P(WebViewScrollGuestContentTest,
+                       MAYBE_ScrollGuestContent) {
   LoadAppWithGuest("web_view/scrollable_embedder_and_guest");
 
   content::WebContents* embedder_contents = GetEmbedderWebContents();
diff --git a/chrome/browser/chooser_controller/chooser_controller.h b/chrome/browser/chooser_controller/chooser_controller.h
index 33658c6..4de65c10 100644
--- a/chrome/browser/chooser_controller/chooser_controller.h
+++ b/chrome/browser/chooser_controller/chooser_controller.h
@@ -130,6 +130,9 @@
   void set_view(View* view) { view_ = view; }
   View* view() const { return view_; }
 
+ protected:
+  void set_title_for_testing(const base::string16& title) { title_ = title; }
+
  private:
   base::string16 title_;
   View* view_ = nullptr;
diff --git a/chrome/browser/chooser_controller/mock_chooser_controller.cc b/chrome/browser/chooser_controller/mock_chooser_controller.cc
index 9763d8c..92fc1e1 100644
--- a/chrome/browser/chooser_controller/mock_chooser_controller.cc
+++ b/chrome/browser/chooser_controller/mock_chooser_controller.cc
@@ -14,7 +14,9 @@
 MockChooserController::MockChooserController()
     : ChooserController(nullptr,
                         IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN,
-                        IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME) {}
+                        IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME) {
+  set_title_for_testing(base::ASCIIToUTF16("Mock Chooser Dialog"));
+}
 
 MockChooserController::~MockChooserController() {}
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 0a963d1..ab1628f6 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -72,6 +72,7 @@
 #include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
 #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h"
+#include "chrome/browser/safe_browsing/browser_url_loader_throttle.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
 #include "chrome/browser/safe_browsing/mojo_safe_browsing_impl.h"
@@ -186,6 +187,7 @@
 #include "content/public/common/sandbox_type.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
+#include "content/public/common/url_loader_throttle.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/web_preferences.h"
 #include "device/bluetooth/adapter_factory.h"
@@ -2452,6 +2454,9 @@
 
 void ChromeContentBrowserClient::ResourceDispatcherHostCreated() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  safe_browsing_service_ = g_browser_process->safe_browsing_service();
+
   for (size_t i = 0; i < extra_parts_.size(); ++i)
     extra_parts_[i]->ResourceDispatcherHostCreated();
 
@@ -2938,11 +2943,10 @@
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableNetworkService)) {
     registry->AddInterface(
-        base::Bind(
-            &safe_browsing::MojoSafeBrowsingImpl::Create,
-            g_browser_process->safe_browsing_service()->database_manager(),
-            g_browser_process->safe_browsing_service()->ui_manager(),
-            render_process_host->GetID()),
+        base::Bind(&safe_browsing::MojoSafeBrowsingImpl::Create,
+                   safe_browsing_service_->database_manager(),
+                   safe_browsing_service_->ui_manager(),
+                   render_process_host->GetID()),
         BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
   }
 
@@ -3433,6 +3437,20 @@
   return logging::GetLogFileName();
 }
 
+std::vector<std::unique_ptr<content::URLLoaderThrottle>>
+ChromeContentBrowserClient::CreateURLLoaderThrottles(
+    const base::Callback<content::WebContents*()>& wc_getter) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableNetworkService));
+
+  std::vector<std::unique_ptr<content::URLLoaderThrottle>> result;
+  result.push_back(base::MakeUnique<safe_browsing::BrowserURLLoaderThrottle>(
+      safe_browsing_service_->database_manager(),
+      safe_browsing_service_->ui_manager(), wc_getter));
+  return result;
+}
+
 // static
 void ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(
     const storage::QuotaSettings* settings) {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 7fc27d390..9456a061 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -15,6 +15,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "content/public/browser/content_browser_client.h"
@@ -40,6 +41,10 @@
 class QuotaPermissionContext;
 }
 
+namespace safe_browsing {
+class SafeBrowsingService;
+}
+
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -317,6 +322,9 @@
   std::unique_ptr<base::TaskScheduler::InitParams> GetTaskSchedulerInitParams()
       override;
   base::FilePath GetLoggingFileName() override;
+  std::vector<std::unique_ptr<content::URLLoaderThrottle>>
+  CreateURLLoaderThrottles(
+      const base::Callback<content::WebContents*()>& wc_getter) override;
 
  private:
   friend class DisableWebRtcEncryptionFlagTest;
@@ -372,6 +380,8 @@
 
   service_manager::BinderRegistry gpu_binder_registry_;
 
+  scoped_refptr<safe_browsing::SafeBrowsingService> safe_browsing_service_;
+
   base::WeakPtrFactory<ChromeContentBrowserClient> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeContentBrowserClient);
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 32b52ad..2d8618d2 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1247,11 +1247,10 @@
     "power/renderer_freezer.h",
     "preferences.cc",
     "preferences.h",
-    "printer_detector/cups_printer_detector.cc",
-    "printer_detector/legacy_printer_detector.cc",
-    "printer_detector/printer_detector.h",
-    "printer_detector/printer_detector_factory.cc",
-    "printer_detector/printer_detector_factory.h",
+    "printer_detector/usb_printer_detector.cc",
+    "printer_detector/usb_printer_detector.h",
+    "printer_detector/usb_printer_detector_factory.cc",
+    "printer_detector/usb_printer_detector_factory.h",
     "printing/cups_print_job.cc",
     "printing/cups_print_job.h",
     "printing/cups_print_job_manager.cc",
@@ -1768,7 +1767,6 @@
     "power/power_prefs_unittest.cc",
     "power/renderer_freezer_unittest.cc",
     "preferences_unittest.cc",
-    "printer_detector/printer_detector_unittest.cc",
     "printing/printers_manager_unittest.cc",
     "printing/specifics_translation_unittest.cc",
     "profiles/profile_list_chromeos_unittest.cc",
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 42fd5682..136cc7c 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -990,8 +990,8 @@
   // fetch of the initial CrosSettings DeviceRebootOnShutdown policy.
   shutdown_policy_forwarder_ = base::MakeUnique<ShutdownPolicyForwarder>();
 
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ash::switches::kAshDisableNightLight)) {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          ash::switches::kAshEnableNightLight)) {
     night_light_client_ = base::MakeUnique<NightLightClient>(
         g_browser_process->system_request_context());
     night_light_client_->Start();
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index c0254c90..6877c0f9 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -103,7 +103,6 @@
     ::switches::kDisableTouchDragDrop,
     ::switches::kDisableZeroCopy,
     ::switches::kEnableBlinkFeatures,
-    ::switches::kEnableColorCorrectRendering,
     ::switches::kDisableDisplayList2dCanvas,
     ::switches::kEnableDisplayList2dCanvas,
     ::switches::kForceDisplayList2dCanvas,
diff --git a/chrome/browser/chromeos/printer_detector/legacy_printer_detector.cc b/chrome/browser/chromeos/printer_detector/legacy_printer_detector.cc
deleted file mode 100644
index e40dc51..0000000
--- a/chrome/browser/chromeos/printer_detector/legacy_printer_detector.cc
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/scoped_observer.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/printer_detector/printer_detector.h"
-#include "chrome/browser/chromeos/printing/usb_printer_util.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/notifications/notification.h"
-#include "chrome/browser/notifications/notification_delegate.h"
-#include "chrome/browser/notifications/notification_ui_manager.h"
-#include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/common/extensions/api/webstore_widget_private.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/theme_resources.h"
-#include "components/user_manager/user.h"
-#include "components/user_manager/user_manager.h"
-#include "device/base/device_client.h"
-#include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
-#include "device/usb/usb_ids.h"
-#include "device/usb/usb_service.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_set.h"
-#include "extensions/common/one_shot_event.h"
-#include "extensions/common/permissions/api_permission.h"
-#include "extensions/common/permissions/permissions_data.h"
-#include "extensions/common/permissions/usb_device_permission.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-
-namespace webstore_widget_private_api =
-    extensions::api::webstore_widget_private;
-
-namespace chromeos {
-namespace {
-
-const char kPrinterProviderFoundNotificationID[] =
-    "chrome://settings/printer/printer_app_found";
-
-const char kNoPrinterProviderNotificationID[] =
-    "chrome://settings/printer/no_printer_app";
-
-enum PrinterServiceEvent {
-  PRINTER_ADDED,
-  DEPRECATED_PAGE_DISPLAYED,
-  NOTIFICATION_SHOWN_PRINTER_SUPPORTED,
-  NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED,
-  WEBSTORE_WIDGET_APP_LAUNCHED,
-  PRINTER_SERVICE_EVENT_MAX,
-};
-
-base::string16 GetNotificationTitle(uint16_t vendor_id, uint16_t product_id) {
-  const char* vendor_name = device::UsbIds::GetVendorName(vendor_id);
-  if (vendor_name) {
-    return l10n_util::GetStringFUTF16(IDS_PRINTER_DETECTED_NOTIFICATION_TITLE,
-                                      base::UTF8ToUTF16(vendor_name));
-  } else {
-    return l10n_util::GetStringUTF16(
-        IDS_PRINTER_DETECTED_NOTIFICATION_TITLE_UNKNOWN_VENDOR);
-  }
-}
-
-std::string GetNotificationTag(const std::string& vendor_id,
-                               const std::string& product_id) {
-  return vendor_id + ":" + product_id;
-}
-
-// Checks if there is an enabled extension with printerProvider permission and
-// usbDevices persmission for the USB (vendor_id, product_id) pair.
-bool HasAppThatSupportsPrinter(Profile* profile,
-                               const scoped_refptr<device::UsbDevice>& device) {
-  const extensions::ExtensionSet& enabled_extensions =
-      extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
-  for (const auto& extension : enabled_extensions) {
-    if (!extension->permissions_data() ||
-        !extension->permissions_data()->HasAPIPermission(
-            extensions::APIPermission::kPrinterProvider) ||
-        !extension->permissions_data()->HasAPIPermission(
-            extensions::APIPermission::kUsb)) {
-      continue;
-    }
-
-    const extensions::UsbPrinterManifestData* manifest_data =
-        extensions::UsbPrinterManifestData::Get(extension.get());
-    if (manifest_data && manifest_data->SupportsDevice(device)) {
-      return true;
-    }
-
-    std::unique_ptr<extensions::UsbDevicePermission::CheckParam> param =
-        extensions::UsbDevicePermission::CheckParam::ForUsbDevice(
-            extension.get(), device.get());
-    if (extension->permissions_data()->CheckAPIPermissionWithParam(
-            extensions::APIPermission::kUsbDevice, param.get())) {
-      return true;
-    }
-  }
-  return false;
-}
-
-// Delegate for notification shown when a printer provider app for the plugged
-// in printer is found.
-class PrinterProviderExistsNotificationDelegate : public NotificationDelegate {
- public:
-  PrinterProviderExistsNotificationDelegate(const std::string& vendor_id,
-                                            const std::string& product_id)
-      : vendor_id_(vendor_id), product_id_(product_id) {}
-
-  std::string id() const override {
-    return "system.printer.printer_provider_exists/" +
-           GetNotificationTag(vendor_id_, product_id_);
-  }
-
- private:
-  ~PrinterProviderExistsNotificationDelegate() override = default;
-
-  const std::string vendor_id_;
-  const std::string product_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(PrinterProviderExistsNotificationDelegate);
-};
-
-// Delegate for notification shown when there are no printer provider apps that
-// support the plugged in printer found.
-// The notification is clickable, and clicking it is supposed to launch
-// Chrome Web Store widget listing apps that can support the plugged in printer.
-// (not implemented yet).
-class SearchPrinterAppNotificationDelegate : public NotificationDelegate {
- public:
-  SearchPrinterAppNotificationDelegate(content::BrowserContext* browser_context,
-                                       uint16_t vendor_id,
-                                       const std::string& vendor_id_str,
-                                       uint16_t product_id,
-                                       const std::string& product_id_str)
-      : browser_context_(browser_context),
-        vendor_id_(vendor_id),
-        vendor_id_str_(vendor_id_str),
-        product_id_(product_id),
-        product_id_str_(product_id_str) {}
-
-  std::string id() const override {
-    return "system.printer.no_printer_provider_found/" +
-           GetNotificationTag(vendor_id_str_, product_id_str_);
-  }
-
-  bool HasClickedListener() override { return true; }
-
-  void Click() override {
-    UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
-                              WEBSTORE_WIDGET_APP_LAUNCHED,
-                              PRINTER_SERVICE_EVENT_MAX);
-    webstore_widget_private_api::Options options;
-    options.type = webstore_widget_private_api::TYPE_PRINTER_PROVIDER;
-    options.usb_id.reset(new webstore_widget_private_api::UsbId());
-    options.usb_id->vendor_id = vendor_id_;
-    options.usb_id->product_id = product_id_;
-
-    extensions::EventRouter* event_router =
-        extensions::EventRouter::Get(browser_context_);
-    std::unique_ptr<extensions::Event> event(new extensions::Event(
-        extensions::events::WEBSTORE_WIDGET_PRIVATE_ON_SHOW_WIDGET,
-        webstore_widget_private_api::OnShowWidget::kEventName,
-        webstore_widget_private_api::OnShowWidget::Create(options)));
-    event_router->DispatchEventToExtension(extension_misc::kWebstoreWidgetAppId,
-                                           std::move(event));
-  }
-
- private:
-  ~SearchPrinterAppNotificationDelegate() override = default;
-
-  content::BrowserContext* browser_context_;
-  uint16_t vendor_id_;
-  std::string vendor_id_str_;
-  uint16_t product_id_;
-  std::string product_id_str_;
-
-  DISALLOW_COPY_AND_ASSIGN(SearchPrinterAppNotificationDelegate);
-};
-
-// The PrinterDetector that initiates extension-based USB printer setup:
-//
-// * if there is a printer provider extension for a detected USB printer
-// installed, it shows a notification informing the user the printer is ready to
-// be used.
-//
-// * otherwise, it shows a notification offering the user an option to install a
-// printer provider extension for the printer from the Chrome Web Store.
-//
-// TODO(justincarlson) - Remove this implementation when CUPS support is enabled
-// by default.
-class LegacyPrinterDetectorImpl : public PrinterDetector,
-                                  public device::UsbService::Observer {
- public:
-  explicit LegacyPrinterDetectorImpl(Profile* profile)
-      : profile_(profile),
-        notification_ui_manager_(nullptr),
-        observer_(this),
-        weak_ptr_factory_(this) {
-    extensions::ExtensionSystem::Get(profile)->ready().Post(
-        FROM_HERE, base::Bind(&LegacyPrinterDetectorImpl::Initialize,
-                              weak_ptr_factory_.GetWeakPtr()));
-  }
-  ~LegacyPrinterDetectorImpl() override = default;
-
- private:
-  // UsbService::observer override:
-  void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override {
-    const user_manager::User* user =
-        ProfileHelper::Get()->GetUserByProfile(profile_);
-    if (!user || !user->HasGaiaAccount() || !user_manager::UserManager::Get() ||
-        user != user_manager::UserManager::Get()->GetActiveUser()) {
-      return;
-    }
-
-    if (!UsbDeviceIsPrinter(*device)) {
-      return;
-    }
-
-    if (notification_ui_manager_ == nullptr) {
-      notification_ui_manager_ = g_browser_process->notification_ui_manager();
-    }
-
-    UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
-                              PRINTER_ADDED, PRINTER_SERVICE_EVENT_MAX);
-    ShowPrinterPluggedNotification(device);
-  }
-
-  // Initializes the printer detector.
-  void Initialize() {
-    device::UsbService* usb_service =
-        device::DeviceClient::Get()->GetUsbService();
-    if (!usb_service)
-      return;
-    observer_.Add(usb_service);
-  }
-
-  void SetNotificationUIManagerForTesting(
-      NotificationUIManager* manager) override {
-    notification_ui_manager_ = manager;
-  }
-
-  // Shows a notification for a plugged in printer.
-  // If there is a printerProvider app that handles the printer's USB
-  // (vendor_id, product_id) pair, the notification informs the user that the
-  // printer is ready to be used, otherwise it offers the user to search the
-  // Chrome Web Store for an app that can handle the printer.
-  void ShowPrinterPluggedNotification(
-      const scoped_refptr<device::UsbDevice>& device) {
-    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
-    std::unique_ptr<Notification> notification;
-
-    const std::string kVendorIdStr = base::IntToString(device->vendor_id());
-    const std::string kProductIdStr = base::IntToString(device->product_id());
-
-    if (HasAppThatSupportsPrinter(profile_, device)) {
-      UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
-                                NOTIFICATION_SHOWN_PRINTER_SUPPORTED,
-                                PRINTER_SERVICE_EVENT_MAX);
-      notification.reset(new Notification(
-          message_center::NOTIFICATION_TYPE_SIMPLE,
-          GetNotificationTitle(device->vendor_id(), device->product_id()),
-          l10n_util::GetStringUTF16(
-              IDS_PRINTER_DETECTED_NOTIFICATION_PRINT_APP_FOUND_BODY),
-          bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION),
-          message_center::NotifierId(
-              message_center::NotifierId::SYSTEM_COMPONENT,
-              kPrinterProviderFoundNotificationID),
-          base::string16(), GURL(kPrinterProviderFoundNotificationID),
-          GetNotificationTag(kVendorIdStr, kProductIdStr),
-          message_center::RichNotificationData(),
-          new PrinterProviderExistsNotificationDelegate(kVendorIdStr,
-                                                        kProductIdStr)));
-    } else {
-      UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
-                                NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED,
-                                PRINTER_SERVICE_EVENT_MAX);
-      message_center::RichNotificationData options;
-      options.clickable = true;
-      notification.reset(new Notification(
-          message_center::NOTIFICATION_TYPE_SIMPLE,
-          GetNotificationTitle(device->vendor_id(), device->product_id()),
-          l10n_util::GetStringUTF16(
-              IDS_PRINTER_DETECTED_NOTIFICATION_NO_PRINT_APP_BODY),
-          bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION),
-          message_center::NotifierId(
-              message_center::NotifierId::SYSTEM_COMPONENT,
-              kNoPrinterProviderNotificationID),
-          base::string16(), GURL(kNoPrinterProviderNotificationID),
-          GetNotificationTag(kVendorIdStr, kProductIdStr), options,
-          new SearchPrinterAppNotificationDelegate(
-              profile_, device->vendor_id(), kVendorIdStr, device->product_id(),
-              kProductIdStr)));
-    }
-
-    notification->SetSystemPriority();
-    notification_ui_manager_->Add(*notification, profile_);
-  }
-
-  std::unique_ptr<Notification> notification_;
-
-  Profile* profile_;
-  NotificationUIManager* notification_ui_manager_;
-  ScopedObserver<device::UsbService, device::UsbService::Observer> observer_;
-  base::WeakPtrFactory<LegacyPrinterDetectorImpl> weak_ptr_factory_;
-};
-
-}  // namespace
-
-// static
-std::unique_ptr<PrinterDetector> PrinterDetector::CreateLegacy(
-    Profile* profile) {
-  return base::MakeUnique<LegacyPrinterDetectorImpl>(profile);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printer_detector/printer_detector.h b/chrome/browser/chromeos/printer_detector/printer_detector.h
deleted file mode 100644
index 1789377..0000000
--- a/chrome/browser/chromeos/printer_detector/printer_detector.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_PRINTER_DETECTOR_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_PRINTER_DETECTOR_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "chromeos/printing/printer_configuration.h"
-#include "components/keyed_service/core/keyed_service.h"
-
-class NotificationUIManager;
-class Profile;
-
-namespace chromeos {
-
-// Observes device::UsbService for addition of USB printers (devices with
-// interface class 7).  What it does with this depends on whether or not
-// CUPS printing support is enabled.
-//
-// If CUPS is disabled, the Legacy implementation should be used.  The legacy
-// implementation shows a notification depending on whether there are printer
-// provider apps that declared support for the USB device installed.  If such
-// app exists, the notification notifies the user the printer is ready.
-// Otherwise the notification offers user to search Chrome Web Store for apps
-// that support the printer. Clicking the notification launches webstore_widget
-// app for the printer.  The notification is shown only for active user's
-// profile.
-//
-// If CUPS is enabled, the Cups implementation should be used.  This
-// implementation to guides the user through setting up a new USB printer in the
-// CUPS backend.
-class PrinterDetector : public KeyedService {
- public:
-  class Observer {
-   public:
-    virtual ~Observer() = default;
-
-    // The set of available printers has changed.
-    virtual void OnAvailableUsbPrintersChanged(
-        const std::vector<Printer>& printers) = 0;
-  };
-
-  // Factory function for the Legacy implementation.
-  static std::unique_ptr<PrinterDetector> CreateLegacy(Profile* profile);
-
-  // Factory function for the CUPS implementation.
-  static std::unique_ptr<PrinterDetector> CreateCups(Profile* profile);
-  ~PrinterDetector() override {}
-
-  // Observer management.  Note these are only implemented for the cups backend.
-  // TODO(justincarlson) - Change these all to pure virtual functions when the
-  // legacy backend is retired.
-
-  virtual void AddObserver(Observer* observer) {}
-  virtual void RemoveObserver(Observer* observer) {}
-
-  // Get the current set of detected printers.
-  virtual std::vector<Printer> GetPrinters();
-
- protected:
-  PrinterDetector() = default;
-
- private:
-  friend class PrinterDetectorAppSearchEnabledTest;
-
-  virtual void SetNotificationUIManagerForTesting(
-      NotificationUIManager* manager) = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(PrinterDetector);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_PRINTER_DETECTOR_H_
diff --git a/chrome/browser/chromeos/printer_detector/printer_detector_factory.cc b/chrome/browser/chromeos/printer_detector/printer_detector_factory.cc
deleted file mode 100644
index 4e5dc707..0000000
--- a/chrome/browser/chromeos/printer_detector/printer_detector_factory.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/printer_detector/printer_detector_factory.h"
-
-#include "base/command_line.h"
-#include "chrome/browser/chromeos/printer_detector/printer_detector.h"
-#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_switches.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "extensions/browser/extensions_browser_client.h"
-
-namespace chromeos {
-
-namespace {
-
-static base::LazyInstance<PrinterDetectorFactory>::DestructorAtExit g_factory =
-    LAZY_INSTANCE_INITIALIZER;
-
-}  // namespace
-
-// static
-PrinterDetectorFactory* PrinterDetectorFactory::GetInstance() {
-  return g_factory.Pointer();
-}
-
-PrinterDetector* PrinterDetectorFactory::Get(content::BrowserContext* context) {
-  return static_cast<PrinterDetector*>(
-      GetServiceForBrowserContext(context, false));
-}
-
-PrinterDetectorFactory::PrinterDetectorFactory()
-    : BrowserContextKeyedServiceFactory(
-          "PrinterDetectorFactory",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(
-      extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
-  DependsOn(PrintersManagerFactory::GetInstance());
-}
-
-PrinterDetectorFactory::~PrinterDetectorFactory() {
-}
-
-content::BrowserContext* PrinterDetectorFactory::GetBrowserContextToUse(
-    content::BrowserContext* context) const {
-  return chrome::GetBrowserContextRedirectedInIncognito(context);
-}
-
-KeyedService* PrinterDetectorFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ::switches::kDisableNativeCups)) {
-    return PrinterDetector::CreateLegacy(Profile::FromBrowserContext(context))
-        .release();
-  }
-
-  return PrinterDetector::CreateCups(Profile::FromBrowserContext(context))
-      .release();
-}
-
-bool PrinterDetectorFactory::ServiceIsCreatedWithBrowserContext() const {
-  return true;
-}
-
-bool PrinterDetectorFactory::ServiceIsNULLWhileTesting() const {
-  return true;
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printer_detector/printer_detector_factory.h b/chrome/browser/chromeos/printer_detector/printer_detector_factory.h
deleted file mode 100644
index 39e69d2..0000000
--- a/chrome/browser/chromeos/printer_detector/printer_detector_factory.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_PRINTER_DETECTOR_FACTORY_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_PRINTER_DETECTOR_FACTORY_H_
-
-#include "base/lazy_instance.h"
-#include "base/macros.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-#include "extensions/browser/extension_system_provider.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace chromeos {
-
-class PrinterDetector;
-
-class PrinterDetectorFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static PrinterDetectorFactory* GetInstance();
-
-  PrinterDetector* Get(content::BrowserContext* context);
-
- protected:
-  // BrowserContextKeyedServiceFactory:
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-
- private:
-  friend struct base::LazyInstanceTraitsBase<PrinterDetectorFactory>;
-  PrinterDetectorFactory();
-  ~PrinterDetectorFactory() override;
-
-  // BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* browser_context) const override;
-  bool ServiceIsCreatedWithBrowserContext() const override;
-  bool ServiceIsNULLWhileTesting() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(PrinterDetectorFactory);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_PRINTER_DETECTOR_FACTORY_H_
diff --git a/chrome/browser/chromeos/printer_detector/printer_detector_unittest.cc b/chrome/browser/chromeos/printer_detector/printer_detector_unittest.cc
deleted file mode 100644
index 2c7f55c..0000000
--- a/chrome/browser/chromeos/printer_detector/printer_detector_unittest.cc
+++ /dev/null
@@ -1,345 +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 "chrome/browser/chromeos/printer_detector/printer_detector.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
-#include "chrome/browser/chromeos/printer_detector/printer_detector_factory.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/browser/notifications/notification.h"
-#include "chrome/browser/notifications/notification_test_util.h"
-#include "chrome/browser/notifications/notification_ui_manager.h"
-#include "chrome/test/base/testing_profile.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "device/base/mock_device_client.h"
-#include "device/usb/mock_usb_device.h"
-#include "device/usb/mock_usb_service.h"
-#include "device/usb/usb_descriptors.h"
-#include "device/usb/usb_service.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/common/extension_builder.h"
-#include "extensions/common/value_builder.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using extensions::DictionaryBuilder;
-using extensions::ListBuilder;
-
-namespace chromeos {
-
-namespace {
-
-const uint8_t kPrinterInterfaceClass = 7;
-
-const char kTestUserId[] = "test_user";
-
-const char kPrinterAppExistsDelegateIDTemplate[] =
-    "system.printer.printer_provider_exists/%s:%s";
-
-const char kPrinterAppNotFoundDelegateIDTemplate[] =
-    "system.printer.no_printer_provider_found/%s:%s";
-
-std::unique_ptr<KeyedService> CreatePrinterDetector(
-    content::BrowserContext* context) {
-  return PrinterDetector::CreateLegacy(Profile::FromBrowserContext(context));
-}
-
-}  // namespace
-
-// TODO(tbarzic): Rename this test.
-class PrinterDetectorAppSearchEnabledTest : public testing::Test {
- public:
-  PrinterDetectorAppSearchEnabledTest()
-      : user_manager_(new chromeos::FakeChromeUserManager()),
-        user_manager_enabler_(user_manager_) {}
-
-  ~PrinterDetectorAppSearchEnabledTest() override = default;
-
-  void SetUp() override {
-    device_client_.GetUsbService();
-    // Make sure the profile is created after adding the switch and setting up
-    // device client.
-    profile_.reset(new TestingProfile());
-    chromeos::PrinterDetectorFactory::GetInstance()->SetTestingFactoryAndUse(
-        profile_.get(), &CreatePrinterDetector);
-    AddTestUser();
-    SetExtensionSystemReady(profile_.get());
-  }
-
- protected:
-  void SetExtensionSystemReady(TestingProfile* profile) {
-    extensions::TestExtensionSystem* test_extension_system =
-        static_cast<extensions::TestExtensionSystem*>(
-            extensions::ExtensionSystem::Get(profile));
-    test_extension_system->SetReady();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void AddTestUser() {
-    const user_manager::User* user =
-        user_manager_->AddUser(AccountId::FromUserEmail(kTestUserId));
-    profile_->set_profile_name(kTestUserId);
-    chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
-        user, profile_.get());
-    chromeos::PrinterDetectorFactory::GetInstance()
-        ->Get(profile_.get())
-        ->SetNotificationUIManagerForTesting(&notification_ui_manager_);
-  }
-
-  void InvokeUsbAdded(uint16_t vendor_id,
-                      uint16_t product_id,
-                      uint8_t interface_class) {
-    device::UsbConfigDescriptor config(1, false, false, 0);
-    config.interfaces.emplace_back(1, 0, interface_class, 0, 0);
-    device_client_.usb_service()->AddDevice(
-        new device::MockUsbDevice(vendor_id, product_id, config));
-  }
-
-  // Creates a test extension with the provided permissions.
-  scoped_refptr<extensions::Extension> CreateTestExtension(
-      std::unique_ptr<base::ListValue> permissions_builder,
-      std::unique_ptr<base::DictionaryValue> usb_printers_builder) {
-    return extensions::ExtensionBuilder()
-        .SetID("fake_extension_id")
-        .SetManifest(
-            DictionaryBuilder()
-                .Set("name", "Printer provider extension")
-                .Set("manifest_version", 2)
-                .Set("version", "1.0")
-                // Needed to enable usb API.
-                .Set("app",
-                     DictionaryBuilder()
-                         .Set("background",
-                              DictionaryBuilder()
-                                  .Set("scripts",
-                                       ListBuilder().Append("bg.js").Build())
-                                  .Build())
-                         .Build())
-                .Set("permissions", std::move(permissions_builder))
-                .Set("usb_printers", std::move(usb_printers_builder))
-                .Build())
-        .Build();
-  }
-
-  content::TestBrowserThreadBundle thread_bundle_;
-  StubNotificationUIManager notification_ui_manager_;
-  chromeos::FakeChromeUserManager* user_manager_;
-  chromeos::ScopedUserManagerEnabler user_manager_enabler_;
-  device::MockDeviceClient device_client_;
-  std::unique_ptr<TestingProfile> profile_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PrinterDetectorAppSearchEnabledTest);
-};
-
-TEST_F(PrinterDetectorAppSearchEnabledTest, ShowFindAppNotification) {
-  InvokeUsbAdded(123, 456, kPrinterInterfaceClass);
-
-  ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
-  const Notification& notification =
-      notification_ui_manager_.GetNotificationAt(0);
-  EXPECT_EQ("123:456", notification.tag());
-  EXPECT_EQ(
-      base::StringPrintf(kPrinterAppNotFoundDelegateIDTemplate, "123", "456"),
-      notification.delegate_id());
-}
-
-TEST_F(PrinterDetectorAppSearchEnabledTest, ShowAppFoundNotification) {
-  scoped_refptr<extensions::Extension> extension = CreateTestExtension(
-      ListBuilder()
-          .Append("usb")
-          .Append("printerProvider")
-          .Append(DictionaryBuilder()
-                      .Set("usbDevices", ListBuilder()
-                                             .Append(DictionaryBuilder()
-                                                         .Set("vendorId", 123)
-                                                         .Set("productId", 456)
-                                                         .Build())
-                                             .Build())
-                      .Build())
-          .Build(),
-      DictionaryBuilder().Set("filters", ListBuilder().Build()).Build());
-  ASSERT_TRUE(extensions::ExtensionRegistry::Get(profile_.get())
-                  ->AddEnabled(extension));
-
-  InvokeUsbAdded(123, 456, kPrinterInterfaceClass);
-
-  ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
-  const Notification& notification =
-      notification_ui_manager_.GetNotificationAt(0);
-  EXPECT_EQ("123:456", notification.tag());
-  EXPECT_EQ(
-      base::StringPrintf(kPrinterAppExistsDelegateIDTemplate, "123", "456"),
-      notification.delegate_id());
-}
-
-TEST_F(PrinterDetectorAppSearchEnabledTest,
-       UsbHandlerExists_NotPrinterProvider) {
-  scoped_refptr<extensions::Extension> extension = CreateTestExtension(
-      ListBuilder()
-          .Append("usb")
-          .Append(DictionaryBuilder()
-                      .Set("usbDevices", ListBuilder()
-                                             .Append(DictionaryBuilder()
-                                                         .Set("vendorId", 123)
-                                                         .Set("productId", 756)
-                                                         .Build())
-                                             .Build())
-                      .Build())
-          .Build(),
-      DictionaryBuilder().Set("filters", ListBuilder().Build()).Build());
-  ASSERT_TRUE(extensions::ExtensionRegistry::Get(profile_.get())
-                  ->AddEnabled(extension));
-
-  InvokeUsbAdded(123, 756, kPrinterInterfaceClass);
-
-  ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
-  const Notification& notification =
-      notification_ui_manager_.GetNotificationAt(0);
-  EXPECT_EQ("123:756", notification.tag());
-  EXPECT_EQ(
-      base::StringPrintf(kPrinterAppNotFoundDelegateIDTemplate, "123", "756"),
-      notification.delegate_id());
-}
-
-TEST_F(PrinterDetectorAppSearchEnabledTest,
-       PrinterProvider_DifferentUsbProductId) {
-  scoped_refptr<extensions::Extension> extension = CreateTestExtension(
-      ListBuilder()
-          .Append("usb")
-          .Append("printerProvider")
-          .Append(DictionaryBuilder()
-                      .Set("usbDevices", ListBuilder()
-                                             .Append(DictionaryBuilder()
-                                                         .Set("vendorId", 123)
-                                                         .Set("productId", 001)
-                                                         .Build())
-                                             .Build())
-                      .Build())
-          .Build(),
-      DictionaryBuilder().Set("filters", ListBuilder().Build()).Build());
-  ASSERT_TRUE(extensions::ExtensionRegistry::Get(profile_.get())
-                  ->AddEnabled(extension));
-
-  InvokeUsbAdded(123, 456, kPrinterInterfaceClass);
-
-  ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
-  const Notification& notification =
-      notification_ui_manager_.GetNotificationAt(0);
-  EXPECT_EQ("123:456", notification.tag());
-  EXPECT_EQ(
-      base::StringPrintf(kPrinterAppNotFoundDelegateIDTemplate, "123", "456"),
-      notification.delegate_id());
-}
-
-TEST_F(PrinterDetectorAppSearchEnabledTest,
-       PrinterProvider_UsbPrinters_NotFound) {
-  scoped_refptr<extensions::Extension> extension = CreateTestExtension(
-      ListBuilder().Append("usb").Append("printerProvider").Build(),
-      DictionaryBuilder()
-          .Set("filters", ListBuilder()
-                              .Append(DictionaryBuilder()
-                                          .Set("vendorId", 123)
-                                          .Set("productId", 001)
-                                          .Build())
-                              .Build())
-          .Build());
-  ASSERT_TRUE(extensions::ExtensionRegistry::Get(profile_.get())
-                  ->AddEnabled(extension));
-
-  InvokeUsbAdded(123, 456, kPrinterInterfaceClass);
-
-  ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
-  const Notification& notification =
-      notification_ui_manager_.GetNotificationAt(0);
-  EXPECT_EQ("123:456", notification.tag());
-  EXPECT_EQ(
-      base::StringPrintf(kPrinterAppNotFoundDelegateIDTemplate, "123", "456"),
-      notification.delegate_id());
-}
-
-TEST_F(PrinterDetectorAppSearchEnabledTest,
-       PrinterProvider_UsbPrinters_WithProductId) {
-  scoped_refptr<extensions::Extension> extension = CreateTestExtension(
-      ListBuilder().Append("usb").Append("printerProvider").Build(),
-      DictionaryBuilder()
-          .Set("filters", ListBuilder()
-                              .Append(DictionaryBuilder()
-                                          .Set("vendorId", 123)
-                                          .Set("productId", 456)
-                                          .Build())
-                              .Build())
-          .Build());
-  ASSERT_TRUE(extensions::ExtensionRegistry::Get(profile_.get())
-                  ->AddEnabled(extension));
-
-  InvokeUsbAdded(123, 456, kPrinterInterfaceClass);
-
-  ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
-  const Notification& notification =
-      notification_ui_manager_.GetNotificationAt(0);
-  EXPECT_EQ("123:456", notification.tag());
-  EXPECT_EQ(
-      base::StringPrintf(kPrinterAppExistsDelegateIDTemplate, "123", "456"),
-      notification.delegate_id());
-}
-
-TEST_F(PrinterDetectorAppSearchEnabledTest,
-       PrinterProvider_UsbPrinters_WithInterfaceClass) {
-  scoped_refptr<extensions::Extension> extension = CreateTestExtension(
-      ListBuilder().Append("usb").Append("printerProvider").Build(),
-      DictionaryBuilder()
-          .Set("filters",
-               ListBuilder()
-                   .Append(DictionaryBuilder()
-                               .Set("vendorId", 123)
-                               .Set("interfaceClass", kPrinterInterfaceClass)
-                               .Build())
-                   .Build())
-          .Build());
-  ASSERT_TRUE(extensions::ExtensionRegistry::Get(profile_.get())
-                  ->AddEnabled(extension));
-
-  InvokeUsbAdded(123, 456, kPrinterInterfaceClass);
-
-  ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
-  const Notification& notification =
-      notification_ui_manager_.GetNotificationAt(0);
-  EXPECT_EQ("123:456", notification.tag());
-  EXPECT_EQ(
-      base::StringPrintf(kPrinterAppExistsDelegateIDTemplate, "123", "456"),
-      notification.delegate_id());
-}
-
-TEST_F(PrinterDetectorAppSearchEnabledTest, IgnoreNonPrinters) {
-  scoped_refptr<extensions::Extension> extension = CreateTestExtension(
-      ListBuilder().Append("usb").Append("printerProvider").Build(),
-      DictionaryBuilder()
-          .Set("filters",
-               ListBuilder()
-                   .Append(DictionaryBuilder()
-                               .Set("vendorId", 123)
-                               .Set("interfaceClass", kPrinterInterfaceClass)
-                               .Build())
-                   .Build())
-          .Build());
-  ASSERT_TRUE(extensions::ExtensionRegistry::Get(profile_.get())
-                  ->AddEnabled(extension));
-
-  InvokeUsbAdded(123, 456, 1);
-
-  ASSERT_EQ(0u, notification_ui_manager_.GetNotificationCount());
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc b/chrome/browser/chromeos/printer_detector/usb_printer_detector.cc
similarity index 87%
rename from chrome/browser/chromeos/printer_detector/cups_printer_detector.cc
rename to chrome/browser/chromeos/printer_detector/usb_printer_detector.cc
index 00168bb..88e06a3a 100644
--- a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc
+++ b/chrome/browser/chromeos/printer_detector/usb_printer_detector.cc
@@ -4,7 +4,9 @@
 
 #include <stdint.h>
 
+#include <map>
 #include <memory>
+#include <set>
 #include <utility>
 #include <vector>
 
@@ -18,7 +20,7 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/printer_detector/printer_detector.h"
+#include "chrome/browser/chromeos/printer_detector/usb_printer_detector.h"
 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
 #include "chrome/browser/chromeos/printing/printers_manager_factory.h"
@@ -30,7 +32,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "device/base/device_client.h"
 #include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
 #include "device/usb/usb_service.h"
 
 namespace chromeos {
@@ -71,32 +72,32 @@
 
 // The PrinterDetector that drives the flow for setting up a USB printer to use
 // CUPS backend.
-class CupsPrinterDetectorImpl : public PrinterDetector,
-                                public device::UsbService::Observer {
+class UsbPrinterDetectorImpl : public UsbPrinterDetector,
+                               public device::UsbService::Observer {
  public:
-  explicit CupsPrinterDetectorImpl(Profile* profile)
+  explicit UsbPrinterDetectorImpl(Profile* profile)
       : profile_(profile),
         usb_observer_(this),
         observer_list_(
-            new base::ObserverListThreadSafe<PrinterDetector::Observer>),
+            new base::ObserverListThreadSafe<UsbPrinterDetector::Observer>),
         weak_ptr_factory_(this) {
     device::UsbService* usb_service =
         device::DeviceClient::Get()->GetUsbService();
     if (usb_service) {
       usb_observer_.Add(usb_service);
-      usb_service->GetDevices(base::Bind(&CupsPrinterDetectorImpl::OnGetDevices,
+      usb_service->GetDevices(base::Bind(&UsbPrinterDetectorImpl::OnGetDevices,
                                          weak_ptr_factory_.GetWeakPtr()));
     }
   }
-  ~CupsPrinterDetectorImpl() override = default;
+  ~UsbPrinterDetectorImpl() override = default;
 
   // PrinterDetector interface function.
-  void AddObserver(PrinterDetector::Observer* observer) override {
+  void AddObserver(UsbPrinterDetector::Observer* observer) override {
     observer_list_->AddObserver(observer);
   }
 
   // PrinterDetector interface function.
-  void RemoveObserver(PrinterDetector::Observer* observer) override {
+  void RemoveObserver(UsbPrinterDetector::Observer* observer) override {
     observer_list_->RemoveObserver(observer);
   }
 
@@ -146,7 +147,8 @@
       // We already have pp_lock_, so need to call the pre-locked version of
       // GetPrinters to prevent deadlock.
       observer_list_->Notify(
-          FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged,
+          FROM_HERE,
+          &UsbPrinterDetector::Observer::OnAvailableUsbPrintersChanged,
           GetPrintersLocked());
     } else {
       // If the device has been removed but it's not in present_printers_, it
@@ -203,7 +205,7 @@
         printing::CreateProvider(profile_);
     ppd_provider->ResolveUsbIds(
         device->vendor_id(), device->product_id(),
-        base::Bind(&CupsPrinterDetectorImpl::ResolveUsbIdsDone,
+        base::Bind(&UsbPrinterDetectorImpl::ResolveUsbIdsDone,
                    weak_ptr_factory_.GetWeakPtr(), ppd_provider,
                    base::Passed(std::move(data))));
   }
@@ -214,7 +216,7 @@
     SetUpPrinterData* data_ptr = data.get();
     data_ptr->configurer->SetUpPrinter(
         *(data_ptr->printer),
-        base::Bind(&CupsPrinterDetectorImpl::SetUpPrinterDone,
+        base::Bind(&UsbPrinterDetectorImpl::SetUpPrinterDone,
                    weak_ptr_factory_.GetWeakPtr(),
                    base::Passed(std::move(data))));
   }
@@ -273,16 +275,12 @@
       base::AutoLock auto_lock(pp_lock_);
       present_printers_.emplace(data->device->guid(), std::move(data->printer));
       observer_list_->Notify(
-          FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged,
+          FROM_HERE,
+          &UsbPrinterDetector::Observer::OnAvailableUsbPrintersChanged,
           GetPrintersLocked());
     }
   }
 
-  void SetNotificationUIManagerForTesting(
-      NotificationUIManager* manager) override {
-    LOG(FATAL) << "Not implemented for CUPS";
-  }
-
   // Map from USB GUID to Printer that we have detected as being currently
   // plugged in and have finished processing.  Note present_printers_ may be
   // accessed from multiple threads, so is protected by pp_lock_.
@@ -297,22 +295,17 @@
   Profile* profile_;
   ScopedObserver<device::UsbService, device::UsbService::Observer>
       usb_observer_;
-  scoped_refptr<base::ObserverListThreadSafe<PrinterDetector::Observer>>
+  scoped_refptr<base::ObserverListThreadSafe<UsbPrinterDetector::Observer>>
       observer_list_;
-  base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_;
+  base::WeakPtrFactory<UsbPrinterDetectorImpl> weak_ptr_factory_;
 };
 
 }  // namespace
 
-// Nop base class implementation of GetPrinters().  Because this is non-empty we
-// have to define it out-of-line.
-std::vector<Printer> PrinterDetector::GetPrinters() {
-  return std::vector<Printer>();
-}
-
 // static
-std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) {
-  return base::MakeUnique<CupsPrinterDetectorImpl>(profile);
+std::unique_ptr<UsbPrinterDetector> UsbPrinterDetector::Create(
+    Profile* profile) {
+  return base::MakeUnique<UsbPrinterDetectorImpl>(profile);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printer_detector/usb_printer_detector.h b/chrome/browser/chromeos/printer_detector/usb_printer_detector.h
new file mode 100644
index 0000000..b7e7ad5
--- /dev/null
+++ b/chrome/browser/chromeos/printer_detector/usb_printer_detector.h
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_USB_PRINTER_DETECTOR_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_USB_PRINTER_DETECTOR_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "chromeos/printing/printer_configuration.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class Profile;
+
+namespace chromeos {
+
+// Observes device::UsbService for addition of USB printers (devices with
+// interface class 7).  When a device is detected, it is forwarded to the
+// printing subsystem for either autoconfiguration or user guidance.
+class UsbPrinterDetector : public KeyedService {
+ public:
+  class Observer {
+   public:
+    virtual ~Observer() = default;
+
+    // The set of available printers has changed.
+    virtual void OnAvailableUsbPrintersChanged(
+        const std::vector<Printer>& printers) = 0;
+  };
+
+  // Factory function for the CUPS implementation.
+  static std::unique_ptr<UsbPrinterDetector> Create(Profile* profile);
+  ~UsbPrinterDetector() override = default;
+
+  virtual void AddObserver(Observer* observer) = 0;
+  virtual void RemoveObserver(Observer* observer) = 0;
+
+  // Get the current set of detected printers.
+  virtual std::vector<Printer> GetPrinters() = 0;
+
+ protected:
+  UsbPrinterDetector() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UsbPrinterDetector);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_USB_PRINTER_DETECTOR_H_
diff --git a/chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.cc b/chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.cc
new file mode 100644
index 0000000..66047b65
--- /dev/null
+++ b/chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.cc
@@ -0,0 +1,66 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/chromeos/printer_detector/usb_printer_detector.h"
+#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_switches.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "extensions/browser/extensions_browser_client.h"
+
+namespace chromeos {
+
+namespace {
+
+base::LazyInstance<UsbPrinterDetectorFactory>::DestructorAtExit g_factory =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// static
+UsbPrinterDetectorFactory* UsbPrinterDetectorFactory::GetInstance() {
+  return g_factory.Pointer();
+}
+
+UsbPrinterDetector* UsbPrinterDetectorFactory::Get(
+    content::BrowserContext* context) {
+  return static_cast<UsbPrinterDetector*>(
+      GetServiceForBrowserContext(context, false));
+}
+
+UsbPrinterDetectorFactory::UsbPrinterDetectorFactory()
+    : BrowserContextKeyedServiceFactory(
+          "UsbPrinterDetectorFactory",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(
+      extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
+  DependsOn(PrintersManagerFactory::GetInstance());
+}
+
+UsbPrinterDetectorFactory::~UsbPrinterDetectorFactory() {}
+
+content::BrowserContext* UsbPrinterDetectorFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextRedirectedInIncognito(context);
+}
+
+KeyedService* UsbPrinterDetectorFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return UsbPrinterDetector::Create(Profile::FromBrowserContext(context))
+      .release();
+}
+
+bool UsbPrinterDetectorFactory::ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
+
+bool UsbPrinterDetectorFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.h b/chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.h
new file mode 100644
index 0000000..68f1e6a
--- /dev/null
+++ b/chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_USB_PRINTER_DETECTOR_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_USB_PRINTER_DETECTOR_FACTORY_H_
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "extensions/browser/extension_system_provider.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace chromeos {
+
+class UsbPrinterDetector;
+
+class UsbPrinterDetectorFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static UsbPrinterDetectorFactory* GetInstance();
+
+  UsbPrinterDetector* Get(content::BrowserContext* context);
+
+ protected:
+  // BrowserContextKeyedServiceFactory:
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<UsbPrinterDetectorFactory>;
+  UsbPrinterDetectorFactory();
+  ~UsbPrinterDetectorFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* browser_context) const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+  bool ServiceIsNULLWhileTesting() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(UsbPrinterDetectorFactory);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTER_DETECTOR_USB_PRINTER_DETECTOR_FACTORY_H_
diff --git a/chrome/browser/chromeos/printing/printer_discoverer.cc b/chrome/browser/chromeos/printing/printer_discoverer.cc
index 6781249..cfa53df 100644
--- a/chrome/browser/chromeos/printing/printer_discoverer.cc
+++ b/chrome/browser/chromeos/printing/printer_discoverer.cc
@@ -10,8 +10,8 @@
 #include "base/observer_list.h"
 #include "base/scoped_observer.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/browser/chromeos/printer_detector/printer_detector.h"
-#include "chrome/browser/chromeos/printer_detector/printer_detector_factory.h"
+#include "chrome/browser/chromeos/printer_detector/usb_printer_detector.h"
+#include "chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.h"
 #include "chrome/browser/chromeos/printing/printers_manager.h"
 #include "chrome/browser/chromeos/printing/printers_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -22,15 +22,15 @@
 
 // Implementation of PrinterDiscoverer interface.
 class PrinterDiscovererImpl : public PrinterDiscoverer,
-                              public PrinterDetector::Observer {
+                              public UsbPrinterDetector::Observer {
  public:
   explicit PrinterDiscovererImpl(Profile* profile)
       : detector_observer_(this), profile_(profile), weak_ptr_factory_(this) {
-    PrinterDetector* detector =
-        PrinterDetectorFactory::GetInstance()->Get(profile);
-    DCHECK(detector);
-    detector_observer_.Add(detector);
-    usb_printers_ = detector->GetPrinters();
+    UsbPrinterDetector* usb_detector =
+        UsbPrinterDetectorFactory::GetInstance()->Get(profile);
+    DCHECK(usb_detector);
+    detector_observer_.Add(usb_detector);
+    usb_printers_ = usb_detector->GetPrinters();
   }
   ~PrinterDiscovererImpl() override = default;
 
@@ -103,7 +103,8 @@
 
   std::vector<Printer> usb_printers_;
   base::ObserverList<PrinterDiscoverer::Observer> observer_list_;
-  ScopedObserver<PrinterDetector, PrinterDetector::Observer> detector_observer_;
+  ScopedObserver<UsbPrinterDetector, UsbPrinterDetector::Observer>
+      detector_observer_;
   Profile* profile_;
   base::WeakPtrFactory<PrinterDiscovererImpl> weak_ptr_factory_;
 };
diff --git a/chrome/browser/chromeos/printing/usb_printer_util.cc b/chrome/browser/chromeos/printing/usb_printer_util.cc
index 2221fcc..28ae5c1 100644
--- a/chrome/browser/chromeos/printing/usb_printer_util.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_util.cc
@@ -16,8 +16,9 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/printing/printer_configuration.h"
+#include "device/usb/public/cpp/filter_utils.h"
+#include "device/usb/public/interfaces/device_manager.mojom.h"
 #include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace chromeos {
@@ -112,9 +113,10 @@
 }  // namespace
 
 bool UsbDeviceIsPrinter(const device::UsbDevice& usb_device) {
-  device::UsbDeviceFilter printer_filter;
-  printer_filter.interface_class = kPrinterInterfaceClass;
-  return printer_filter.Matches(usb_device);
+  auto printer_filter = device::mojom::UsbDeviceFilter::New();
+  printer_filter->has_class_code = true;
+  printer_filter->class_code = kPrinterInterfaceClass;
+  return UsbDeviceFilterMatches(*printer_filter, usb_device);
 }
 
 std::string UsbPrinterDeviceDetailsAsString(const device::UsbDevice& device) {
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index 463cedea..7fd7f1cc 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -15,6 +15,8 @@
 #include "base/md5.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/value_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/devtools/devtools_file_watcher.h"
@@ -120,14 +122,14 @@
 };
 
 void WriteToFile(const base::FilePath& path, const std::string& content) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+  base::ThreadRestrictions::AssertIOAllowed();
   DCHECK(!path.empty());
 
   base::WriteFile(path, content.c_str(), content.length());
 }
 
 void AppendToFile(const base::FilePath& path, const std::string& content) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+  base::ThreadRestrictions::AssertIOAllowed();
   DCHECK(!path.empty());
 
   base::AppendToFile(path, content.c_str(), content.size());
@@ -210,14 +212,13 @@
     : web_contents_(web_contents),
       profile_(profile),
       delegate_(delegate),
+      file_task_runner_(
+          base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
       weak_factory_(this) {
   pref_change_registrar_.Init(profile_->GetPrefs());
 }
 
-DevToolsFileHelper::~DevToolsFileHelper() {
-  BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE,
-                            file_watcher_.release());
-}
+DevToolsFileHelper::~DevToolsFileHelper() = default;
 
 void DevToolsFileHelper::Save(const std::string& url,
                               const std::string& content,
@@ -275,8 +276,8 @@
   if (it == saved_files_.end())
     return;
   callback.Run();
-  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
-                          BindOnce(&AppendToFile, it->second, content));
+  file_task_runner_->PostTask(FROM_HERE,
+                              BindOnce(&AppendToFile, it->second, content));
 }
 
 void DevToolsFileHelper::SaveAsFileSelected(const std::string& url,
@@ -292,8 +293,7 @@
   files_map->SetWithoutPathExpansion(base::MD5String(url),
                                      base::CreateFilePathValue(path));
   callback.Run();
-  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
-                          BindOnce(&WriteToFile, path, content));
+  file_task_runner_->PostTask(FROM_HERE, BindOnce(&WriteToFile, path, content));
 }
 
 void DevToolsFileHelper::AddFileSystem(
@@ -308,23 +308,25 @@
     select_file_dialog->Show(ui::SelectFileDialog::SELECT_FOLDER,
                              base::FilePath());
   } else {
-    BrowserThread::PostTask(
-        BrowserThread::FILE, FROM_HERE,
+    file_task_runner_->PostTask(
+        FROM_HERE,
         BindOnce(&DevToolsFileHelper::CheckProjectFileExistsAndAddFileSystem,
                  weak_factory_.GetWeakPtr(), show_info_bar_callback,
                  base::FilePath::FromUTF8Unsafe(file_system_path)));
   }
 }
 
+// static
 void DevToolsFileHelper::CheckProjectFileExistsAndAddFileSystem(
-    const ShowInfoBarCallback& show_info_bar_callback,
-    const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+    base::WeakPtr<DevToolsFileHelper> self,
+    ShowInfoBarCallback show_info_bar_callback,
+    base::FilePath path) {
+  base::ThreadRestrictions::AssertIOAllowed();
   if (base::PathExists(path.Append(FILE_PATH_LITERAL(".devtools")))) {
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
-        BindOnce(&DevToolsFileHelper::InnerAddFileSystem,
-                 weak_factory_.GetWeakPtr(), show_info_bar_callback, path));
+        BindOnce(&DevToolsFileHelper::InnerAddFileSystem, std::move(self),
+                 std::move(show_info_bar_callback), path));
   }
 }
 
@@ -387,8 +389,10 @@
   file_system_paths_ = GetAddedFileSystemPaths(profile_);
   std::vector<FileSystem> file_systems;
   if (!file_watcher_) {
-    file_watcher_.reset(new DevToolsFileWatcher(base::Bind(
-        &DevToolsFileHelper::FilePathsChanged, weak_factory_.GetWeakPtr())));
+    file_watcher_.reset(new DevToolsFileWatcher(
+        base::Bind(&DevToolsFileHelper::FilePathsChanged,
+                   weak_factory_.GetWeakPtr()),
+        base::SequencedTaskRunnerHandle::Get()));
     pref_change_registrar_.Add(
         prefs::kDevToolsFileSystemPaths,
         base::Bind(&DevToolsFileHelper::FileSystemPathsSettingChanged,
@@ -401,10 +405,7 @@
                                                    file_system_id,
                                                    file_system_path);
     file_systems.push_back(filesystem);
-    BrowserThread::PostTask(
-        BrowserThread::FILE, FROM_HERE,
-        BindOnce(&DevToolsFileWatcher::AddWatch,
-                 base::Unretained(file_watcher_.get()), path));
+    file_watcher_->AddWatch(std::move(path));
   }
   return file_systems;
 }
@@ -440,10 +441,7 @@
                                                      file_system_id,
                                                      file_system_path);
       delegate_->FileSystemAdded(filesystem);
-      BrowserThread::PostTask(
-          BrowserThread::FILE, FROM_HERE,
-          BindOnce(&DevToolsFileWatcher::AddWatch,
-                   base::Unretained(file_watcher_.get()), path));
+      file_watcher_->AddWatch(std::move(path));
     } else {
       remaining.erase(file_system_path);
     }
@@ -453,10 +451,7 @@
   for (auto file_system_path : remaining) {
     delegate_->FileSystemRemoved(file_system_path);
     base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
-    BrowserThread::PostTask(
-        BrowserThread::FILE, FROM_HERE,
-        BindOnce(&DevToolsFileWatcher::RemoveWatch,
-                 base::Unretained(file_watcher_.get()), path));
+    file_watcher_->RemoveWatch(std::move(path));
   }
 }
 
diff --git a/chrome/browser/devtools/devtools_file_helper.h b/chrome/browser/devtools/devtools_file_helper.h
index e613d02..cb37572 100644
--- a/chrome/browser/devtools/devtools_file_helper.h
+++ b/chrome/browser/devtools/devtools_file_helper.h
@@ -16,13 +16,14 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
+#include "chrome/browser/devtools/devtools_file_watcher.h"
 #include "components/prefs/pref_change_registrar.h"
 
-class DevToolsFileWatcher;
 class Profile;
 
 namespace base {
 class FilePath;
+class SequencedTaskRunner;
 }
 
 namespace content {
@@ -128,9 +129,6 @@
   void InnerAddFileSystem(
       const ShowInfoBarCallback& show_info_bar_callback,
       const base::FilePath& path);
-  void CheckProjectFileExistsAndAddFileSystem(
-      const ShowInfoBarCallback& show_info_bar_callback,
-      const base::FilePath& path);
   void AddUserConfirmedFileSystem(
       const base::FilePath& path,
       bool allowed);
@@ -139,6 +137,12 @@
                         const std::vector<std::string>& added_paths,
                         const std::vector<std::string>& removed_paths);
 
+  // This should only be called on the file sequence.
+  static void CheckProjectFileExistsAndAddFileSystem(
+      base::WeakPtr<DevToolsFileHelper> self,
+      ShowInfoBarCallback show_info_bar_callback,
+      base::FilePath path);
+
   content::WebContents* web_contents_;
   Profile* profile_;
   DevToolsFileHelper::Delegate* delegate_;
@@ -146,7 +150,9 @@
   PathsMap saved_files_;
   PrefChangeRegistrar pref_change_registrar_;
   std::set<std::string> file_system_paths_;
-  std::unique_ptr<DevToolsFileWatcher> file_watcher_;
+  std::unique_ptr<DevToolsFileWatcher, DevToolsFileWatcher::Deleter>
+      file_watcher_;
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
   base::WeakPtrFactory<DevToolsFileHelper> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(DevToolsFileHelper);
 };
diff --git a/chrome/browser/devtools/devtools_file_watcher.cc b/chrome/browser/devtools/devtools_file_watcher.cc
index e97ef21..1f0f1bd 100644
--- a/chrome/browser/devtools/devtools_file_watcher.cc
+++ b/chrome/browser/devtools/devtools_file_watcher.cc
@@ -15,6 +15,9 @@
 #include "base/files/file_path_watcher.h"
 #include "base/files/file_util.h"
 #include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_scheduler/lazy_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "content/public/browser/browser_thread.h"
 
 using content::BrowserThread;
@@ -51,6 +54,7 @@
   std::set<base::FilePath> pending_paths_;
   base::Time last_event_time_;
   base::TimeDelta last_dispatch_cost_;
+  SEQUENCE_CHECKER(sequence_checker_);
 };
 
 DevToolsFileWatcher::SharedFileWatcher::SharedFileWatcher()
@@ -60,22 +64,26 @@
 }
 
 DevToolsFileWatcher::SharedFileWatcher::~SharedFileWatcher() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DevToolsFileWatcher::s_shared_watcher_ = nullptr;
 }
 
 void DevToolsFileWatcher::SharedFileWatcher::AddListener(
     DevToolsFileWatcher* watcher) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   listeners_.push_back(watcher);
 }
 
 void DevToolsFileWatcher::SharedFileWatcher::RemoveListener(
     DevToolsFileWatcher* watcher) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = std::find(listeners_.begin(), listeners_.end(), watcher);
   listeners_.erase(it);
 }
 
 void DevToolsFileWatcher::SharedFileWatcher::AddWatch(
     const base::FilePath& path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (watchers_.find(path) != watchers_.end())
     return;
   if (!base::FilePathWatcher::RecursiveWatchAvailable())
@@ -104,13 +112,14 @@
 
 void DevToolsFileWatcher::SharedFileWatcher::RemoveWatch(
     const base::FilePath& path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   watchers_.erase(path);
 }
 
 void DevToolsFileWatcher::SharedFileWatcher::DirectoryChanged(
     const base::FilePath& path,
     bool error) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   pending_paths_.insert(path);
   if (pending_paths_.size() > 1)
     return;  // PostDelayedTask is already pending.
@@ -122,8 +131,8 @@
           base::TimeDelta::FromMilliseconds(kFirstThrottleTimeout) :
           last_dispatch_cost_ * 2;
 
-  BrowserThread::PostDelayedTask(
-      BrowserThread::FILE, FROM_HERE,
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
       base::BindOnce(
           &DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications, this),
       shedule_for);
@@ -131,6 +140,7 @@
 }
 
 void DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!pending_paths_.size())
     return;
   base::Time start = base::Time::Now();
@@ -160,30 +170,49 @@
   pending_paths_.clear();
 
   for (auto* watcher : listeners_) {
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::BindOnce(watcher->callback_, changed_paths,
-                                           added_paths, removed_paths));
+    watcher->client_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(watcher->callback_, changed_paths,
+                                  added_paths, removed_paths));
   }
   last_dispatch_cost_ = base::Time::Now() - start;
 }
 
+// DevToolsFileWatcher ---------------------------------------------------------
+
+namespace {
+base::SequencedTaskRunner* impl_task_runner() {
+  constexpr base::TaskTraits kImplTaskTraits = {base::MayBlock(),
+                                                base::TaskPriority::BACKGROUND};
+  static base::LazySequencedTaskRunner s_file_task_runner =
+      LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(kImplTaskTraits);
+
+  return s_file_task_runner.Get().get();
+}
+}  // namespace
+
 // static
 DevToolsFileWatcher::SharedFileWatcher*
 DevToolsFileWatcher::s_shared_watcher_ = nullptr;
 
-// DevToolsFileWatcher ---------------------------------------------------------
+// static
+void DevToolsFileWatcher::Deleter::operator()(const DevToolsFileWatcher* ptr) {
+  impl_task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&DevToolsFileWatcher::Destroy, base::Unretained(ptr)));
+}
 
-DevToolsFileWatcher::DevToolsFileWatcher(const WatchCallback& callback)
-    : callback_(callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  BrowserThread::PostTask(
-      BrowserThread::FILE, FROM_HERE,
-      base::BindOnce(&DevToolsFileWatcher::InitSharedWatcher,
-                     base::Unretained(this)));
+DevToolsFileWatcher::DevToolsFileWatcher(
+    WatchCallback callback,
+    scoped_refptr<base::SequencedTaskRunner> callback_task_runner)
+    : callback_(std::move(callback)),
+      client_task_runner_(std::move(callback_task_runner)) {
+  impl_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&DevToolsFileWatcher::InitSharedWatcher,
+                                base::Unretained(this)));
 }
 
 DevToolsFileWatcher::~DevToolsFileWatcher() {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+  DCHECK(impl_task_runner()->RunsTasksInCurrentSequence());
   shared_watcher_->RemoveListener(this);
 }
 
@@ -194,12 +223,16 @@
   shared_watcher_->AddListener(this);
 }
 
-void DevToolsFileWatcher::AddWatch(const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-  shared_watcher_->AddWatch(path);
+void DevToolsFileWatcher::AddWatch(base::FilePath path) {
+  impl_task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SharedFileWatcher::AddWatch,
+                     base::Unretained(shared_watcher_.get()), std::move(path)));
 }
 
-void DevToolsFileWatcher::RemoveWatch(const base::FilePath& path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-  shared_watcher_->RemoveWatch(path);
+void DevToolsFileWatcher::RemoveWatch(base::FilePath path) {
+  impl_task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SharedFileWatcher::RemoveWatch,
+                     base::Unretained(shared_watcher_.get()), std::move(path)));
 }
diff --git a/chrome/browser/devtools/devtools_file_watcher.h b/chrome/browser/devtools/devtools_file_watcher.h
index eb597fccf..a0f6bd6d 100644
--- a/chrome/browser/devtools/devtools_file_watcher.h
+++ b/chrome/browser/devtools/devtools_file_watcher.h
@@ -12,28 +12,38 @@
 
 namespace base {
 class FilePath;
+class SequencedTaskRunner;
 }
 
 class DevToolsFileWatcher {
  public:
+  struct Deleter {
+    void operator()(const DevToolsFileWatcher* ptr);
+  };
+
   using WatchCallback = base::Callback<void(const std::vector<std::string>&,
                                             const std::vector<std::string>&,
                                             const std::vector<std::string>&)>;
-  explicit DevToolsFileWatcher(const WatchCallback& callback);
-  ~DevToolsFileWatcher();
+  DevToolsFileWatcher(
+      WatchCallback callback,
+      scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
 
-  void AddWatch(const base::FilePath& path);
-  void RemoveWatch(const base::FilePath& path);
+  void AddWatch(base::FilePath path);
+  void RemoveWatch(base::FilePath path);
 
  private:
+  ~DevToolsFileWatcher();  // Use Deleter to destroy objects of this type.
   class SharedFileWatcher;
   static SharedFileWatcher* s_shared_watcher_;
 
+  void Destroy() const { delete this; }
   void InitSharedWatcher();
   void FileChanged(const base::FilePath&, int);
 
   scoped_refptr<SharedFileWatcher> shared_watcher_;
   WatchCallback callback_;
+  scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
+
   DISALLOW_COPY_AND_ASSIGN(DevToolsFileWatcher);
 };
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 12c367a..c9876e02 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1055,6 +1055,11 @@
     "flag. The trace may include personally identifiable information (PII) "
     "such as the titles and URLs of websites you visit.";
 
+const char kEnableNightLightName[] = "Enable Night Light";
+const char kEnableNightLightDescription[] =
+    "Enable the Night Light feature to control the color temperature of the "
+    "screen.";
+
 const char kEnablePictureInPictureName[] = "Enable picture in picture.";
 
 const char kEnablePictureInPictureDescription[] =
@@ -1076,11 +1081,6 @@
     "With this flag on, desktop share picker window will not let the user "
     "choose whether to share audio.";
 
-const char kDisableNightLightName[] = "Disable Night Light";
-const char kDisableNightLightDescription[] =
-    "Disable the Night Light feature which controls the color temperature of "
-    "the screen.";
-
 const char kDisableTabForDesktopShareName[] =
     "Disable Desktop Share with tab source";
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 5bf504af..06fac16 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -139,9 +139,6 @@
 extern const char kDisableAudioForDesktopShareName[];
 extern const char kDisableAudioForDesktopShareDescription[];
 
-extern const char kDisableNightLightName[];
-extern const char kDisableNightLightDescription[];
-
 extern const char kDisableTabForDesktopShareName[];
 extern const char kDisableTabForDesktopShareDescription[];
 
@@ -239,6 +236,9 @@
 extern const char kEnableNavigationTracingName[];
 extern const char kEnableNavigationTracingDescription[];
 
+extern const char kEnableNightLightName[];
+extern const char kEnableNightLightDescription[];
+
 extern const char kEnablePictureInPictureName[];
 extern const char kEnablePictureInPictureDescription[];
 
diff --git a/chrome/browser/offline_pages/background_loader_offliner.cc b/chrome/browser/offline_pages/background_loader_offliner.cc
index fa299ab..c5203bb 100644
--- a/chrome/browser/offline_pages/background_loader_offliner.cc
+++ b/chrome/browser/offline_pages/background_loader_offliner.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h"
 #include "chrome/browser/android/offline_pages/offliner_helper.h"
 #include "chrome/browser/loader/chrome_navigation_data.h"
+#include "chrome/browser/offline_pages/offliner_user_data.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
@@ -38,28 +39,6 @@
     "Content-Transfer-Encoding: binary";
 const char kXHeaderForSignals[] = "X-Chrome-Loading-Metrics-Data: 1";
 
-class OfflinerData : public content::WebContentsUserData<OfflinerData> {
- public:
-  static void AddToWebContents(content::WebContents* webcontents,
-                               BackgroundLoaderOffliner* offliner) {
-    DCHECK(offliner);
-    webcontents->SetUserData(UserDataKey(), std::unique_ptr<OfflinerData>(
-                                                new OfflinerData(offliner)));
-  }
-
-  explicit OfflinerData(BackgroundLoaderOffliner* offliner) {
-    offliner_ = offliner;
-  }
-  BackgroundLoaderOffliner* offliner() { return offliner_; }
-
- private:
-  // The offliner that the WebContents is attached to. The offliner owns the
-  // Delegate which owns the WebContents that this data is attached to.
-  // Therefore, its lifetime should exceed that of the WebContents, so this
-  // should always be non-null.
-  BackgroundLoaderOffliner* offliner_;
-};
-
 std::string AddHistogramSuffix(const ClientId& client_id,
                                const char* histogram_name) {
   if (client_id.name_space.empty()) {
@@ -138,9 +117,11 @@
 // static
 BackgroundLoaderOffliner* BackgroundLoaderOffliner::FromWebContents(
     content::WebContents* contents) {
-  OfflinerData* data = OfflinerData::FromWebContents(contents);
-  if (data)
-    return data->offliner();
+  Offliner* offliner = OfflinerUserData::OfflinerFromWebContents(contents);
+  // Today we only have one kind of offliner that uses OfflinerUserData.  If we
+  // add other types, revisit this cast.
+  if (offliner)
+    return static_cast<BackgroundLoaderOffliner*>(offliner);
   return nullptr;
 }
 
@@ -364,6 +345,12 @@
     std::unique_ptr<SnapshotController> controller) {
   snapshot_controller_ = std::move(controller);
 }
+void BackgroundLoaderOffliner::ObserveResourceLoading(
+    ResourceLoadingObserver::ResourceDataType type,
+    bool started) {
+  // TODO(petewil) Not implemented yet.
+  return;
+}
 
 void BackgroundLoaderOffliner::OnNetworkBytesChanged(int64_t bytes) {
   if (pending_request_ && save_state_ != SAVING) {
@@ -521,7 +508,7 @@
 void BackgroundLoaderOffliner::AttachObservers() {
   content::WebContents* contents = loader_->web_contents();
   content::WebContentsObserver::Observe(contents);
-  OfflinerData::AddToWebContents(contents, this);
+  OfflinerUserData::AddToWebContents(contents, this);
 }
 
 void BackgroundLoaderOffliner::AddLoadingSignal(const char* signal_name) {
@@ -536,5 +523,3 @@
 }
 
 }  // namespace offline_pages
-
-DEFINE_WEB_CONTENTS_USER_DATA_KEY(offline_pages::OfflinerData);
diff --git a/chrome/browser/offline_pages/background_loader_offliner.h b/chrome/browser/offline_pages/background_loader_offliner.h
index bace442..560e830 100644
--- a/chrome/browser/offline_pages/background_loader_offliner.h
+++ b/chrome/browser/offline_pages/background_loader_offliner.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
+#include "chrome/browser/offline_pages/resource_loading_observer.h"
 #include "components/offline_pages/content/background_loader/background_loader_contents.h"
 #include "components/offline_pages/core/background/load_termination_listener.h"
 #include "components/offline_pages/core/background/offliner.h"
@@ -30,7 +31,8 @@
 // OfflinePageModel to save it. Only one request may be active at a time.
 class BackgroundLoaderOffliner : public Offliner,
                                  public content::WebContentsObserver,
-                                 public SnapshotController::Client {
+                                 public SnapshotController::Client,
+                                 public ResourceLoadingObserver {
  public:
   BackgroundLoaderOffliner(
       content::BrowserContext* browser_context,
@@ -63,7 +65,11 @@
 
   void SetSnapshotControllerForTest(
       std::unique_ptr<SnapshotController> controller);
-  void OnNetworkBytesChanged(int64_t bytes);
+
+  // ResourceLoadingObserver implemenation
+  void ObserveResourceLoading(ResourceLoadingObserver::ResourceDataType type,
+                              bool started) override;
+  void OnNetworkBytesChanged(int64_t bytes) override;
 
  protected:
   // Called to reset the loader.
diff --git a/chrome/browser/offline_pages/offliner_user_data.cc b/chrome/browser/offline_pages/offliner_user_data.cc
new file mode 100644
index 0000000..0dc7273a
--- /dev/null
+++ b/chrome/browser/offline_pages/offliner_user_data.cc
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/offline_pages/offliner_user_data.h"
+
+class ResourceTrackerObserver;
+
+namespace offline_pages {
+
+void OfflinerUserData::AddToWebContents(content::WebContents* webcontents,
+                                        BackgroundLoaderOffliner* offliner) {
+  DCHECK(offliner);
+  webcontents->SetUserData(UserDataKey(), std::unique_ptr<OfflinerUserData>(
+                                              new OfflinerUserData(offliner)));
+}
+
+// static - gets the data pointer as a BackgroundLoaderOffliner
+BackgroundLoaderOffliner* OfflinerUserData::OfflinerFromWebContents(
+    content::WebContents* webcontents) {
+  OfflinerUserData* data = OfflinerUserData::FromWebContents(webcontents);
+  if (data)
+    return data->offliner();
+
+  return nullptr;
+}
+
+// static - gets the data pointer as a ResourceLoadingObserver
+ResourceLoadingObserver*
+OfflinerUserData::ResourceLoadingObserverFromWebContents(
+    content::WebContents* webcontents) {
+  OfflinerUserData* data = OfflinerUserData::FromWebContents(webcontents);
+  if (data)
+    return data->offliner();
+
+  return nullptr;
+}
+
+}  // namespace offline_pages
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(offline_pages::OfflinerUserData);
diff --git a/chrome/browser/offline_pages/offliner_user_data.h b/chrome/browser/offline_pages/offliner_user_data.h
new file mode 100644
index 0000000..9ed7c6a
--- /dev/null
+++ b/chrome/browser/offline_pages/offliner_user_data.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_OFFLINER_USER_DATA_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_OFFLINER_USER_DATA_H_
+
+#include "chrome/browser/offline_pages/background_loader_offliner.h"
+#include "chrome/browser/offline_pages/resource_loading_observer.h"
+#include "components/offline_pages/features/features.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace offline_pages {
+
+class OfflinerUserData : public content::WebContentsUserData<OfflinerUserData> {
+ public:
+  static void AddToWebContents(content::WebContents* webcontents,
+                               BackgroundLoaderOffliner* offliner);
+
+  static BackgroundLoaderOffliner* OfflinerFromWebContents(
+      content::WebContents* webcontents);
+
+  static ResourceLoadingObserver* ResourceLoadingObserverFromWebContents(
+      content::WebContents* webcontents);
+
+  explicit OfflinerUserData(BackgroundLoaderOffliner* offliner) {
+    offliner_ = offliner;
+  }
+  BackgroundLoaderOffliner* offliner() { return offliner_; }
+
+ private:
+  // The offliner that the WebContents is attached to. The offliner owns the
+  // Delegate which owns the WebContents that this data is attached to.
+  // Therefore, its lifetime should exceed that of the WebContents, so this
+  // should always be non-null.
+  BackgroundLoaderOffliner* offliner_;
+};
+
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_OFFLINER_USER_DATA_H_
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
index 92eed1c4..eeb6acd7 100644
--- a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
+++ b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
@@ -14,7 +14,6 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h"
-#include "components/offline_pages/core/prefetch/prefetch_in_memory_store.h"
 #include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
 #include "content/public/browser/browser_context.h"
@@ -47,12 +46,11 @@
       base::MakeUnique<OfflineMetricsCollectorImpl>();
   auto suggested_articles_observer =
       base::MakeUnique<SuggestedArticlesObserver>(prefetch_dispatcher.get());
-  auto in_memory_store = base::MakeUnique<PrefetchInMemoryStore>();
 
-  return new PrefetchServiceImpl(
-      std::move(offline_metrics_collector), std::move(prefetch_dispatcher),
-      std::move(prefetch_gcm_app_handler), std::move(in_memory_store),
-      std::move(suggested_articles_observer));
+  return new PrefetchServiceImpl(std::move(offline_metrics_collector),
+                                 std::move(prefetch_dispatcher),
+                                 std::move(prefetch_gcm_app_handler),
+                                 std::move(suggested_articles_observer));
 }
 
 }  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/resource_loading_observer.h b/chrome/browser/offline_pages/resource_loading_observer.h
new file mode 100644
index 0000000..6a6e90d
--- /dev/null
+++ b/chrome/browser/offline_pages/resource_loading_observer.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_RESOURCE_LOADING_OBSERVER_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_RESOURCE_LOADING_OBSERVER_H_
+
+#include <stdint.h>
+
+namespace offline_pages {
+
+// This interface is used by clients who want to be notified when a resource is
+// requested or completes loading, and to report the size of the resource.
+class ResourceLoadingObserver {
+ public:
+  enum ResourceDataType {
+    IMAGE,
+    TEXT_CSS,
+    OTHER,
+    RESOURCE_DATA_TYPE_COUNT,
+  };
+
+  // Report when a resource starts or completes loading.
+  virtual void ObserveResourceLoading(ResourceDataType type, bool started) = 0;
+
+  // Report how many bytes were received for a resource.
+  virtual void OnNetworkBytesChanged(int64_t received_bytes) = 0;
+};
+
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_RESOURCE_LOADING_OBSERVER_H_
diff --git a/chrome/browser/plugins/flash_download_interception.cc b/chrome/browser/plugins/flash_download_interception.cc
index b4f6678..f9a7b44 100644
--- a/chrome/browser/plugins/flash_download_interception.cc
+++ b/chrome/browser/plugins/flash_download_interception.cc
@@ -159,5 +159,5 @@
   }
 
   return base::MakeUnique<navigation_interception::InterceptNavigationThrottle>(
-      handle, base::Bind(&InterceptNavigation, source_url), true);
+      handle, base::Bind(&InterceptNavigation, source_url));
 }
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index bf641e6..6d143fa 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -119,7 +119,7 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/printer_detector/printer_detector_factory.h"
+#include "chrome/browser/chromeos/printer_detector/usb_printer_detector_factory.h"
 #include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
 #include "chrome/browser/chromeos/printing/printers_manager_factory.h"
 #include "chrome/browser/chromeos/tether/tether_service_factory.h"
@@ -230,7 +230,7 @@
   EnhancedBookmarkKeyServiceFactory::GetInstance();
 #endif
 #if defined(OS_CHROMEOS)
-  chromeos::PrinterDetectorFactory::GetInstance();
+  chromeos::UsbPrinterDetectorFactory::GetInstance();
   chromeos::CupsPrintJobManagerFactory::GetInstance();
   chromeos::PrintersManagerFactory::GetInstance();
   TetherServiceFactory::GetInstance();
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/externs.js b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/externs.js
index 5d6280b..bf3e55c 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/externs.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/externs.js
@@ -162,12 +162,3 @@
  * @typedef {Object}
  */
 function mediaWiki() {}
-
-
-/**
- * @param {?function(this:S, T, number, !Array<T>): ?} callback
- * @param {S=} opt_thisobj
- * @this {{length: number}|Array<T>}
- * @template T,S
- */
-NodeList.prototype.forEach = function(callback, opt_thisobj) {};
diff --git a/chrome/browser/resources/extensions/extensions.css b/chrome/browser/resources/extensions/extensions.css
index 011d0f7..57468d4 100644
--- a/chrome/browser/resources/extensions/extensions.css
+++ b/chrome/browser/resources/extensions/extensions.css
@@ -98,6 +98,16 @@
   line-height: 150%;
 }
 
+#page-header {
+  -webkit-padding-start: 23px;
+  left: 0;
+}
+
+[dir='rtl'] #page-header {
+  left: auto;
+  right: 0;
+}
+
 #page-header > .page-banner > .page-banner-gradient {
   -webkit-margin-end: 0;
 }
diff --git a/chrome/browser/safe_browsing/browser_url_loader_throttle.cc b/chrome/browser/safe_browsing/browser_url_loader_throttle.cc
new file mode 100644
index 0000000..cc4ec69
--- /dev/null
+++ b/chrome/browser/safe_browsing/browser_url_loader_throttle.cc
@@ -0,0 +1,87 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/browser_url_loader_throttle.h"
+
+#include "base/logging.h"
+#include "chrome/browser/safe_browsing/safe_browsing_url_checker_impl.h"
+#include "chrome/browser/safe_browsing/ui_manager.h"
+#include "components/safe_browsing_db/database_manager.h"
+#include "net/url_request/redirect_info.h"
+
+namespace safe_browsing {
+
+BrowserURLLoaderThrottle::BrowserURLLoaderThrottle(
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    scoped_refptr<SafeBrowsingUIManager> ui_manager,
+    const base::Callback<content::WebContents*()>& web_contents_getter)
+    : database_manager_(database_manager),
+      ui_manager_(ui_manager),
+      web_contents_getter_(web_contents_getter) {}
+
+BrowserURLLoaderThrottle::~BrowserURLLoaderThrottle() = default;
+
+void BrowserURLLoaderThrottle::WillStartRequest(
+    const GURL& url,
+    int load_flags,
+    content::ResourceType resource_type,
+    bool* defer) {
+  DCHECK_EQ(0u, pending_checks_);
+  DCHECK(!blocked_);
+  DCHECK(!url_checker_);
+
+  pending_checks_++;
+  url_checker_ = base::MakeUnique<SafeBrowsingUrlCheckerImpl>(
+      load_flags, resource_type, std::move(database_manager_),
+      std::move(ui_manager_), web_contents_getter_);
+  url_checker_->CheckUrl(url,
+                         base::Bind(&BrowserURLLoaderThrottle::OnCheckUrlResult,
+                                    base::Unretained(this)));
+}
+
+void BrowserURLLoaderThrottle::WillRedirectRequest(
+    const net::RedirectInfo& redirect_info,
+    bool* defer) {
+  // If |blocked_| is true, the resource load has been canceled and there
+  // shouldn't be such a notification.
+  DCHECK(!blocked_);
+
+  pending_checks_++;
+  url_checker_->CheckUrl(
+      redirect_info.new_url,
+      base::BindOnce(&BrowserURLLoaderThrottle::OnCheckUrlResult,
+                     base::Unretained(this)));
+}
+
+void BrowserURLLoaderThrottle::WillProcessResponse(bool* defer) {
+  // If |blocked_| is true, the resource load has been canceled and there
+  // shouldn't be such a notification.
+  DCHECK(!blocked_);
+
+  if (pending_checks_ > 0)
+    *defer = true;
+}
+
+void BrowserURLLoaderThrottle::OnCheckUrlResult(bool safe) {
+  if (blocked_)
+    return;
+
+  DCHECK_LT(0u, pending_checks_);
+  pending_checks_--;
+
+  if (safe) {
+    if (pending_checks_ == 0) {
+      // The resource load is not necessarily deferred, in that case Resume() is
+      // a no-op.
+      delegate_->Resume();
+    }
+  } else {
+    url_checker_.reset();
+    blocked_ = true;
+    pending_checks_ = 0;
+    delegate_->CancelWithError(net::ERR_ABORTED);
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/browser_url_loader_throttle.h b/chrome/browser/safe_browsing/browser_url_loader_throttle.h
new file mode 100644
index 0000000..1eca9d49
--- /dev/null
+++ b/chrome/browser/safe_browsing/browser_url_loader_throttle.h
@@ -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.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_BROWSER_URL_LOADER_THROTTLE_H_
+#define CHROME_BROWSER_SAFE_BROWSING_BROWSER_URL_LOADER_THROTTLE_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/common/url_loader_throttle.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace safe_browsing {
+
+class SafeBrowsingDatabaseManager;
+class SafeBrowsingUIManager;
+class SafeBrowsingUrlCheckerImpl;
+
+// BrowserURLLoaderThrottle is used in the browser process to query
+// SafeBrowsing to determine whether a URL and also its redirect URLs are safe
+// to load. It defers response processing until all URL checks are completed;
+// cancels the load if any URLs turn out to be bad.
+// Used when --enable-network-service is in effect.
+class BrowserURLLoaderThrottle : public content::URLLoaderThrottle {
+ public:
+  // |web_contents_getter| is used for displaying SafeBrowsing UI when
+  // necessary.
+  BrowserURLLoaderThrottle(
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      scoped_refptr<SafeBrowsingUIManager> ui_manager,
+      const base::Callback<content::WebContents*()>& web_contents_getter);
+  ~BrowserURLLoaderThrottle() override;
+
+  // content::URLLoaderThrottle implementation.
+  void WillStartRequest(const GURL& url,
+                        int load_flags,
+                        content::ResourceType resource_type,
+                        bool* defer) override;
+  void WillRedirectRequest(const net::RedirectInfo& redirect_info,
+                           bool* defer) override;
+  void WillProcessResponse(bool* defer) override;
+
+ private:
+  void OnCheckUrlResult(bool safe);
+
+  // The following two members stay valid until |url_checker_| is created.
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+  scoped_refptr<SafeBrowsingUIManager> ui_manager_;
+
+  base::Callback<content::WebContents*()> web_contents_getter_;
+
+  std::unique_ptr<SafeBrowsingUrlCheckerImpl> url_checker_;
+
+  size_t pending_checks_ = 0;
+  bool blocked_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserURLLoaderThrottle);
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_BROWSER_SAFE_BROWSING_BROWSER_URL_LOADER_THROTTLE_H_
diff --git a/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc b/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc
index 8aed827..67e38a72 100644
--- a/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc
+++ b/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc
@@ -7,10 +7,7 @@
 #include <vector>
 
 #include "base/memory/ptr_util.h"
-#include "base/memory/weak_ptr.h"
-#include "base/timer/timer.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/safe_browsing/safe_browsing_url_checker_impl.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "content/public/browser/browser_thread.h"
@@ -23,12 +20,6 @@
 namespace safe_browsing {
 namespace {
 
-// TODO(yzshen): Share such value with safe_browsing::BaseResourceThrottle.
-// Maximum time in milliseconds to wait for the SafeBrowsing service reputation
-// check. After this amount of time the outstanding check will be aborted, and
-// the resource will be treated as if it were safe.
-const int kCheckUrlTimeoutMs = 5000;
-
 content::WebContents* GetWebContentsFromID(int render_process_id,
                                            int render_frame_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -40,222 +31,23 @@
   return content::WebContents::FromRenderFrameHost(render_frame_host);
 }
 
-// TODO(yzshen): Handle the case where SafeBrowsing is not enabled, or
-// !database_manager()->IsSupported().
-// TODO(yzshen): Make sure it also works on Andorid.
-// TODO(yzshen): Do all the logging like what BaseResourceThrottle does.
-class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
-                                   public SafeBrowsingDatabaseManager::Client {
+// This class wraps a base::OnceCallback<void(bool)> and runs it on destruction,
+// if it hasn't been run yet.
+class BooleanCallbackWrapper {
  public:
-  SafeBrowsingUrlCheckerImpl(
-      int load_flags,
-      content::ResourceType resource_type,
-      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-      scoped_refptr<SafeBrowsingUIManager> ui_manager,
-      int render_process_id,
-      int render_frame_id)
-      : load_flags_(load_flags),
-        resource_type_(resource_type),
-        render_process_id_(render_process_id),
-        render_frame_id_(render_frame_id),
-        database_manager_(std::move(database_manager)),
-        ui_manager_(std::move(ui_manager)),
-        weak_factory_(this) {
-    DCHECK_NE(resource_type, content::RESOURCE_TYPE_MAIN_FRAME);
-    DCHECK_NE(resource_type, content::RESOURCE_TYPE_SUB_FRAME);
+  using BooleanCallback = base::OnceCallback<void(bool)>;
+
+  explicit BooleanCallbackWrapper(BooleanCallback callback)
+      : callback_(std::move(callback)) {}
+  ~BooleanCallbackWrapper() {
+    if (callback_)
+      Run(false);
   }
 
-  ~SafeBrowsingUrlCheckerImpl() override {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-    if (state_ == STATE_CHECKING_URL)
-      database_manager_->CancelCheck(this);
-
-    for (size_t i = next_index_; i < callbacks_.size(); ++i)
-      std::move(callbacks_[i]).Run(false);
-  }
-
-  // mojom::SafeBrowsingUrlChecker implementation.
-  void CheckUrl(const GURL& url, CheckUrlCallback callback) override {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-    DVLOG(1) << "SafeBrowsingUrlCheckerImpl checks URL: " << url;
-    urls_.push_back(url);
-    callbacks_.push_back(std::move(callback));
-
-    ProcessUrls();
-  }
+  void Run(bool value) { std::move(callback_).Run(value); }
 
  private:
-  // SafeBrowsingDatabaseManager::Client implementation:
-  void OnCheckBrowseUrlResult(const GURL& url,
-                              SBThreatType threat_type,
-                              const ThreatMetadata& metadata) override {
-    DCHECK_EQ(STATE_CHECKING_URL, state_);
-    DCHECK_LT(next_index_, urls_.size());
-    DCHECK_EQ(urls_[next_index_], url);
-
-    timer_.Stop();
-    if (threat_type == SB_THREAT_TYPE_SAFE) {
-      state_ = STATE_NONE;
-      std::move(callbacks_[next_index_]).Run(true);
-      next_index_++;
-      ProcessUrls();
-      return;
-    }
-
-    if (load_flags_ & net::LOAD_PREFETCH) {
-      BlockAndProcessUrls();
-      return;
-    }
-
-    security_interstitials::UnsafeResource resource;
-    resource.url = url;
-    resource.original_url = urls_[0];
-    if (urls_.size() > 1)
-      resource.redirect_urls =
-          std::vector<GURL>(urls_.begin() + 1, urls_.end());
-    resource.is_subresource = true;
-    resource.is_subframe = false;
-    resource.threat_type = threat_type;
-    resource.threat_metadata = metadata;
-    resource.callback =
-        base::Bind(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete,
-                   weak_factory_.GetWeakPtr());
-    resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread(
-        content::BrowserThread::IO);
-    resource.web_contents_getter =
-        base::Bind(&GetWebContentsFromID, render_process_id_, render_frame_id_);
-    resource.threat_source = database_manager_->GetThreatSource();
-
-    state_ = STATE_DISPLAYING_BLOCKING_PAGE;
-
-    content::BrowserThread::PostTask(
-        content::BrowserThread::UI, FROM_HERE,
-        base::Bind(&SafeBrowsingUrlCheckerImpl::StartDisplayingBlockingPage,
-                   weak_factory_.GetWeakPtr(), ui_manager_, resource));
-  }
-
-  static void StartDisplayingBlockingPage(
-      const base::WeakPtr<SafeBrowsingUrlCheckerImpl>& checker,
-      scoped_refptr<BaseUIManager> ui_manager,
-      const security_interstitials::UnsafeResource& resource) {
-    content::WebContents* web_contents = resource.web_contents_getter.Run();
-    if (web_contents) {
-      prerender::PrerenderContents* prerender_contents =
-          prerender::PrerenderContents::FromWebContents(web_contents);
-      if (prerender_contents) {
-        prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING);
-      } else {
-        ui_manager->DisplayBlockingPage(resource);
-        return;
-      }
-    }
-
-    // Tab is gone or it's being prerendered.
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::BindOnce(&SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls,
-                       checker));
-  }
-
-  void OnCheckUrlTimeout() {
-    database_manager_->CancelCheck(this);
-
-    OnCheckBrowseUrlResult(urls_[next_index_],
-                           safe_browsing::SB_THREAT_TYPE_SAFE,
-                           ThreatMetadata());
-  }
-
-  void ProcessUrls() {
-    DCHECK_NE(STATE_BLOCKED, state_);
-
-    if (state_ == STATE_CHECKING_URL ||
-        state_ == STATE_DISPLAYING_BLOCKING_PAGE) {
-      return;
-    }
-
-    while (next_index_ < urls_.size()) {
-      DCHECK_EQ(STATE_NONE, state_);
-      // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer
-      // side. That would save some IPCs. It requires a method on the
-      // SafeBrowsing mojo interface to query all supported resource types.
-      if (!database_manager_->CanCheckResourceType(resource_type_) ||
-          database_manager_->CheckBrowseUrl(urls_[next_index_], this)) {
-        std::move(callbacks_[next_index_]).Run(true);
-        next_index_++;
-        continue;
-      }
-
-      state_ = STATE_CHECKING_URL;
-      // Start a timer to abort the check if it takes too long.
-      timer_.Start(FROM_HERE,
-                   base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this,
-                   &SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout);
-
-      break;
-    }
-  }
-
-  void BlockAndProcessUrls() {
-    DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: " << urls_[next_index_];
-    state_ = STATE_BLOCKED;
-
-    // If user decided to not proceed through a warning, mark all the remaining
-    // redirects as "bad".
-    for (; next_index_ < callbacks_.size(); ++next_index_)
-      std::move(callbacks_[next_index_]).Run(false);
-  }
-
-  void OnBlockingPageComplete(bool proceed) {
-    DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_);
-
-    if (proceed) {
-      state_ = STATE_NONE;
-      std::move(callbacks_[next_index_]).Run(true);
-      next_index_++;
-      ProcessUrls();
-    } else {
-      BlockAndProcessUrls();
-    }
-  }
-
-  enum State {
-    // Haven't started checking or checking is complete.
-    STATE_NONE,
-    // We have one outstanding URL-check.
-    STATE_CHECKING_URL,
-    // We're displaying a blocking page.
-    STATE_DISPLAYING_BLOCKING_PAGE,
-    // The blocking page has returned *not* to proceed.
-    STATE_BLOCKED
-  };
-
-  const int load_flags_;
-  const content::ResourceType resource_type_;
-  const int render_process_id_;
-  const int render_frame_id_;
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
-  scoped_refptr<BaseUIManager> ui_manager_;
-
-  // The redirect chain for this resource, including the original URL and
-  // subsequent redirect URLs.
-  std::vector<GURL> urls_;
-  // Callbacks corresponding to |urls_| to report check results. |urls_| and
-  // |callbacks_| are always of the same size.
-  std::vector<CheckUrlCallback> callbacks_;
-  // |urls_| before |next_index_| have been checked. If |next_index_| is smaller
-  // than the size of |urls_|, the URL at |next_index_| is being processed.
-  size_t next_index_ = 0;
-
-  State state_ = STATE_NONE;
-
-  // Timer to abort the SafeBrowsing check if it takes too long.
-  base::OneShotTimer timer_;
-
-  base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl);
+  BooleanCallback callback_;
 };
 
 }  // namespace
@@ -296,8 +88,14 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   auto checker_impl = base::MakeUnique<SafeBrowsingUrlCheckerImpl>(
       static_cast<int>(load_flags), resource_type, database_manager_,
-      ui_manager_, render_process_id_, static_cast<int>(render_frame_id));
-  checker_impl->CheckUrl(url, std::move(callback));
+      ui_manager_,
+      base::Bind(&GetWebContentsFromID, render_process_id_,
+                 static_cast<int>(render_frame_id)));
+
+  checker_impl->CheckUrl(
+      url, base::BindOnce(
+               &BooleanCallbackWrapper::Run,
+               base::Owned(new BooleanCallbackWrapper(std::move(callback)))));
   mojo::MakeStrongBinding(std::move(checker_impl), std::move(request));
 }
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_url_checker_impl.cc b/chrome/browser/safe_browsing/safe_browsing_url_checker_impl.cc
new file mode 100644
index 0000000..405d493
--- /dev/null
+++ b/chrome/browser/safe_browsing/safe_browsing_url_checker_impl.cc
@@ -0,0 +1,188 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/safe_browsing_url_checker_impl.h"
+
+#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/safe_browsing/ui_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/load_flags.h"
+
+namespace safe_browsing {
+namespace {
+
+// TODO(yzshen): Share such value with safe_browsing::BaseResourceThrottle.
+// Maximum time in milliseconds to wait for the SafeBrowsing service reputation
+// check. After this amount of time the outstanding check will be aborted, and
+// the resource will be treated as if it were safe.
+const int kCheckUrlTimeoutMs = 5000;
+
+}  // namespace
+
+SafeBrowsingUrlCheckerImpl::SafeBrowsingUrlCheckerImpl(
+    int load_flags,
+    content::ResourceType resource_type,
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    scoped_refptr<SafeBrowsingUIManager> ui_manager,
+    const base::Callback<content::WebContents*()>& web_contents_getter)
+    : load_flags_(load_flags),
+      resource_type_(resource_type),
+      web_contents_getter_(web_contents_getter),
+      database_manager_(std::move(database_manager)),
+      ui_manager_(std::move(ui_manager)),
+      weak_factory_(this) {}
+
+SafeBrowsingUrlCheckerImpl::~SafeBrowsingUrlCheckerImpl() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  if (state_ == STATE_CHECKING_URL)
+    database_manager_->CancelCheck(this);
+}
+
+void SafeBrowsingUrlCheckerImpl::CheckUrl(const GURL& url,
+                                          CheckUrlCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  DVLOG(1) << "SafeBrowsingUrlCheckerImpl checks URL: " << url;
+  urls_.push_back(url);
+  callbacks_.push_back(std::move(callback));
+
+  ProcessUrls();
+}
+
+void SafeBrowsingUrlCheckerImpl::OnCheckBrowseUrlResult(
+    const GURL& url,
+    SBThreatType threat_type,
+    const ThreatMetadata& metadata) {
+  DCHECK_EQ(STATE_CHECKING_URL, state_);
+  DCHECK_LT(next_index_, urls_.size());
+  DCHECK_EQ(urls_[next_index_], url);
+
+  timer_.Stop();
+  if (threat_type == SB_THREAT_TYPE_SAFE) {
+    state_ = STATE_NONE;
+    std::move(callbacks_[next_index_]).Run(true);
+    next_index_++;
+    ProcessUrls();
+    return;
+  }
+
+  if (load_flags_ & net::LOAD_PREFETCH) {
+    // TODO(yzshen): Destroy prerender contents if necessary.
+
+    BlockAndProcessUrls();
+    return;
+  }
+
+  security_interstitials::UnsafeResource resource;
+  resource.url = url;
+  resource.original_url = urls_[0];
+  if (urls_.size() > 1)
+    resource.redirect_urls = std::vector<GURL>(urls_.begin() + 1, urls_.end());
+  resource.is_subresource = resource_type_ != content::RESOURCE_TYPE_MAIN_FRAME;
+  resource.is_subframe = resource_type_ == content::RESOURCE_TYPE_SUB_FRAME;
+  resource.threat_type = threat_type;
+  resource.threat_metadata = metadata;
+  resource.callback =
+      base::Bind(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete,
+                 weak_factory_.GetWeakPtr());
+  resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread(
+      content::BrowserThread::IO);
+  resource.web_contents_getter = web_contents_getter_;
+  resource.threat_source = database_manager_->GetThreatSource();
+
+  state_ = STATE_DISPLAYING_BLOCKING_PAGE;
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::Bind(&SafeBrowsingUrlCheckerImpl::StartDisplayingBlockingPage,
+                 weak_factory_.GetWeakPtr(), ui_manager_, resource));
+}
+
+// static
+void SafeBrowsingUrlCheckerImpl::StartDisplayingBlockingPage(
+    const base::WeakPtr<SafeBrowsingUrlCheckerImpl>& checker,
+    scoped_refptr<BaseUIManager> ui_manager,
+    const security_interstitials::UnsafeResource& resource) {
+  content::WebContents* web_contents = resource.web_contents_getter.Run();
+  if (web_contents) {
+    prerender::PrerenderContents* prerender_contents =
+        prerender::PrerenderContents::FromWebContents(web_contents);
+    if (prerender_contents) {
+      prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING);
+    } else {
+      ui_manager->DisplayBlockingPage(resource);
+      return;
+    }
+  }
+
+  // Tab is gone or it's being prerendered.
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls,
+                     checker));
+}
+
+void SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout() {
+  database_manager_->CancelCheck(this);
+
+  OnCheckBrowseUrlResult(urls_[next_index_], safe_browsing::SB_THREAT_TYPE_SAFE,
+                         ThreatMetadata());
+}
+
+void SafeBrowsingUrlCheckerImpl::ProcessUrls() {
+  DCHECK_NE(STATE_BLOCKED, state_);
+
+  if (state_ == STATE_CHECKING_URL ||
+      state_ == STATE_DISPLAYING_BLOCKING_PAGE) {
+    return;
+  }
+
+  while (next_index_ < urls_.size()) {
+    DCHECK_EQ(STATE_NONE, state_);
+    // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer
+    // side. That would save some IPCs. It requires a method on the
+    // SafeBrowsing mojo interface to query all supported resource types.
+    if (!database_manager_->CanCheckResourceType(resource_type_) ||
+        database_manager_->CheckBrowseUrl(urls_[next_index_], this)) {
+      std::move(callbacks_[next_index_]).Run(true);
+      next_index_++;
+      continue;
+    }
+
+    state_ = STATE_CHECKING_URL;
+    // Start a timer to abort the check if it takes too long.
+    timer_.Start(FROM_HERE,
+                 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this,
+                 &SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout);
+
+    break;
+  }
+}
+
+void SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls() {
+  DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: " << urls_[next_index_];
+  state_ = STATE_BLOCKED;
+
+  // If user decided to not proceed through a warning, mark all the remaining
+  // redirects as "bad".
+  for (; next_index_ < callbacks_.size(); ++next_index_)
+    std::move(callbacks_[next_index_]).Run(false);
+}
+
+void SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete(bool proceed) {
+  DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_);
+
+  if (proceed) {
+    state_ = STATE_NONE;
+    std::move(callbacks_[next_index_]).Run(true);
+    next_index_++;
+    ProcessUrls();
+  } else {
+    BlockAndProcessUrls();
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/safe_browsing_url_checker_impl.h b/chrome/browser/safe_browsing/safe_browsing_url_checker_impl.h
new file mode 100644
index 0000000..4815e99
--- /dev/null
+++ b/chrome/browser/safe_browsing/safe_browsing_url_checker_impl.h
@@ -0,0 +1,116 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_URL_CHECKER_IMPL_H_
+#define CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_URL_CHECKER_IMPL_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing_db/database_manager.h"
+#include "content/public/common/resource_type.h"
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace security_interstitials {
+struct UnsafeResource;
+}
+
+namespace safe_browsing {
+
+class SafeBrowsingUIManager;
+class BaseUIManager;
+
+// A SafeBrowsingUrlCheckerImpl instance is used to perform SafeBrowsing check
+// for a URL and its redirect URLs. It implements Mojo interface so that it can
+// be used to handle queries from renderers. But it is also used to handle
+// queries from the browser. In that case, the public methods are called
+// directly instead of through Mojo.
+// Used when --enable-network-service is in effect.
+//
+// TODO(yzshen): Handle the case where SafeBrowsing is not enabled, or
+// !database_manager()->IsSupported().
+// TODO(yzshen): Make sure it also works on Andorid.
+// TODO(yzshen): Do all the logging like what BaseResourceThrottle does.
+class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
+                                   public SafeBrowsingDatabaseManager::Client {
+ public:
+  SafeBrowsingUrlCheckerImpl(
+      int load_flags,
+      content::ResourceType resource_type,
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      scoped_refptr<SafeBrowsingUIManager> ui_manager,
+      const base::Callback<content::WebContents*()>& web_contents_getter);
+
+  ~SafeBrowsingUrlCheckerImpl() override;
+
+  // mojom::SafeBrowsingUrlChecker implementation.
+  void CheckUrl(const GURL& url, CheckUrlCallback callback) override;
+
+ private:
+  // SafeBrowsingDatabaseManager::Client implementation:
+  void OnCheckBrowseUrlResult(const GURL& url,
+                              SBThreatType threat_type,
+                              const ThreatMetadata& metadata) override;
+
+  static void StartDisplayingBlockingPage(
+      const base::WeakPtr<SafeBrowsingUrlCheckerImpl>& checker,
+      scoped_refptr<BaseUIManager> ui_manager,
+      const security_interstitials::UnsafeResource& resource);
+
+  void OnCheckUrlTimeout();
+
+  void ProcessUrls();
+
+  void BlockAndProcessUrls();
+
+  void OnBlockingPageComplete(bool proceed);
+
+  enum State {
+    // Haven't started checking or checking is complete.
+    STATE_NONE,
+    // We have one outstanding URL-check.
+    STATE_CHECKING_URL,
+    // We're displaying a blocking page.
+    STATE_DISPLAYING_BLOCKING_PAGE,
+    // The blocking page has returned *not* to proceed.
+    STATE_BLOCKED
+  };
+
+  const int load_flags_;
+  const content::ResourceType resource_type_;
+  base::Callback<content::WebContents*()> web_contents_getter_;
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+  scoped_refptr<BaseUIManager> ui_manager_;
+
+  // The redirect chain for this resource, including the original URL and
+  // subsequent redirect URLs.
+  std::vector<GURL> urls_;
+  // Callbacks corresponding to |urls_| to report check results. |urls_| and
+  // |callbacks_| are always of the same size.
+  std::vector<CheckUrlCallback> callbacks_;
+  // |urls_| before |next_index_| have been checked. If |next_index_| is smaller
+  // than the size of |urls_|, the URL at |next_index_| is being processed.
+  size_t next_index_ = 0;
+
+  State state_ = STATE_NONE;
+
+  // Timer to abort the SafeBrowsing check if it takes too long.
+  base::OneShotTimer timer_;
+
+  base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl);
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_URL_CHECKER_IMPL_H_
diff --git a/chrome/browser/search_engines/template_url_service_android.cc b/chrome/browser/search_engines/template_url_service_android.cc
index f7dc785..8e8f5e1 100644
--- a/chrome/browser/search_engines/template_url_service_android.cc
+++ b/chrome/browser/search_engines/template_url_service_android.cc
@@ -91,7 +91,7 @@
   return template_urls_.size();
 }
 
-jboolean TemplateUrlServiceAndroid::IsSearchProviderManaged(
+jboolean TemplateUrlServiceAndroid::IsDefaultSearchManaged(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
   return template_url_service_->is_default_search_managed();
diff --git a/chrome/browser/search_engines/template_url_service_android.h b/chrome/browser/search_engines/template_url_service_android.h
index 764708a..af6c8b05 100644
--- a/chrome/browser/search_engines/template_url_service_android.h
+++ b/chrome/browser/search_engines/template_url_service_android.h
@@ -43,7 +43,7 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       jint index) const;
-  jboolean IsSearchProviderManaged(
+  jboolean IsDefaultSearchManaged(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
   jboolean IsSearchByImageAvailable(
diff --git a/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.cc b/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.cc
index c1a90a65..a22b65a 100644
--- a/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_content_settings_manager.cc
@@ -76,6 +76,8 @@
 }
 
 void SubresourceFilterContentSettingsManager::WhitelistSite(const GURL& url) {
+  DCHECK(base::FeatureList::IsEnabled(
+      subresource_filter::kSafeBrowsingSubresourceFilterExperimentalUI));
   base::AutoReset<bool> resetter(&ignore_settings_changes_, true);
   settings_map_->SetContentSettingDefaultScope(
       url, GURL(), ContentSettingsType::CONTENT_SETTINGS_TYPE_ADS,
@@ -123,6 +125,9 @@
 void SubresourceFilterContentSettingsManager::SetSiteMetadata(
     const GURL& url,
     std::unique_ptr<base::DictionaryValue> dict) {
+  if (!base::FeatureList::IsEnabled(
+          subresource_filter::kSafeBrowsingSubresourceFilterExperimentalUI))
+    return;
   settings_map_->SetWebsiteSettingDefaultScope(
       url, GURL(), ContentSettingsType::CONTENT_SETTINGS_TYPE_ADS_DATA,
       std::string(), std::move(dict));
diff --git a/chrome/browser/subresource_filter/subresource_filter_content_settings_manager_unittest.cc b/chrome/browser/subresource_filter/subresource_filter_content_settings_manager_unittest.cc
index 7df821c8..f4f6b68 100644
--- a/chrome/browser/subresource_filter/subresource_filter_content_settings_manager_unittest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_content_settings_manager_unittest.cc
@@ -24,12 +24,15 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/test/history_service_test_util.h"
+#include "components/subresource_filter/core/browser/subresource_filter_features.h"
+#include "components/subresource_filter/core/browser/subresource_filter_features_test_support.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
 namespace {
 
+using subresource_filter::testing::ScopedSubresourceFilterFeatureToggle;
 const char kActionsHistogram[] = "SubresourceFilter.Actions";
 
 class SubresourceFilterContentSettingsManagerTest : public testing::Test {
@@ -37,6 +40,9 @@
   SubresourceFilterContentSettingsManagerTest() {}
 
   void SetUp() override {
+    scoped_feature_toggle().ResetSubresourceFilterState(
+        base::FeatureList::OVERRIDE_ENABLE_FEATURE,
+        "SubresourceFilterExperimentalUI" /* additional_features */);
     settings_manager_ =
         SubresourceFilterProfileContextFactory::GetForProfile(&testing_profile_)
             ->settings_manager();
@@ -56,6 +62,10 @@
     return settings_manager_;
   }
 
+  ScopedSubresourceFilterFeatureToggle& scoped_feature_toggle() {
+    return scoped_feature_toggle_;
+  }
+
   TestingProfile* profile() { return &testing_profile_; }
 
   ContentSetting GetContentSettingMatchingUrlWithEmptyPath(const GURL& url) {
@@ -78,6 +88,7 @@
   base::ScopedTempDir scoped_dir_;
 
   content::TestBrowserThreadBundle thread_bundle_;
+  ScopedSubresourceFilterFeatureToggle scoped_feature_toggle_;
   base::HistogramTester histogram_tester_;
   TestingProfile testing_profile_;
 
@@ -188,8 +199,7 @@
 
   // Showing the UI should trigger a forced content setting update, but no
   // metrics should be recorded.
-  histogram_tester().ExpectBucketCount(kActionsHistogram,
-                                       kActionContentSettingsBlocked, 0);
+  histogram_tester().ExpectTotalCount(kActionsHistogram, 0);
 
   // Fast forward the clock.
   test_clock()->Advance(
@@ -226,9 +236,6 @@
                                        kActionContentSettingsAllowedFromUI, 0);
   histogram_tester().ExpectBucketCount(
       kActionsHistogram, kActionContentSettingsAllowedWhileUISuppressed, 1);
-
-  // Smart UI should be reset.
-  EXPECT_TRUE(settings_manager()->ShouldShowUIForSite(url));
 }
 
 TEST_F(SubresourceFilterContentSettingsManagerTest,
@@ -276,6 +283,23 @@
                                        kActionContentSettingsBlockedGlobal, 1);
 }
 
+TEST_F(SubresourceFilterContentSettingsManagerTest,
+       NoExperimentalUI_NoWebsiteSetting) {
+  GURL url("https://example.test/");
+
+  // Do no explicitly allow the experimental UI.
+  scoped_feature_toggle().ResetSubresourceFilterState(
+      base::FeatureList::OVERRIDE_ENABLE_FEATURE);
+  settings_manager()->OnDidShowUI(url);
+  EXPECT_FALSE(settings_manager()->GetSiteMetadata(url));
+
+  scoped_feature_toggle().ResetSubresourceFilterState(
+      base::FeatureList::OVERRIDE_ENABLE_FEATURE,
+      "SubresourceFilterExperimentalUI" /* additional_features */);
+  settings_manager()->OnDidShowUI(url);
+  EXPECT_TRUE(settings_manager()->GetSiteMetadata(url));
+}
+
 TEST_F(SubresourceFilterContentSettingsManagerHistoryTest,
        HistoryUrlDeleted_ClearsWebsiteSetting) {
   if (!settings_manager()->should_use_smart_ui())
diff --git a/chrome/browser/subresource_filter/subresource_filter_unittest.cc b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
index a98db80..4886201 100644
--- a/chrome/browser/subresource_filter/subresource_filter_unittest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
@@ -63,7 +63,7 @@
     // Ensure correct features.
     scoped_feature_toggle_.ResetSubresourceFilterState(
         base::FeatureList::OVERRIDE_ENABLE_FEATURE,
-        "SafeBrowsingV4OnlyEnabled" /* additional_features */);
+        "SafeBrowsingV4OnlyEnabled,SubresourceFilterExperimentalUI");
     scoped_configuration_.ResetConfiguration(subresource_filter::Configuration(
         subresource_filter::ActivationLevel::ENABLED,
         subresource_filter::ActivationScope::ACTIVATION_LIST,
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index d2fb1b92..b2ee2422 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -90,9 +90,8 @@
 // Defaults for properties which are not stored in the browser theme pack.
 
 constexpr SkColor kDefaultColorControlBackground = SK_ColorWHITE;
-const SkColor kDefaultDetachedBookmarkBarSeparator =
-    SkColorSetRGB(0xB6, 0xB4, 0xB6);
-const SkColor kDefaultDetachedBookmarkBarSeparatorIncognito =
+const SkColor kDefaultToolbarBottomSeparator = SkColorSetRGB(0xB6, 0xB4, 0xB6);
+const SkColor kDefaultToolbarBottomSeparatorIncognito =
     SkColorSetRGB(0x28, 0x28, 0x28);
 const SkColor kDefaultToolbarTopSeparator = SkColorSetA(SK_ColorBLACK, 0x40);
 
@@ -262,13 +261,17 @@
     case COLOR_BOOKMARK_BAR_INSTRUCTIONS_TEXT:
       return incognito ? kDefaultColorBookmarkInstructionsTextIncognito
                        : kDefaultColorBookmarkInstructionsText;
-    case COLOR_TOOLBAR_BOTTOM_SEPARATOR:
     case COLOR_DETACHED_BOOKMARK_BAR_SEPARATOR:
-      return incognito ? kDefaultDetachedBookmarkBarSeparatorIncognito
-                       : kDefaultDetachedBookmarkBarSeparator;
+      // We shouldn't reach this case because the color is calculated from
+      // others.
+      NOTREACHED();
+      return gfx::kPlaceholderColor;
     case COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND:
       return incognito ? kDefaultDetachedBookmarkBarBackgroundIncognito
                        : kDefaultDetachedBookmarkBarBackground;
+    case COLOR_TOOLBAR_BOTTOM_SEPARATOR:
+      return incognito ? kDefaultToolbarBottomSeparatorIncognito
+                       : kDefaultToolbarBottomSeparator;
     case COLOR_TOOLBAR_TOP_SEPARATOR:
     case COLOR_TOOLBAR_TOP_SEPARATOR_INACTIVE:
       return kDefaultToolbarTopSeparator;
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index ed768b4..f5c851be 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -472,16 +472,18 @@
       if (UsingDefaultTheme())
         break;
       return GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT, incognito);
+    case ThemeProperties::COLOR_TOOLBAR_BOTTOM_SEPARATOR:
+      if (UsingDefaultTheme())
+        break;
+      return GetColor(ThemeProperties::COLOR_LOCATION_BAR_BORDER, incognito);
     case ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND:
       if (UsingDefaultTheme())
         break;
       return GetColor(ThemeProperties::COLOR_TOOLBAR, incognito);
     case ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_SEPARATOR:
-      if (UsingDefaultTheme())
-        break;
-      // Use 50% of bookmark text color as separator color.
+      // Use a faint version of the text color as the separator color.
       return SkColorSetA(
-          GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT, incognito), 128);
+          GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT, incognito), 0x20);
     case ThemeProperties::COLOR_NTP_TEXT_LIGHT:
       return IncreaseLightness(GetColor(kNtpText, incognito), 0.40);
     case ThemeProperties::COLOR_TAB_THROBBER_SPINNING:
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d8b19e3..c617085 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2356,6 +2356,7 @@
     deps += [
       "//chrome/browser:jni_headers",
       "//components/web_contents_delegate_android",
+      "//device/usb/public/cpp",
     ]
     deps -= [ "//ui/events" ]
   } else {
diff --git a/chrome/browser/ui/android/infobars/reader_mode_infobar.cc b/chrome/browser/ui/android/infobars/reader_mode_infobar.cc
index e424ebd6..bcb2144 100644
--- a/chrome/browser/ui/android/infobars/reader_mode_infobar.cc
+++ b/chrome/browser/ui/android/infobars/reader_mode_infobar.cc
@@ -46,6 +46,14 @@
   return Java_ReaderModeInfoBar_create(env);
 }
 
+base::android::ScopedJavaLocalRef<jobject> ReaderModeInfoBar::GetTab(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj) {
+  content::WebContents* web_contents =
+      InfoBarService::WebContentsFromInfoBar(this);
+  return TabAndroid::FromWebContents(web_contents)->GetJavaObject();
+}
+
 void ReaderModeInfoBar::ProcessButton(int action) {}
 
 void Create(JNIEnv* env,
diff --git a/chrome/browser/ui/android/infobars/reader_mode_infobar.h b/chrome/browser/ui/android/infobars/reader_mode_infobar.h
index 22059e2..430c7f9 100644
--- a/chrome/browser/ui/android/infobars/reader_mode_infobar.h
+++ b/chrome/browser/ui/android/infobars/reader_mode_infobar.h
@@ -19,6 +19,10 @@
       std::unique_ptr<ReaderModeInfoBarDelegate> delegate);
   ~ReaderModeInfoBar() override;
 
+  base::android::ScopedJavaLocalRef<jobject> GetTab(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
+
  protected:
   infobars::InfoBarDelegate* GetDelegate();
 
diff --git a/chrome/browser/ui/android/usb_chooser_dialog_android.cc b/chrome/browser/ui/android/usb_chooser_dialog_android.cc
index 39d3a6e..c1c50ccd 100644
--- a/chrome/browser/ui/android/usb_chooser_dialog_android.cc
+++ b/chrome/browser/ui/android/usb_chooser_dialog_android.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <utility>
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
@@ -26,8 +27,8 @@
 #include "content/public/browser/web_contents.h"
 #include "device/base/device_client.h"
 #include "device/usb/mojo/type_converters.h"
+#include "device/usb/public/cpp/filter_utils.h"
 #include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
 #include "device/usb/webusb_descriptors.h"
 #include "device/vr/features/features.h"
 #include "jni/UsbChooserDialog_jni.h"
@@ -39,7 +40,6 @@
 #endif  // BUILDFLAG(ENABLE_VR)
 
 using device::UsbDevice;
-using device::UsbDeviceFilter;
 
 namespace {
 
@@ -56,13 +56,13 @@
 }  // namespace
 
 UsbChooserDialogAndroid::UsbChooserDialogAndroid(
-    const std::vector<UsbDeviceFilter>& filters,
+    std::vector<device::mojom::UsbDeviceFilterPtr> filters,
     content::RenderFrameHost* render_frame_host,
     const device::mojom::UsbChooserService::GetPermissionCallback& callback)
     : render_frame_host_(render_frame_host),
       callback_(callback),
       usb_service_observer_(this),
-      filters_(filters),
+      filters_(std::move(filters)),
       weak_factory_(this) {
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host_);
@@ -240,7 +240,7 @@
 
 bool UsbChooserDialogAndroid::DisplayDevice(
     scoped_refptr<UsbDevice> device) const {
-  if (!UsbDeviceFilter::MatchesAny(*device, filters_))
+  if (!UsbDeviceFilterMatchesAny(filters_, *device))
     return false;
 
   if (UsbBlocklist::Get().IsExcluded(device))
diff --git a/chrome/browser/ui/android/usb_chooser_dialog_android.h b/chrome/browser/ui/android/usb_chooser_dialog_android.h
index 318d835..09cbcdb1 100644
--- a/chrome/browser/ui/android/usb_chooser_dialog_android.h
+++ b/chrome/browser/ui/android/usb_chooser_dialog_android.h
@@ -23,7 +23,6 @@
 
 namespace device {
 class UsbDevice;
-struct UsbDeviceFilter;
 }
 
 // Represents a way to ask the user to select a USB device from a list of
@@ -31,7 +30,7 @@
 class UsbChooserDialogAndroid : public device::UsbService::Observer {
  public:
   UsbChooserDialogAndroid(
-      const std::vector<device::UsbDeviceFilter>& filters,
+      std::vector<device::mojom::UsbDeviceFilterPtr> filters,
       content::RenderFrameHost* render_frame_host,
       const device::mojom::UsbChooserService::GetPermissionCallback& callback);
   ~UsbChooserDialogAndroid() override;
@@ -74,7 +73,7 @@
   device::mojom::UsbChooserService::GetPermissionCallback callback_;
   ScopedObserver<device::UsbService, device::UsbService::Observer>
       usb_service_observer_;
-  std::vector<device::UsbDeviceFilter> filters_;
+  std::vector<device::mojom::UsbDeviceFilterPtr> filters_;
 
   std::vector<scoped_refptr<device::UsbDevice>> devices_;
 
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index d8ce7ba..53dae5c 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -896,8 +896,6 @@
   colors_[ThemeProperties::COLOR_TOOLBAR_BOTTOM_SEPARATOR] = tab_border;
   // Separates entries in the downloads bar.
   colors_[ThemeProperties::COLOR_TOOLBAR_VERTICAL_SEPARATOR] = tab_border;
-  // Separates the bookmark bar from the web content.
-  colors_[ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_SEPARATOR] = tab_border;
 
   // These colors represent the border drawn around tabs and between
   // the tabstrip and toolbar.
diff --git a/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc b/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
index e9d7fe43..18af0d7 100644
--- a/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
+++ b/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
@@ -214,7 +214,7 @@
   views::View* GetContentsView() override { return this; }
   views::NonClientFrameView* CreateNonClientFrameView(
       views::Widget* widget) override {
-    return views::DialogDelegate::CreateDialogFrameView(widget, gfx::Insets());
+    return views::DialogDelegate::CreateDialogFrameView(widget);
   }
   bool ShouldShowCloseButton() const override {
     // No close button if the dialog doesn't want a title bar.
diff --git a/chrome/browser/ui/views/extensions/chooser_dialog_view.cc b/chrome/browser/ui/views/extensions/chooser_dialog_view.cc
index 93dcf5b..839322c6 100644
--- a/chrome/browser/ui/views/extensions/chooser_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/chooser_dialog_view.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/background.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/link.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/layout/fill_layout.h"
@@ -43,6 +44,9 @@
   DCHECK(chooser_controller);
   device_chooser_content_view_ =
       new DeviceChooserContentView(this, std::move(chooser_controller));
+  device_chooser_content_view_->SetBorder(
+      views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric(
+          views::INSETS_DIALOG_CONTENTS)));
   chrome::RecordDialogCreation(chrome::DialogIdentifier::CHOOSER);
 }
 
@@ -70,27 +74,13 @@
 }
 
 views::View* ChooserDialogView::CreateFootnoteView() {
-  return device_chooser_content_view_->footnote_link();
-}
-
-views::ClientView* ChooserDialogView::CreateClientView(views::Widget* widget) {
-  views::DialogClientView* client =
-      new views::DialogClientView(widget, GetContentsView());
-  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
-  client->SetButtonRowInsets(gfx::Insets(
-      provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL),
-      0, 0, 0));
-  return client;
-}
-
-views::NonClientFrameView* ChooserDialogView::CreateNonClientFrameView(
-    views::Widget* widget) {
-  // ChooserDialogView always has a parent, so ShouldUseCustomFrame() should
-  // always be true.
-  DCHECK(ShouldUseCustomFrame());
-  return views::DialogDelegate::CreateDialogFrameView(
-      widget, ChromeLayoutProvider::Get()->GetInsetsMetric(
-                  views::INSETS_BUBBLE_CONTENTS));
+  views::View* footnote_link = device_chooser_content_view_->footnote_link();
+  if (footnote_link) {
+    footnote_link->SetBorder(
+        views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric(
+            views::INSETS_DIALOG_CONTENTS)));
+  }
+  return footnote_link;
 }
 
 bool ChooserDialogView::Accept() {
diff --git a/chrome/browser/ui/views/extensions/chooser_dialog_view.h b/chrome/browser/ui/views/extensions/chooser_dialog_view.h
index 231dd9f..dcb4f6e 100644
--- a/chrome/browser/ui/views/extensions/chooser_dialog_view.h
+++ b/chrome/browser/ui/views/extensions/chooser_dialog_view.h
@@ -32,9 +32,6 @@
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   views::View* CreateFootnoteView() override;
-  views::ClientView* CreateClientView(views::Widget* widget) override;
-  views::NonClientFrameView* CreateNonClientFrameView(
-      views::Widget* widget) override;
   bool Accept() override;
   bool Cancel() override;
   bool Close() override;
diff --git a/chrome/browser/ui/views/extensions/chooser_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/chooser_dialog_view_browsertest.cc
new file mode 100644
index 0000000..34b2d90
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/chooser_dialog_view_browsertest.cc
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/extensions/chooser_dialog_view.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chooser_controller/mock_chooser_controller.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+
+// Invokes the device chooser dialog with mock content. See
+// test_browser_dialog.h.
+class ChooserDialogViewBrowserTest : public DialogBrowserTest {
+ public:
+  ChooserDialogViewBrowserTest() : controller_(new MockChooserController()) {}
+
+  // DialogBrowserTest:
+  void ShowDialog(const std::string& name) override {
+    auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+    web_modal::WebContentsModalDialogManager* manager =
+        web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
+    if (manager) {
+      constrained_window::ShowWebModalDialogViews(
+          new ChooserDialogView(std::move(controller_)), web_contents);
+    }
+  }
+
+  MockChooserController& controller() { return *controller_; }
+
+ private:
+  std::unique_ptr<MockChooserController> controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChooserDialogViewBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ChooserDialogViewBrowserTest, InvokeDialog_noDevices) {
+  RunDialog();
+}
+
+IN_PROC_BROWSER_TEST_F(ChooserDialogViewBrowserTest, InvokeDialog_withDevices) {
+  controller().OptionAdded(base::ASCIIToUTF16("Device 1"),
+                           MockChooserController::kNoSignalStrengthLevelImage,
+                           MockChooserController::NONE);
+  controller().OptionAdded(base::ASCIIToUTF16("Device 2"),
+                           MockChooserController::kSignalStrengthLevel2Bar,
+                           MockChooserController::PAIRED);
+  controller().OptionAdded(base::ASCIIToUTF16("Device 3"),
+                           MockChooserController::kSignalStrengthLevel4Bar,
+                           MockChooserController::CONNECTED);
+  controller().OptionAdded(
+      base::ASCIIToUTF16("Device 4"),
+      MockChooserController::kSignalStrengthLevel1Bar,
+      MockChooserController::PAIRED | MockChooserController::CONNECTED);
+  RunDialog();
+}
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 290e386..acf909d 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -207,18 +207,10 @@
       fill_rect,
       tp->GetColor(ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND));
 
-  // Draw the separators above and below bookmark bar;
-  // if animating, these are fading in/out.
-  SkColor separator_color =
-      tp->GetColor(ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_SEPARATOR);
-
-  // For the bottom separator, increase the luminance. Either double it or halve
-  // the distance to 1.0, whichever is less of a difference.
-  color_utils::HSL hsl;
-  color_utils::SkColorToHSL(separator_color, &hsl);
-  hsl.l = std::min((hsl.l + 1) / 2, hsl.l * 2);
+  // Draw the separator below the detached bookmark bar.
   BrowserView::Paint1pxHorizontalLine(
-      canvas, color_utils::HSLToSkColor(hsl, SK_AlphaOPAQUE),
+      canvas,
+      tp->GetColor(ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_SEPARATOR),
       view->GetLocalBounds(), true);
 }
 
diff --git a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
index 9ccfde4..c080507b 100644
--- a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
@@ -21,9 +21,9 @@
 #include "components/autofill/core/browser/country_combobox_model.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/browser/region_combobox_model.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/autofill_l10n_util.h"
 #include "components/payments/content/payment_request_state.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payments_profile_comparator.h"
@@ -84,6 +84,25 @@
         *profile_to_edit_, state()->GetApplicationLocale());
   }
 
+  if (type == autofill::ADDRESS_HOME_STATE) {
+    // For the state, check if the inital value matches either a region code or
+    // a region name.
+    base::string16 initial_region = profile_to_edit_->GetInfo(
+        autofill::AutofillType(type), state()->GetApplicationLocale());
+    autofill::l10n::CaseInsensitiveCompare compare;
+
+    for (const auto& region : region_model_->GetRegions()) {
+      base::string16 region_name = base::UTF8ToUTF16(region.second);
+      if (compare.StringsEqual(initial_region,
+                               base::UTF8ToUTF16(region.first)) ||
+          compare.StringsEqual(initial_region, region_name)) {
+        return region_name;
+      }
+    }
+
+    return initial_region;
+  }
+
   return profile_to_edit_->GetInfo(autofill::AutofillType(type),
                                    state()->GetApplicationLocale());
 }
@@ -142,6 +161,7 @@
     case autofill::ADDRESS_HOME_STATE: {
       std::unique_ptr<autofill::RegionComboboxModel> model =
           base::MakeUnique<autofill::RegionComboboxModel>();
+      region_model_ = model.get();
       if (chosen_country_index_ < countries_.size()) {
         model->LoadRegionData(countries_[chosen_country_index_].first,
                               state()->GetRegionDataLoader(),
diff --git a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h
index ab863a74..250e956 100644
--- a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h
+++ b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h
@@ -14,6 +14,7 @@
 #include "base/strings/string16.h"
 #include "chrome/browser/ui/views/payments/editor_view_controller.h"
 #include "chrome/browser/ui/views/payments/validating_textfield.h"
+#include "components/autofill/core/browser/region_combobox_model.h"
 
 namespace autofill {
 class AutofillProfile;
@@ -126,6 +127,9 @@
   // Identifies whether we tried and failed to load region data.
   bool failed_to_load_region_data_;
 
+  // Owned by the state combobox, which is owned by this object's base class.
+  autofill::RegionComboboxModel* region_model_;
+
   // Updates |countries_| with the content of |model| if it's not null,
   // otherwise use a local model.
   void UpdateCountries(autofill::CountryComboboxModel* model);
diff --git a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller_browsertest.cc
index 35ea784..c5db770 100644
--- a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller_browsertest.cc
@@ -967,4 +967,66 @@
   EXPECT_TRUE(save_button->enabled());
 }
 
+// Tests that the state dropdown is set to the right value if the value from the
+// profile is a region code.
+IN_PROC_BROWSER_TEST_F(PaymentRequestShippingAddressEditorTest,
+                       DefaultRegion_RegionCode) {
+  // Add address without a country but a valid state for the default country.
+  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
+  profile.SetInfo(autofill::AutofillType(autofill::ADDRESS_HOME_COUNTRY),
+                  base::ASCIIToUTF16(""), kLocale);
+  profile.SetInfo(autofill::AutofillType(autofill::ADDRESS_HOME_STATE),
+                  base::ASCIIToUTF16("ca"), kLocale);
+  AddAutofillProfile(profile);
+
+  InvokePaymentRequestUI();
+  SetRegionDataLoader(&test_region_data_loader_);
+
+  test_region_data_loader_.set_synchronous_callback(true);
+  std::vector<std::pair<std::string, std::string>> regions;
+  regions.push_back(std::make_pair("AL", "Alabama"));
+  regions.push_back(std::make_pair("CA", "California"));
+  test_region_data_loader_.SetRegionData(regions);
+  OpenShippingAddressSectionScreen();
+
+  ResetEventObserver(DialogEvent::SHIPPING_ADDRESS_EDITOR_OPENED);
+  ClickOnChildInListViewAndWait(/*child_index=*/0, /*num_children=*/1,
+                                DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW);
+
+  // Expect that the state was selected.
+  EXPECT_EQ(base::ASCIIToUTF16("California"),
+            GetComboboxValue(autofill::ADDRESS_HOME_STATE));
+}
+
+// Tests that the state dropdown is set to the right value if the value from the
+// profile is a region name.
+IN_PROC_BROWSER_TEST_F(PaymentRequestShippingAddressEditorTest,
+                       DefaultRegion_RegionName) {
+  // Add address without a country but a valid state for the default country.
+  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
+  profile.SetInfo(autofill::AutofillType(autofill::ADDRESS_HOME_COUNTRY),
+                  base::ASCIIToUTF16(""), kLocale);
+  profile.SetInfo(autofill::AutofillType(autofill::ADDRESS_HOME_STATE),
+                  base::ASCIIToUTF16("california"), kLocale);
+  AddAutofillProfile(profile);
+
+  InvokePaymentRequestUI();
+  SetRegionDataLoader(&test_region_data_loader_);
+
+  test_region_data_loader_.set_synchronous_callback(true);
+  std::vector<std::pair<std::string, std::string>> regions;
+  regions.push_back(std::make_pair("AL", "Alabama"));
+  regions.push_back(std::make_pair("CA", "California"));
+  test_region_data_loader_.SetRegionData(regions);
+  OpenShippingAddressSectionScreen();
+
+  ResetEventObserver(DialogEvent::SHIPPING_ADDRESS_EDITOR_OPENED);
+  ClickOnChildInListViewAndWait(/*child_index=*/0, /*num_children=*/1,
+                                DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW);
+
+  // Expect that the state was selected.
+  EXPECT_EQ(base::ASCIIToUTF16("California"),
+            GetComboboxValue(autofill::ADDRESS_HOME_STATE));
+}
+
 }  // namespace payments
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
index 2d32a54..af7cbcc 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -335,7 +335,7 @@
     const extensions::DevicePermissions* device_permissions =
         permissions_manager->GetForExtension(extension->id());
     for (const auto& device : devices) {
-      if (manifest_data->SupportsDevice(device)) {
+      if (manifest_data->SupportsDevice(*device)) {
         std::unique_ptr<extensions::UsbDevicePermission::CheckParam> param =
             extensions::UsbDevicePermission::CheckParam::ForUsbDevice(
                 extension.get(), device.get());
diff --git a/chrome/browser/usb/usb_browsertest.cc b/chrome/browser/usb/usb_browsertest.cc
index 011ac35..5f555e9 100644
--- a/chrome/browser/usb/usb_browsertest.cc
+++ b/chrome/browser/usb/usb_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "base/command_line.h"
 #include "base/memory/ref_counted.h"
@@ -74,10 +75,11 @@
   ~FakeChooserService() override {}
 
   // device::mojom::UsbChooserService:
-  void GetPermission(const std::vector<device::UsbDeviceFilter>& device_filters,
-                     const GetPermissionCallback& callback) override {
+  void GetPermission(
+      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
+      const GetPermissionCallback& callback) override {
     auto chooser_controller = base::MakeUnique<UsbChooserController>(
-        render_frame_host_, device_filters, callback);
+        render_frame_host_, std::move(device_filters), callback);
     new FakeChooserView(std::move(chooser_controller));
   }
 
diff --git a/chrome/browser/usb/usb_chooser_controller.cc b/chrome/browser/usb/usb_chooser_controller.cc
index 9433805..662d292 100644
--- a/chrome/browser/usb/usb_chooser_controller.cc
+++ b/chrome/browser/usb/usb_chooser_controller.cc
@@ -26,8 +26,8 @@
 #include "content/public/browser/web_contents.h"
 #include "device/base/device_client.h"
 #include "device/usb/mojo/type_converters.h"
+#include "device/usb/public/cpp/filter_utils.h"
 #include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
 #include "device/usb/usb_ids.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
@@ -35,7 +35,6 @@
 using content::RenderFrameHost;
 using content::WebContents;
 using device::UsbDevice;
-using device::UsbDeviceFilter;
 
 namespace {
 
@@ -74,12 +73,12 @@
 
 UsbChooserController::UsbChooserController(
     RenderFrameHost* render_frame_host,
-    const std::vector<UsbDeviceFilter>& device_filters,
+    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
     const device::mojom::UsbChooserService::GetPermissionCallback& callback)
     : ChooserController(render_frame_host,
                         IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN,
                         IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME),
-      filters_(device_filters),
+      filters_(std::move(device_filters)),
       callback_(callback),
       usb_service_observer_(this),
       weak_factory_(this) {
@@ -223,7 +222,7 @@
 
 bool UsbChooserController::DisplayDevice(
     scoped_refptr<UsbDevice> device) const {
-  if (!UsbDeviceFilter::MatchesAny(*device, filters_))
+  if (!UsbDeviceFilterMatchesAny(filters_, *device))
     return false;
 
   if (UsbBlocklist::Get().IsExcluded(device))
diff --git a/chrome/browser/usb/usb_chooser_controller.h b/chrome/browser/usb/usb_chooser_controller.h
index 02611fd..22bc030 100644
--- a/chrome/browser/usb/usb_chooser_controller.h
+++ b/chrome/browser/usb/usb_chooser_controller.h
@@ -24,7 +24,6 @@
 
 namespace device {
 class UsbDevice;
-struct UsbDeviceFilter;
 }
 
 class UsbChooserContext;
@@ -36,7 +35,7 @@
  public:
   UsbChooserController(
       content::RenderFrameHost* render_frame_host,
-      const std::vector<device::UsbDeviceFilter>& device_filters,
+      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
       const device::mojom::UsbChooserService::GetPermissionCallback& callback);
   ~UsbChooserController() override;
 
@@ -62,7 +61,7 @@
       const std::vector<scoped_refptr<device::UsbDevice>>& devices);
   bool DisplayDevice(scoped_refptr<device::UsbDevice> device) const;
 
-  std::vector<device::UsbDeviceFilter> filters_;
+  std::vector<device::mojom::UsbDeviceFilterPtr> filters_;
   device::mojom::UsbChooserService::GetPermissionCallback callback_;
   GURL requesting_origin_;
   GURL embedding_origin_;
diff --git a/chrome/browser/usb/usb_chooser_controller_unittest.cc b/chrome/browser/usb/usb_chooser_controller_unittest.cc
index 8e63cd5..484c5d9 100644
--- a/chrome/browser/usb/usb_chooser_controller_unittest.cc
+++ b/chrome/browser/usb/usb_chooser_controller_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <utility>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -47,13 +48,13 @@
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
 
-    std::vector<device::UsbDeviceFilter> device_filters;
+    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters;
     device::mojom::UsbChooserService::GetPermissionCallback callback;
     content::WebContentsTester* web_contents_tester =
         content::WebContentsTester::For(web_contents());
     web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
-    usb_chooser_controller_.reset(
-        new UsbChooserController(main_rfh(), device_filters, callback));
+    usb_chooser_controller_.reset(new UsbChooserController(
+        main_rfh(), std::move(device_filters), callback));
     mock_usb_chooser_view_.reset(new MockUsbChooserView());
     usb_chooser_controller_->set_view(mock_usb_chooser_view_.get());
   }
diff --git a/chrome/browser/usb/web_usb_chooser_service.cc b/chrome/browser/usb/web_usb_chooser_service.cc
index 2dfacec..00a5fc4 100644
--- a/chrome/browser/usb/web_usb_chooser_service.cc
+++ b/chrome/browser/usb/web_usb_chooser_service.cc
@@ -29,13 +29,14 @@
 }
 
 void WebUsbChooserService::GetPermission(
-    const std::vector<device::UsbDeviceFilter>& device_filters,
+    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
     const GetPermissionCallback& callback) {
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host_);
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
   std::unique_ptr<UsbChooserController> usb_chooser_controller(
-      new UsbChooserController(render_frame_host_, device_filters, callback));
+      new UsbChooserController(render_frame_host_, std::move(device_filters),
+                               callback));
   std::unique_ptr<ChooserBubbleDelegate> chooser_bubble_delegate(
       new ChooserBubbleDelegate(render_frame_host_,
                                 std::move(usb_chooser_controller)));
diff --git a/chrome/browser/usb/web_usb_chooser_service.h b/chrome/browser/usb/web_usb_chooser_service.h
index 9dd7e88c..99dae9b 100644
--- a/chrome/browser/usb/web_usb_chooser_service.h
+++ b/chrome/browser/usb/web_usb_chooser_service.h
@@ -27,8 +27,9 @@
   ~WebUsbChooserService() override;
 
   // device::usb::ChooserService:
-  void GetPermission(const std::vector<device::UsbDeviceFilter>& device_filters,
-                     const GetPermissionCallback& callback) override;
+  void GetPermission(
+      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
+      const GetPermissionCallback& callback) override;
 
   void Bind(device::mojom::UsbChooserServiceRequest request);
 
diff --git a/chrome/installer/linux/debian/expected_deps_x64_jessie b/chrome/installer/linux/debian/expected_deps_x64_jessie
index e88b1fd..3f08359e 100644
--- a/chrome/installer/linux/debian/expected_deps_x64_jessie
+++ b/chrome/installer/linux/debian/expected_deps_x64_jessie
@@ -4,14 +4,14 @@
 libc6 (>= 2.15)
 libcairo2 (>= 1.6.0)
 libcups2 (>= 1.4.0)
-libdbus-1-3 (>= 1.1.4)
+libdbus-1-3 (>= 1.2.14)
 libexpat1 (>= 2.0.1)
 libfontconfig1 (>= 2.11)
 libgcc1 (>= 1:4.1.1)
 libgconf-2-4 (>= 3.2.5)
 libgdk-pixbuf2.0-0 (>= 2.22.0)
 libglib2.0-0 (>= 2.28.0)
-libgtk-3-0 (>= 3.3.16)
+libgtk-3-0 (>= 3.9.10)
 libnspr4 (>= 2:4.9-2~)
 libnss3 (>= 2:3.13.4-2~)
 libpango-1.0-0 (>= 1.14.0)
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 87494b8d..7923c36 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -84,8 +84,8 @@
     "prerender/prerender_helper.h",
     "prerender/prerenderer_client.cc",
     "prerender/prerenderer_client.h",
-    "safe_browsing/safe_browsing_url_loader_throttle.cc",
-    "safe_browsing/safe_browsing_url_loader_throttle.h",
+    "safe_browsing/renderer_url_loader_throttle.cc",
+    "safe_browsing/renderer_url_loader_throttle.h",
     "sandbox_status_extension_android.cc",
     "sandbox_status_extension_android.h",
     "searchbox/search_bouncer.cc",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index fedd53c..d55fd27 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -57,7 +57,7 @@
 #include "chrome/renderer/prerender/prerender_helper.h"
 #include "chrome/renderer/prerender/prerenderer_client.h"
 #include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h"
-#include "chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h"
+#include "chrome/renderer/safe_browsing/renderer_url_loader_throttle.h"
 #include "chrome/renderer/searchbox/search_bouncer.h"
 #include "chrome/renderer/searchbox/searchbox.h"
 #include "chrome/renderer/searchbox/searchbox_extension.h"
@@ -1213,7 +1213,7 @@
     }
     RenderFrame* render_frame = content::RenderFrame::FromWebFrame(frame);
     throttles->push_back(
-        base::MakeUnique<safe_browsing::SafeBrowsingURLLoaderThrottle>(
+        base::MakeUnique<safe_browsing::RendererURLLoaderThrottle>(
             safe_browsing_.get(), render_frame->GetRoutingID()));
   }
 
diff --git a/chrome/renderer/resources/extensions/input.ime_custom_bindings.js b/chrome/renderer/resources/extensions/input.ime_custom_bindings.js
index 512c4e8..f3daa9c 100644
--- a/chrome/renderer/resources/extensions/input.ime_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/input.ime_custom_bindings.js
@@ -5,41 +5,48 @@
 // Custom binding for the input ime API. Only injected into the
 // v8 contexts for extensions which have permission for the API.
 
-var binding = require('binding').Binding.create('input.ime');
-
-var Event = require('event_bindings').Event;
-
+var binding = apiBridge || require('binding').Binding.create('input.ime');
 var appWindowNatives = requireNative('app_window_natives');
+var registerArgumentMassager = bindingUtil ?
+    $Function.bind(bindingUtil.registerEventArgumentMassager, bindingUtil) :
+    require('event_bindings').registerArgumentMassager;
+
+// TODO(crbug.com/733825): These bindings have some issues.
+
+var inputIme;
+registerArgumentMassager('input.ime.onKeyEvent',
+                         function(args, dispatch) {
+  var keyData = args[1];
+  var result = false;
+  try {
+    // dispatch() is weird - it returns an object {results: array<results>} iff
+    // there is at least one result value that !== undefined. Since onKeyEvent
+    // has a maximum of one listener, we know that any result we find is the one
+    // we're interested in.
+    var dispatchResult = dispatch(args);
+    if (dispatchResult && dispatchResult.results)
+      result = dispatchResult.results[0];
+  } catch (e) {
+    console.error('Error in event handler for onKeyEvent: ' + e.stack);
+  }
+  if (!inputIme.onKeyEvent.async)
+    inputIme.keyEventHandled(keyData.requestId, result);
+});
 
 binding.registerCustomHook(function(api) {
-  var input_ime = api.compiledApi;
+ inputIme = api.compiledApi;
 
-  input_ime.onKeyEvent.dispatchToListener = function(callback, args) {
-    var engineID = args[0];
-    var keyData = args[1];
-
-    var result = false;
-    try {
-      result = $Function.call(Event.prototype.dispatchToListener,
-          this, callback, args);
-    } catch (e) {
-      console.error('Error in event handler for onKeyEvent: ' + e.stack);
-    }
-    if (!input_ime.onKeyEvent.async) {
-      input_ime.keyEventHandled(keyData.requestId, result);
-    }
-  };
-
-  input_ime.onKeyEvent.addListener = function(cb, opt_extraInfo) {
-    input_ime.onKeyEvent.async = false;
+  var originalAddListener = inputIme.onKeyEvent.addListener;
+  inputIme.onKeyEvent.addListener = function(cb, opt_extraInfo) {
+    inputIme.onKeyEvent.async = false;
     if (opt_extraInfo instanceof Array) {
       for (var i = 0; i < opt_extraInfo.length; ++i) {
-        if (opt_extraInfo[i] == "async") {
-          input_ime.onKeyEvent.async = true;
+        if (opt_extraInfo[i] == 'async') {
+          inputIme.onKeyEvent.async = true;
         }
       }
     }
-    $Function.call(Event.prototype.addListener, this, cb);
+    $Function.call(originalAddListener, this, cb);
   };
 
   api.apiFunctions.setCustomCallback('createWindow',
@@ -57,4 +64,5 @@
   });
 });
 
-exports.$set('binding', binding.generate());
+if (!apiBridge)
+  exports.$set('binding', binding.generate());
diff --git a/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.cc b/chrome/renderer/safe_browsing/renderer_url_loader_throttle.cc
similarity index 73%
rename from chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.cc
rename to chrome/renderer/safe_browsing/renderer_url_loader_throttle.cc
index 16837d5..d2ea46c 100644
--- a/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.cc
+++ b/chrome/renderer/safe_browsing/renderer_url_loader_throttle.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h"
+#include "chrome/renderer/safe_browsing/renderer_url_loader_throttle.h"
 
 #include "base/logging.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
@@ -10,16 +10,16 @@
 
 namespace safe_browsing {
 
-SafeBrowsingURLLoaderThrottle::SafeBrowsingURLLoaderThrottle(
+RendererURLLoaderThrottle::RendererURLLoaderThrottle(
     mojom::SafeBrowsing* safe_browsing,
     int render_frame_id)
     : safe_browsing_(safe_browsing),
       render_frame_id_(render_frame_id),
       weak_factory_(this) {}
 
-SafeBrowsingURLLoaderThrottle::~SafeBrowsingURLLoaderThrottle() = default;
+RendererURLLoaderThrottle::~RendererURLLoaderThrottle() = default;
 
-void SafeBrowsingURLLoaderThrottle::WillStartRequest(
+void RendererURLLoaderThrottle::WillStartRequest(
     const GURL& url,
     int load_flags,
     content::ResourceType resource_type,
@@ -34,16 +34,15 @@
   safe_browsing_->CreateCheckerAndCheck(
       render_frame_id_, mojo::MakeRequest(&url_checker_), url, load_flags,
       resource_type,
-      base::BindOnce(&SafeBrowsingURLLoaderThrottle::OnCheckUrlResult,
+      base::BindOnce(&RendererURLLoaderThrottle::OnCheckUrlResult,
                      weak_factory_.GetWeakPtr()));
   safe_browsing_ = nullptr;
 
-  url_checker_.set_connection_error_handler(
-      base::Bind(&SafeBrowsingURLLoaderThrottle::OnConnectionError,
-                 base::Unretained(this)));
+  url_checker_.set_connection_error_handler(base::Bind(
+      &RendererURLLoaderThrottle::OnConnectionError, base::Unretained(this)));
 }
 
-void SafeBrowsingURLLoaderThrottle::WillRedirectRequest(
+void RendererURLLoaderThrottle::WillRedirectRequest(
     const net::RedirectInfo& redirect_info,
     bool* defer) {
   // If |blocked_| is true, the resource load has been canceled and there
@@ -58,11 +57,11 @@
   pending_checks_++;
   url_checker_->CheckUrl(
       redirect_info.new_url,
-      base::BindOnce(&SafeBrowsingURLLoaderThrottle::OnCheckUrlResult,
+      base::BindOnce(&RendererURLLoaderThrottle::OnCheckUrlResult,
                      base::Unretained(this)));
 }
 
-void SafeBrowsingURLLoaderThrottle::WillProcessResponse(bool* defer) {
+void RendererURLLoaderThrottle::WillProcessResponse(bool* defer) {
   // If |blocked_| is true, the resource load has been canceled and there
   // shouldn't be such a notification.
   DCHECK(!blocked_);
@@ -71,7 +70,7 @@
     *defer = true;
 }
 
-void SafeBrowsingURLLoaderThrottle::OnCheckUrlResult(bool safe) {
+void RendererURLLoaderThrottle::OnCheckUrlResult(bool safe) {
   if (blocked_ || !url_checker_)
     return;
 
@@ -92,7 +91,7 @@
   }
 }
 
-void SafeBrowsingURLLoaderThrottle::OnConnectionError() {
+void RendererURLLoaderThrottle::OnConnectionError() {
   DCHECK(!blocked_);
 
   // If a service-side disconnect happens, treat all URLs as if they are safe.
diff --git a/chrome/renderer/safe_browsing/renderer_url_loader_throttle.h b/chrome/renderer/safe_browsing/renderer_url_loader_throttle.h
new file mode 100644
index 0000000..f332381
--- /dev/null
+++ b/chrome/renderer/safe_browsing/renderer_url_loader_throttle.h
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_SAFE_BROWSING_RENDERER_URL_LOADER_THROTTLE_H_
+#define CHROME_RENDERER_SAFE_BROWSING_RENDERER_URL_LOADER_THROTTLE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "content/public/common/url_loader_throttle.h"
+
+namespace safe_browsing {
+
+// RendererURLLoaderThrottle is used in renderer processes to query
+// SafeBrowsing and determine whether a URL and its redirect URLs are safe to
+// load. It defers response processing until all URL checks are completed;
+// cancels the load if any URLs turn out to be bad.
+class RendererURLLoaderThrottle : public content::URLLoaderThrottle {
+ public:
+  // |safe_browsing| must stay alive until WillStartRequest() (if it is called)
+  // or the end of this object.
+  // |render_frame_id| is used for displaying SafeBrowsing UI when necessary.
+  RendererURLLoaderThrottle(mojom::SafeBrowsing* safe_browsing,
+                            int render_frame_id);
+  ~RendererURLLoaderThrottle() override;
+
+  // content::URLLoaderThrottle implementation.
+  void WillStartRequest(const GURL& url,
+                        int load_flags,
+                        content::ResourceType resource_type,
+                        bool* defer) override;
+  void WillRedirectRequest(const net::RedirectInfo& redirect_info,
+                           bool* defer) override;
+  void WillProcessResponse(bool* defer) override;
+
+ private:
+  void OnCheckUrlResult(bool safe);
+
+  void OnConnectionError();
+
+  mojom::SafeBrowsing* safe_browsing_;
+  const int render_frame_id_;
+
+  mojom::SafeBrowsingUrlCheckerPtr url_checker_;
+
+  size_t pending_checks_ = 0;
+  bool blocked_ = false;
+
+  base::WeakPtrFactory<RendererURLLoaderThrottle> weak_factory_;
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_RENDERER_SAFE_BROWSING_RENDERER_URL_LOADER_THROTTLE_H_
diff --git a/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h b/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h
deleted file mode 100644
index 3cd284a..0000000
--- a/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_RENDERER_SAFE_BROWSING_SAFE_BROWSING_URL_LOADER_THROTTLE_H_
-#define CHROME_RENDERER_SAFE_BROWSING_SAFE_BROWSING_URL_LOADER_THROTTLE_H_
-
-#include "base/memory/weak_ptr.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "content/public/common/url_loader_throttle.h"
-
-namespace chrome {
-namespace mojom {
-class SafeBrowsing;
-}
-}
-
-namespace safe_browsing {
-
-// SafeBrowsingURLLoaderThrottle queries SafeBrowsing to determine whether the
-// URL and also redirect URLs are safe to load. It defers response processing
-// until all URL checks are completed; cancels the load if any URLs turn out to
-// be bad.
-class SafeBrowsingURLLoaderThrottle : public content::URLLoaderThrottle {
- public:
-  // |safe_browsing| must stay alive until WillStartRequest() (if it is called)
-  // or the end of this object.
-  // |render_frame_id| is used for displaying SafeBrowsing UI when necessary.
-  SafeBrowsingURLLoaderThrottle(mojom::SafeBrowsing* safe_browsing,
-                                int render_frame_id);
-  ~SafeBrowsingURLLoaderThrottle() override;
-
-  // content::URLLoaderThrottle implementation.
-  void WillStartRequest(const GURL& url,
-                        int load_flags,
-                        content::ResourceType resource_type,
-                        bool* defer) override;
-  void WillRedirectRequest(const net::RedirectInfo& redirect_info,
-                           bool* defer) override;
-  void WillProcessResponse(bool* defer) override;
-
- private:
-  void OnCheckUrlResult(bool safe);
-
-  void OnConnectionError();
-
-  mojom::SafeBrowsing* safe_browsing_;
-  const int render_frame_id_;
-
-  mojom::SafeBrowsingUrlCheckerPtr url_checker_;
-
-  size_t pending_checks_ = 0;
-  bool blocked_ = false;
-
-  base::WeakPtrFactory<SafeBrowsingURLLoaderThrottle> weak_factory_;
-};
-
-}  // namespace safe_browsing
-
-#endif  // CHROME_RENDERER_SAFE_BROWSING_SAFE_BROWSING_URL_LOADER_THROTTLE_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index bab75a5..9434f09 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1522,6 +1522,7 @@
       "../browser/ui/toolbar/mock_component_toolbar_actions_factory.h",
       "../browser/ui/update_chrome_dialog_browsertest.cc",
       "../browser/ui/views/chrome_cleaner_dialog_browsertest_win.cc",
+      "../browser/ui/views/extensions/chooser_dialog_view_browsertest.cc",
       "../browser/ui/views/hung_renderer_view_browsertest.cc",
       "../browser/ui/webui/bidi_checker_web_ui_test.cc",
       "../browser/ui/webui/bidi_checker_web_ui_test.h",
diff --git a/chrome/test/data/webui/settings/device_page_tests.js b/chrome/test/data/webui/settings/device_page_tests.js
index f02d8d0..227fcfe 100644
--- a/chrome/test/data/webui/settings/device_page_tests.js
+++ b/chrome/test/data/webui/settings/device_page_tests.js
@@ -85,31 +85,11 @@
     return {
       ash: {
         night_light: {
-          enabled: {
-            key: 'ash.night_light.enabled',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          color_temperature: {
-            key: 'ash.night_light.color_temperature',
-            type: chrome.settingsPrivate.PrefType.NUMBER,
-            value: 0,
-          },
           schedule_type: {
             key: 'ash.night_light.schedule_type',
             type: chrome.settingsPrivate.PrefType.NUMBER,
             value: 0,
           },
-          custom_start_time: {
-            key: 'ash.night_light.custom_start_time',
-            type: chrome.settingsPrivate.PrefType.NUMBER,
-            value: 0,
-          },
-          custom_end_time: {
-            key: 'ash.night_light.custom_end_time',
-            type: chrome.settingsPrivate.PrefType.NUMBER,
-            value: 0,
-          },
         },
       },
       settings: {
diff --git a/chrome/utility/extensions/extensions_handler.cc b/chrome/utility/extensions/extensions_handler.cc
index c02c962..a55385e6 100644
--- a/chrome/utility/extensions/extensions_handler.cc
+++ b/chrome/utility/extensions/extensions_handler.cc
@@ -21,7 +21,6 @@
 #include "media/base/media.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/base/ui_base_switches.h"
 
 #if !defined(MEDIA_DISABLE_FFMPEG)
diff --git a/chrome/utility/extensions/extensions_handler.h b/chrome/utility/extensions/extensions_handler.h
index 6230fa8..f2e1141 100644
--- a/chrome/utility/extensions/extensions_handler.h
+++ b/chrome/utility/extensions/extensions_handler.h
@@ -14,15 +14,12 @@
 #include "chrome/utility/utility_message_handler.h"
 #include "extensions/features/features.h"
 #include "extensions/utility/utility_handler.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 
 #if !BUILDFLAG(ENABLE_EXTENSIONS)
 #error "Extensions must be enabled"
 #endif
 
-namespace service_manager {
-class BinderRegistry;
-}
-
 namespace extensions {
 
 // Dispatches IPCs for Chrome extensions utility messages.
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 02f7185..97ab081 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -309,8 +309,6 @@
 
   additional_options = [ "--ozone-platform=headless --test-launcher-bot-mode" ]
 
-  build_tests = true
-
   test_groups = [ ":cast_tests" ]
 
   if (chromecast_branding != "public") {
@@ -336,7 +334,6 @@
     build_list_path = "$root_out_dir/junit/build_junit_test_list.txt"
     runtime_deps_path = "$root_out_dir/junit/runtime_deps.json"
     run_list_path = "$root_out_dir/junit/run_junit_test_list.txt"
-    build_tests = true
     test_groups = [ ":cast_junit_tests" ]
     if (chromecast_branding != "public") {
       test_groups += [ "//chromecast/internal:internal_cast_junit_tests" ]
diff --git a/chromecast/base/cast_features.cc b/chromecast/base/cast_features.cc
index 636f145..d65ce5f 100644
--- a/chromecast/base/cast_features.cc
+++ b/chromecast/base/cast_features.cc
@@ -40,8 +40,79 @@
   g_experiment_ids.Get().swap(ids);
   g_experiment_ids_initialized = true;
 }
+
 }  // namespace
 
+// PLEASE READ!
+// Cast Platform Features are listed below. These features may be
+// toggled via configs fetched from DCS for devices in the field, or via
+// command-line flags set by the developer. For the end-to-end details of the
+// system design, please see go/dcs-experiments.
+//
+// Below are useful steps on how to use these features in your code.
+//
+// 1) Declaring and defining the feature.
+//    All Cast Platform Features should be declared in a common file with other
+//    Cast Platform Features (ex. chromecast/base/cast_features.h). When
+//    defining your feature, you will need to assign a default value. This is
+//    the value that the feature will hold until overriden by the server or the
+//    command line. Here's an exmaple:
+//
+//      const base::Feature kSuperSecretSauce{
+//          "enable-super-secret-sauce", base::FEATURE_DISABLED_BY_DEFAULT};
+//
+//    IMPORTANT NOTE:
+//    The first parameter that you pass in the definition is the feature's name.
+//    This MUST match the DCS experiement key for this feature. Use dashes (not
+//    underscores) in the names.
+//
+// 2) Using the feature in client code.
+//    Using these features in your code is easy. Here's an example:
+//
+//      #include “base/feature_list.h”
+//      #include “chromecast/base/chromecast_switches.h”
+//
+//      std::unique_ptr<Foo> CreateFoo() {
+//        if (base::FeatureList::IsEnabled(kSuperSecretSauce))
+//          return base::MakeUnique<SuperSecretFoo>();
+//        return base::MakeUnique<BoringOldFoo>();
+//      }
+//
+//    base::FeatureList can be called from any thread, in any process, at any
+//    time after PreCreateThreads(). It will return whether the feature is
+//    enabled.
+//
+// 3) Overriding the default value from the server.
+//    For devices in the field, DCS will issue different configs to different
+//    groups of devices, allowing us to run experiments on features. These
+//    feature settings will manifest on the next boot of cast_shell. In the
+//    example, if the latest config for a particular device set the value of
+//    kSuperSecretSauce to true, the appropriate code path would be taken.
+//    Otherwise, the default value, false, would be used. For more details on
+//    setting up experiments, see go/dcs-launch.
+//
+// 4) Overriding the default and server values from the command-line.
+//    While the server value trumps the default values, the command line trumps
+//    both. Enable features by passing this command line arg to cast_shell:
+//
+//      --enable-features=enable-foo,enable-super-secret-sauce
+//
+//    Features are separated by commas. Disable features by passing:
+//
+//      --disable-features=enable-foo,enable-bar
+//
+
+// Begin Chromecast Feature definitions.
+
+// Enables the use of QUIC in Cast-specific URLRequestContextGetters. See
+// chromecast/browser/url_request_context_factory.cc for usage.
+// NOTE: This feature has a legacy name - do not use it as your convention.
+// Dashes, not underscores, should be used in Feature names.
+const base::Feature kEnableQuic{"enable_quic",
+                                base::FEATURE_DISABLED_BY_DEFAULT};
+
+// End Chromecast Feature definitions.
+
 // An iterator for a base::DictionaryValue. Use an alias for brevity in loops.
 using Iterator = base::DictionaryValue::Iterator;
 
diff --git a/chromecast/base/cast_features.h b/chromecast/base/cast_features.h
index 928399a..138bd97 100644
--- a/chromecast/base/cast_features.h
+++ b/chromecast/base/cast_features.h
@@ -20,6 +20,13 @@
 
 namespace chromecast {
 
+// Add Cast Features here.
+extern const base::Feature kEnableQuic;
+
+// Below are utilities needed by the Cast receiver to persist feature
+// information. Client code which is simply querying the status of a feature
+// will not need to use these utilities.
+
 // Initialize the global base::FeatureList instance, taking into account
 // overrides from DCS and the command line. |dcs_features| and
 // |dcs_experiment_ids| are read from the PrefService in the browser process.
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index cd8783e..a72abae 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -514,11 +514,6 @@
           cast_browser_process_->browser_context(),
           cast_browser_process_->pref_service(),
           url_request_context_factory_->GetSystemGetter(),
-          base::BindOnce(&URLRequestContextFactory::DisableQuic,
-                         // Safe since |url_request_context_factory_| is owned
-                         // by CastContentBrowserClient, which lives for the
-                         // entire lifetime of cast_shell.
-                         base::Unretained(url_request_context_factory_)),
           video_plane_controller_.get(), window_manager_.get()));
   cast_browser_process_->cast_service()->Initialize();
 
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index d744703c..39b22c1 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -60,7 +60,6 @@
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -149,7 +148,6 @@
     content::BrowserContext* browser_context,
     PrefService* pref_service,
     net::URLRequestContextGetter* request_context_getter,
-    DisableQuicClosure disable_quic_closure,
     media::VideoPlaneController* video_plane_controller,
     CastWindowManager* window_manager) {
   return base::MakeUnique<CastServiceSimple>(browser_context, pref_service,
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 27a6447..01278c15 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -10,7 +10,6 @@
 #include <string>
 #include <vector>
 
-#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
 #include "build/build_config.h"
@@ -18,6 +17,7 @@
 #include "chromecast/chromecast_features.h"
 #include "content/public/browser/certificate_request_result_type.h"
 #include "content/public/browser/content_browser_client.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 
 class PrefService;
 
@@ -34,10 +34,6 @@
 class X509Certificate;
 }
 
-namespace service_manager {
-class BinderRegistry;
-}
-
 namespace chromecast {
 class CastService;
 class CastWindowManager;
@@ -58,8 +54,6 @@
 class CastResourceDispatcherHostDelegate;
 class URLRequestContextFactory;
 
-using DisableQuicClosure = base::OnceClosure;
-
 class CastContentBrowserClient : public content::ContentBrowserClient {
  public:
   // Creates an implementation of CastContentBrowserClient. Platform should
@@ -81,7 +75,6 @@
       content::BrowserContext* browser_context,
       PrefService* pref_service,
       net::URLRequestContextGetter* request_context_getter,
-      DisableQuicClosure disable_quic_closure,
       media::VideoPlaneController* video_plane_controller,
       CastWindowManager* window_manager);
 
diff --git a/chromecast/browser/url_request_context_factory.cc b/chromecast/browser/url_request_context_factory.cc
index 1305f99..f9a7eb76 100644
--- a/chromecast/browser/url_request_context_factory.cc
+++ b/chromecast/browser/url_request_context_factory.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task_scheduler/post_task.h"
+#include "chromecast/base/cast_features.h"
 #include "chromecast/base/chromecast_switches.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/cast_http_user_agent_settings.h"
@@ -164,8 +165,7 @@
       system_network_delegate_(CastNetworkDelegate::Create()),
       system_dependencies_initialized_(false),
       main_dependencies_initialized_(false),
-      media_dependencies_initialized_(false),
-      enable_quic_(true) {}
+      media_dependencies_initialized_(false) {}
 
 URLRequestContextFactory::~URLRequestContextFactory() {
   pref_proxy_config_tracker_impl_->DetachFromPrefService();
@@ -328,8 +328,12 @@
   session_context->proxy_service = proxy_service_.get();
 
   session_params->ignore_certificate_errors = ignore_certificate_errors;
-  LOG(INFO) << "Set HttpNetworkSessionParams.enable_quic = " << enable_quic_;
-  session_params->enable_quic = enable_quic_;
+
+  // Enable QUIC if instructed by DCS. This remains constant for the lifetime of
+  // the process.
+  session_params->enable_quic = base::FeatureList::IsEnabled(kEnableQuic);
+  LOG(INFO) << "Set HttpNetworkSessionParams.enable_quic = "
+            << session_params->enable_quic;
 }
 
 net::URLRequestContext* URLRequestContextFactory::CreateSystemRequestContext() {
@@ -437,43 +441,5 @@
   LOG(INFO) << "Initialized system network delegate.";
 }
 
-void URLRequestContextFactory::DisableQuic() {
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO, FROM_HERE,
-      base::Bind(&URLRequestContextFactory::DisableQuicOnBrowserIOThread,
-                 base::Unretained(this)));
-}
-
-void URLRequestContextFactory::DisableQuicOnBrowserIOThread() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  if (!enable_quic_)
-    return;
-
-  LOG(INFO) << "Disabled QUIC.";
-
-  enable_quic_ = false;
-
-  if (main_getter_) {
-    main_getter_->GetURLRequestContext()
-        ->http_transaction_factory()
-        ->GetSession()
-        ->DisableQuic();
-  }
-
-  if (system_getter_) {
-    system_getter_->GetURLRequestContext()
-        ->http_transaction_factory()
-        ->GetSession()
-        ->DisableQuic();
-  }
-
-  if (media_getter_) {
-    media_getter_->GetURLRequestContext()
-        ->http_transaction_factory()
-        ->GetSession()
-        ->DisableQuic();
-  }
-}
-
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/url_request_context_factory.h b/chromecast/browser/url_request_context_factory.h
index ba8deda..85814a4 100644
--- a/chromecast/browser/url_request_context_factory.h
+++ b/chromecast/browser/url_request_context_factory.h
@@ -67,8 +67,6 @@
   // after the CastService is created, but before any URL requests are made.
   void InitializeNetworkDelegates();
 
-  void DisableQuic();
-
  private:
   class URLRequestContextGetter;
   class MainURLRequestContextGetter;
@@ -86,7 +84,6 @@
       bool ignore_certificate_errors,
       net::HttpNetworkSession::Params* session_params,
       net::HttpNetworkSession::Context* session_context);
-  void DisableQuicOnBrowserIOThread();
 
   // These are called by the RequestContextGetters to create each
   // RequestContext.
@@ -136,11 +133,6 @@
 
   std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_impl_;
 
-  // Determines if QUIC is enabled for a URLContextGetter when it is created.
-  // QUIC can be disabled at runtime by calling DisableQuic() above.
-  // Only accessed on content::BrowserThread::IO thread.
-  bool enable_quic_;
-
   net::NetLog* net_log_;
 };
 
diff --git a/chromecast/build/tests/cast_test.gni b/chromecast/build/tests/cast_test.gni
index 2da7866..0621295 100644
--- a/chromecast/build/tests/cast_test.gni
+++ b/chromecast/build/tests/cast_test.gni
@@ -70,6 +70,13 @@
 import("//chromecast/chromecast.gni")
 import("//testing/test.gni")
 
+declare_args() {
+  # Set this true to build all tests in a cast_test_group_list by default. This
+  # can be overriden in each cast_test_group_list instance by setting
+  # |build_tests|.
+  build_cast_tests_by_default = true
+}
+
 # Do not allow mixing of gtests and junit tests. All gtests must go into one
 # directory, while all junit tests must go into another directory.
 _gtest_gen_dir = "$root_gen_dir/chromecast/tests"
@@ -308,9 +315,12 @@
 #       Options which are passed to the python script, and applied to every test
 #
 #   build_tests (optional)
-#       Set this to true to build all of the tests included in |test_groups|.
-#       Defaults to false. Note that if this is set to true, the test targets
-#       will be built after all the lists are generated.
+#       If true, all of the tests included in |test_groups| will be built as
+#       dependencies of this target. If false, only the lists will be generated.
+#       If not explicitly set, this defaults to the value of
+#       |build_cast_tests_by_default|. Note that if this is set to true,
+#       the test targets will be built after all the lists are generated.
+#
 #   test_type (optional)
 #       A string, which must be either "junit" or "gtest". If not defined,
 #       defaults to "gtest".
@@ -357,12 +367,18 @@
     _filter_actions += [ _test_group + "_filters" ]
   }
 
+  # Decide whether tests are built as dependencies.
+  _build_cast_tests = build_cast_tests_by_default
+  if (defined(invoker.build_tests)) {
+    _build_cast_tests = invoker.build_tests
+  }
+
   # Generate a list of the groups of targets, so that they can be depended upon
   # by the "pack_run" step and built when this target is invoked.
-  if (defined(invoker.build_tests) && invoker.build_tests) {
-    _build_tests = []
+  if (_build_cast_tests) {
+    _build_test_targets = []
     foreach(_test_group, invoker.test_groups) {
-      _build_tests += [ _test_group + "_build_tests" ]
+      _build_test_targets += [ _test_group + "_build_tests" ]
     }
   }
 
@@ -429,10 +445,10 @@
     # script executes.
     deps += _filter_actions
 
-    # If |build_tests| has been set to true, depend on the testing targets so
-    # that the tests are built.
-    if (defined(_build_tests)) {
-      deps += _build_tests
+    # If |_build_cast_tests| has been set to true, depend on the testing targets
+    # so that the tests are built.
+    if (_build_cast_tests) {
+      deps += _build_test_targets
     }
 
     if (chromecast_branding != "public") {
diff --git a/chromeos/components/tether/ble_scanner.cc b/chromeos/components/tether/ble_scanner.cc
index 5ed761d..709e5f5 100644
--- a/chromeos/components/tether/ble_scanner.cc
+++ b/chromeos/components/tether/ble_scanner.cc
@@ -228,7 +228,7 @@
   std::string service_data_str;
   char* string_contents_ptr =
       base::WriteInto(&service_data_str, service_data->size() + 1);
-  memcpy(string_contents_ptr, service_data->data(), service_data->size() + 1);
+  memcpy(string_contents_ptr, service_data->data(), service_data->size());
 
   CheckForMatchingScanFilters(bluetooth_device, service_data_str);
 }
diff --git a/components/autofill/core/browser/region_combobox_model.h b/components/autofill/core/browser/region_combobox_model.h
index d148e53..e6a89b2 100644
--- a/components/autofill/core/browser/region_combobox_model.h
+++ b/components/autofill/core/browser/region_combobox_model.h
@@ -43,6 +43,10 @@
 
   bool failed_to_load_data() const { return failed_to_load_data_; }
 
+  const std::vector<std::pair<std::string, std::string>>& GetRegions() const {
+    return regions_;
+  }
+
   // ui::ComboboxModel implementation:
   int GetItemCount() const override;
   base::string16 GetItemAt(int index) override;
diff --git a/components/cast_channel/cast_socket.cc b/components/cast_channel/cast_socket.cc
index 555c5a0..e60004e 100644
--- a/components/cast_channel/cast_socket.cc
+++ b/components/cast_channel/cast_socket.cc
@@ -26,6 +26,7 @@
 #include "components/cast_channel/cast_framer.h"
 #include "components/cast_channel/cast_message_util.h"
 #include "components/cast_channel/cast_transport.h"
+#include "components/cast_channel/keep_alive_delegate.h"
 #include "components/cast_channel/logger.h"
 #include "components/cast_channel/proto/cast_channel.pb.h"
 #include "net/base/address_list.h"
@@ -87,29 +88,33 @@
 
 CastSocketImpl::CastSocketImpl(const net::IPEndPoint& ip_endpoint,
                                net::NetLog* net_log,
-                               const base::TimeDelta& timeout,
-                               bool keep_alive,
+                               base::TimeDelta timeout,
+                               base::TimeDelta liveness_timeout,
+                               base::TimeDelta ping_interval,
                                const scoped_refptr<Logger>& logger,
                                uint64_t device_capabilities)
     : CastSocketImpl(ip_endpoint,
                      net_log,
                      timeout,
-                     keep_alive,
+                     liveness_timeout,
+                     ping_interval,
                      logger,
                      device_capabilities,
                      AuthContext::Create()) {}
 
 CastSocketImpl::CastSocketImpl(const net::IPEndPoint& ip_endpoint,
                                net::NetLog* net_log,
-                               const base::TimeDelta& timeout,
-                               bool keep_alive,
+                               base::TimeDelta timeout,
+                               base::TimeDelta liveness_timeout,
+                               base::TimeDelta ping_interval,
                                const scoped_refptr<Logger>& logger,
                                uint64_t device_capabilities,
                                const AuthContext& auth_context)
     : channel_id_(0),
       ip_endpoint_(ip_endpoint),
       net_log_(net_log),
-      keep_alive_(keep_alive),
+      liveness_timeout_(liveness_timeout),
+      ping_interval_(ping_interval),
       logger_(logger),
       auth_context_(auth_context),
       connect_timeout_(timeout),
@@ -156,7 +161,7 @@
 }
 
 bool CastSocketImpl::keep_alive() const {
-  return keep_alive_;
+  return liveness_timeout_ > base::TimeDelta();
 }
 
 bool CastSocketImpl::audio_only() const {
@@ -529,6 +534,12 @@
 
   if (error_state_ == ChannelError::NONE) {
     SetReadyState(ReadyState::OPEN);
+    if (keep_alive()) {
+      auto* keep_alive_delegate =
+          new KeepAliveDelegate(this, logger_, std::move(delegate_),
+                                ping_interval_, liveness_timeout_);
+      delegate_.reset(keep_alive_delegate);
+    }
     transport_->SetReadDelegate(std::move(delegate_));
   } else {
     CloseInternal();
diff --git a/components/cast_channel/cast_socket.h b/components/cast_channel/cast_socket.h
index 7026710..6afee6a 100644
--- a/components/cast_channel/cast_socket.h
+++ b/components/cast_channel/cast_socket.h
@@ -127,11 +127,14 @@
   // |ip_endpoint|: IP address of the remote host.
   // |net_log|: Log of socket events.
   // |connect_timeout|: Connection timeout interval.
+  // |liveness_timeout|: Amount of idle time to wait before disconnecting.
+  // |ping_interval|: Amount of idle time to wait before pinging the receiver.
   // |logger|: Log of cast channel events.
   CastSocketImpl(const net::IPEndPoint& ip_endpoint,
                  net::NetLog* net_log,
-                 const base::TimeDelta& connect_timeout,
-                 bool keep_alive,
+                 base::TimeDelta connect_timeout,
+                 base::TimeDelta liveness_timeout,
+                 base::TimeDelta ping_interval,
                  const scoped_refptr<Logger>& logger,
                  uint64_t device_capabilities);
 
@@ -139,8 +142,9 @@
   // This constructor allows for setting a custom AuthContext.
   CastSocketImpl(const net::IPEndPoint& ip_endpoint,
                  net::NetLog* net_log,
-                 const base::TimeDelta& connect_timeout,
-                 bool keep_alive,
+                 base::TimeDelta connect_timeout,
+                 base::TimeDelta liveness_timeout,
+                 base::TimeDelta ping_interval,
                  const scoped_refptr<Logger>& logger,
                  uint64_t device_capabilities,
                  const AuthContext& auth_context);
@@ -282,8 +286,14 @@
   net::NetLog* net_log_;
   // The NetLog source for this service.
   net::NetLogSource net_log_source_;
-  // True when keep-alive signaling should be handled for this socket.
-  bool keep_alive_;
+
+  // Amount of idle time to wait before disconnecting. If |liveness_timeout_| is
+  // set, wraps |delegate_| with a KeepAliveDelegate.
+  base::TimeDelta liveness_timeout_;
+
+  // Amount of idle time to wait before pinging the receiver, used to create
+  // KeepAliveDelegate.
+  base::TimeDelta ping_interval_;
 
   // Shared logging object, used to log CastSocket events for diagnostics.
   scoped_refptr<Logger> logger_;
diff --git a/components/cast_channel/cast_socket_unittest.cc b/components/cast_channel/cast_socket_unittest.cc
index bf9f4ec..c05487885 100644
--- a/components/cast_channel/cast_socket_unittest.cc
+++ b/components/cast_channel/cast_socket_unittest.cc
@@ -194,7 +194,8 @@
       : CastSocketImpl(ip_endpoint,
                        capturing_net_log,
                        base::TimeDelta::FromMilliseconds(timeout_ms),
-                       false,
+                       base::TimeDelta(),
+                       base::TimeDelta(),
                        logger,
                        device_capabilities,
                        AuthContext::Create()),
diff --git a/components/download/content/internal/download_driver_impl.cc b/components/download/content/internal/download_driver_impl.cc
index 0300deb..d6581da 100644
--- a/components/download/content/internal/download_driver_impl.cc
+++ b/components/download/content/internal/download_driver_impl.cc
@@ -4,6 +4,9 @@
 
 #include "components/download/content/internal/download_driver_impl.h"
 
+#include <set>
+#include <vector>
+
 #include "components/download/internal/driver_entry.h"
 #include "content/public/browser/download_interrupt_reasons.h"
 #include "content/public/browser/download_url_parameters.h"
@@ -152,6 +155,23 @@
   return base::nullopt;
 }
 
+std::set<std::string> DownloadDriverImpl::GetActiveDownloads() {
+  std::set<std::string> guids;
+  if (!download_manager_)
+    return guids;
+
+  std::vector<content::DownloadItem*> items;
+  download_manager_->GetAllDownloads(&items);
+
+  for (auto* item : items) {
+    DriverEntry::State state = ToDriverEntryState(item->GetState());
+    if (state == DriverEntry::State::IN_PROGRESS)
+      guids.insert(item->GetGuid());
+  }
+
+  return guids;
+}
+
 void DownloadDriverImpl::OnDownloadUpdated(content::DownloadItem* item) {
   DCHECK(client_);
 
@@ -168,6 +188,8 @@
   } else if (reason !=
              content::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_NONE) {
     client_->OnDownloadFailed(entry, static_cast<int>(reason));
+    // TODO(dtrainor, xingliu): This actually might not be correct.  What if we
+    // restart the download?
     item->RemoveObserver(this);
   }
 }
diff --git a/components/download/content/internal/download_driver_impl.h b/components/download/content/internal/download_driver_impl.h
index 5330733..63acc5f 100644
--- a/components/download/content/internal/download_driver_impl.h
+++ b/components/download/content/internal/download_driver_impl.h
@@ -43,6 +43,7 @@
   void Pause(const std::string& guid) override;
   void Resume(const std::string& guid) override;
   base::Optional<DriverEntry> Find(const std::string& guid) override;
+  std::set<std::string> GetActiveDownloads() override;
 
  private:
   // content::DownloadItem::Observer implementation.
diff --git a/components/download/content/internal/download_driver_impl_unittest.cc b/components/download/content/internal/download_driver_impl_unittest.cc
index 41a7198..5fc7c86b 100644
--- a/components/download/content/internal/download_driver_impl_unittest.cc
+++ b/components/download/content/internal/download_driver_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 
+#include "base/guid.h"
 #include "base/memory/ptr_util.h"
 #include "content/public/test/fake_download_item.h"
 #include "content/public/test/mock_download_manager.h"
@@ -15,6 +16,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::_;
+using testing::Invoke;
 using testing::NiceMock;
 using testing::Return;
 
@@ -22,6 +24,10 @@
 
 namespace {
 
+ACTION_P(PopulateVector, items) {
+  arg0->insert(arg0->begin(), items.begin(), items.end());
+}
+
 const char kFakeGuid[] = "fake_guid";
 
 // Matcher to compare driver entries. Not all the memeber fields are compared.
@@ -126,4 +132,39 @@
       ->OnDownloadUpdated(&fake_item);
 }
 
+TEST_F(DownloadDriverImplTest, TestGetActiveDownloadsCall) {
+  using DownloadState = content::DownloadItem::DownloadState;
+  content::FakeDownloadItem item1;
+  item1.SetState(DownloadState::IN_PROGRESS);
+  item1.SetGuid(base::GenerateGUID());
+
+  content::FakeDownloadItem item2;
+  item2.SetState(DownloadState::CANCELLED);
+  item2.SetGuid(base::GenerateGUID());
+
+  content::FakeDownloadItem item3;
+  item3.SetState(DownloadState::COMPLETE);
+  item3.SetGuid(base::GenerateGUID());
+
+  content::FakeDownloadItem item4;
+  item4.SetState(DownloadState::INTERRUPTED);
+  item4.SetGuid(base::GenerateGUID());
+
+  std::vector<content::DownloadItem*> items{&item1, &item2, &item3, &item4};
+
+  ON_CALL(mock_manager_, GetAllDownloads(_))
+      .WillByDefault(PopulateVector(items));
+
+  EXPECT_CALL(mock_manager_, IsManagerInitialized())
+      .Times(1)
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_client_, OnDriverReady(true)).Times(1);
+  driver_->Initialize(&mock_client_);
+
+  auto guids = driver_->GetActiveDownloads();
+
+  EXPECT_EQ(1U, guids.size());
+  EXPECT_NE(guids.end(), guids.find(item1.GetGuid()));
+}
+
 }  // namespace download
diff --git a/components/download/internal/controller_impl.cc b/components/download/internal/controller_impl.cc
index 136a145..46ac490 100644
--- a/components/download/internal/controller_impl.cc
+++ b/components/download/internal/controller_impl.cc
@@ -72,13 +72,16 @@
       model_(std::move(model)),
       device_status_listener_(std::move(device_status_listener)),
       scheduler_(std::move(scheduler)),
-      task_scheduler_(std::move(task_scheduler)) {}
+      task_scheduler_(std::move(task_scheduler)),
+      initializing_internals_(false),
+      weak_ptr_factory_(this) {}
 
 ControllerImpl::~ControllerImpl() = default;
 
 void ControllerImpl::Initialize() {
   DCHECK(!startup_status_.Complete());
 
+  initializing_internals_ = true;
   driver_->Initialize(this);
   model_->Initialize(this);
 }
@@ -90,6 +93,10 @@
 void ControllerImpl::StartDownload(const DownloadParams& params) {
   DCHECK(startup_status_.Ok());
 
+  // TODO(dtrainor): Check if there are any downloads we can cancel.  We don't
+  // want to return a BACKOFF if we technically could time out a download to
+  // start this one.
+
   if (start_callbacks_.find(params.guid) != start_callbacks_.end() ||
       model_->Get(params.guid) != nullptr) {
     HandleStartDownloadResponse(params.client, params.guid,
@@ -255,10 +262,13 @@
 }
 
 void ControllerImpl::OnDownloadCreated(const DriverEntry& download) {
+  if (initializing_internals_)
+    return;
+
   Entry* entry = model_->Get(download.guid);
 
   if (!entry) {
-    // TODO(xingliu): Log non download service initiated downloads.
+    HandleExternalDownload(download.guid, true);
     return;
   }
 
@@ -273,35 +283,54 @@
 }
 
 void ControllerImpl::OnDownloadFailed(const DriverEntry& download, int reason) {
-  Entry* entry = model_->Get(download.guid);
-  if (!entry)
+  if (initializing_internals_)
     return;
 
+  Entry* entry = model_->Get(download.guid);
+  if (!entry) {
+    HandleExternalDownload(download.guid, false);
+    return;
+  }
+
   // TODO(dtrainor): Add retry logic here.  Connect to restart code for tracking
   // number of retries.
 
+  // TODO(dtrainor, xingliu): We probably have to prevent cancel calls from
+  // coming through here as we remove downloads (especially through
+  // initialization).
   HandleCompleteDownload(CompletionType::FAIL, download.guid);
 }
 
 void ControllerImpl::OnDownloadSucceeded(const DriverEntry& download,
                                          const base::FilePath& path) {
-  Entry* entry = model_->Get(download.guid);
-  if (!entry)
+  if (initializing_internals_)
     return;
 
+  Entry* entry = model_->Get(download.guid);
+  if (!entry) {
+    HandleExternalDownload(download.guid, false);
+    return;
+  }
+
   HandleCompleteDownload(CompletionType::SUCCEED, download.guid);
 }
 
 void ControllerImpl::OnDownloadUpdated(const DriverEntry& download) {
-  Entry* entry = model_->Get(download.guid);
-  if (!entry)
+  if (initializing_internals_)
     return;
 
+  Entry* entry = model_->Get(download.guid);
+  if (!entry) {
+    HandleExternalDownload(download.guid, !download.paused);
+    return;
+  }
+
   DCHECK_EQ(download.state, DriverEntry::State::IN_PROGRESS);
 
-  download::Client* client = clients_->GetClient(entry->client);
-  if (client)
-    client->OnDownloadUpdated(download.guid, download.bytes_downloaded);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&ControllerImpl::SendOnDownloadUpdated,
+                            weak_ptr_factory_.GetWeakPtr(), entry->client,
+                            download.guid, download.bytes_downloaded));
 }
 
 void ControllerImpl::OnModelReady(bool success) {
@@ -374,17 +403,29 @@
   }
 
   device_status_listener_->Start(this);
+  PollActiveDriverDownloads();
   CancelOrphanedRequests();
   ResolveInitialRequestStates();
-  UpdateDriverStates();
-  PullCurrentRequestStatus();
   NotifyClientsOfStartup();
+
+  initializing_internals_ = false;
+
+  UpdateDriverStates();
   ProcessScheduledTasks();
 
   // Pull the initial straw if active downloads haven't reach maximum.
   ActivateMoreDownloads();
 }
 
+void ControllerImpl::PollActiveDriverDownloads() {
+  std::set<std::string> guids = driver_->GetActiveDownloads();
+
+  for (auto guid : guids) {
+    if (!model_->Get(guid))
+      externally_active_downloads_.insert(guid);
+  }
+}
+
 void ControllerImpl::CancelOrphanedRequests() {
   auto entries = model_->PeekEntries();
 
@@ -395,14 +436,124 @@
   }
 
   for (const auto& guid : guids_to_remove) {
-    model_->Remove(guid);
     // TODO(xingliu): Use file monitor to delete the files.
     driver_->Remove(guid);
+    model_->Remove(guid);
   }
 }
 
 void ControllerImpl::ResolveInitialRequestStates() {
-  // TODO(dtrainor): Implement.
+  auto entries = model_->PeekEntries();
+  for (auto* entry : entries) {
+    // Pull the initial Entry::State and DriverEntry::State.
+    Entry::State state = entry->state;
+    auto driver_entry = driver_->Find(entry->guid);
+    base::Optional<DriverEntry::State> driver_state;
+    if (driver_entry.has_value()) {
+      DCHECK_NE(DriverEntry::State::UNKNOWN, driver_entry->state);
+      driver_state = driver_entry->state;
+    }
+
+    // Determine what the new Entry::State should be based on the two original
+    // states of the two different systems.
+    Entry::State new_state = state;
+    switch (state) {
+      case Entry::State::NEW:
+        // This means we shut down but may have not ACK'ed the download.  That
+        // is OK, we will still notify the Client about the GUID when we send
+        // them our initialize method.
+        new_state = Entry::State::AVAILABLE;
+        break;
+      case Entry::State::COMPLETE:
+        // We're already in our end state.  Just stay here.
+        new_state = Entry::State::COMPLETE;
+        break;
+      case Entry::State::AVAILABLE:  // Intentional fallthrough.
+      case Entry::State::ACTIVE:     // Intentional fallthrough.
+      case Entry::State::PAUSED: {
+        // All three of these states are effectively driven by the DriverEntry
+        // state.
+        if (!driver_state.has_value()) {
+          // If we don't have a DriverEntry::State, just leave the state alone.
+          new_state = state;
+          break;
+        }
+
+        // If we have a real DriverEntry::State, we need to determine which of
+        // those states makes sense for our Entry.  Our entry can either be in
+        // two states now: It's effective 'active' state (ACTIVE or PAUSED) or
+        // COMPLETE.
+        bool is_paused = state == Entry::State::PAUSED;
+        Entry::State active =
+            is_paused ? Entry::State::PAUSED : Entry::State::ACTIVE;
+
+        switch (driver_state.value()) {
+          case DriverEntry::State::IN_PROGRESS:  // Intentional fallthrough.
+          case DriverEntry::State::INTERRUPTED:
+            // The DriverEntry isn't done, so we need to set the Entry to the
+            // 'active' state.
+            new_state = active;
+            break;
+          case DriverEntry::State::COMPLETE:  // Intentional fallthrough.
+          // TODO(dtrainor, xingliu) Revisit this CANCELLED state to make sure
+          // all embedders behave properly.
+          case DriverEntry::State::CANCELLED:
+            // The DriverEntry is done.  We need to set the Entry to the
+            // COMPLETE state.
+            new_state = Entry::State::COMPLETE;
+            break;
+          default:
+            NOTREACHED();
+            break;
+        }
+        break;
+      }
+      default:
+        NOTREACHED();
+        break;
+    }
+
+    // Update the Entry::State to the new correct state.
+    if (new_state != entry->state) {
+      stats::LogRecoveryOperation(new_state);
+      TransitTo(entry, new_state, model_.get());
+    }
+
+    // Given the new correct state, update the DriverEntry to reflect the Entry.
+    switch (new_state) {
+      case Entry::State::NEW:        // Intentional fallthrough.
+      case Entry::State::AVAILABLE:  // Intentional fallthrough.
+        // We should not have a DriverEntry here.
+        if (driver_entry.has_value())
+          driver_->Remove(entry->guid);
+        break;
+      case Entry::State::ACTIVE:  // Intentional fallthrough.
+      case Entry::State::PAUSED:
+        // We're in the correct state.  Let UpdateDriverStates() restart us if
+        // it wants to.
+        break;
+      case Entry::State::COMPLETE:
+        if (state != Entry::State::COMPLETE) {
+          // We are changing states to COMPLETE.  Handle this like a normal
+          // completed download.
+
+          // Treat CANCELLED and INTERRUPTED as failures.  We have to assume the
+          // DriverEntry might not have persisted in time.
+          CompletionType completion_type =
+              (!driver_entry.has_value() ||
+               driver_entry->state == DriverEntry::State::CANCELLED ||
+               driver_entry->state == DriverEntry::State::INTERRUPTED)
+                  ? CompletionType::UNKNOWN
+                  : CompletionType::SUCCEED;
+          HandleCompleteDownload(completion_type, entry->guid);
+        } else {
+          // We're staying in COMPLETE.  Make sure there is no DriverEntry here.
+          if (driver_entry.has_value())
+            driver_->Remove(entry->guid);
+        }
+        break;
+    }
+  }
 }
 
 void ControllerImpl::UpdateDriverStates() {
@@ -413,46 +564,39 @@
 }
 
 void ControllerImpl::UpdateDriverState(const Entry& entry) {
+  DCHECK(!initializing_internals_);
+
+  if (entry.state != Entry::State::ACTIVE &&
+      entry.state != Entry::State::PAUSED) {
+    return;
+  }
+
+  // This method will need to figure out what to do with a failed download and
+  // either a) restart it or b) fail the download.
+
   base::Optional<DriverEntry> driver_entry = driver_->Find(entry.guid);
 
   bool meets_device_criteria = device_status_listener_->CurrentDeviceStatus()
                                    .MeetsCondition(entry.scheduling_params)
                                    .MeetsRequirements();
-  switch (entry.state) {
-    case Entry::State::ACTIVE:
-      if (!meets_device_criteria) {
-        driver_->Pause(entry.guid);
-        break;
-      }
-      // Start or resume the download if it should be running.
-      if (!driver_entry.has_value()) {
-        driver_->Start(entry.request_params, entry.guid,
-                       NO_TRAFFIC_ANNOTATION_YET);
-        break;
-      }
-      if (driver_entry->state != DriverEntry::State::IN_PROGRESS) {
-        driver_->Resume(entry.guid);
-      }
-      break;
-    case Entry::State::PAUSED:
-      // Pause the in progress downloads that should not be running.
-      if (driver_entry.has_value() &&
-          driver_entry->state == DriverEntry::State::IN_PROGRESS) {
-        driver_->Pause(entry.guid);
-      }
-      break;
-    // Fall through.
-    case Entry::State::AVAILABLE:
-    case Entry::State::NEW:
-    case Entry::State::COMPLETE:
-      break;
-    default:
-      NOTREACHED();
-  }
-}
+  bool force_pause =
+      !externally_active_downloads_.empty() &&
+      entry.scheduling_params.priority != SchedulingParams::Priority::UI;
+  bool entry_paused = entry.state == Entry::State::PAUSED;
 
-void ControllerImpl::PullCurrentRequestStatus() {
-  // TODO(dtrainor): Implement.
+  bool pause_driver = entry_paused || force_pause || !meets_device_criteria;
+
+  if (pause_driver) {
+    if (driver_entry.has_value())
+      driver_->Pause(entry.guid);
+  } else {
+    if (driver_entry.has_value()) {
+      driver_->Resume(entry.guid);
+    } else {
+      driver_->Start(entry.request_params, entry.guid,
+                     NO_TRAFFIC_ANNOTATION_YET);
+    }
+  }
 }
 
 void ControllerImpl::NotifyClientsOfStartup() {
@@ -505,20 +649,22 @@
     return;
   }
 
-  auto* client = clients_->GetClient(entry->client);
-  DCHECK(client);
-
   if (type == CompletionType::SUCCEED) {
     auto driver_entry = driver_->Find(guid);
     DCHECK(driver_entry.has_value());
     // TODO(dtrainor): Move the FilePath generation to the controller and store
     // it in Entry.  Then pass it into the DownloadDriver.
-    // TODO(dtrainor): PostTask this instead of putting it inline.
-    client->OnDownloadSucceeded(guid, base::FilePath(),
-                                driver_entry->bytes_downloaded);
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&ControllerImpl::SendOnDownloadSucceeded,
+                   weak_ptr_factory_.GetWeakPtr(), entry->client, guid,
+                   base::FilePath(), driver_entry->bytes_downloaded));
     TransitTo(entry, Entry::State::COMPLETE, model_.get());
   } else {
-    client->OnDownloadFailed(guid, FailureReasonFromCompletionType(type));
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&ControllerImpl::SendOnDownloadFailed,
+                              weak_ptr_factory_.GetWeakPtr(), entry->client,
+                              guid, FailureReasonFromCompletionType(type)));
     model_->Remove(guid);
   }
 
@@ -526,6 +672,9 @@
 }
 
 void ControllerImpl::ActivateMoreDownloads() {
+  if (initializing_internals_)
+    return;
+
   // TODO(xingliu): Check the configuration to throttle downloads.
   Entry* next = scheduler_->Next(
       model_->PeekEntries(), device_status_listener_->CurrentDeviceStatus());
@@ -540,4 +689,44 @@
   scheduler_->Reschedule(model_->PeekEntries());
 }
 
+void ControllerImpl::HandleExternalDownload(const std::string& guid,
+                                            bool active) {
+  if (active) {
+    externally_active_downloads_.insert(guid);
+  } else {
+    externally_active_downloads_.erase(guid);
+  }
+
+  UpdateDriverStates();
+}
+
+void ControllerImpl::SendOnDownloadUpdated(DownloadClient client_id,
+                                           const std::string& guid,
+                                           uint64_t bytes_downloaded) {
+  if (!model_->Get(guid))
+    return;
+
+  auto* client = clients_->GetClient(client_id);
+  DCHECK(client);
+  client->OnDownloadUpdated(guid, bytes_downloaded);
+}
+
+void ControllerImpl::SendOnDownloadSucceeded(DownloadClient client_id,
+                                             const std::string& guid,
+                                             const base::FilePath& path,
+                                             uint64_t size) {
+  auto* client = clients_->GetClient(client_id);
+  DCHECK(client);
+  client->OnDownloadSucceeded(guid, path, size);
+}
+
+void ControllerImpl::SendOnDownloadFailed(
+    DownloadClient client_id,
+    const std::string& guid,
+    download::Client::FailureReason reason) {
+  auto* client = clients_->GetClient(client_id);
+  DCHECK(client);
+  client->OnDownloadFailed(guid, reason);
+}
+
 }  // namespace download
diff --git a/components/download/internal/controller_impl.h b/components/download/internal/controller_impl.h
index 0d94b35..0c20f9d 100644
--- a/components/download/internal/controller_impl.h
+++ b/components/download/internal/controller_impl.h
@@ -7,8 +7,10 @@
 
 #include <map>
 #include <memory>
+#include <set>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "components/download/internal/controller.h"
 #include "components/download/internal/download_driver.h"
@@ -17,6 +19,7 @@
 #include "components/download/internal/scheduler/device_status_listener.h"
 #include "components/download/internal/startup_status.h"
 #include "components/download/internal/stats.h"
+#include "components/download/public/client.h"
 #include "components/download/public/download_params.h"
 #include "components/download/public/task_scheduler.h"
 
@@ -89,6 +92,10 @@
   // internal state initialization.
   void AttemptToFinalizeSetup();
 
+  // Checks for all the currently active driver downloads.  This lets us know
+  // which ones are active that we haven't tracked.
+  void PollActiveDriverDownloads();
+
   // Cancels and cleans upany requests that are no longer associated with a
   // Client in |clients_|.
   void CancelOrphanedRequests();
@@ -110,10 +117,6 @@
   // DownloadClient.
   void NotifyClientsOfStartup();
 
-  // Pulls the current state of requests from |model_| and |driver_| and sets
-  // the other internal states appropriately.
-  void PullCurrentRequestStatus();
-
   void HandleStartDownloadResponse(DownloadClient client,
                                    const std::string& guid,
                                    DownloadParams::StartResult result);
@@ -137,6 +140,21 @@
   // reached maximum.
   void ActivateMoreDownloads();
 
+  void HandleExternalDownload(const std::string& guid, bool active);
+
+  // Postable methods meant to just be pass throughs to Client APIs.  This is
+  // meant to help prevent reentrancy.
+  void SendOnDownloadUpdated(DownloadClient client_id,
+                             const std::string& guid,
+                             uint64_t bytes_downloaded);
+  void SendOnDownloadSucceeded(DownloadClient client_id,
+                               const std::string& guid,
+                               const base::FilePath& path,
+                               uint64_t size);
+  void SendOnDownloadFailed(DownloadClient client_id,
+                            const std::string& guid,
+                            download::Client::FailureReason reason);
+
   Configuration* config_;
 
   // Owned Dependencies.
@@ -148,10 +166,19 @@
   std::unique_ptr<TaskScheduler> task_scheduler_;
 
   // Internal state.
+  // Is set to true if this class is currently in the process of initializing
+  // it's internal state.  This will be false until |startup_status_| signals it
+  // is complete *and* all internal structures are set up.  This is to prevent
+  // outside signals from triggering state updates before we are ready.
+  bool initializing_internals_;
   StartupStatus startup_status_;
+  std::set<std::string> externally_active_downloads_;
   std::map<std::string, DownloadParams::StartCallback> start_callbacks_;
   std::map<DownloadTaskType, TaskFinishedCallback> task_finished_callbacks_;
 
+  // Only used to post tasks on the same thread.
+  base::WeakPtrFactory<ControllerImpl> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ControllerImpl);
 };
 
diff --git a/components/download/internal/controller_impl_unittest.cc b/components/download/internal/controller_impl_unittest.cc
index b88ceae..48a27877 100644
--- a/components/download/internal/controller_impl_unittest.cc
+++ b/components/download/internal/controller_impl_unittest.cc
@@ -33,6 +33,15 @@
 
 namespace {
 
+bool GuidInEntryList(const std::vector<Entry>& entries,
+                     const std::string& guid) {
+  for (const auto& entry : entries) {
+    if (entry.guid == guid)
+      return true;
+  }
+  return false;
+}
+
 DriverEntry BuildDriverEntry(const Entry& entry, DriverEntry::State state) {
   DriverEntry dentry;
   dentry.guid = entry.guid;
@@ -288,7 +297,7 @@
       .Times(1);
   controller_->StartDownload(params);
 
-  EXPECT_TRUE(store_->updated_entries().empty());
+  EXPECT_FALSE(GuidInEntryList(store_->updated_entries(), params.guid));
 
   task_runner_->RunUntilIdle();
 }
@@ -317,8 +326,6 @@
       .Times(1);
   controller_->StartDownload(params);
 
-  EXPECT_TRUE(store_->updated_entries().empty());
-
   task_runner_->RunUntilIdle();
 }
 
@@ -350,6 +357,8 @@
   controller_->StartDownload(params);
   store_->TriggerUpdate(true);
 
+  EXPECT_TRUE(GuidInEntryList(store_->updated_entries(), params.guid));
+
   task_runner_->RunUntilIdle();
 }
 
@@ -611,6 +620,8 @@
               OnDownloadUpdated(entry.guid, driver_entry.bytes_downloaded));
   driver_->NotifyDownloadUpdate(driver_entry);
   EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry.guid)->state);
+
+  task_runner_->RunUntilIdle();
 }
 
 TEST_F(DownloadServiceControllerImplTest, DownloadCompletionTest) {
@@ -664,4 +675,286 @@
   task_runner_->RunUntilIdle();
 }
 
+TEST_F(DownloadServiceControllerImplTest, StartupRecovery) {
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+
+  std::vector<Entry> entries;
+  std::vector<DriverEntry> driver_entries;
+  entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+  entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+  entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+
+  entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+  entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+  entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+
+  entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+  entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+  entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+
+  entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+  entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+  entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+
+  entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+  entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+  entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+  driver_entries.push_back(
+      BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+  entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+
+  // Set up the Controller.
+  device_status_listener_->SetDeviceStatus(
+      DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+  controller_->Initialize();
+  driver_->AddTestData(driver_entries);
+  driver_->MakeReady();
+  store_->AutomaticallyTriggerAllFutureCallbacks(true);
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+
+  // Allow the initialization routines and persistent layers to do their thing.
+  task_runner_->RunUntilIdle();
+
+  // Validate Model and DownloadDriver states.
+  // Note that we are accessing the Model instead of the Store here to make it
+  // easier to query the states.
+  // TODO(dtrainor): Check more of the DriverEntry state to validate that the
+  // entries are either paused or resumed accordingly.
+
+  // Entry::State::NEW.
+  EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[0].guid)->state);
+  EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[1].guid)->state);
+  EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[2].guid)->state);
+  EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[3].guid)->state);
+  EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[4].guid)->state);
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[0].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[1].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[2].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[3].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[4].guid));
+
+  // Entry::State::AVAILABLE.
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[5].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[6].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[7].guid)->state);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[8].guid)->state);
+  EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[9].guid)->state);
+  EXPECT_NE(base::nullopt, driver_->Find(entries[5].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[6].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[7].guid));
+  EXPECT_NE(base::nullopt, driver_->Find(entries[8].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[9].guid));
+
+  // Entry::State::ACTIVE.
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[10].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[11].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[12].guid)->state);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[13].guid)->state);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[14].guid)->state);
+  EXPECT_NE(base::nullopt, driver_->Find(entries[10].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[11].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[12].guid));
+  EXPECT_NE(base::nullopt, driver_->Find(entries[13].guid));
+  EXPECT_NE(base::nullopt, driver_->Find(entries[14].guid));
+
+  // Entry::State::PAUSED.
+  EXPECT_EQ(Entry::State::PAUSED, model_->Get(entries[15].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[16].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[17].guid)->state);
+  EXPECT_EQ(Entry::State::PAUSED, model_->Get(entries[18].guid)->state);
+  EXPECT_EQ(Entry::State::PAUSED, model_->Get(entries[19].guid)->state);
+  EXPECT_NE(base::nullopt, driver_->Find(entries[15].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[16].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[17].guid));
+  EXPECT_NE(base::nullopt, driver_->Find(entries[18].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[19].guid));
+
+  // prog, comp, canc, int, __
+  // Entry::State::COMPLETE.
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[20].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[21].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[22].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[23].guid)->state);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[24].guid)->state);
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[20].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[21].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[22].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[23].guid));
+  EXPECT_EQ(base::nullopt, driver_->Find(entries[24].guid));
+}
+
+TEST_F(DownloadServiceControllerImplTest, ExistingExternalDownload) {
+  Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE);
+  Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
+  Entry entry3 = test::BuildBasicEntry(Entry::State::ACTIVE);
+  entry3.scheduling_params.priority = SchedulingParams::Priority::UI;
+
+  // Simulate an existing download the service knows about and one it does not.
+  DriverEntry dentry1 =
+      BuildDriverEntry(entry2, DriverEntry::State::IN_PROGRESS);
+  DriverEntry dentry2;
+  dentry2.guid = base::GenerateGUID();
+  dentry2.state = DriverEntry::State::IN_PROGRESS;
+
+  std::vector<Entry> entries = {entry1, entry2, entry3};
+  std::vector<DriverEntry> dentries = {dentry1, dentry2};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+
+  // Set up the Controller.
+  device_status_listener_->SetDeviceStatus(
+      DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+  driver_->AddTestData(dentries);
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+  task_runner_->RunUntilIdle();
+
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry1.guid)->state);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry2.guid)->state);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry3.guid)->state);
+
+  EXPECT_FALSE(driver_->Find(entry1.guid).has_value());
+
+  EXPECT_TRUE(driver_->Find(entry2.guid).has_value());
+  EXPECT_TRUE(driver_->Find(entry2.guid).value().paused);
+
+  EXPECT_TRUE(driver_->Find(entry3.guid).has_value());
+  EXPECT_FALSE(driver_->Find(entry3.guid).value().paused);
+
+  // Simulate a successful external download.
+  driver_->NotifyDownloadSucceeded(dentry2, base::FilePath());
+
+  EXPECT_TRUE(driver_->Find(entry1.guid).has_value());
+  EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+  EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+  EXPECT_FALSE(driver_->Find(entry3.guid).value().paused);
+}
+
+TEST_F(DownloadServiceControllerImplTest, NewExternalDownload) {
+  Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE);
+  Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
+  entry2.scheduling_params.priority = SchedulingParams::Priority::UI;
+
+  DriverEntry dentry1 =
+      BuildDriverEntry(entry2, DriverEntry::State::IN_PROGRESS);
+
+  std::vector<Entry> entries = {entry1, entry2};
+  std::vector<DriverEntry> dentries = {dentry1};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+
+  // Set up the Controller.
+  device_status_listener_->SetDeviceStatus(
+      DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+  driver_->AddTestData(dentries);
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+  task_runner_->RunUntilIdle();
+
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry1.guid)->state);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry2.guid)->state);
+
+  EXPECT_TRUE(driver_->Find(entry1.guid).has_value());
+  EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+  EXPECT_TRUE(driver_->Find(entry2.guid).has_value());
+  EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+  DriverEntry dentry2;
+  dentry2.guid = base::GenerateGUID();
+  dentry2.state = DriverEntry::State::IN_PROGRESS;
+
+  // Simulate a newly created external download.
+  driver_->Start(RequestParams(), dentry2.guid, NO_TRAFFIC_ANNOTATION_YET);
+
+  EXPECT_TRUE(driver_->Find(entry1.guid).value().paused);
+  EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+  // Simulate a paused external download.
+  dentry2.paused = true;
+  driver_->NotifyDownloadUpdate(dentry2);
+
+  EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+  EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+  // Simulate a resumed external download.
+  dentry2.paused = false;
+  driver_->NotifyDownloadUpdate(dentry2);
+
+  EXPECT_TRUE(driver_->Find(entry1.guid).value().paused);
+  EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+  // Simulate a failed external download.
+  dentry2.state = DriverEntry::State::INTERRUPTED;
+  driver_->NotifyDownloadFailed(dentry2, 1);
+
+  EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+  EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+  // Rebuild the download so we can simulate more.
+  dentry2.state = DriverEntry::State::IN_PROGRESS;
+  driver_->Start(RequestParams(), dentry2.guid, NO_TRAFFIC_ANNOTATION_YET);
+
+  EXPECT_TRUE(driver_->Find(entry1.guid).value().paused);
+  EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+  // Simulate a successful external download.
+  dentry2.state = DriverEntry::State::COMPLETE;
+  driver_->NotifyDownloadSucceeded(dentry2, base::FilePath());
+
+  EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+  EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+}
+
 }  // namespace download
diff --git a/components/download/internal/download_driver.h b/components/download/internal/download_driver.h
index 149e4160..bf385696 100644
--- a/components/download/internal/download_driver.h
+++ b/components/download/internal/download_driver.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_DOWNLOAD_INTERNAL_DOWNLOAD_DRIVER_H_
 #define COMPONENTS_DOWNLOAD_INTERNAL_DOWNLOAD_DRIVER_H_
 
+#include <set>
 #include <string>
 
 #include "base/optional.h"
@@ -75,6 +76,10 @@
 
   // Finds a download record from low level download library.
   virtual base::Optional<DriverEntry> Find(const std::string& guid) = 0;
+
+  // Called to query the current set of active downloads.  This doesn't
+  // necessarily mean downloads started by the service.
+  virtual std::set<std::string> GetActiveDownloads() = 0;
 };
 
 }  // namespace download
diff --git a/components/download/internal/proto_conversions_unittest.cc b/components/download/internal/proto_conversions_unittest.cc
index 1f39dbb..3d1605d 100644
--- a/components/download/internal/proto_conversions_unittest.cc
+++ b/components/download/internal/proto_conversions_unittest.cc
@@ -34,8 +34,7 @@
 
 TEST_F(ProtoConversionsTest, DownloadClientConversion) {
   DownloadClient clients[] = {DownloadClient::INVALID, DownloadClient::TEST,
-                              DownloadClient::OFFLINE_PAGE_PREFETCH,
-                              DownloadClient::BOUNDARY};
+                              DownloadClient::TEST_2, DownloadClient::BOUNDARY};
   for (auto client : clients) {
     ASSERT_EQ(client, DownloadClientFromProto(DownloadClientToProto(client)));
   }
@@ -135,8 +134,8 @@
   std::vector<Entry> expected;
   expected.push_back(
       test::BuildEntry(DownloadClient::TEST, base::GenerateGUID()));
-  expected.push_back(test::BuildEntry(DownloadClient::OFFLINE_PAGE_PREFETCH,
-                                      base::GenerateGUID()));
+  expected.push_back(
+      test::BuildEntry(DownloadClient::TEST_2, base::GenerateGUID()));
   expected.push_back(test::BuildEntry(
       DownloadClient::TEST, base::GenerateGUID(), base::Time::Now(),
       SchedulingParams::NetworkRequirements::OPTIMISTIC,
diff --git a/components/download/internal/stats.cc b/components/download/internal/stats.cc
index b532f879..b581c4d4 100644
--- a/components/download/internal/stats.cc
+++ b/components/download/internal/stats.cc
@@ -38,5 +38,9 @@
   // TODO(xingliu): Log completion.
 }
 
+void LogRecoveryOperation(Entry::State to_state) {
+  // TODO(dtrainor): Log |to_state|.
+}
+
 }  // namespace stats
 }  // namespace download
diff --git a/components/download/internal/stats.h b/components/download/internal/stats.h
index 7a9cecb..9997592 100644
--- a/components/download/internal/stats.h
+++ b/components/download/internal/stats.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_DOWNLOAD_INTERNAL_STATS_H_
 
 #include "components/download/internal/controller.h"
+#include "components/download/internal/entry.h"
 #include "components/download/public/clients.h"
 #include "components/download/public/download_params.h"
 #include "components/download/public/download_task_types.h"
@@ -96,6 +97,10 @@
 // Logs download completion event.
 void LogDownloadCompletion(CompletionType type);
 
+// Logs recovery operations that happened when we had to move from one state
+// to another on startup.
+void LogRecoveryOperation(Entry::State to_state);
+
 }  // namespace stats
 }  // namespace download
 
diff --git a/components/download/internal/test/test_download_driver.cc b/components/download/internal/test/test_download_driver.cc
index c9f11a4..440ed974 100644
--- a/components/download/internal/test/test_download_driver.cc
+++ b/components/download/internal/test/test_download_driver.cc
@@ -104,5 +104,16 @@
   return it->second;
 }
 
+std::set<std::string> TestDownloadDriver::GetActiveDownloads() {
+  std::set<std::string> guids;
+
+  for (auto& entry : entries_) {
+    if (entry.second.state == DriverEntry::State::IN_PROGRESS)
+      guids.insert(entry.second.guid);
+  }
+
+  return guids;
+}
+
 }  // namespace test
 }  // namespace download
diff --git a/components/download/internal/test/test_download_driver.h b/components/download/internal/test/test_download_driver.h
index 05fd808..95d9b55 100644
--- a/components/download/internal/test/test_download_driver.h
+++ b/components/download/internal/test/test_download_driver.h
@@ -45,6 +45,7 @@
   void Pause(const std::string& guid) override;
   void Resume(const std::string& guid) override;
   base::Optional<DriverEntry> Find(const std::string& guid) override;
+  std::set<std::string> GetActiveDownloads() override;
 
  private:
   bool is_ready_;
diff --git a/components/download/internal/test/test_store.cc b/components/download/internal/test/test_store.cc
index c9e472b94..0b5648f 100644
--- a/components/download/internal/test/test_store.cc
+++ b/components/download/internal/test/test_store.cc
@@ -4,6 +4,7 @@
 
 #include "components/download/internal/test/test_store.h"
 
+#include "base/memory/ptr_util.h"
 #include "components/download/internal/entry.h"
 
 namespace download {
@@ -20,19 +21,32 @@
 void TestStore::Initialize(InitCallback callback) {
   init_called_ = true;
   init_callback_ = std::move(callback);
+
+  if (automatic_callback_response_.has_value())
+    TriggerInit(automatic_callback_response_.value(),
+                base::MakeUnique<std::vector<Entry>>());
 }
 
 void TestStore::Update(const Entry& entry, StoreCallback callback) {
   updated_entries_.push_back(entry);
   update_callback_ = std::move(callback);
+
+  if (automatic_callback_response_.has_value())
+    TriggerUpdate(automatic_callback_response_.value());
 }
 
 void TestStore::Remove(const std::string& guid, StoreCallback callback) {
   removed_entries_.push_back(guid);
   remove_callback_ = std::move(callback);
+
+  if (automatic_callback_response_.has_value())
+    TriggerRemove(automatic_callback_response_.value());
 }
 
-// Callback trigger methods.
+void TestStore::AutomaticallyTriggerAllFutureCallbacks(bool success) {
+  automatic_callback_response_ = success;
+}
+
 void TestStore::TriggerInit(bool success,
                             std::unique_ptr<std::vector<Entry>> entries) {
   ready_ = success;
diff --git a/components/download/internal/test/test_store.h b/components/download/internal/test/test_store.h
index 2cb5f57..d3b667e 100644
--- a/components/download/internal/test/test_store.h
+++ b/components/download/internal/test/test_store.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "components/download/internal/store.h"
 
 namespace download {
@@ -30,6 +31,7 @@
   void Remove(const std::string& guid, StoreCallback callback) override;
 
   // Callback trigger methods.
+  void AutomaticallyTriggerAllFutureCallbacks(bool success);
   void TriggerInit(bool success, std::unique_ptr<std::vector<Entry>> entries);
   void TriggerUpdate(bool success);
   void TriggerRemove(bool success);
@@ -51,6 +53,7 @@
   std::vector<Entry> updated_entries_;
   std::vector<std::string> removed_entries_;
 
+  base::Optional<bool> automatic_callback_response_;
   InitCallback init_callback_;
   StoreCallback update_callback_;
   StoreCallback remove_callback_;
diff --git a/components/handoff/handoff_manager.mm b/components/handoff/handoff_manager.mm
index cdcb8f1..420eca0 100644
--- a/components/handoff/handoff_manager.mm
+++ b/components/handoff/handoff_manager.mm
@@ -96,10 +96,11 @@
   [self.userActivity invalidate];
 
   Class aClass = NSClassFromString(@"NSUserActivity");
-  NSUserActivity* userActivity = [[aClass performSelector:@selector(alloc)]
-      performSelector:@selector(initWithActivityType:)
-           withObject:handoff::kChromeHandoffActivityType];
-  self.userActivity = base::scoped_nsobject<NSUserActivity>(userActivity);
+  base::scoped_nsobject<NSUserActivity> userActivity(
+      [[aClass performSelector:@selector(alloc)]
+          performSelector:@selector(initWithActivityType:)
+               withObject:handoff::kChromeHandoffActivityType]);
+  self.userActivity = userActivity;
   self.userActivity.webpageURL = net::NSURLWithGURL(_activeURL);
   NSString* origin = handoff::StringFromOrigin(_origin);
   DCHECK(origin);
diff --git a/components/navigation_interception/intercept_navigation_delegate.cc b/components/navigation_interception/intercept_navigation_delegate.cc
index b8cd316..b6a3b11 100644
--- a/components/navigation_interception/intercept_navigation_delegate.cc
+++ b/components/navigation_interception/intercept_navigation_delegate.cc
@@ -89,7 +89,7 @@
 InterceptNavigationDelegate::CreateThrottleFor(
     content::NavigationHandle* handle) {
   return base::MakeUnique<InterceptNavigationThrottle>(
-      handle, base::Bind(&CheckIfShouldIgnoreNavigationOnUIThread), false);
+      handle, base::Bind(&CheckIfShouldIgnoreNavigationOnUIThread));
 }
 
 // static
diff --git a/components/navigation_interception/intercept_navigation_throttle.cc b/components/navigation_interception/intercept_navigation_throttle.cc
index a6bbcd0..db7a1dd1 100644
--- a/components/navigation_interception/intercept_navigation_throttle.cc
+++ b/components/navigation_interception/intercept_navigation_throttle.cc
@@ -12,38 +12,11 @@
 
 namespace navigation_interception {
 
-namespace {
-
-using ChecksPerformedCallback = base::Callback<void(bool)>;
-
-// This is used to run |should_ignore_callback| if it can destroy the
-// WebContents (and the InterceptNavigationThrottle along). In that case,
-// |on_checks_performed_callback| will be a no-op.
-void RunCallback(
-    content::WebContents* web_contents,
-    const NavigationParams& navigation_params,
-    InterceptNavigationThrottle::CheckCallback should_ignore_callback,
-    ChecksPerformedCallback on_checks_performed_callback,
-    base::WeakPtr<InterceptNavigationThrottle> throttle) {
-  bool should_ignore_navigation =
-      should_ignore_callback.Run(web_contents, navigation_params);
-
-  // If the InterceptNavigationThrottle that called RunCallback is still alive
-  // after |should_ignore_callback| has run, this will run
-  // InterceptNavigationThrottle::OnAsynchronousChecksPerformed.
-  on_checks_performed_callback.Run(should_ignore_navigation);
-}
-
-}  // namespace
-
 InterceptNavigationThrottle::InterceptNavigationThrottle(
     content::NavigationHandle* navigation_handle,
-    CheckCallback should_ignore_callback,
-    bool run_callback_synchronously)
+    CheckCallback should_ignore_callback)
     : content::NavigationThrottle(navigation_handle),
-      should_ignore_callback_(should_ignore_callback),
-      run_callback_synchronously_(run_callback_synchronously),
-      weak_factory_(this) {}
+      should_ignore_callback_(should_ignore_callback) {}
 
 InterceptNavigationThrottle::~InterceptNavigationThrottle() {}
 
@@ -72,58 +45,11 @@
       navigation_handle()->GetPageTransition(), is_redirect,
       navigation_handle()->IsExternalProtocol(), true,
       navigation_handle()->GetBaseURLForDataURL());
-
-  if (run_callback_synchronously_) {
-    bool should_ignore_navigation = should_ignore_callback_.Run(
-        navigation_handle()->GetWebContents(), navigation_params);
-    return should_ignore_navigation
-               ? content::NavigationThrottle::CANCEL_AND_IGNORE
-               : content::NavigationThrottle::PROCEED;
-  }
-
-  // When the callback can potentially destroy the WebContents, along with the
-  // NavigationHandle and this InterceptNavigationThrottle, it should be run
-  // asynchronously. This will ensure that no objects on the stack can be
-  // deleted, and that the stack does not unwind through them in a deleted
-  // state.
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&InterceptNavigationThrottle::RunCallbackAsynchronously,
-                 weak_factory_.GetWeakPtr(), navigation_params));
-  return DEFER;
-}
-
-void InterceptNavigationThrottle::RunCallbackAsynchronously(
-    const NavigationParams& navigation_params) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // Run the callback in a helper function as it may lead ot the destruction of
-  // this InterceptNavigationThrottle.
-  RunCallback(
-      navigation_handle()->GetWebContents(), navigation_params,
-      should_ignore_callback_,
-      base::Bind(&InterceptNavigationThrottle::OnAsynchronousChecksPerformed,
-                 weak_factory_.GetWeakPtr()),
-      weak_factory_.GetWeakPtr());
-
-  // DO NOT ADD CODE AFTER HERE: at this point the InterceptNavigationThrottle
-  // may have been destroyed by the |should_ignore_callback_|. Adding code here
-  // will cause use-after-free bugs.
-  //
-  // Code that needs to act on the result of the |should_ignore_callback_|
-  // should be put inside OnAsynchronousChecksPerformed. This function will be
-  // called after |should_ignore_callback_| has run, if this
-  // InterceptNavigationThrottle is still alive.
-}
-
-void InterceptNavigationThrottle::OnAsynchronousChecksPerformed(
-    bool should_ignore_navigation) {
-  if (should_ignore_navigation) {
-    navigation_handle()->CancelDeferredNavigation(
-        content::NavigationThrottle::CANCEL_AND_IGNORE);
-  } else {
-    navigation_handle()->Resume();
-  }
+  bool should_ignore_navigation = should_ignore_callback_.Run(
+      navigation_handle()->GetWebContents(), navigation_params);
+  return should_ignore_navigation
+             ? content::NavigationThrottle::CANCEL_AND_IGNORE
+             : content::NavigationThrottle::PROCEED;
 }
 
 }  // namespace navigation_interception
diff --git a/components/navigation_interception/intercept_navigation_throttle.h b/components/navigation_interception/intercept_navigation_throttle.h
index 09158b8..7607fd5f 100644
--- a/components/navigation_interception/intercept_navigation_throttle.h
+++ b/components/navigation_interception/intercept_navigation_throttle.h
@@ -30,8 +30,7 @@
       CheckCallback;
 
   InterceptNavigationThrottle(content::NavigationHandle* navigation_handle,
-                              CheckCallback should_ignore_callback,
-                              bool run_callback_synchronously);
+                              CheckCallback should_ignore_callback);
   ~InterceptNavigationThrottle() override;
 
   // content::NavigationThrottle implementation:
@@ -42,19 +41,8 @@
  private:
   ThrottleCheckResult CheckIfShouldIgnoreNavigation(bool is_redirect);
 
-  // Called to perform the checks asynchronously
-  void RunCallbackAsynchronously(const NavigationParams& navigation_params);
-  void OnAsynchronousChecksPerformed(bool should_ignore_navigation);
-
   CheckCallback should_ignore_callback_;
 
-  // Whether the callback will be run synchronously or not. If the callback can
-  // lead to the destruction of the WebContents, this should be false.
-  // Otherwise this should be true.
-  const bool run_callback_synchronously_;
-
-  base::WeakPtrFactory<InterceptNavigationThrottle> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(InterceptNavigationThrottle);
 };
 
diff --git a/components/navigation_interception/intercept_navigation_throttle_unittest.cc b/components/navigation_interception/intercept_navigation_throttle_unittest.cc
index a6d27c49..0b3ed14 100644
--- a/components/navigation_interception/intercept_navigation_throttle_unittest.cc
+++ b/components/navigation_interception/intercept_navigation_throttle_unittest.cc
@@ -68,8 +68,7 @@
         base::MakeUnique<InterceptNavigationThrottle>(
             test_handle.get(),
             base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
-                       base::Unretained(mock_callback_receiver_.get())),
-            true));
+                       base::Unretained(mock_callback_receiver_.get()))));
     return test_handle->CallWillStartRequestForTesting(
         is_post, content::Referrer(), false, ui::PAGE_TRANSITION_LINK, false);
   }
@@ -82,8 +81,7 @@
         base::MakeUnique<InterceptNavigationThrottle>(
             test_handle.get(),
             base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
-                       base::Unretained(mock_callback_receiver_.get())),
-            true));
+                       base::Unretained(mock_callback_receiver_.get()))));
     test_handle->CallWillStartRequestForTesting(
         true, content::Referrer(), false, ui::PAGE_TRANSITION_LINK, false);
     return test_handle->CallWillRedirectRequestForTesting(GURL(kTestUrl), false,
diff --git a/components/offline_pages/core/prefetch/BUILD.gn b/components/offline_pages/core/prefetch/BUILD.gn
index 68e0e51..396615e 100644
--- a/components/offline_pages/core/prefetch/BUILD.gn
+++ b/components/offline_pages/core/prefetch/BUILD.gn
@@ -22,8 +22,6 @@
     "prefetch_gcm_app_handler.cc",
     "prefetch_gcm_app_handler.h",
     "prefetch_gcm_handler.h",
-    "prefetch_in_memory_store.cc",
-    "prefetch_in_memory_store.h",
     "prefetch_item.cc",
     "prefetch_item.h",
     "prefetch_proto_utils.cc",
@@ -33,7 +31,6 @@
     "prefetch_service.h",
     "prefetch_service_impl.cc",
     "prefetch_service_impl.h",
-    "prefetch_store.h",
     "prefetch_types.cc",
     "prefetch_types.h",
     "suggested_articles_observer.cc",
@@ -85,7 +82,6 @@
     "proto/offline_pages.proto",
     "proto/operation.proto",
     "proto/status.proto",
-    "proto/timestamp.proto",
   ]
 }
 
diff --git a/components/offline_pages/core/prefetch/README.md b/components/offline_pages/core/prefetch/README.md
index 46e5a0e..bd1baf0 100644
--- a/components/offline_pages/core/prefetch/README.md
+++ b/components/offline_pages/core/prefetch/README.md
@@ -1,6 +1,28 @@
-# Prefetching Offline Pages: development guidelines
+# Prefetching Offline Pages
 
-* Implementations that are injected dependencies should always provide
-  lightweight construction and postpone heavier initialization (i.e. DB
+## Architecture overview
+
+### PrefetchService
+
+Is the ownership holder for the main components of the prefetching system and
+controls their lifetime.
+
+### PrefetchDispatcher
+
+Manages the prefetching pipeline tasks. It receives signals from external
+clients and creates the appropriate tasks to execute them. It _might_ at some
+point execute advanced task management operations like canceling queued tasks or
+changing their order of execution.
+
+### \*Task(s) (i.e. AddUniqueUrlsTask)
+
+They are the main wrapper of pipeline steps and interact with different 
+abstracted components (Downloads, persistent store, GCM, etc) to execute them.
+They implement TaskQueue's Task API so that they can be exclusively executed.
+
+## Development guidelines
+
+* Implementations that are injected dependencies during service creation should
+  have lightweight construction and postpone heavier initialization (i.e. DB
   connection) to a later moment. Lazy initialization upon first actual usage is
   recommended.
diff --git a/components/offline_pages/core/prefetch/add_unique_urls_task.cc b/components/offline_pages/core/prefetch/add_unique_urls_task.cc
index 58f46a0..e25cc54 100644
--- a/components/offline_pages/core/prefetch/add_unique_urls_task.cc
+++ b/components/offline_pages/core/prefetch/add_unique_urls_task.cc
@@ -4,21 +4,55 @@
 
 #include "components/offline_pages/core/prefetch/add_unique_urls_task.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/bind.h"
+#include "base/callback.h"
+#include "url/gurl.h"
 
 namespace offline_pages {
 
+namespace {
+
+// Adds new prefetch item entries to the store using the URLs and client IDs
+// from |prefetch_urls| and the client's |name_space|. Also cleans up entries in
+// the Zombie state from the client's |name_space| except for the ones
+// whose URL is contained in |prefetch_urls|.
+// Returns the number of added prefecth items.
+static int AddUrlsAndCleanupZombies(
+    const std::string& name_space,
+    const std::vector<PrefetchURL>& prefetch_urls) {
+  NOTIMPLEMENTED();
+  return 1;
+}
+
+// TODO(fgorski): replace this with the SQL executor.
+static void Execute(base::RepeatingCallback<int()> command_callback,
+                    base::OnceCallback<void(int)> result_callback) {
+  std::move(result_callback).Run(command_callback.Run());
+}
+}
+
 AddUniqueUrlsTask::AddUniqueUrlsTask(
-    PrefetchStore* store,
+    const std::string& name_space,
     const std::vector<PrefetchURL>& prefetch_urls)
-    : prefetch_store_(store),
+    : name_space_(name_space),
       prefetch_urls_(prefetch_urls),
       weak_ptr_factory_(this) {}
 
 AddUniqueUrlsTask::~AddUniqueUrlsTask() {}
 
 void AddUniqueUrlsTask::Run() {
-  CHECK(prefetch_store_);
+  Execute(base::BindRepeating(&AddUrlsAndCleanupZombies, name_space_,
+                              prefetch_urls_),
+          base::BindOnce(&AddUniqueUrlsTask::OnUrlsAdded,
+                         weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AddUniqueUrlsTask::OnUrlsAdded(int added_entry_count) {
+  // TODO(carlosk): schedule NWake here if at least one new entry was added to
+  // the store.
   TaskComplete();
 }
 
diff --git a/components/offline_pages/core/prefetch/add_unique_urls_task.h b/components/offline_pages/core/prefetch/add_unique_urls_task.h
index c7a1161..c265cd3b 100644
--- a/components/offline_pages/core/prefetch/add_unique_urls_task.h
+++ b/components/offline_pages/core/prefetch/add_unique_urls_task.h
@@ -5,13 +5,12 @@
 #ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_ADD_UNIQUE_URLS_TASK_H_
 #define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_ADD_UNIQUE_URLS_TASK_H_
 
+#include <string>
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
-#include "components/offline_pages/core/prefetch/prefetch_store.h"
 #include "components/offline_pages/core/prefetch/prefetch_types.h"
 #include "components/offline_pages/core/task.h"
-#include "url/gurl.h"
 
 namespace offline_pages {
 
@@ -26,14 +25,16 @@
 // from the store.
 class AddUniqueUrlsTask : public Task {
  public:
-  AddUniqueUrlsTask(PrefetchStore* store,
+  AddUniqueUrlsTask(const std::string& name_space,
                     const std::vector<PrefetchURL>& prefetch_urls);
   ~AddUniqueUrlsTask() override;
 
   void Run() override;
 
  private:
-  PrefetchStore* prefetch_store_;
+  void OnUrlsAdded(int added_entry_count);
+
+  const std::string& name_space_;
   std::vector<PrefetchURL> prefetch_urls_;
 
   base::WeakPtrFactory<AddUniqueUrlsTask> weak_ptr_factory_;
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher.h b/components/offline_pages/core/prefetch/prefetch_dispatcher.h
index f5e0d63..033cd545 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher.h
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher.h
@@ -45,6 +45,7 @@
   // with the client's unique namespace. URLs that are currently in the system
   // for this client are acceptable but ignored.
   virtual void AddCandidatePrefetchURLs(
+      const std::string& name_space,
       const std::vector<PrefetchURL>& prefetch_urls) = 0;
 
   // Called when all existing suggestions are no longer considered valid for a
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
index ae20c62..55b95a1 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
@@ -34,12 +34,13 @@
 }
 
 void PrefetchDispatcherImpl::AddCandidatePrefetchURLs(
+    const std::string& name_space,
     const std::vector<PrefetchURL>& prefetch_urls) {
   if (!IsPrefetchingOfflinePagesEnabled())
     return;
 
-  std::unique_ptr<Task> add_task = base::MakeUnique<AddUniqueUrlsTask>(
-      service_->GetPrefetchStore(), prefetch_urls);
+  std::unique_ptr<Task> add_task =
+      base::MakeUnique<AddUniqueUrlsTask>(name_space, prefetch_urls);
   task_queue_.AddTask(std::move(add_task));
 }
 
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
index 4f2b5af..c3c5236 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
@@ -24,6 +24,7 @@
   // PrefetchDispatcher implementation:
   void SetService(PrefetchService* service) override;
   void AddCandidatePrefetchURLs(
+      const std::string& name_space,
       const std::vector<PrefetchURL>& prefetch_urls) override;
   void RemoveAllUnprocessedPrefetchURLs(const std::string& name_space) override;
   void RemovePrefetchURLsByClientId(const ClientId& client_id) override;
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
index 846f697..0572782 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
@@ -12,12 +12,14 @@
 #include "components/offline_pages/core/client_namespace_constants.h"
 #include "components/offline_pages/core/offline_event_logger.h"
 #include "components/offline_pages/core/offline_page_feature.h"
-#include "components/offline_pages/core/prefetch/prefetch_in_memory_store.h"
 #include "components/offline_pages/core/prefetch/prefetch_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 namespace offline_pages {
 
+namespace {
+
 class TestScopedBackgroundTask
     : public PrefetchDispatcher::ScopedBackgroundTask {
  public:
@@ -31,8 +33,12 @@
   bool needs_reschedule_called = false;
 };
 
+}  // namespace
+
 class PrefetchDispatcherTest : public testing::Test, public PrefetchService {
  public:
+  const std::string TEST_NAMESPACE = "TestPrefetchClientNamespace";
+
   PrefetchDispatcherTest();
 
   // Test implementation.
@@ -44,7 +50,6 @@
   OfflineMetricsCollector* GetOfflineMetricsCollector() override;
   PrefetchDispatcher* GetPrefetchDispatcher() override;
   PrefetchGCMHandler* GetPrefetchGCMHandler() override;
-  PrefetchStore* GetPrefetchStore() override;
   SuggestedArticlesObserver* GetSuggestedArticlesObserver() override;
 
   // KeyedService implementation.
@@ -57,13 +62,13 @@
 
   TaskQueue* dispatcher_task_queue() { return &dispatcher_impl_->task_queue_; }
 
+ protected:
+  std::vector<PrefetchURL> test_urls_;
+
  private:
+  std::unique_ptr<PrefetchDispatcherImpl> dispatcher_impl_;
   OfflineEventLogger logger_;
   base::test::ScopedFeatureList feature_list_;
-
-  std::unique_ptr<PrefetchInMemoryStore> in_memory_store_;
-  std::unique_ptr<PrefetchDispatcherImpl> dispatcher_impl_;
-
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
 };
@@ -77,9 +82,12 @@
 void PrefetchDispatcherTest::SetUp() {
   ASSERT_EQ(base::ThreadTaskRunnerHandle::Get(), task_runner_);
   ASSERT_FALSE(task_runner_->HasPendingTask());
-  in_memory_store_ = base::MakeUnique<PrefetchInMemoryStore>();
   dispatcher_impl_ = base::MakeUnique<PrefetchDispatcherImpl>();
   dispatcher_impl_->SetService(this);
+
+  ASSERT_TRUE(test_urls_.empty());
+  test_urls_.push_back({"1", GURL("http://testurl.com/foo")});
+  test_urls_.push_back({"2", GURL("https://testurl.com/bar")});
 }
 
 void PrefetchDispatcherTest::TearDown() {
@@ -104,10 +112,6 @@
   return nullptr;
 }
 
-PrefetchStore* PrefetchDispatcherTest::GetPrefetchStore() {
-  return in_memory_store_.get();
-}
-
 SuggestedArticlesObserver*
 PrefetchDispatcherTest::GetSuggestedArticlesObserver() {
   NOTREACHED();
@@ -119,7 +123,7 @@
 }
 
 TEST_F(PrefetchDispatcherTest, DispatcherDoesNotCrash) {
-  GetPrefetchDispatcher()->AddCandidatePrefetchURLs(std::vector<PrefetchURL>());
+  GetPrefetchDispatcher()->AddCandidatePrefetchURLs(TEST_NAMESPACE, test_urls_);
   GetPrefetchDispatcher()->RemoveAllUnprocessedPrefetchURLs(
       kSuggestedArticlesNamespace);
   GetPrefetchDispatcher()->RemovePrefetchURLsByClientId(
@@ -127,7 +131,7 @@
 }
 
 TEST_F(PrefetchDispatcherTest, AddCandidatePrefetchURLsTask) {
-  GetPrefetchDispatcher()->AddCandidatePrefetchURLs(std::vector<PrefetchURL>());
+  GetPrefetchDispatcher()->AddCandidatePrefetchURLs(TEST_NAMESPACE, test_urls_);
   EXPECT_TRUE(dispatcher_task_queue()->HasPendingTasks());
   EXPECT_TRUE(dispatcher_task_queue()->HasRunningTask());
   PumpLoop();
@@ -140,10 +144,9 @@
   disabled_feature_list.InitAndDisableFeature(kPrefetchingOfflinePagesFeature);
 
   // Don't add a task for new prefetch URLs.
-  ClientId client_id("namespace", "id");
-  PrefetchURL prefetch_url(client_id, GURL("https://www.chromium.org"));
+  PrefetchURL prefetch_url("id", GURL("https://www.chromium.org"));
   GetPrefetchDispatcher()->AddCandidatePrefetchURLs(
-      std::vector<PrefetchURL>(1, prefetch_url));
+      TEST_NAMESPACE, std::vector<PrefetchURL>(1, prefetch_url));
   EXPECT_FALSE(dispatcher_task_queue()->HasRunningTask());
 
   // Do nothing with a new background task.
diff --git a/components/offline_pages/core/prefetch/prefetch_in_memory_store.cc b/components/offline_pages/core/prefetch/prefetch_in_memory_store.cc
deleted file mode 100644
index efab3dba..0000000
--- a/components/offline_pages/core/prefetch/prefetch_in_memory_store.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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/offline_pages/core/prefetch/prefetch_in_memory_store.h"
-
-namespace offline_pages {
-
-PrefetchInMemoryStore::PrefetchInMemoryStore() = default;
-
-PrefetchInMemoryStore::~PrefetchInMemoryStore() = default;
-
-}  // namespace offline_pages
\ No newline at end of file
diff --git a/components/offline_pages/core/prefetch/prefetch_in_memory_store.h b/components/offline_pages/core/prefetch/prefetch_in_memory_store.h
deleted file mode 100644
index 700b3a8..0000000
--- a/components/offline_pages/core/prefetch/prefetch_in_memory_store.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_IN_MEMORY_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_IN_MEMORY_STORE_H_
-
-#include "components/offline_pages/core/prefetch/prefetch_store.h"
-
-namespace offline_pages {
-
-// A PrefetchStore implementation that keeps all persisted data in memory.
-class PrefetchInMemoryStore : public PrefetchStore {
- public:
-  PrefetchInMemoryStore();
-  ~PrefetchInMemoryStore() override;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_IN_MEMORY_STORE_H_
diff --git a/components/offline_pages/core/prefetch/prefetch_service.h b/components/offline_pages/core/prefetch/prefetch_service.h
index 3d00d98..d797fa7 100644
--- a/components/offline_pages/core/prefetch/prefetch_service.h
+++ b/components/offline_pages/core/prefetch/prefetch_service.h
@@ -12,7 +12,6 @@
 class OfflineMetricsCollector;
 class PrefetchDispatcher;
 class PrefetchGCMHandler;
-class PrefetchStore;
 class SuggestedArticlesObserver;
 
 // Main class and entry point for the Offline Pages Prefetching feature, that
@@ -26,11 +25,10 @@
   // The service manages lifetime, hookup and initialization of Prefetch
   // system that consists of multiple specialized objects, all vended by this
   // service.
+  virtual OfflineEventLogger* GetLogger() = 0;
   virtual OfflineMetricsCollector* GetOfflineMetricsCollector() = 0;
   virtual PrefetchDispatcher* GetPrefetchDispatcher() = 0;
   virtual PrefetchGCMHandler* GetPrefetchGCMHandler() = 0;
-  virtual PrefetchStore* GetPrefetchStore() = 0;
-  virtual OfflineEventLogger* GetLogger() = 0;
 
   // May be |nullptr| in tests.  The PrefetchService does not depend on the
   // SuggestedArticlesObserver, it merely owns it for lifetime purposes.
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.cc b/components/offline_pages/core/prefetch/prefetch_service_impl.cc
index 11d1d6f..e84b846 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_service_impl.cc
@@ -11,7 +11,6 @@
 #include "components/offline_pages/core/prefetch/offline_metrics_collector.h"
 #include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
-#include "components/offline_pages/core/prefetch/prefetch_store.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
 
 namespace offline_pages {
@@ -20,12 +19,10 @@
     std::unique_ptr<OfflineMetricsCollector> offline_metrics_collector,
     std::unique_ptr<PrefetchDispatcher> dispatcher,
     std::unique_ptr<PrefetchGCMHandler> gcm_handler,
-    std::unique_ptr<PrefetchStore> store,
     std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer)
     : offline_metrics_collector_(std::move(offline_metrics_collector)),
       prefetch_dispatcher_(std::move(dispatcher)),
       prefetch_gcm_handler_(std::move(gcm_handler)),
-      prefetch_store_(std::move(store)),
       suggested_articles_observer_(std::move(suggested_articles_observer)) {
   prefetch_dispatcher_->SetService(this);
   prefetch_gcm_handler_->SetService(this);
@@ -45,10 +42,6 @@
   return prefetch_gcm_handler_.get();
 }
 
-PrefetchStore* PrefetchServiceImpl::GetPrefetchStore() {
-  return prefetch_store_.get();
-}
-
 SuggestedArticlesObserver* PrefetchServiceImpl::GetSuggestedArticlesObserver() {
   return suggested_articles_observer_.get();
 }
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.h b/components/offline_pages/core/prefetch/prefetch_service_impl.h
index eb70d04..800ac4ed 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_impl.h
+++ b/components/offline_pages/core/prefetch/prefetch_service_impl.h
@@ -23,7 +23,6 @@
       std::unique_ptr<OfflineMetricsCollector> offline_metrics_collector,
       std::unique_ptr<PrefetchDispatcher> dispatcher,
       std::unique_ptr<PrefetchGCMHandler> gcm_handler,
-      std::unique_ptr<PrefetchStore> store,
       std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer);
   ~PrefetchServiceImpl() override;
 
@@ -31,7 +30,6 @@
   OfflineMetricsCollector* GetOfflineMetricsCollector() override;
   PrefetchDispatcher* GetPrefetchDispatcher() override;
   PrefetchGCMHandler* GetPrefetchGCMHandler() override;
-  PrefetchStore* GetPrefetchStore() override;
   SuggestedArticlesObserver* GetSuggestedArticlesObserver() override;
   OfflineEventLogger* GetLogger() override;
 
@@ -44,7 +42,6 @@
   std::unique_ptr<OfflineMetricsCollector> offline_metrics_collector_;
   std::unique_ptr<PrefetchDispatcher> prefetch_dispatcher_;
   std::unique_ptr<PrefetchGCMHandler> prefetch_gcm_handler_;
-  std::unique_ptr<PrefetchStore> prefetch_store_;
   std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(PrefetchServiceImpl);
diff --git a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
index 4161a07..96c6783 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
+++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
@@ -10,7 +10,6 @@
 #include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
 #include "components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
-#include "components/offline_pages/core/prefetch/prefetch_in_memory_store.h"
 #include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
 #include "components/offline_pages/core/prefetch/test_offline_metrics_collector.h"
@@ -22,7 +21,6 @@
   metrics_collector_ = base::MakeUnique<TestOfflineMetricsCollector>();
   dispatcher_ = base::MakeUnique<PrefetchDispatcherImpl>();
   gcm_handler_ = base::MakeUnique<TestPrefetchGCMHandler>();
-  store_ = base::MakeUnique<PrefetchInMemoryStore>();
 }
 
 PrefetchServiceTestTaco::~PrefetchServiceTestTaco() = default;
@@ -45,12 +43,6 @@
   gcm_handler_ = std::move(gcm_handler);
 }
 
-void PrefetchServiceTestTaco::SetPrefetchStore(
-    std::unique_ptr<PrefetchStore> store) {
-  CHECK(!prefetch_service_);
-  store_ = std::move(store);
-}
-
 void PrefetchServiceTestTaco::SetSuggestedArticlesObserver(
     std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer) {
   CHECK(!prefetch_service_);
@@ -60,8 +52,7 @@
 void PrefetchServiceTestTaco::CreatePrefetchService() {
   prefetch_service_ = base::MakeUnique<PrefetchServiceImpl>(
       std::move(metrics_collector_), std::move(dispatcher_),
-      std::move(gcm_handler_), std::move(store_),
-      std::move(suggested_articles_observer_));
+      std::move(gcm_handler_), std::move(suggested_articles_observer_));
 }
 
 }  // namespace offline_page
diff --git a/components/offline_pages/core/prefetch/prefetch_service_test_taco.h b/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
index 08ccfd6..69708f7 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
+++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
@@ -14,7 +14,6 @@
 class PrefetchDispatcher;
 class PrefetchGCMHandler;
 class PrefetchService;
-class PrefetchStore;
 class SuggestedArticlesObserver;
 
 // The taco class acts as a wrapper around the prefetch service making
@@ -27,7 +26,6 @@
 // * TestOfflineMetricsCollector
 // * PrefetchDispatcherImpl
 // * TestPrefetchGCMHandler
-// * PrefetchInMemoryStore
 // * |nullptr| SuggestedArticlesObserver, since this is default just a lifetime
 //   arrangement.
 class PrefetchServiceTestTaco {
@@ -41,7 +39,6 @@
       std::unique_ptr<OfflineMetricsCollector> metrics_collector);
   void SetPrefetchDispatcher(std::unique_ptr<PrefetchDispatcher> dispatcher);
   void SetPrefetchGCMHandler(std::unique_ptr<PrefetchGCMHandler> gcm_handler);
-  void SetPrefetchStore(std::unique_ptr<PrefetchStore> prefetch_store);
   void SetSuggestedArticlesObserver(
       std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer);
 
@@ -61,7 +58,6 @@
   std::unique_ptr<OfflineMetricsCollector> metrics_collector_;
   std::unique_ptr<PrefetchDispatcher> dispatcher_;
   std::unique_ptr<PrefetchGCMHandler> gcm_handler_;
-  std::unique_ptr<PrefetchStore> store_;
   std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_;
 
   std::unique_ptr<PrefetchService> prefetch_service_;
diff --git a/components/offline_pages/core/prefetch/prefetch_store.h b/components/offline_pages/core/prefetch/prefetch_store.h
deleted file mode 100644
index 4f084d5e..0000000
--- a/components/offline_pages/core/prefetch/prefetch_store.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_STORE_H_
-
-namespace offline_pages {
-
-// Persistent storage access class for prefetching offline pages data.
-class PrefetchStore {
- public:
-  virtual ~PrefetchStore() = default;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_STORE_H_
diff --git a/components/offline_pages/core/prefetch/prefetch_types.h b/components/offline_pages/core/prefetch/prefetch_types.h
index 8fb4bbe4..20d9abea 100644
--- a/components/offline_pages/core/prefetch/prefetch_types.h
+++ b/components/offline_pages/core/prefetch/prefetch_types.h
@@ -109,15 +109,15 @@
                         const std::string& operation_name,
                         const std::vector<RenderPageInfo>& pages)>;
 
-// Holds information about a new URL to be prefetched.
+// Holds information about a suggested URL to be prefetched.
 struct PrefetchURL {
-  PrefetchURL(const ClientId& client_id, const GURL& url)
-      : client_id(client_id), url(url) {}
+  PrefetchURL(const std::string& id, const GURL& url) : id(id), url(url) {}
 
-  // Client provided ID to allow the matching of URLs to the respective work
-  // item in the prefetching system. It can be anything useful to identify the
-  // page . It will not be used internally for de-duplication.
-  ClientId client_id;
+  // Client provided ID to allow the matching of provided URLs to the respective
+  // work item in the prefetching system within that client's assigned
+  // namespace. It can be any string value and it will not be used internally
+  // for de-duplication.
+  std::string id;
 
   // This URL will be prefetched by the service.
   GURL url;
diff --git a/components/offline_pages/core/prefetch/proto/any.proto b/components/offline_pages/core/prefetch/proto/any.proto
index bc71002..c575c70 100644
--- a/components/offline_pages/core/prefetch/proto/any.proto
+++ b/components/offline_pages/core/prefetch/proto/any.proto
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-syntax = "proto3";
+syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
 package offline_pages.proto;
 
 message Any {
-  string type_url = 1;
-  bytes value = 2;
+  optional string type_url = 1;
+  optional bytes value = 2;
 }
diff --git a/components/offline_pages/core/prefetch/proto/offline_pages.proto b/components/offline_pages/core/prefetch/proto/offline_pages.proto
index bffc766..37580909 100644
--- a/components/offline_pages/core/prefetch/proto/offline_pages.proto
+++ b/components/offline_pages/core/prefetch/proto/offline_pages.proto
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-syntax = "proto3";
+syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
 package offline_pages.proto;
 
 import "status.proto";
-import "timestamp.proto";
 
 // Type of transformation applied to a web page.
 enum Transformation {
@@ -26,6 +25,19 @@
   FORMAT_MHTML = 1;
 }
 
+message Timestamp {
+  // Represents seconds of UTC time since Unix epoch
+  // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+  // 9999-12-31T23:59:59Z inclusive.
+  optional int64 seconds = 1;
+
+  // Non-negative fractions of a second at nanosecond resolution. Negative
+  // second values with fractions must still have non-negative nanos values
+  // that count forward in time. Must be from 0 to 999,999,999
+  // inclusive.
+  optional int32 nanos = 2;
+}
+
 // Response to the GeneratePageBundle request.
 message PageBundle {
   // The list of archives in the bundle. The distribution of pages into archives
@@ -40,49 +52,49 @@
   // those that encountered an error or were elided due to size considerations.
   repeated PageInfo page_infos = 1;
   // Format of the body.
-  OutputFormat output_format = 2;
+  optional OutputFormat output_format = 2;
   // Resource name for the body which can be read via the ByteStream API.
   // This resource will remain available for a minimum of 24 hours after the
   // GeneratePageBundle request.
-  string body_name = 3;
+  optional string body_name = 3;
   // Length of the body in bytes.
-  int64 body_length = 4;
+  optional int64 body_length = 4;
 }
 
 // Information about a single returned offline page.
 message PageInfo {
   // The URL of the page that was rendered.
-  string url = 1;
+  optional string url = 1;
   // The final URL after redirects. Empty if the final URL is url.
-  string redirect_url = 2;
+  optional string redirect_url = 2;
   // Status of the render attempt. If status.code != OK, fields below this will
   // be unset. If the operation is still running, status is NotFound to
   // indicate the page is still being processed.
   // If the page was not returned due to bundle size limits, status is
   // FailedPrecondition. If the page failed to render for any other reason,
   // status is Unknown.
-  Status status = 3;
+  optional Status status = 3;
   // Transformation that was applied to the page.
-  Transformation transformation = 4;
+  optional Transformation transformation = 4;
   // Time the page was rendered.
-  Timestamp render_time = 5;
+  optional Timestamp render_time = 5;
 }
 
 // Request to return a list of pages in a format suitable for offline viewing.
 message GeneratePageBundleRequest {
   // The client's browser's user agent string.
-  string user_agent = 1;
+  optional string user_agent = 1;
   // Preferred browser language(s) as defined by
   // [IETF BCP 47](https://tools.ietf.org/html/bcp47).
   repeated string browser_languages = 2;
   // Desired format of the web page archive(s).
-  OutputFormat output_format = 3;
+  optional OutputFormat output_format = 3;
   // Maximum size of the generated body. If all pages' output would exceed this
   // size, only the first N pages are returned.
-  int64 max_bundle_size_bytes = 4;
+  optional int64 max_bundle_size_bytes = 4;
   // The GCM registration ID that can be used to inform the client
   // of LRO completion.
-  string gcm_registration_id = 5;
+  optional string gcm_registration_id = 5;
   // List of individual page requests, in order of priority. At most 100 pages
   // may be requested at a time.
   repeated PageParameters pages = 6;
@@ -92,7 +104,7 @@
 // viewing.
 message PageParameters {
   // URL of the web page to return.
-  string url = 1;
+  optional string url = 1;
   // Transformation to apply. Must not be TRANSFORMATION_UNSPECIFIED.
-  Transformation transformation = 2;
+  optional Transformation transformation = 2;
 }
diff --git a/components/offline_pages/core/prefetch/proto/operation.proto b/components/offline_pages/core/prefetch/proto/operation.proto
index 77558d9f..db0b1eee 100644
--- a/components/offline_pages/core/prefetch/proto/operation.proto
+++ b/components/offline_pages/core/prefetch/proto/operation.proto
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-syntax = "proto3";
+syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
 package offline_pages.proto;
-;
 
 import "any.proto";
 import "status.proto";
@@ -16,18 +15,18 @@
 message Operation {
   // The server-assigned name, which is only unique within the same service that
   // originally returns it.
-  string name = 1;
+  optional string name = 1;
 
   // Service-specific metadata associated with the operation.  It typically
   // contains progress information and common metadata such as create time.
   // Some services might not provide such metadata.  Any method that returns a
   // long-running operation should document the metadata type, if any.
-  Any metadata = 2;
+  optional Any metadata = 2;
 
   // If the value is 'false', it means the operation is still in progress.
   // If true, the operation is completed, and either 'error' or 'response' is
   // available.
-  bool done = 3;
+  optional bool done = 3;
 
   // The operation result, which can be either an 'error' or a valid 'response'.
   // If 'done' == 'false', neither 'error' nor 'response' is set.
diff --git a/components/offline_pages/core/prefetch/proto/status.proto b/components/offline_pages/core/prefetch/proto/status.proto
index f7187993..affec0b 100644
--- a/components/offline_pages/core/prefetch/proto/status.proto
+++ b/components/offline_pages/core/prefetch/proto/status.proto
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-syntax = "proto3";
+syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
 package offline_pages.proto;
@@ -18,10 +18,10 @@
 
 message Status {
   // The status code.
-  int32 code = 1;
+  optional int32 code = 1;
 
   // A developer-facing error message, which should be in English.
-  string message = 2;
+  optional string message = 2;
 
   // A list of messages that carry the error details.  There will be a
   // common set of message types for APIs to use.
diff --git a/components/offline_pages/core/prefetch/proto/timestamp.proto b/components/offline_pages/core/prefetch/proto/timestamp.proto
deleted file mode 100644
index c9658e7..0000000
--- a/components/offline_pages/core/prefetch/proto/timestamp.proto
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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.
-
-syntax = "proto3";
-option optimize_for = LITE_RUNTIME;
-
-package offline_pages.proto;
-
-message Timestamp {
-  // Represents seconds of UTC time since Unix epoch
-  // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
-  // 9999-12-31T23:59:59Z inclusive.
-  int64 seconds = 1;
-
-  // Non-negative fractions of a second at nanosecond resolution. Negative
-  // second values with fractions must still have non-negative nanos values
-  // that count forward in time. Must be from 0 to 999,999,999
-  // inclusive.
-  int32 nanos = 2;
-}
diff --git a/components/offline_pages/core/prefetch/suggested_articles_observer.cc b/components/offline_pages/core/prefetch/suggested_articles_observer.cc
index e7225a5a..d0fb3ba 100644
--- a/components/offline_pages/core/prefetch/suggested_articles_observer.cc
+++ b/components/offline_pages/core/prefetch/suggested_articles_observer.cc
@@ -28,10 +28,6 @@
   return articles;
 }
 
-ClientId CreateClientIDFromSuggestionId(const ContentSuggestion::ID& id) {
-  return ClientId(kSuggestedArticlesNamespace, id.id_within_category());
-}
-
 }  // namespace
 
 SuggestedArticlesObserver::SuggestedArticlesObserver(
@@ -71,10 +67,11 @@
   std::vector<PrefetchURL> prefetch_urls;
   for (const ContentSuggestion& suggestion : suggestions) {
     prefetch_urls.push_back(
-        {CreateClientIDFromSuggestionId(suggestion.id()), suggestion.url()});
+        {suggestion.id().id_within_category(), suggestion.url()});
   }
 
-  prefetch_dispatcher_->AddCandidatePrefetchURLs(prefetch_urls);
+  prefetch_dispatcher_->AddCandidatePrefetchURLs(kSuggestedArticlesNamespace,
+                                                 prefetch_urls);
 }
 
 void SuggestedArticlesObserver::OnCategoryStatusChanged(
@@ -96,8 +93,8 @@
 
 void SuggestedArticlesObserver::OnSuggestionInvalidated(
     const ContentSuggestion::ID& suggestion_id) {
-  prefetch_dispatcher_->RemovePrefetchURLsByClientId(
-      CreateClientIDFromSuggestionId(suggestion_id));
+  prefetch_dispatcher_->RemovePrefetchURLsByClientId(ClientId(
+      kSuggestedArticlesNamespace, suggestion_id.id_within_category()));
 }
 
 void SuggestedArticlesObserver::OnFullRefreshRequired() {
diff --git a/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc b/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc
index 0338550..0b2febb 100644
--- a/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc
+++ b/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc
@@ -76,9 +76,8 @@
   EXPECT_EQ(1U, test_prefetch_dispatcher()->latest_prefetch_urls.size());
   EXPECT_EQ(test_url_1,
             test_prefetch_dispatcher()->latest_prefetch_urls[0].url);
-  EXPECT_EQ(
-      kSuggestedArticlesNamespace,
-      test_prefetch_dispatcher()->latest_prefetch_urls[0].client_id.name_space);
+  EXPECT_EQ(kSuggestedArticlesNamespace,
+            test_prefetch_dispatcher()->latest_name_space);
 }
 
 TEST_F(OfflinePageSuggestedArticlesObserverTest, RemovesAllOnBadStatus) {
@@ -119,9 +118,8 @@
   EXPECT_NE(nullptr, test_prefetch_dispatcher()->last_removed_client_id.get());
   EXPECT_EQ(test_url_1.spec(),
             test_prefetch_dispatcher()->last_removed_client_id->id);
-  EXPECT_EQ(
-      kSuggestedArticlesNamespace,
-      test_prefetch_dispatcher()->latest_prefetch_urls[0].client_id.name_space);
+  EXPECT_EQ(kSuggestedArticlesNamespace,
+            test_prefetch_dispatcher()->latest_name_space);
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc b/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc
index da98463..c0a555e 100644
--- a/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc
+++ b/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc
@@ -13,8 +13,10 @@
 TestPrefetchDispatcher::~TestPrefetchDispatcher() = default;
 
 void TestPrefetchDispatcher::AddCandidatePrefetchURLs(
-    const std::vector<PrefetchURL>& suggested_urls) {
-  latest_prefetch_urls = suggested_urls;
+    const std::string& name_space,
+    const std::vector<PrefetchURL>& prefetch_urls) {
+  latest_name_space = name_space;
+  latest_prefetch_urls = prefetch_urls;
   new_suggestions_count++;
 }
 
diff --git a/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h b/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h
index f768101..fb6620d5 100644
--- a/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h
+++ b/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h
@@ -22,7 +22,8 @@
 
   // PrefetchDispatcher implementation.
   void AddCandidatePrefetchURLs(
-      const std::vector<PrefetchURL>& suggested_urls) override;
+      const std::string& name_space,
+      const std::vector<PrefetchURL>& prefetch_urls) override;
   void RemoveAllUnprocessedPrefetchURLs(const std::string& name_space) override;
   void RemovePrefetchURLsByClientId(const ClientId& client_id) override;
   void BeginBackgroundTask(std::unique_ptr<ScopedBackgroundTask> task) override;
@@ -32,6 +33,7 @@
       const std::string& operation_name) override;
   void RequestFinishBackgroundTaskForTest() override;
 
+  std::string latest_name_space;
   std::vector<PrefetchURL> latest_prefetch_urls;
   std::unique_ptr<ClientId> last_removed_client_id;
   std::vector<std::string> operation_list;
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index b5da7d5..7ce26936 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -262,9 +262,7 @@
   WebLocalFrame* web_frame = WebLocalFrame::Create(
       blink::WebTreeScopeType::kDocument, this, nullptr, nullptr);
   web_view_->SetMainFrame(web_frame);
-  // TODO(dcheng): The main frame widget currently has a special case.
-  // Eliminate this once WebView is no longer a WebWidget.
-  WebFrameWidget::Create(this, web_view_, web_frame);
+  WebFrameWidget::Create(this, web_frame);
 }
 
 WebViewPlugin::WebViewHelper::~WebViewHelper() {
diff --git a/components/printing/renderer/print_web_view_helper.cc b/components/printing/renderer/print_web_view_helper.cc
index fba82e0..b7cce0b 100644
--- a/components/printing/renderer/print_web_view_helper.cc
+++ b/components/printing/renderer/print_web_view_helper.cc
@@ -586,7 +586,7 @@
       blink::WebTreeScopeType::kDocument, &frame_client, nullptr, nullptr);
   web_view->SetMainFrame(frame);
   blink::WebWidgetClient web_widget_client;
-  blink::WebFrameWidget::Create(&web_widget_client, web_view, frame);
+  blink::WebFrameWidget::Create(&web_widget_client, frame);
 
   base::Value html(
       base::UTF8ToUTF16(ResourceBundle::GetSharedInstance().GetRawDataResource(
@@ -808,7 +808,7 @@
   blink::WebLocalFrame* main_frame = blink::WebLocalFrame::Create(
       blink::WebTreeScopeType::kDocument, this, nullptr, nullptr);
   web_view->SetMainFrame(main_frame);
-  blink::WebFrameWidget::Create(this, web_view, main_frame);
+  blink::WebFrameWidget::Create(this, main_frame);
   frame_.Reset(web_view->MainFrame()->ToWebLocalFrame());
   node_to_print_.Reset();
 
diff --git a/components/proximity_auth/webui/resources/common.css b/components/proximity_auth/webui/resources/common.css
index 10c3e09..728a573 100644
--- a/components/proximity_auth/webui/resources/common.css
+++ b/components/proximity_auth/webui/resources/common.css
@@ -31,7 +31,7 @@
 
 /** CSS for controls panel */
 #controls {
-  width: 60%;
+  width: 50%;
   display: flex;
   flex-direction: column;
   overflow-y: hidden;
@@ -82,7 +82,7 @@
 
 /** CSS for logs panel */
 #logs-panel {
-  width: 40%;
+  width: 50%;
   display: flex;
   flex-direction: column;
   border-left: 1px solid rgba(0,0,0,0.12);
@@ -92,20 +92,24 @@
   flex-direction: row-reverse;
 }
 
+#copy-logs-button,
 #clear-logs-button {
-  cursor: pointer;
   background-color: rgba(0,0,0,0);
   border: none;
-  font-size: 30px;
-  height: 100%;
   color: #fff;
+  cursor: pointer;
+  font-size: 20px;
+  height: 100%;
+  padding: 0 25px;
 }
 
+#copy-logs-button:hover,
 #clear-logs-button:hover {
   cursor: pointer;
   color: #535553;
 }
 
+#copy-logs-button:focus,
 #clear-logs-button:focus {
   outline: 0;
 }
diff --git a/components/proximity_auth/webui/resources/logs.js b/components/proximity_auth/webui/resources/logs.js
index 2b4144c..8fec5ef1 100644
--- a/components/proximity_auth/webui/resources/logs.js
+++ b/components/proximity_auth/webui/resources/logs.js
@@ -17,8 +17,20 @@
       WebUI.clearLogs();
     };
 
+    var copyLogsButton = document.getElementById('copy-logs-button');
+    copyLogsButton.onclick = () => {
+      this.copyLogsToClipboard();
+    };
+
     WebUI.getLogMessages();
   },
+
+  copyLogsToClipboard: function() {
+    window.getSelection().selectAllChildren(
+        document.getElementById('logs-list'));
+    document.execCommand('copy');
+    window.getSelection().removeAllRanges();
+  },
 };
 
 /**
diff --git a/components/proximity_auth/webui/resources/proximity_auth.html b/components/proximity_auth/webui/resources/proximity_auth.html
index 83ee20d..87e55094 100644
--- a/components/proximity_auth/webui/resources/proximity_auth.html
+++ b/components/proximity_auth/webui/resources/proximity_auth.html
@@ -88,7 +88,8 @@
   <!-- Panel for logs list. -->
   <section id='logs-panel'>
     <header>
-      <button id='clear-logs-button'>∅</button>
+      <button id='clear-logs-button'>Clear</button>
+      <button id='copy-logs-button'>Copy to Clipboard</button>
     </header>
     <div id='logs-list'> </div>
   </section>
@@ -119,7 +120,7 @@
         <div class="flex"></div>
         <div class='item-source'></div>
       </div>
-      <pre class="item-text flex">This is an error.</pre>
+      <p class="item-text flex">This is an error.</p>
     </div>
   </template>
 </body>
diff --git a/components/safe_browsing/password_protection/BUILD.gn b/components/safe_browsing/password_protection/BUILD.gn
index dcd862f..5bf08a8 100644
--- a/components/safe_browsing/password_protection/BUILD.gn
+++ b/components/safe_browsing/password_protection/BUILD.gn
@@ -24,6 +24,7 @@
       "//components/safe_browsing:csd_proto",
       "//components/safe_browsing_db:database_manager",
       "//components/safe_browsing_db:v4_protocol_manager_util",
+      "//components/safe_browsing_db:whitelist_checker_client",
       "//content/public/browser:browser",
       "//net:net",
       "//third_party/protobuf:protobuf_lite",
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
index 909c848..f58d30b 100644
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/password_protection/password_protection_request.cc
@@ -1,6 +1,7 @@
 // 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/safe_browsing/password_protection/password_protection_request.h"
 
 #include "base/memory/ptr_util.h"
@@ -8,6 +9,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/password_manager/core/browser/password_reuse_detector.h"
+#include "components/safe_browsing_db/whitelist_checker_client.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
@@ -50,26 +52,37 @@
 
 void PasswordProtectionRequest::Start() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  CheckWhitelistOnUIThread();
+  CheckWhitelist();
 }
 
-void PasswordProtectionRequest::CheckWhitelistOnUIThread() {
+void PasswordProtectionRequest::CheckWhitelist() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  bool* match_whitelist = new bool(false);
 
-  tracker_.PostTaskAndReply(
+  // Start a task on the IO thread to check the whitelist. It may
+  // callback immediately on the IO thread or take some time if a full-hash-
+  // check is required.
+  auto result_callback = base::Bind(&OnWhitelistCheckDoneOnIO, GetWeakPtr());
+  tracker_.PostTask(
       BrowserThread::GetTaskRunnerForThread(BrowserThread::IO).get(), FROM_HERE,
-      base::Bind(&PasswordProtectionService::CheckCsdWhitelistOnIOThread,
-                 base::Unretained(password_protection_service_),
-                 main_frame_url_, match_whitelist),
-      base::Bind(&PasswordProtectionRequest::OnWhitelistCheckDone, this,
-                 base::Owned(match_whitelist)));
+      base::Bind(&WhitelistCheckerClient::StartCheckCsdWhitelist,
+                 password_protection_service_->database_manager(),
+                 main_frame_url_, result_callback));
 }
 
-void PasswordProtectionRequest::OnWhitelistCheckDone(
-    const bool* match_whitelist) {
+// static
+void PasswordProtectionRequest::OnWhitelistCheckDoneOnIO(
+    base::WeakPtr<PasswordProtectionRequest> weak_request,
+    bool match_whitelist) {
+  // Don't access weak_request on IO thread. Move it back to UI thread first.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&PasswordProtectionRequest::OnWhitelistCheckDone, weak_request,
+                 match_whitelist));
+}
+
+void PasswordProtectionRequest::OnWhitelistCheckDone(bool match_whitelist) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (*match_whitelist)
+  if (match_whitelist)
     Finish(PasswordProtectionService::MATCHED_WHITELIST, nullptr);
   else
     CheckCachedVerdicts();
diff --git a/components/safe_browsing/password_protection/password_protection_request.h b/components/safe_browsing/password_protection/password_protection_request.h
index 721739b..4619011 100644
--- a/components/safe_browsing/password_protection/password_protection_request.h
+++ b/components/safe_browsing/password_protection/password_protection_request.h
@@ -85,16 +85,16 @@
   friend class base::DeleteHelper<PasswordProtectionRequest>;
   ~PasswordProtectionRequest() override;
 
-  void CheckWhitelistOnUIThread();
+  // Start checking the whitelist.
+  void CheckWhitelist();
+
+  static void OnWhitelistCheckDoneOnIO(
+      base::WeakPtr<PasswordProtectionRequest> weak_request,
+      bool match_whitelist);
 
   // If |main_frame_url_| matches whitelist, call Finish() immediately;
-  // otherwise call CheckCachedVerdicts(). It is the task posted back to UI
-  // thread by the PostTaskAndReply() in CheckWhitelistOnUIThread().
-  // |match_whitelist| boolean pointer is used to pass whitelist checking result
-  // between UI and IO thread. The object it points to will be deleted at the
-  // end of OnWhitelistCheckDone(), since base::Owned() transfers its ownership
-  // to this callback function.
-  void OnWhitelistCheckDone(const bool* match_whitelist);
+  // otherwise call CheckCachedVerdicts().
+  void OnWhitelistCheckDone(bool match_whitelist);
 
   // Looks up cached verdicts. If verdict is already cached, call SendRequest();
   // otherwise call Finish().
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
index c7ec2a9d..51d17e93 100644
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/password_protection/password_protection_service.cc
@@ -18,6 +18,7 @@
 #include "components/safe_browsing/password_protection/password_protection_request.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
+#include "components/safe_browsing_db/whitelist_checker_client.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "google_apis/google_api_keys.h"
@@ -105,14 +106,6 @@
   weak_factory_.InvalidateWeakPtrs();
 }
 
-void PasswordProtectionService::CheckCsdWhitelistOnIOThread(
-    const GURL& url,
-    bool* check_result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  *check_result =
-      url.is_valid() ? database_manager_->MatchCsdWhitelistUrl(url) : true;
-}
-
 bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) {
   if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS())
     return false;
diff --git a/components/safe_browsing/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
index d297ef1..fb61afd5 100644
--- a/components/safe_browsing/password_protection/password_protection_service_unittest.cc
+++ b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
@@ -19,6 +19,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
+using testing::ElementsAre;
+using testing::Return;
+
 namespace {
 
 const char kFormActionUrl[] = "https://form_action.com/";
@@ -36,7 +40,8 @@
  public:
   MockSafeBrowsingDatabaseManager() {}
 
-  MOCK_METHOD1(MatchCsdWhitelistUrl, bool(const GURL&));
+  MOCK_METHOD2(CheckCsdWhitelistUrl,
+               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
 
  protected:
   ~MockSafeBrowsingDatabaseManager() override {}
@@ -167,8 +172,9 @@
   void InitializeAndStartPasswordOnFocusRequest(bool match_whitelist,
                                                 int timeout_in_ms) {
     GURL target_url(kTargetUrl);
-    EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(target_url))
-        .WillRepeatedly(testing::Return(match_whitelist));
+    EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url, _))
+        .WillRepeatedly(
+            Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
 
     request_ = new PasswordProtectionRequest(
         nullptr, target_url, GURL(kFormActionUrl), GURL(kPasswordFrameUrl),
@@ -181,8 +187,9 @@
                                               bool match_whitelist,
                                               int timeout_in_ms) {
     GURL target_url(kTargetUrl);
-    EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(target_url))
-        .WillRepeatedly(testing::Return(match_whitelist));
+    EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url, _))
+        .WillRepeatedly(
+            Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
 
     request_ = new PasswordProtectionRequest(
         nullptr, target_url, GURL(), GURL(), saved_domain,
@@ -449,7 +456,7 @@
   EXPECT_EQ(nullptr, password_protection_service_->latest_response());
   EXPECT_THAT(
       histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
-      testing::ElementsAre(base::Bucket(4 /* MATCHED_WHITELIST */, 1)));
+      ElementsAre(base::Bucket(4 /* MATCHED_WHITELIST */, 1)));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestNoRequestSentIfVerdictAlreadyCached) {
@@ -461,7 +468,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_THAT(
       histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
-      testing::ElementsAre(base::Bucket(5 /* RESPONSE_ALREADY_CACHED */, 1)));
+      ElementsAre(base::Bucket(5 /* RESPONSE_ALREADY_CACHED */, 1)));
   EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
             password_protection_service_->latest_response()->verdict_type());
 }
@@ -480,7 +487,7 @@
   EXPECT_EQ(nullptr, password_protection_service_->latest_response());
   EXPECT_THAT(
       histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
-      testing::ElementsAre(base::Bucket(9 /* FETCH_FAILED */, 1)));
+      ElementsAre(base::Bucket(9 /* FETCH_FAILED */, 1)));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestMalformedResponse) {
@@ -499,7 +506,7 @@
   EXPECT_EQ(nullptr, password_protection_service_->latest_response());
   EXPECT_THAT(
       histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
-      testing::ElementsAre(base::Bucket(10 /* RESPONSE_MALFORMED */, 1)));
+      ElementsAre(base::Bucket(10 /* RESPONSE_MALFORMED */, 1)));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestRequestTimedout) {
@@ -510,7 +517,7 @@
   EXPECT_EQ(nullptr, password_protection_service_->latest_response());
   EXPECT_THAT(
       histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
-      testing::ElementsAre(base::Bucket(3 /* TIMEDOUT */, 1)));
+      ElementsAre(base::Bucket(3 /* TIMEDOUT */, 1)));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestRequestAndResponseSuccessfull) {
@@ -530,9 +537,9 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_THAT(
       histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
-      testing::ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
   EXPECT_THAT(histograms_.GetAllSamples(kVerdictHistogramName),
-              testing::ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
   LoginReputationClientResponse* actual_response =
       password_protection_service_->latest_response();
   EXPECT_EQ(expected_response.verdict_type(), actual_response->verdict_type());
@@ -545,8 +552,8 @@
 TEST_F(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
   histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogramName, 0);
   GURL target_url(kTargetUrl);
-  EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(target_url))
-      .WillRepeatedly(testing::Return(false));
+  EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url, _))
+      .WillRepeatedly(Return(AsyncMatch::NO_MATCH));
   password_protection_service_->StartRequest(
       nullptr, target_url, GURL("http://foo.com/submit"),
       GURL("http://foo.com/frame"), std::string(),
@@ -558,7 +565,7 @@
 
   EXPECT_THAT(
       histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
-      testing::ElementsAre(base::Bucket(2 /* CANCELED */, 1)));
+      ElementsAre(base::Bucket(2 /* CANCELED */, 1)));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestCleanUpExpiredVerdict) {
diff --git a/components/safe_browsing_db/BUILD.gn b/components/safe_browsing_db/BUILD.gn
index 5eeacf1..28fd0e30 100644
--- a/components/safe_browsing_db/BUILD.gn
+++ b/components/safe_browsing_db/BUILD.gn
@@ -72,6 +72,10 @@
     "//net",
     "//url",
   ]
+
+  public_deps = [
+    ":safebrowsing_proto",
+  ]
 }
 
 static_library("hit_report") {
@@ -406,6 +410,7 @@
     "util_unittest.cc",
     "v4_get_hash_protocol_manager_unittest.cc",
     "v4_protocol_manager_util_unittest.cc",
+    "whitelist_checker_client_unittest.cc",
   ]
   deps = [
     ":database_manager",
@@ -416,6 +421,7 @@
     ":v4_get_hash_protocol_manager",
     ":v4_protocol_manager_util",
     ":v4_test_util",
+    ":whitelist_checker_client",
     "//base",
     "//content/public/browser",
     "//content/test:test_support",
@@ -489,3 +495,30 @@
     cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
   }
 }
+
+static_library("whitelist_checker_client") {
+  sources = [
+    "whitelist_checker_client.cc",
+    "whitelist_checker_client.h",
+  ]
+  deps = [
+    ":database_manager",
+    "//base:base",
+  ]
+}
+
+source_set("whitelist_checker_client_unittest") {
+  testonly = true
+  sources = [
+    "whitelist_checker_client_unittest.cc",
+  ]
+  deps = [
+    ":database_manager",
+    ":test_database_manager",
+    ":whitelist_checker_client",
+    "//base:base",
+    "//base/test:test_support",
+    "//testing/gmock:gmock",
+    "//testing/gtest:gtest",
+  ]
+}
diff --git a/components/safe_browsing_db/whitelist_checker_client.cc b/components/safe_browsing_db/whitelist_checker_client.cc
new file mode 100644
index 0000000..2e6464bd
--- /dev/null
+++ b/components/safe_browsing_db/whitelist_checker_client.cc
@@ -0,0 +1,64 @@
+// 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/safe_browsing_db/whitelist_checker_client.h"
+
+#include "base/bind.h"
+
+namespace safe_browsing {
+
+// Static
+void WhitelistCheckerClient::StartCheckCsdWhitelist(
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    const GURL& url,
+    base::Callback<void(bool)> callback_for_result) {
+  // TODO(nparker): Maybe also call SafeBrowsingDatabaseManager::CanCheckUrl()
+  if (!url.is_valid()) {
+    callback_for_result.Run(true /* is_whitelisted */);
+    return;
+  }
+
+  // Make a client for each request. The caller could have several in
+  // flight at once.
+  std::unique_ptr<WhitelistCheckerClient> client =
+      base::MakeUnique<WhitelistCheckerClient>(callback_for_result);
+  AsyncMatch match = database_manager->CheckCsdWhitelistUrl(url, client.get());
+
+  switch (match) {
+    case AsyncMatch::MATCH:
+      callback_for_result.Run(true /* is_whitelisted */);
+      break;
+    case AsyncMatch::NO_MATCH:
+      callback_for_result.Run(false /* is_whitelisted */);
+      break;
+    case AsyncMatch::ASYNC:
+      // Client is now self-owned. When it gets called back with the result,
+      // it will delete itself.
+      client.release();
+      break;
+  }
+}
+
+WhitelistCheckerClient::WhitelistCheckerClient(
+    base::Callback<void(bool)> callback_for_result)
+    : callback_for_result_(callback_for_result), weak_factory_(this) {
+  // Set a timer to fail open, i.e. call it "whitelisted", if the full
+  // check takes too long.
+  auto timeout_callback =
+      base::Bind(&WhitelistCheckerClient::OnCheckWhitelistUrlResult,
+                 weak_factory_.GetWeakPtr(), true /* is_whitelisted */);
+  timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMsec),
+               timeout_callback);
+}
+
+WhitelistCheckerClient::~WhitelistCheckerClient() {}
+
+// SafeBrowsingDatabaseMananger::Client impl
+void WhitelistCheckerClient::OnCheckWhitelistUrlResult(bool is_whitelisted) {
+  // This method is invoked only if we're already self-owned.
+  callback_for_result_.Run(is_whitelisted);
+  delete this;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing_db/whitelist_checker_client.h b/components/safe_browsing_db/whitelist_checker_client.h
new file mode 100644
index 0000000..40bd669
--- /dev/null
+++ b/components/safe_browsing_db/whitelist_checker_client.h
@@ -0,0 +1,49 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_DB_WHITELIST_CHECKER_CLIENT_H_
+#define COMPONENTS_SAFE_BROWSING_DB_WHITELIST_CHECKER_CLIENT_H_
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing_db/database_manager.h"
+
+namespace safe_browsing {
+
+// This provides a simpler interface to
+// SafeBrowsingDatabaseManager::CheckCsdWhitelistUrl() for callers that
+// don't want to track their own clients.
+
+class WhitelistCheckerClient : public SafeBrowsingDatabaseManager::Client {
+ public:
+  using BoolCallback = base::Callback<void(bool /* is_whitelisted */)>;
+
+  // Static method to instantiate and start a check. The callback will
+  // be invoked when it's done, times out, or if database_manager gets
+  // shut down. Must be called on IO thread.
+  static void StartCheckCsdWhitelist(
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      const GURL& url,
+      BoolCallback callback_for_result);
+
+  WhitelistCheckerClient(BoolCallback callback_for_result);
+  ~WhitelistCheckerClient() override;
+
+  // SafeBrowsingDatabaseMananger::Client impl
+  void OnCheckWhitelistUrlResult(bool is_whitelisted) override;
+
+ protected:
+  static const int kTimeoutMsec = 5000;
+  base::OneShotTimer timer_;
+  BoolCallback callback_for_result_;
+  base::WeakPtrFactory<WhitelistCheckerClient> weak_factory_;
+
+ private:
+  WhitelistCheckerClient();
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_DB_WHITELIST_CHECKER_CLIENT_H_
diff --git a/components/safe_browsing_db/whitelist_checker_client_unittest.cc b/components/safe_browsing_db/whitelist_checker_client_unittest.cc
new file mode 100644
index 0000000..8835a96c
--- /dev/null
+++ b/components/safe_browsing_db/whitelist_checker_client_unittest.cc
@@ -0,0 +1,116 @@
+// 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/safe_browsing_db/whitelist_checker_client.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/safe_browsing_db/test_database_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+
+using base::TimeDelta;
+using testing::_;
+using testing::Return;
+using testing::SaveArg;
+
+using BoolCallback = base::Callback<void(bool /* is_whitelisted */)>;
+using MockBoolCallback = testing::StrictMock<base::MockCallback<BoolCallback>>;
+
+class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
+ public:
+  MockSafeBrowsingDatabaseManager(){};
+
+  MOCK_METHOD2(CheckCsdWhitelistUrl,
+               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
+
+ protected:
+  ~MockSafeBrowsingDatabaseManager() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
+};
+
+class WhitelistCheckerClientTest : public testing::Test {
+ public:
+  WhitelistCheckerClientTest() : target_url_("http://foo.bar"){};
+
+  void SetUp() override {
+    database_manager_ = new MockSafeBrowsingDatabaseManager;
+    task_runner_ = new base::TestMockTimeTaskRunner(base::Time::Now(),
+                                                    base::TimeTicks::Now());
+    message_loop_.reset(new base::MessageLoop);
+    message_loop_->SetTaskRunner(task_runner_);
+  }
+
+  void TearDown() override {
+    // Verify no callback is remaining.
+    // TODO(nparker): We should somehow EXPECT that no entry is remaining,
+    // rather than just invoking it.
+    task_runner_->FastForwardUntilNoTasksRemain();
+  }
+
+ protected:
+  GURL target_url_;
+  scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
+
+  std::unique_ptr<base::MessageLoop> message_loop_;
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+};
+
+TEST_F(WhitelistCheckerClientTest, TestMatch) {
+  EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url_, _))
+      .WillOnce(Return(AsyncMatch::MATCH));
+
+  MockBoolCallback callback;
+  EXPECT_CALL(callback, Run(true /* is_whitelisted */));
+  WhitelistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+                                                 callback.Get());
+}
+
+TEST_F(WhitelistCheckerClientTest, TestNoMatch) {
+  EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url_, _))
+      .WillOnce(Return(AsyncMatch::NO_MATCH));
+
+  MockBoolCallback callback;
+  EXPECT_CALL(callback, Run(false /* is_whitelisted */));
+  WhitelistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+                                                 callback.Get());
+}
+
+TEST_F(WhitelistCheckerClientTest, TestAsyncNoMatch) {
+  SafeBrowsingDatabaseManager::Client* client;
+  EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url_, _))
+      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+
+  MockBoolCallback callback;
+  WhitelistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+                                                 callback.Get());
+  // Callback should not be called yet.
+
+  EXPECT_CALL(callback, Run(false /* is_whitelisted */));
+  // The self-owned client deletes itself here.
+  client->OnCheckWhitelistUrlResult(false);
+}
+
+TEST_F(WhitelistCheckerClientTest, TestAsyncTimeout) {
+  EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url_, _))
+      .WillOnce(Return(AsyncMatch::ASYNC));
+
+  MockBoolCallback callback;
+  WhitelistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+                                                 callback.Get());
+  task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
+  // No callback yet.
+
+  EXPECT_CALL(callback, Run(true /* is_whitelisted */));
+  task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(5));
+}
+
+}  // namespace safe_browsing
diff --git a/components/search_engines/template_url_service.h b/components/search_engines/template_url_service.h
index 058b0a0..884c582 100644
--- a/components/search_engines/template_url_service.h
+++ b/components/search_engines/template_url_service.h
@@ -257,7 +257,9 @@
   // Returns the default search provider. If the TemplateURLService hasn't been
   // loaded, the default search provider is pulled from preferences.
   //
-  // NOTE: At least in unittest mode, this may return NULL.
+  // NOTE: This may return null in certain circumstances such as:
+  //       1.) Unit test mode
+  //       2.) The default search engine is disabled by policy.
   const TemplateURL* GetDefaultSearchProvider() const;
 
   // Returns true if the |url| is a search results page from the default search
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter.cc b/components/subresource_filter/content/browser/async_document_subresource_filter.cc
index 0ed9d1c..3b0b7cc 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter.cc
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter.cc
@@ -27,6 +27,12 @@
       "SubresourceFilter.DocumentLoad.Activation.WallDuration");
   SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(
       "SubresourceFilter.DocumentLoad.Activation.CPUDuration");
+  if (parent_document_origin.unique()) {
+    SCOPED_UMA_HISTOGRAM_MICRO_TIMER(
+        "SubresourceFilter.PageLoad.Activation.WallDuration");
+    SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(
+        "SubresourceFilter.PageLoad.Activation.CPUDuration");
+  }
 
   IndexedRulesetMatcher matcher(ruleset->data(), ruleset->length());
   ActivationState activation_state = parent_activation_state;
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
index 6c1cff4..a61a90d 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h"
@@ -130,6 +131,10 @@
                                         kActivationConsoleMessage);
       }
     }
+    ActivationLevel level = filter ? filter->activation_state().activation_level
+                                   : ActivationLevel::DISABLED;
+    UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.PageLoad.ActivationState",
+                              level, ActivationLevel::LAST);
   }
 
   // Make sure |activated_frame_hosts_| is updated or cleaned up depending on
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
index fbd140e..fc7b8e4 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/histogram_tester.h"
 #include "base/test/test_simple_task_runner.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
@@ -676,6 +677,37 @@
   EXPECT_EQ(0, disallowed_notification_count());
 }
 
+TEST_F(ContentSubresourceFilterThrottleManagerTest, LogActivation) {
+  base::HistogramTester tester;
+  NavigateAndCommitMainFrame(GURL(kTestURLWithActivation));
+  const char kActivationStateHistogram[] =
+      "SubresourceFilter.PageLoad.ActivationState";
+  tester.ExpectBucketCount(kActivationStateHistogram,
+                           static_cast<int>(ActivationLevel::ENABLED), 1);
+
+  NavigateAndCommitMainFrame(GURL(kTestURLWithDryRun));
+  tester.ExpectBucketCount(kActivationStateHistogram,
+                           static_cast<int>(ActivationLevel::DRYRUN), 1);
+
+  NavigateAndCommitMainFrame(GURL(kTestURLWithNoActivation));
+  tester.ExpectBucketCount(kActivationStateHistogram,
+                           static_cast<int>(ActivationLevel::DISABLED), 1);
+
+  // Navigate a subframe that is not filtered, but should still activate.
+  CreateSubframeWithTestNavigation(GURL("https://whitelist.com"), main_rfh());
+  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  content::RenderFrameHost* subframe1 =
+      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+  ExpectActivationSignalForFrame(subframe1, true /* expect_activation */);
+
+  tester.ExpectTotalCount(kActivationStateHistogram, 3);
+  // Only those with page level activation do ruleset lookups.
+  tester.ExpectTotalCount("SubresourceFilter.PageLoad.Activation.WallDuration",
+                          2);
+  tester.ExpectTotalCount("SubresourceFilter.PageLoad.Activation.CPUDuration",
+                          2);
+}
+
 // TODO(csharrison): Make sure the following conditions are exercised in tests:
 //
 // - Synchronous navigations to about:blank. These hit issues with the
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index 1d2ecba..ccaaa90 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -4,14 +4,16 @@
 
 #include "content/browser/devtools/protocol/storage_handler.h"
 
-#include <string>
 #include <unordered_set>
+#include <utility>
 #include <vector>
 
 #include "base/strings/string_split.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
+#include "storage/browser/quota/quota_manager.h"
+#include "storage/common/quota/quota_status_code.h"
 
 namespace content {
 namespace protocol {
@@ -27,8 +29,44 @@
 static const char kServiceWorkers[] = "service_workers";
 static const char kCacheStorage[] = "cache_storage";
 static const char kAll[] = "all";
+
+void ReportUsageAndQuotaDataOnUIThread(
+    std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback,
+    storage::QuotaStatusCode code,
+    int64_t usage,
+    int64_t quota) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (code != storage::kQuotaStatusOk) {
+    return callback->sendFailure(
+        Response::Error("Quota information is not available"));
+  }
+  callback->sendSuccess(usage, quota);
 }
 
+void GotUsageAndQuotaDataCallback(
+    std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback,
+    storage::QuotaStatusCode code,
+    int64_t usage,
+    int64_t quota) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(ReportUsageAndQuotaDataOnUIThread,
+                 base::Passed(std::move(callback)), code, usage, quota));
+}
+
+void GetUsageAndQuotaOnIOThread(
+    storage::QuotaManager* manager,
+    const GURL& url,
+    std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  manager->GetUsageAndQuotaForWebApps(
+      url, storage::kStorageTypeTemporary,
+      base::Bind(&GotUsageAndQuotaDataCallback,
+                 base::Passed(std::move(callback))));
+}
+}  // namespace
+
 StorageHandler::StorageHandler()
     : DevToolsDomainHandler(Storage::Metainfo::domainName),
       host_(nullptr) {
@@ -88,5 +126,25 @@
   return Response::OK();
 }
 
+void StorageHandler::GetUsageAndQuota(
+    const String& origin,
+    std::unique_ptr<GetUsageAndQuotaCallback> callback) {
+  if (!host_)
+    return callback->sendFailure(Response::InternalError());
+
+  GURL origin_url(origin);
+  if (!origin_url.is_valid()) {
+    return callback->sendFailure(
+        Response::Error(origin + " is not a valid URL"));
+  }
+
+  storage::QuotaManager* manager =
+      host_->GetProcess()->GetStoragePartition()->GetQuotaManager();
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&GetUsageAndQuotaOnIOThread, manager, origin_url,
+                 base::Passed(std::move(callback))));
+}
+
 }  // namespace protocol
 }  // namespace content
diff --git a/content/browser/devtools/protocol/storage_handler.h b/content/browser/devtools/protocol/storage_handler.h
index b5c9db6..23731d2 100644
--- a/content/browser/devtools/protocol/storage_handler.h
+++ b/content/browser/devtools/protocol/storage_handler.h
@@ -5,6 +5,9 @@
 #ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_STORAGE_HANDLER_H_
 #define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_STORAGE_HANDLER_H_
 
+#include <memory>
+#include <string>
+
 #include "base/macros.h"
 #include "content/browser/devtools/protocol/devtools_domain_handler.h"
 #include "content/browser/devtools/protocol/storage.h"
@@ -27,6 +30,9 @@
   Response ClearDataForOrigin(
       const std::string& origin,
       const std::string& storage_types) override;
+  void GetUsageAndQuota(
+      const String& origin,
+      std::unique_ptr<GetUsageAndQuotaCallback> callback) override;
 
  private:
   RenderFrameHostImpl* host_;
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index 2b3209c2..5d58560 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -67,7 +67,8 @@
                 "domain": "ServiceWorker"
             },
             {
-                "domain": "Storage"
+                "domain": "Storage",
+                "async": ["getUsageAndQuota"]
             },
             {
                 "domain": "SystemInfo",
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 58e04bd..1b862ad 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -133,7 +133,6 @@
 #if defined(OS_WIN)
     switches::kEnableAcceleratedVpxDecode,
 #endif
-    switches::kEnableColorCorrectRendering,
     switches::kEnableGpuRasterization,
     switches::kEnableHeapProfiling,
     switches::kEnableLogging,
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index 8947d37..df66bde 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -26,8 +26,11 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/browser/webui/web_ui_url_loader_factory.h"
+#include "content/common/throttling_url_loader.h"
+#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/navigation_data.h"
 #include "content/public/browser/navigation_ui_data.h"
@@ -35,10 +38,10 @@
 #include "content/public/browser/stream_handle.h"
 #include "content/public/common/referrer.h"
 #include "content/public/common/url_constants.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request_context.h"
+#include "services/service_manager/public/cpp/connector.h"
 
 namespace content {
 
@@ -58,43 +61,6 @@
   return WebContentsImpl::FromFrameTreeNode(frame_tree_node);
 }
 
-class AssociatedURLLoaderWrapper final : public mojom::URLLoader {
- public:
-  static void CreateLoaderAndStart(
-      mojom::URLLoaderFactory* factory,
-      mojom::URLLoaderRequest request,
-      uint32_t options,
-      const ResourceRequest& resource_request,
-      mojom::URLLoaderClientPtr client,
-      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
-    mojom::URLLoaderAssociatedPtr associated_ptr;
-    mojom::URLLoaderAssociatedRequest associated_request =
-        mojo::MakeRequest(&associated_ptr);
-    factory->CreateLoaderAndStart(
-        std::move(associated_request), 0 /* routing_id */, 0 /* request_id */,
-        options, resource_request, std::move(client), traffic_annotation);
-    mojo::MakeStrongBinding(
-        base::MakeUnique<AssociatedURLLoaderWrapper>(std::move(associated_ptr)),
-        std::move(request));
-  }
-
-  explicit AssociatedURLLoaderWrapper(
-      mojom::URLLoaderAssociatedPtr associated_ptr)
-      : associated_ptr_(std::move(associated_ptr)) {}
-  ~AssociatedURLLoaderWrapper() override {}
-
-  void FollowRedirect() override { associated_ptr_->FollowRedirect(); }
-
-  void SetPriority(net::RequestPriority priority,
-                   int intra_priority_value) override {
-    associated_ptr_->SetPriority(priority, intra_priority_value);
-  }
-
- private:
-  mojom::URLLoaderAssociatedPtr associated_ptr_;
-  DISALLOW_COPY_AND_ASSIGN(AssociatedURLLoaderWrapper);
-};
-
 }  // namespace
 
 // Kept around during the lifetime of the navigation request, and is
@@ -103,17 +69,20 @@
 // of URLLoaderRequestHandler's and successively calls MaybeCreateLoader
 // on each until the request is successfully handled. The same sequence
 // may be performed multiple times when redirects happen.
-class NavigationURLLoaderNetworkService::URLLoaderRequestController {
+class NavigationURLLoaderNetworkService::URLLoaderRequestController
+    : public mojom::URLLoaderClient {
  public:
   URLLoaderRequestController(
       std::unique_ptr<ResourceRequest> resource_request,
       ResourceContext* resource_context,
-      scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter)
+      scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter,
+      const base::WeakPtr<NavigationURLLoaderNetworkService>& owner)
       : resource_request_(std::move(resource_request)),
         resource_context_(resource_context),
-        url_loader_factory_getter_(url_loader_factory_getter) {}
+        url_loader_factory_getter_(url_loader_factory_getter),
+        owner_(owner) {}
 
-  virtual ~URLLoaderRequestController() {
+  ~URLLoaderRequestController() override {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
   }
 
@@ -123,11 +92,9 @@
       std::unique_ptr<NavigationRequestInfo> request_info,
       mojom::URLLoaderFactoryPtrInfo factory_for_webui,
       const base::Callback<WebContents*(void)>& web_contents_getter,
-      mojom::URLLoaderRequest url_loader_request,
-      mojom::URLLoaderClientPtrInfo url_loader_client_ptr_info,
       std::unique_ptr<service_manager::Connector> connector) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    url_loader_client_ptr_.Bind(std::move(url_loader_client_ptr_info));
+    web_contents_getter_ = web_contents_getter;
     const ResourceType resource_type = request_info->is_main_frame
                                            ? RESOURCE_TYPE_MAIN_FRAME
                                            : RESOURCE_TYPE_SUB_FRAME;
@@ -140,12 +107,13 @@
     // Requests to WebUI scheme won't get redirected to/from other schemes
     // or be intercepted, so we just let it go here.
     if (factory_for_webui.is_valid()) {
-      mojom::URLLoaderFactoryPtr factory_ptr;
-      factory_ptr.Bind(std::move(factory_for_webui));
-      AssociatedURLLoaderWrapper::CreateLoaderAndStart(
-          factory_ptr.get(), std::move(url_loader_request),
-          mojom::kURLLoadOptionSendSSLInfo, *resource_request_,
-          std::move(url_loader_client_ptr_),
+      webui_factory_ptr_.Bind(std::move(factory_for_webui));
+      url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
+          webui_factory_ptr_.get(),
+          GetContentClient()->browser()->CreateURLLoaderThrottles(
+              web_contents_getter_),
+          0 /* routing_id? */, 0 /* request_id? */,
+          mojom::kURLLoadOptionSendSSLInfo, *resource_request_, this,
           net::MutableNetworkTrafficAnnotationTag(NO_TRAFFIC_ANNOTATION_YET));
       return;
     }
@@ -178,25 +146,22 @@
         handlers_.push_back(std::move(appcache_handler));
     }
 
-    Restart(std::move(url_loader_request), std::move(url_loader_client_ptr_));
+    Restart();
   }
 
   // This could be called multiple times.
-  void Restart(mojom::URLLoaderRequest url_loader_request,
-               mojom::URLLoaderClientPtr url_loader_client_ptr) {
-    url_loader_request_ = std::move(url_loader_request);
-    url_loader_client_ptr_ = std::move(url_loader_client_ptr);
+  void Restart() {
     handler_index_ = 0;
     MaybeStartLoader(StartLoaderCallback());
   }
 
   void MaybeStartLoader(StartLoaderCallback start_loader_callback) {
-    DCHECK(url_loader_client_ptr_.is_bound());
-
     if (start_loader_callback) {
-      std::move(start_loader_callback)
-          .Run(std::move(url_loader_request_),
-               std::move(url_loader_client_ptr_));
+      url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
+          std::move(start_loader_callback),
+          GetContentClient()->browser()->CreateURLLoaderThrottles(
+              web_contents_getter_),
+          *resource_request_, this);
       return;
     }
 
@@ -215,25 +180,84 @@
     } else {
       factory = url_loader_factory_getter_->GetNetworkFactory()->get();
     }
-    AssociatedURLLoaderWrapper::CreateLoaderAndStart(
-        factory, std::move(url_loader_request_),
-        mojom::kURLLoadOptionSendSSLInfo, *resource_request_,
-        std::move(url_loader_client_ptr_),
+    url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
+        factory,
+        GetContentClient()->browser()->CreateURLLoaderThrottles(
+            web_contents_getter_),
+        0 /* routing_id? */, 0 /* request_id? */,
+        mojom::kURLLoadOptionSendSSLInfo, *resource_request_, this,
         net::MutableNetworkTrafficAnnotationTag(NO_TRAFFIC_ANNOTATION_YET));
   }
 
+  void FollowRedirect() {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    DCHECK(url_loader_);
+
+    url_loader_->FollowRedirect();
+  }
+
  private:
+  // mojom::URLLoaderClient implementation:
+  void OnReceiveResponse(
+      const ResourceResponseHead& head,
+      const base::Optional<net::SSLInfo>& ssl_info,
+      mojom::DownloadedTempFilePtr downloaded_file) override {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&NavigationURLLoaderNetworkService::OnReceiveResponse,
+                   owner_, head, ssl_info, base::Passed(&downloaded_file)));
+  }
+
+  void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
+                         const ResourceResponseHead& head) override {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&NavigationURLLoaderNetworkService::OnReceiveRedirect,
+                   owner_, redirect_info, head));
+  }
+
+  void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override {}
+
+  void OnUploadProgress(int64_t current_position,
+                        int64_t total_size,
+                        OnUploadProgressCallback callback) override {}
+
+  void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override {}
+
+  void OnTransferSizeUpdated(int32_t transfer_size_diff) override {}
+
+  void OnStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle body) override {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(
+            &NavigationURLLoaderNetworkService::OnStartLoadingResponseBody,
+            owner_, base::Passed(&body)));
+  }
+
+  void OnComplete(
+      const ResourceRequestCompletionStatus& completion_status) override {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&NavigationURLLoaderNetworkService::OnComplete, owner_,
+                   completion_status));
+  }
+
   std::vector<std::unique_ptr<URLLoaderRequestHandler>> handlers_;
   size_t handler_index_ = 0;
 
   std::unique_ptr<ResourceRequest> resource_request_;
   ResourceContext* resource_context_;
+  base::Callback<WebContents*()> web_contents_getter_;
 
   scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter_;
 
-  // Kept around until we create a loader.
-  mojom::URLLoaderRequest url_loader_request_;
-  mojom::URLLoaderClientPtr url_loader_client_ptr_;
+  mojom::URLLoaderFactoryPtr webui_factory_ptr_;
+
+  std::unique_ptr<ThrottlingURLLoader> url_loader_;
+
+  // This is referenced only on the UI thread.
+  base::WeakPtr<NavigationURLLoaderNetworkService> owner_;
 
   DISALLOW_COPY_AND_ASSIGN(URLLoaderRequestController);
 };
@@ -246,7 +270,7 @@
     ServiceWorkerNavigationHandle* service_worker_navigation_handle,
     AppCacheNavigationHandle* appcache_handle,
     NavigationURLLoaderDelegate* delegate)
-    : delegate_(delegate), binding_(this) {
+    : delegate_(delegate), weak_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1(
@@ -286,10 +310,6 @@
 
   int frame_tree_node_id = request_info->frame_tree_node_id;
 
-  mojom::URLLoaderRequest loader_request = mojo::MakeRequest(&url_loader_ptr_);
-  mojom::URLLoaderClientPtr url_loader_client_ptr_to_pass;
-  binding_.Bind(mojo::MakeRequest(&url_loader_client_ptr_to_pass));
-
   // Check if a web UI scheme wants to handle this request.
   mojom::URLLoaderFactoryPtrInfo factory_for_webui;
   const auto& schemes = URLDataManagerBackend::GetWebUISchemes();
@@ -306,7 +326,8 @@
   request_controller_ = base::MakeUnique<URLLoaderRequestController>(
       std::move(new_request), resource_context,
       static_cast<StoragePartitionImpl*>(storage_partition)
-          ->url_loader_factory_getter());
+          ->url_loader_factory_getter(),
+      weak_factory_.GetWeakPtr());
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(
@@ -319,8 +340,6 @@
           base::Passed(std::move(request_info)),
           base::Passed(std::move(factory_for_webui)),
           base::Bind(&GetWebContentsFromFrameTreeNodeID, frame_tree_node_id),
-          base::Passed(std::move(loader_request)),
-          base::Passed(url_loader_client_ptr_to_pass.PassInterface()),
           base::Passed(ServiceManagerConnection::GetForProcess()
                            ->GetConnector()
                            ->Clone())));
@@ -332,7 +351,10 @@
 }
 
 void NavigationURLLoaderNetworkService::FollowRedirect() {
-  url_loader_ptr_->FollowRedirect();
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&URLLoaderRequestController::FollowRedirect,
+                 base::Unretained(request_controller_.get())));
 }
 
 void NavigationURLLoaderNetworkService::ProceedWithResponse() {}
@@ -361,21 +383,6 @@
   delegate_->OnRequestRedirected(redirect_info, response);
 }
 
-void NavigationURLLoaderNetworkService::OnDataDownloaded(
-    int64_t data_length,
-    int64_t encoded_length) {}
-
-void NavigationURLLoaderNetworkService::OnUploadProgress(
-    int64_t current_position,
-    int64_t total_size,
-    OnUploadProgressCallback callback) {}
-
-void NavigationURLLoaderNetworkService::OnReceiveCachedMetadata(
-    const std::vector<uint8_t>& data) {}
-
-void NavigationURLLoaderNetworkService::OnTransferSizeUpdated(
-    int32_t transfer_size_diff) {}
-
 void NavigationURLLoaderNetworkService::OnStartLoadingResponseBody(
     mojo::ScopedDataPipeConsumerHandle body) {
   DCHECK(response_);
diff --git a/content/browser/loader/navigation_url_loader_network_service.h b/content/browser/loader/navigation_url_loader_network_service.h
index 204e4f4..6e57f8f 100644
--- a/content/browser/loader/navigation_url_loader_network_service.h
+++ b/content/browser/loader/navigation_url_loader_network_service.h
@@ -6,12 +6,10 @@
 #define CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_NETWORK_SERVICE_H_
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "content/browser/loader/navigation_url_loader.h"
 #include "content/common/url_loader.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/ssl_status.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace net {
 struct RedirectInfo;
@@ -24,8 +22,7 @@
 
 // This is an implementation of NavigationURLLoader used when
 // --enable-network-service is used.
-class NavigationURLLoaderNetworkService : public NavigationURLLoader,
-                                          public mojom::URLLoaderClient {
+class NavigationURLLoaderNetworkService : public NavigationURLLoader {
  public:
   // The caller is responsible for ensuring that |delegate| outlives the loader.
   NavigationURLLoaderNetworkService(
@@ -42,36 +39,27 @@
   void FollowRedirect() override;
   void ProceedWithResponse() override;
 
-  // mojom::URLLoaderClient implementation:
   void OnReceiveResponse(const ResourceResponseHead& head,
                          const base::Optional<net::SSLInfo>& ssl_info,
-                         mojom::DownloadedTempFilePtr downloaded_file) override;
+                         mojom::DownloadedTempFilePtr downloaded_file);
   void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
-                         const ResourceResponseHead& head) override;
-  void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override;
-  void OnUploadProgress(int64_t current_position,
-                        int64_t total_size,
-                        OnUploadProgressCallback callback) override;
-  void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override;
-  void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
-  void OnStartLoadingResponseBody(
-      mojo::ScopedDataPipeConsumerHandle body) override;
-  void OnComplete(
-      const ResourceRequestCompletionStatus& completion_status) override;
+                         const ResourceResponseHead& head);
+  void OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body);
+  void OnComplete(const ResourceRequestCompletionStatus& completion_status);
 
  private:
   class URLLoaderRequestController;
 
   NavigationURLLoaderDelegate* delegate_;
 
-  mojo::Binding<mojom::URLLoaderClient> binding_;
-  mojom::URLLoaderPtr url_loader_ptr_;
   scoped_refptr<ResourceResponse> response_;
   SSLStatus ssl_status_;
 
   // Lives on the IO thread.
   std::unique_ptr<URLLoaderRequestController> request_controller_;
 
+  base::WeakPtrFactory<NavigationURLLoaderNetworkService> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(NavigationURLLoaderNetworkService);
 };
 
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index 61d5aea..e1e43dd 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -962,28 +962,26 @@
                                const media::MediaLogEvent& event) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  // Do not save instantaneous events that happen frequently and have little
-  // value in the future.
-  if (event.type == media::MediaLogEvent::NETWORK_ACTIVITY_SET ||
-      event.type == media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED) {
-    return;
-  }
+// Save the event and limit the total number per renderer. At the time of
+// writing, 512 events of the kind: { "property": value } together consume
+// ~88kb of memory on linux.
+#if defined(OS_ANDROID)
+  const size_t kEventLimit = 128;
+#else
+  const size_t kEventLimit = 512;
+#endif
 
-  // Save the event and limit the total number per renderer. At the time of
-  // writing, 512 events of the kind: { "property": value } together consume
-  // ~88kb of memory on linux.
-  std::list<media::MediaLogEvent>& saved_events =
-      saved_events_by_process_[process_id];
+  auto& saved_events = saved_events_by_process_[process_id];
   saved_events.push_back(event);
-  if (saved_events.size() > 512) {
+  if (saved_events.size() > kEventLimit) {
     // Remove all events for a given player as soon as we have to remove a
     // single event for that player to avoid showing incomplete players.
-    int id_to_remove = saved_events.front().id;
-    auto new_end = std::remove_if(saved_events.begin(), saved_events.end(),
-                                  [&](const media::MediaLogEvent& event) {
-                                    return event.id == id_to_remove;
-                                  });
-    saved_events.erase(new_end, saved_events.end());
+    const int id_to_remove = saved_events.front().id;
+    saved_events.erase(std::remove_if(saved_events.begin(), saved_events.end(),
+                                      [&](const media::MediaLogEvent& event) {
+                                        return event.id == id_to_remove;
+                                      }),
+                       saved_events.end());
   }
 }
 
diff --git a/content/browser/media/media_internals_proxy.cc b/content/browser/media/media_internals_proxy.cc
index f180589a..5b62036 100644
--- a/content/browser/media/media_internals_proxy.cc
+++ b/content/browser/media/media_internals_proxy.cc
@@ -20,22 +20,9 @@
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_ui.h"
-#include "net/log/net_log_capture_mode.h"
-#include "net/log/net_log_entry.h"
-#include "net/log/net_log_event_type.h"
 
 namespace content {
 
-static const int kMediaInternalsProxyEventDelayMilliseconds = 100;
-
-static const net::NetLogEventType kNetEventTypeFilter[] = {
-    net::NetLogEventType::DISK_CACHE_ENTRY_IMPL,
-    net::NetLogEventType::SPARSE_READ,
-    net::NetLogEventType::SPARSE_WRITE,
-    net::NetLogEventType::URL_REQUEST_START_JOB,
-    net::NetLogEventType::HTTP_TRANSACTION_READ_RESPONSE_HEADERS,
-};
-
 MediaInternalsProxy::MediaInternalsProxy() {
   registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                  NotificationService::AllBrowserContextsAndSources());
@@ -58,22 +45,13 @@
   handler_ = handler;
   update_callback_ = base::Bind(&MediaInternalsProxy::UpdateUIOnUIThread, this);
   MediaInternals::GetInstance()->AddUpdateCallback(update_callback_);
-
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::Bind(&MediaInternalsProxy::ObserveMediaInternalsOnIOThread, this));
 }
 
 void MediaInternalsProxy::Detach() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  handler_ = NULL;
+  handler_ = nullptr;
   MediaInternals::GetInstance()->RemoveUpdateCallback(update_callback_);
-
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::Bind(
-          &MediaInternalsProxy::StopObservingMediaInternalsOnIOThread, this));
 }
 
 void MediaInternalsProxy::GetEverything() {
@@ -85,67 +63,10 @@
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(&MediaInternalsProxy::GetEverythingOnIOThread, this));
-
-  // Send the page names for constants.
-  CallJavaScriptFunctionOnUIThread("media.onReceiveConstants", GetConstants());
-}
-
-void MediaInternalsProxy::OnAddEntry(const net::NetLogEntry& entry) {
-  bool is_event_interesting = false;
-  for (size_t i = 0; i < arraysize(kNetEventTypeFilter); i++) {
-    if (entry.type() == kNetEventTypeFilter[i]) {
-      is_event_interesting = true;
-      break;
-    }
-  }
-
-  if (!is_event_interesting)
-    return;
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&MediaInternalsProxy::AddNetEventOnUIThread, this,
-                 base::Passed(entry.ToValue())));
 }
 
 MediaInternalsProxy::~MediaInternalsProxy() {}
 
-std::unique_ptr<base::Value> MediaInternalsProxy::GetConstants() {
-  auto event_phases = base::MakeUnique<base::DictionaryValue>();
-  event_phases->SetInteger(
-      net::NetLog::EventPhaseToString(net::NetLogEventPhase::NONE),
-      static_cast<int>(net::NetLogEventPhase::NONE));
-  event_phases->SetInteger(
-      net::NetLog::EventPhaseToString(net::NetLogEventPhase::BEGIN),
-      static_cast<int>(net::NetLogEventPhase::BEGIN));
-  event_phases->SetInteger(
-      net::NetLog::EventPhaseToString(net::NetLogEventPhase::END),
-      static_cast<int>(net::NetLogEventPhase::END));
-
-  auto constants = base::MakeUnique<base::DictionaryValue>();
-  constants->Set("eventTypes", net::NetLog::GetEventTypesAsValue());
-  constants->Set("eventPhases", std::move(event_phases));
-
-  return std::move(constants);
-}
-
-void MediaInternalsProxy::ObserveMediaInternalsOnIOThread() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (GetContentClient()->browser()->GetNetLog()) {
-    net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
-    net_log->DeprecatedAddObserver(
-        this, net::NetLogCaptureMode::IncludeCookiesAndCredentials());
-  }
-}
-
-void MediaInternalsProxy::StopObservingMediaInternalsOnIOThread() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (GetContentClient()->browser()->GetNetLog()) {
-    net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
-    net_log->DeprecatedRemoveObserver(this);
-  }
-}
-
 void MediaInternalsProxy::GetEverythingOnIOThread() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // TODO(xhwang): Investigate whether we can update on UI thread directly.
@@ -160,29 +81,6 @@
     handler_->OnUpdate(update);
 }
 
-void MediaInternalsProxy::AddNetEventOnUIThread(
-    std::unique_ptr<base::Value> entry) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // Send the updates to the page in kMediaInternalsProxyEventDelayMilliseconds
-  // if an update is not already pending.
-  if (!pending_net_updates_) {
-    pending_net_updates_.reset(new base::ListValue());
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&MediaInternalsProxy::SendNetEventsOnUIThread, this),
-        base::TimeDelta::FromMilliseconds(
-            kMediaInternalsProxyEventDelayMilliseconds));
-  }
-  pending_net_updates_->Append(std::move(entry));
-}
-
-void MediaInternalsProxy::SendNetEventsOnUIThread() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  CallJavaScriptFunctionOnUIThread("media.onNetUpdate",
-                                   std::move(pending_net_updates_));
-}
-
 void MediaInternalsProxy::CallJavaScriptFunctionOnUIThread(
     const std::string& function,
     std::unique_ptr<base::Value> args) {
diff --git a/content/browser/media/media_internals_proxy.h b/content/browser/media/media_internals_proxy.h
index a9dbba4..47b7db4d 100644
--- a/content/browser/media/media_internals_proxy.h
+++ b/content/browser/media/media_internals_proxy.h
@@ -14,12 +14,10 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
-#include "net/log/net_log.h"
 
 namespace base {
-class ListValue;
 class Value;
-}
+}  // namespace base
 
 namespace content {
 class MediaInternalsMessageHandler;
@@ -29,10 +27,8 @@
 // It is ref_counted to ensure that it completes all pending Tasks on both
 // threads before destruction.
 class MediaInternalsProxy
-    : public base::RefCountedThreadSafe<
-          MediaInternalsProxy,
-          BrowserThread::DeleteOnUIThread>,
-      public net::NetLog::ThreadSafeObserver,
+    : public base::RefCountedThreadSafe<MediaInternalsProxy,
+                                        BrowserThread::DeleteOnUIThread>,
       public NotificationObserver {
  public:
   MediaInternalsProxy();
@@ -51,36 +47,21 @@
   // Have MediaInternals send all the data it has.
   void GetEverything();
 
-  // net::NetLog::ThreadSafeObserver implementation. Callable from any thread:
-  void OnAddEntry(const net::NetLogEntry& entry) override;
-
  private:
   friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
   friend class base::DeleteHelper<MediaInternalsProxy>;
   ~MediaInternalsProxy() override;
 
-  // Build a dictionary mapping constant names to values.
-  std::unique_ptr<base::Value> GetConstants();
-
-  void ObserveMediaInternalsOnIOThread();
-  void StopObservingMediaInternalsOnIOThread();
   void GetEverythingOnIOThread();
 
   // Callback for MediaInternals to update. Must be called on UI thread.
   void UpdateUIOnUIThread(const base::string16& update);
 
-  // Put |entry| on a list of events to be sent to the page.
-  void AddNetEventOnUIThread(std::unique_ptr<base::Value> entry);
-
-  // Send all pending events to the page.
-  void SendNetEventsOnUIThread();
-
   // Call a JavaScript function on the page.
   void CallJavaScriptFunctionOnUIThread(const std::string& function,
                                         std::unique_ptr<base::Value> args);
 
   MediaInternalsMessageHandler* handler_;
-  std::unique_ptr<base::ListValue> pending_net_updates_;
   NotificationRegistrar registrar_;
   MediaInternals::UpdateCallback update_callback_;
 
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.cc b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
index dc58efa..07194411 100644
--- a/content/browser/renderer_host/media/service_launched_video_capture_device.cc
+++ b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
@@ -24,7 +24,8 @@
 void ServiceLaunchedVideoCaptureDevice::GetPhotoState(
     media::VideoCaptureDevice::GetPhotoStateCallback callback) const {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  NOTIMPLEMENTED();
+  // Not yet implemented on service side, but is called during
+  // JsLevelVideoCaptureBrowserTest. Do nothing here.
 }
 
 void ServiceLaunchedVideoCaptureDevice::SetPhotoOptions(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 2c2d83dc..51cd76d4 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -695,7 +695,14 @@
     std::map<ProcessID, Count>& counts_per_process = result->second;
     for (auto iter : counts_per_process) {
       RenderProcessHost* host = RenderProcessHost::FromID(iter.first);
-      DCHECK(host);
+      if (!host) {
+        // TODO(clamy): This shouldn't happen but we are getting reports from
+        // the field that this is happening. We need to figure out why some
+        // RenderProcessHosts are not taken out of the map when they're
+        // destroyed.
+        NOTREACHED();
+        continue;
+      }
       if (host->VisibleWidgetCount())
         foreground_processes->insert(host);
       else
@@ -2149,7 +2156,6 @@
     switches::kDomAutomationController,
     switches::kEnableBlinkFeatures,
     switches::kEnableBrowserSideNavigation,
-    switches::kEnableColorCorrectRendering,
     switches::kEnableDisplayList2dCanvas,
     switches::kEnableDistanceFieldText,
     switches::kEnableExperimentalCanvasFeatures,
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 2b9c1d5..006dab2 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -332,10 +332,8 @@
     force_srgb_image_decode_color_space = true;
   // When color correct rendering is enabled, the image_decode_color_space
   // parameter should not be used (and all users of it should be using sRGB).
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableColorCorrectRendering)) {
+  if (base::FeatureList::IsEnabled(features::kColorCorrectRendering))
     force_srgb_image_decode_color_space = true;
-  }
   if (force_srgb_image_decode_color_space) {
     gfx::ColorSpace::CreateSRGB().GetICCProfile(
         &params->image_decode_color_space);
@@ -514,7 +512,7 @@
       command_line.HasSwitch(switches::kMainFrameResizesAreOrientationChanges);
 
   prefs.color_correct_rendering_enabled =
-      command_line.HasSwitch(switches::kEnableColorCorrectRendering);
+      base::FeatureList::IsEnabled(features::kColorCorrectRendering);
 
   prefs.spatial_navigation_enabled = command_line.HasSwitch(
       switches::kEnableSpatialNavigation);
diff --git a/content/browser/resources/media/cache_entry.js b/content/browser/resources/media/cache_entry.js
deleted file mode 100644
index 2286fe03..0000000
--- a/content/browser/resources/media/cache_entry.js
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('media', function() {
-  'use strict';
-
-  /**
-   * This class represents a file cached by net.
-   */
-  function CacheEntry() {
-    this.read_ = new media.DisjointRangeSet;
-    this.written_ = new media.DisjointRangeSet;
-    this.available_ = new media.DisjointRangeSet;
-
-    // Set to true when we know the entry is sparse.
-    this.sparse = false;
-    this.key = null;
-    this.size = null;
-
-    // The <details> element representing this CacheEntry.
-    this.details_ = document.createElement('details');
-    this.details_.className = 'cache-entry';
-    this.details_.open = false;
-
-    // The <details> summary line. It contains a chart of requested file ranges
-    // and the url if we know it.
-    var summary = document.createElement('summary');
-
-    this.summaryText_ = document.createTextNode('');
-    summary.appendChild(this.summaryText_);
-
-    summary.appendChild(document.createTextNode(' '));
-
-    // Controls to modify this CacheEntry.
-    var controls = document.createElement('span');
-    controls.className = 'cache-entry-controls';
-    summary.appendChild(controls);
-    summary.appendChild(document.createElement('br'));
-
-    // A link to clear recorded data from this CacheEntry.
-    var clearControl = document.createElement('a');
-    clearControl.href = 'javascript:void(0)';
-    clearControl.onclick = this.clear.bind(this);
-    clearControl.textContent = '(clear entry)';
-    controls.appendChild(clearControl);
-
-    this.details_.appendChild(summary);
-
-    // The canvas for drawing cache writes.
-    this.writeCanvas = document.createElement('canvas');
-    this.writeCanvas.width = media.BAR_WIDTH;
-    this.writeCanvas.height = media.BAR_HEIGHT;
-    this.details_.appendChild(this.writeCanvas);
-
-    // The canvas for drawing cache reads.
-    this.readCanvas = document.createElement('canvas');
-    this.readCanvas.width = media.BAR_WIDTH;
-    this.readCanvas.height = media.BAR_HEIGHT;
-    this.details_.appendChild(this.readCanvas);
-
-    // A tabular representation of the data in the above canvas.
-    this.detailTable_ = document.createElement('table');
-    this.detailTable_.className = 'cache-table';
-    this.details_.appendChild(this.detailTable_);
-  }
-
-  CacheEntry.prototype = {
-    /**
-     * Mark a range of bytes as read from the cache.
-     * @param {int} start The first byte read.
-     * @param {int} length The number of bytes read.
-     */
-    readBytes: function(start, length) {
-      start = parseInt(start);
-      length = parseInt(length);
-      this.read_.add(start, start + length);
-      this.available_.add(start, start + length);
-      this.sparse = true;
-    },
-
-    /**
-     * Mark a range of bytes as written to the cache.
-     * @param {int} start The first byte written.
-     * @param {int} length The number of bytes written.
-     */
-    writeBytes: function(start, length) {
-      start = parseInt(start);
-      length = parseInt(length);
-      this.written_.add(start, start + length);
-      this.available_.add(start, start + length);
-      this.sparse = true;
-    },
-
-    /**
-     * Merge this CacheEntry with another, merging recorded ranges and flags.
-     * @param {CacheEntry} other The CacheEntry to merge into this one.
-     */
-    merge: function(other) {
-      this.read_.merge(other.read_);
-      this.written_.merge(other.written_);
-      this.available_.merge(other.available_);
-      this.sparse = this.sparse || other.sparse;
-      this.key = this.key || other.key;
-      this.size = this.size || other.size;
-    },
-
-    /**
-     * Clear all recorded ranges from this CacheEntry and redraw this.details_.
-     */
-    clear: function() {
-      this.read_ = new media.DisjointRangeSet;
-      this.written_ = new media.DisjointRangeSet;
-      this.available_ = new media.DisjointRangeSet;
-      this.generateDetails();
-    },
-
-    /**
-     * Helper for drawCacheReadsToCanvas() and drawCacheWritesToCanvas().
-     *
-     * Accepts the entries to draw, a canvas fill style, and the canvas to
-     * draw on.
-     */
-    drawCacheEntriesToCanvas: function(entries, fillStyle, canvas) {
-      // Don't bother drawing anything if we don't know the total size.
-      if (!this.size) {
-        return;
-      }
-
-      var width = canvas.width;
-      var height = canvas.height;
-      var context = canvas.getContext('2d');
-      var fileSize = this.size;
-
-      context.fillStyle = '#aaa';
-      context.fillRect(0, 0, width, height);
-
-      function drawRange(start, end) {
-        var left = start / fileSize * width;
-        var right = end / fileSize * width;
-        context.fillRect(left, 0, right - left, height);
-      }
-
-      context.fillStyle = fillStyle;
-      entries.map(function(start, end) {
-        drawRange(start, end);
-      });
-    },
-
-    /**
-     * Draw cache writes to the given canvas.
-     *
-     * It should consist of a horizontal bar with highlighted sections to
-     * represent which parts of a file have been written to the cache.
-     *
-     * e.g. |xxxxxx----------x|
-     */
-    drawCacheWritesToCanvas: function(canvas) {
-      this.drawCacheEntriesToCanvas(this.written_, '#00a', canvas);
-    },
-
-    /**
-     * Draw cache reads to the given canvas.
-     *
-     * It should consist of a horizontal bar with highlighted sections to
-     * represent which parts of a file have been read from the cache.
-     *
-     * e.g. |xxxxxx----------x|
-     */
-    drawCacheReadsToCanvas: function(canvas) {
-      this.drawCacheEntriesToCanvas(this.read_, '#0a0', canvas);
-    },
-
-    /**
-     * Update this.details_ to contain everything we currently know about
-     * this file.
-     */
-    generateDetails: function() {
-      function makeElement(tag, content) {
-        var toReturn = document.createElement(tag);
-        toReturn.textContent = content;
-        return toReturn;
-      }
-
-      this.details_.id = this.key;
-      this.summaryText_.textContent = this.key || 'Unknown File';
-
-      this.detailTable_.textContent = '';
-      var header = document.createElement('thead');
-      var footer = document.createElement('tfoot');
-      var body = document.createElement('tbody');
-      this.detailTable_.appendChild(header);
-      this.detailTable_.appendChild(footer);
-      this.detailTable_.appendChild(body);
-
-      var headerRow = document.createElement('tr');
-      headerRow.appendChild(makeElement('th', 'Read From Cache'));
-      headerRow.appendChild(makeElement('th', 'Written To Cache'));
-      header.appendChild(headerRow);
-
-      var footerRow = document.createElement('tr');
-      var footerCell = document.createElement('td');
-      footerCell.textContent = 'Out of ' + (this.size || 'unknown size');
-      footerCell.setAttribute('colspan', 2);
-      footerRow.appendChild(footerCell);
-      footer.appendChild(footerRow);
-
-      var read = this.read_.map(function(start, end) {
-        return start + ' - ' + end;
-      });
-      var written = this.written_.map(function(start, end) {
-        return start + ' - ' + end;
-      });
-
-      var length = Math.max(read.length, written.length);
-      for (var i = 0; i < length; i++) {
-        var row = document.createElement('tr');
-        row.appendChild(makeElement('td', read[i] || ''));
-        row.appendChild(makeElement('td', written[i] || ''));
-        body.appendChild(row);
-      }
-
-      this.drawCacheWritesToCanvas(this.writeCanvas);
-      this.drawCacheReadsToCanvas(this.readCanvas);
-    },
-
-    /**
-     * Render this CacheEntry as a <li>.
-     * @return {HTMLElement} A <li> representing this CacheEntry.
-     */
-    toListItem: function() {
-      this.generateDetails();
-
-      var result = document.createElement('li');
-      result.appendChild(this.details_);
-      return result;
-    }
-  };
-
-  return {
-    CacheEntry: CacheEntry
-  };
-});
diff --git a/content/browser/resources/media/client_renderer.js b/content/browser/resources/media/client_renderer.js
index adfa319a..3206ac9 100644
--- a/content/browser/resources/media/client_renderer.js
+++ b/content/browser/resources/media/client_renderer.js
@@ -24,11 +24,6 @@
     this.filterFunction = function() { return true; };
     this.filterText = document.getElementById('filter-text');
     this.filterText.onkeyup = this.onTextChange_.bind(this);
-
-    this.bufferCanvas = document.createElement('canvas');
-    this.bufferCanvas.width = media.BAR_WIDTH;
-    this.bufferCanvas.height = media.BAR_HEIGHT;
-
     this.clipboardDialog = document.getElementById('clipboard-dialog');
 
     this.clipboardTextarea = document.getElementById('clipboard-textarea');
@@ -55,7 +50,8 @@
   ClientRenderer.Css_ = {
     NO_PLAYERS_SELECTED: 'no-players-selected',
     NO_COMPONENTS_SELECTED: 'no-components-selected',
-    SELECTABLE_BUTTON: 'selectable-button'
+    SELECTABLE_BUTTON: 'selectable-button',
+    DESTRUCTED_PLAYER: 'destructed-player'
   };
 
   function removeChildren(element) {
@@ -64,7 +60,8 @@
     }
   };
 
-  function createSelectableButton(id, groupName, text, select_cb) {
+  function createSelectableButton(id, groupName, text, select_cb,
+                                  isDestructed) {
     // For CSS styling.
     var radioButton = document.createElement('input');
     radioButton.classList.add(ClientRenderer.Css_.SELECTABLE_BUTTON);
@@ -74,6 +71,8 @@
 
     var buttonLabel = document.createElement('label');
     buttonLabel.classList.add(ClientRenderer.Css_.SELECTABLE_BUTTON);
+    if (isDestructed)
+      buttonLabel.classList.add(ClientRenderer.Css_.DESTRUCTED_PLAYER);
     buttonLabel.setAttribute('for', radioButton.id);
     buttonLabel.appendChild(document.createTextNode(text));
 
@@ -177,11 +176,16 @@
       if (player === this.selectedPlayer) {
         this.drawProperties_(player.properties, this.playerPropertiesTable);
         this.drawLog_();
-        this.drawGraphs_();
       }
       if (key === 'name' || key === 'url') {
         this.redrawPlayerList_(players);
       }
+      if (key === 'event' && value === 'WEBMEDIAPLAYER_DESTROYED') {
+        player.destructed = true;
+        document.querySelector(
+            "label.selectable-button[for='" + player.id + "']").classList.add(
+                ClientRenderer.Css_.DESTRUCTED_PLAYER);
+      }
     },
 
     createVideoCaptureFormatTable: function(formats) {
@@ -351,7 +355,7 @@
         var li = document.createElement('li');
         var button_cb = this.selectPlayer_.bind(this, player);
         li.appendChild(createSelectableButton(
-            id, buttonGroupName, usableName, button_cb));
+            id, buttonGroupName, usableName, button_cb, player.destructed));
         fragment.appendChild(li);
       }
       removeChildren(this.playerListElement);
@@ -378,7 +382,6 @@
       removeChildren(this.logTable);
       removeChildren(this.graphElement);
       this.drawLog_();
-      this.drawGraphs_();
     },
 
     drawProperties_: function(propertyMap, propertiesTable) {
@@ -428,76 +431,6 @@
       downloadLog(JSON.stringify(strippedPlayers, null, 2));
     },
 
-    drawGraphs_: function() {
-      function addToGraphs(name, graph, graphElement) {
-        var li = document.createElement('li');
-        li.appendChild(graph);
-        li.appendChild(document.createTextNode(name));
-        graphElement.appendChild(li);
-      }
-
-      var url = this.selectedPlayer.properties.url;
-      if (!url) {
-        return;
-      }
-
-      var cache = media.cacheForUrl(url);
-
-      var player = this.selectedPlayer;
-      var props = player.properties;
-
-      var cacheExists = false;
-      var bufferExists = false;
-
-      if (props['buffer_start'] !== undefined &&
-          props['buffer_current'] !== undefined &&
-          props['buffer_end'] !== undefined &&
-          props['total_bytes'] !== undefined) {
-        this.drawBufferGraph_(props['buffer_start'],
-                              props['buffer_current'],
-                              props['buffer_end'],
-                              props['total_bytes']);
-        bufferExists = true;
-      }
-
-      if (cache) {
-        if (player.properties['total_bytes']) {
-          cache.size = Number(player.properties['total_bytes']);
-        }
-        cache.generateDetails();
-        cacheExists = true;
-
-      }
-
-      if (!this.graphElement.hasChildNodes()) {
-        if (bufferExists) {
-          addToGraphs('buffer', this.bufferCanvas, this.graphElement);
-        }
-        if (cacheExists) {
-          addToGraphs('cache read', cache.readCanvas, this.graphElement);
-          addToGraphs('cache write', cache.writeCanvas, this.graphElement);
-        }
-      }
-    },
-
-    drawBufferGraph_: function(start, current, end, size) {
-      var ctx = this.bufferCanvas.getContext('2d');
-      var width = this.bufferCanvas.width;
-      var height = this.bufferCanvas.height;
-      ctx.fillStyle = '#aaa';
-      ctx.fillRect(0, 0, width, height);
-
-      var scale_factor = width / size;
-      var left = start * scale_factor;
-      var middle = current * scale_factor;
-      var right = end * scale_factor;
-
-      ctx.fillStyle = '#a0a';
-      ctx.fillRect(left, 0, middle - left, height);
-      ctx.fillStyle = '#aa0';
-      ctx.fillRect(middle, 0, right - middle, height);
-    },
-
     showClipboard: function(string) {
       this.clipboardTextarea.value = string;
       this.clipboardDialog.showModal();
@@ -506,7 +439,8 @@
     },
 
     hideClipboard_: function() {
-      this.clipboardDialog.close();
+      if (this.clipboardDialog.open)
+        this.clipboardDialog.close();
     },
 
     copyToClipboard_: function() {
@@ -546,7 +480,6 @@
       if (this.selectedPlayer) {
         removeChildren(this.logTable);
         this.selectedPlayerLogIndex = 0;
-        this.drawLog_();
       }
     },
   };
diff --git a/content/browser/resources/media/disjoint_range_set.js b/content/browser/resources/media/disjoint_range_set.js
deleted file mode 100644
index bd504bb..0000000
--- a/content/browser/resources/media/disjoint_range_set.js
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('media', function() {
-
-  /**
-   * This class represents a collection of non-intersecting ranges. Ranges
-   * specified by (start, end) can be added and removed at will. It is used to
-   * record which sections of a media file have been cached, e.g. the first and
-   * last few kB plus several MB in the middle.
-   *
-   * Example usage:
-   * someRange.add(0, 100);     // Contains 0-100.
-   * someRange.add(150, 200);   // Contains 0-100, 150-200.
-   * someRange.remove(25, 75);  // Contains 0-24, 76-100, 150-200.
-   * someRange.add(25, 149);    // Contains 0-200.
-   */
-  function DisjointRangeSet() {
-    this.ranges_ = {};
-  }
-
-  DisjointRangeSet.prototype = {
-    /**
-     * Deletes all ranges intersecting with (start ... end) and returns the
-     * extents of the cleared area.
-     * @param {int} start The start of the range to remove.
-     * @param {int} end The end of the range to remove.
-     * @param {int} sloppiness 0 removes only strictly overlapping ranges, and
-     *                         1 removes adjacent ones.
-     * @return {Object} The start and end of the newly cleared range.
-     */
-    clearRange: function(start, end, sloppiness) {
-      var ranges = this.ranges_;
-      var result = {start: start, end: end};
-
-      for (var rangeStart in this.ranges_) {
-        rangeEnd = this.ranges_[rangeStart];
-        // A range intersects another if its start lies within the other range
-        // or vice versa.
-        if ((rangeStart >= start && rangeStart <= (end + sloppiness)) ||
-            (start >= rangeStart && start <= (rangeEnd + sloppiness))) {
-          delete ranges[rangeStart];
-          result.start = Math.min(result.start, rangeStart);
-          result.end = Math.max(result.end, rangeEnd);
-        }
-      }
-
-      return result;
-    },
-
-    /**
-     * Adds a range to this DisjointRangeSet.
-     * Joins adjacent and overlapping ranges together.
-     * @param {int} start The beginning of the range to add, inclusive.
-     * @param {int} end The end of the range to add, inclusive.
-     */
-    add: function(start, end) {
-      if (end < start)
-        return;
-
-      // Remove all touching ranges.
-      result = this.clearRange(start, end, 1);
-      // Add back a single contiguous range.
-      this.ranges_[Math.min(start, result.start)] = Math.max(end, result.end);
-    },
-
-    /**
-     * Combines a DisjointRangeSet with this one.
-     * @param {DisjointRangeSet} ranges A DisjointRangeSet to be squished into
-     * this one.
-     */
-    merge: function(other) {
-      var ranges = this;
-      other.forEach(function(start, end) { ranges.add(start, end); });
-    },
-
-    /**
-     * Removes a range from this DisjointRangeSet.
-     * Will split existing ranges if necessary.
-     * @param {int} start The beginning of the range to remove, inclusive.
-     * @param {int} end The end of the range to remove, inclusive.
-     */
-    remove: function(start, end) {
-      if (end < start)
-        return;
-
-      // Remove instersecting ranges.
-      result = this.clearRange(start, end, 0);
-
-      // Add back non-overlapping ranges.
-      if (result.start < start)
-        this.ranges_[result.start] = start - 1;
-      if (result.end > end)
-        this.ranges_[end + 1] = result.end;
-    },
-
-    /**
-     * Iterates over every contiguous range in this DisjointRangeSet, calling a
-     * function for each (start, end).
-     * @param {function(int, int)} iterator The function to call on each range.
-     */
-    forEach: function(iterator) {
-      for (var start in this.ranges_)
-        iterator(start, this.ranges_[start]);
-    },
-
-    /**
-     * Maps this DisjointRangeSet to an array by calling a given function on the
-     * start and end of each contiguous range, sorted by start.
-     * @param {function(int, int)} mapper Maps a range to an array element.
-     * @return {Array} An array of each mapper(range).
-     */
-    map: function(mapper) {
-      var starts = [];
-      for (var start in this.ranges_)
-        starts.push(parseInt(start));
-      starts.sort(function(a, b) {
-        return a - b;
-      });
-
-      var ranges = this.ranges_;
-      var results = starts.map(function(s) {
-        return mapper(s, ranges[s]);
-      });
-
-      return results;
-    },
-
-    /**
-     * Finds the maximum value present in any of the contained ranges.
-     * @return {int} The maximum value contained by this DisjointRangeSet.
-     */
-    max: function() {
-      var max = -Infinity;
-      for (var start in this.ranges_)
-        max = Math.max(max, this.ranges_[start]);
-      return max;
-    },
-  };
-
-  return {
-    DisjointRangeSet: DisjointRangeSet
-  };
-});
diff --git a/content/browser/resources/media/disjoint_range_set_test.html b/content/browser/resources/media/disjoint_range_set_test.html
deleted file mode 100644
index 424fc65..0000000
--- a/content/browser/resources/media/disjoint_range_set_test.html
+++ /dev/null
@@ -1,96 +0,0 @@
-<!doctype html>
-<html>
-<!--
-Copyright (c) 2011 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.
--->
-  <head>
-    <title></title>
-    <script src="https://cdn.rawgit.com/google/closure-library/master/closure/goog/base.js"></script>
-    <script src="../../../../ui/webui/resources/js/cr.js"></script>
-    <script src="disjoint_range_set.js"></script>
-    <script>
-      goog.require('goog.testing.jsunit');
-    </script>
-  </head>
-  <body>
-    <script>
-
-      var range;
-
-      function assertRangeEquals(ranges) {
-        assertArrayEquals(
-        ranges, range.map(function(start, end) { return [start, end]; }));
-      };
-
-      function setUp() {
-        range = new media.DisjointRangeSet;
-      };
-
-      function testAdd() {
-        range.add(1, 6);
-        assertRangeEquals([[1, 6]]);
-        range.add(-5, -3);
-        assertRangeEquals([[-5, -3], [1, 6]]);
-      };
-
-      function testAddAdjacent() {
-        range.add(3, 6);
-        assertRangeEquals([[3, 6]]);
-        range.add(1, 2);
-        assertRangeEquals([[1, 6]]);
-        range.add(7, 9);
-        assertRangeEquals([[1, 9]]);
-      };
-
-      function testAddNotQuiteAdjacent() {
-        range.add(3, 6);
-        assertRangeEquals([[3, 6]]);
-        range.add(0, 1);
-        assertRangeEquals([[0, 1], [3, 6]]);
-        range.add(8, 9);
-        assertRangeEquals([[0, 1], [3, 6], [8, 9]]);
-      };
-
-      function testAddOverlapping() {
-        range.add(1, 6);
-        assertRangeEquals([[1, 6]]);
-        range.add(5, 8);
-        assertRangeEquals([[1, 8]]);
-        range.add(0, 1);
-        assertRangeEquals([[0, 8]]);
-      };
-
-      function testMax() {
-        assertNull(range.max());
-        range.add(1, 6);
-        assertEquals(range.max(), 6);
-        range.add(3, 8);
-        assertEquals(range.max(), 8);
-        range.remove(2, 3);
-        assertEquals(range.max(), 8);
-        range.remove(4, 10);
-        assertEquals(range.max(), 1);
-        range.remove(1, 1);
-        assertNull(range.max());
-      };
-
-      function testRemove() {
-        range.add(1, 20);
-        assertRangeEquals([[1, 20]]);
-        range.remove(0, 3);
-        assertRangeEquals([[4, 20]]);
-        range.remove(18, 20);
-        assertRangeEquals([[4, 17]]);
-        range.remove(5, 16);
-        assertRangeEquals([[4, 4], [17, 17]]);
-      };
-
-      function testStartsEmpty() {
-        assertRangeEquals([]);
-      };
-
-    </script>
-  </body>
-</html>
diff --git a/content/browser/resources/media/main.js b/content/browser/resources/media/main.js
index 5ca4bed..65bbaca 100644
--- a/content/browser/resources/media/main.js
+++ b/content/browser/resources/media/main.js
@@ -10,28 +10,6 @@
 
   var manager = null;
 
-  // A number->string mapping that is populated through the backend that
-  // describes the phase that the network entity is in.
-  var eventPhases = {};
-
-  // A number->string mapping that is populated through the backend that
-  // describes the type of event sent from the network.
-  var eventTypes = {};
-
-  // A mapping of number->CacheEntry where the number is a unique id for that
-  // network request.
-  var cacheEntries = {};
-
-  // A mapping of url->CacheEntity where the url is the url of the resource.
-  var cacheEntriesByKey = {};
-
-  var requrestURLs = {};
-
-  var media = {
-    BAR_WIDTH: 200,
-    BAR_HEIGHT: 25
-  };
-
   /**
    * Users of |media| must call initialize prior to calling other methods.
    */
@@ -49,76 +27,6 @@
     manager.updateVideoCaptureCapabilities(videoCaptureCapabilities)
   }
 
-  media.onReceiveConstants = function(constants) {
-    for (var key in constants.eventTypes) {
-      var value = constants.eventTypes[key];
-      eventTypes[value] = key;
-    }
-
-    for (var key in constants.eventPhases) {
-      var value = constants.eventPhases[key];
-      eventPhases[value] = key;
-    }
-  };
-
-  media.cacheForUrl = function(url) {
-    return cacheEntriesByKey[url];
-  };
-
-  media.onNetUpdate = function(updates) {
-    updates.forEach(function(update) {
-      var id = update.source.id;
-      if (!cacheEntries[id])
-        cacheEntries[id] = new media.CacheEntry;
-
-      switch (eventPhases[update.phase] + '.' + eventTypes[update.type]) {
-        case 'PHASE_BEGIN.DISK_CACHE_ENTRY_IMPL':
-          var key = update.params.key;
-
-          // Merge this source with anything we already know about this key.
-          if (cacheEntriesByKey[key]) {
-            cacheEntriesByKey[key].merge(cacheEntries[id]);
-            cacheEntries[id] = cacheEntriesByKey[key];
-          } else {
-            cacheEntriesByKey[key] = cacheEntries[id];
-          }
-          cacheEntriesByKey[key].key = key;
-          break;
-
-        case 'PHASE_BEGIN.SPARSE_READ':
-          cacheEntries[id].readBytes(update.params.offset,
-                                      update.params.buff_len);
-          cacheEntries[id].sparse = true;
-          break;
-
-        case 'PHASE_BEGIN.SPARSE_WRITE':
-          cacheEntries[id].writeBytes(update.params.offset,
-                                       update.params.buff_len);
-          cacheEntries[id].sparse = true;
-          break;
-
-        case 'PHASE_BEGIN.URL_REQUEST_START_JOB':
-          requrestURLs[update.source.id] = update.params.url;
-          break;
-
-        case 'PHASE_NONE.HTTP_TRANSACTION_READ_RESPONSE_HEADERS':
-          // Record the total size of the file if this was a range request.
-          var range = /content-range:\s*bytes\s*\d+-\d+\/(\d+)/i.exec(
-              update.params.headers);
-          var key = requrestURLs[update.source.id];
-          delete requrestURLs[update.source.id];
-          if (range && key) {
-            if (!cacheEntriesByKey[key]) {
-              cacheEntriesByKey[key] = new media.CacheEntry;
-              cacheEntriesByKey[key].key = key;
-            }
-            cacheEntriesByKey[key].size = range[1];
-          }
-          break;
-      }
-    });
-  };
-
   media.onRendererTerminated = function(renderId) {
     util.object.forEach(manager.players_, function(playerInfo, id) {
       if (playerInfo.properties['render_id'] == renderId) {
@@ -159,18 +67,7 @@
     var propertyCount = 0;
     util.object.forEach(event.params, function(value, key) {
       key = key.trim();
-
-      // These keys get spammed *a lot*, so put them on the display
-      // but don't log list.
-      if (key === 'buffer_start' ||
-          key === 'buffer_end' ||
-          key === 'buffer_current' ||
-          key === 'is_downloading_data') {
-        manager.updatePlayerInfoNoRecord(
-            source, event.ticksMillis, key, value);
-      } else {
-        manager.updatePlayerInfo(source, event.ticksMillis, key, value);
-      }
+      manager.updatePlayerInfo(source, event.ticksMillis, key, value);
       propertyCount += 1;
     });
 
diff --git a/content/browser/resources/media/media_internals.css b/content/browser/resources/media/media_internals.css
index 33f969d..372d2a8 100644
--- a/content/browser/resources/media/media_internals.css
+++ b/content/browser/resources/media/media_internals.css
@@ -193,6 +193,10 @@
   color: white;
 }
 
+label.destructed-player {
+  background-color: #E4854A;
+}
+
 .no-players-selected #players .property-wrapper,
 .no-players-selected #players #log-wrapper {
   display: none;
diff --git a/content/browser/resources/media/media_internals.js b/content/browser/resources/media/media_internals.js
index cad5797..b285a4c 100644
--- a/content/browser/resources/media/media_internals.js
+++ b/content/browser/resources/media/media_internals.js
@@ -6,8 +6,6 @@
 
 // <include src="main.js">
 // <include src="util.js">
-// <include src="cache_entry.js">
-// <include src="disjoint_range_set.js">
 // <include src="player_info.js">
 // <include src="manager.js">
 // <include src="client_renderer.js">
diff --git a/content/browser/webrtc/webrtc_video_capture_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_browsertest.cc
new file mode 100644
index 0000000..884551c
--- /dev/null
+++ b/content/browser/webrtc/webrtc_video_capture_browsertest.cc
@@ -0,0 +1,99 @@
+// 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 "base/command_line.h"
+#include "build/build_config.h"
+#include "content/browser/webrtc/webrtc_webcam_browsertest.h"
+#include "content/public/browser/browser_child_process_host.h"
+#include "content/public/common/child_process_host.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/service_manager_connection.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "media/base/media_switches.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/video_capture/public/cpp/constants.h"
+#include "services/video_capture/public/interfaces/constants.mojom.h"
+#include "services/video_capture/public/interfaces/testing_controls.mojom.h"
+
+namespace content {
+
+#if defined(OS_ANDROID)
+// Mojo video capture is currently not supported on Android
+// TODO(chfremer): Enable as soon as https://crbug.com/720500 is resolved.
+#define MAYBE_RecoverFromCrashInVideoCaptureProcess \
+  DISABLED_RecoverFromCrashInVideoCaptureProcess
+#else
+#define MAYBE_RecoverFromCrashInVideoCaptureProcess \
+  RecoverFromCrashInVideoCaptureProcess
+#endif  // defined(OS_ANDROID)
+
+namespace {
+
+static const char kVideoCaptureHtmlFile[] = "/media/video_capture_test.html";
+static const char kStartVideoCaptureAndVerifySize[] =
+    "startVideoCaptureAndVerifySize()";
+static const char kWaitForVideoToTurnBlack[] = "waitForVideoToTurnBlack()";
+static const char kVerifyHasReceivedTrackEndedEvent[] =
+    "verifyHasReceivedTrackEndedEvent()";
+
+}  // anonymous namespace
+
+// Integration test that exercises video capture functionality from the
+// JavaScript level.
+class WebRtcVideoCaptureBrowserTest : public ContentBrowserTest {
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
+    command_line->AppendSwitchASCII(switches::kEnableFeatures,
+                                    video_capture::kMojoVideoCapture.name);
+    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kEnableBlinkFeatures, "GetUserMedia");
+  }
+
+  void SetUp() override {
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+    EnablePixelOutput();
+    ContentBrowserTest::SetUp();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(WebRtcVideoCaptureBrowserTest,
+                       MAYBE_RecoverFromCrashInVideoCaptureProcess) {
+  embedded_test_server()->StartAcceptingConnections();
+  GURL url(embedded_test_server()->GetURL(kVideoCaptureHtmlFile));
+  NavigateToURL(shell(), url);
+
+  std::string result;
+  // Start video capture and wait until it started rendering
+  ASSERT_TRUE(ExecuteScriptAndExtractString(
+      shell(), kStartVideoCaptureAndVerifySize, &result));
+  ASSERT_EQ("OK", result);
+
+  // Simulate crash in video capture process
+  service_manager::Connector* connector =
+      ServiceManagerConnection::GetForProcess()->GetConnector();
+  video_capture::mojom::TestingControlsPtr service_controls;
+  connector->BindInterface(video_capture::mojom::kServiceName,
+                           mojo::MakeRequest(&service_controls));
+  service_controls->Crash();
+
+  // Wait for video element to turn black
+  ASSERT_TRUE(ExecuteScriptAndExtractString(shell(), kWaitForVideoToTurnBlack,
+                                            &result));
+  ASSERT_EQ("OK", result);
+  ASSERT_TRUE(ExecuteScriptAndExtractString(
+      shell(), kVerifyHasReceivedTrackEndedEvent, &result));
+  ASSERT_EQ("OK", result);
+
+  // Start capturing again and expect it to work.
+  ASSERT_TRUE(ExecuteScriptAndExtractString(
+      shell(), kStartVideoCaptureAndVerifySize, &result));
+  ASSERT_EQ("OK", result);
+}
+
+}  // namespace content
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc
index a2d633f7..f1fc6813 100644
--- a/content/child/resource_dispatcher.cc
+++ b/content/child/resource_dispatcher.cc
@@ -670,7 +670,7 @@
     std::unique_ptr<ThrottlingURLLoader> url_loader =
         ThrottlingURLLoader::CreateLoaderAndStart(
             url_loader_factory, std::move(throttles), routing_id, request_id,
-            mojom::kURLLoadOptionNone, std::move(request), client.get(),
+            mojom::kURLLoadOptionNone, *request, client.get(),
             net::MutableNetworkTrafficAnnotationTag(NO_TRAFFIC_ANNOTATION_YET),
             std::move(task_runner));
     pending_requests_[request_id]->url_loader = std::move(url_loader);
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 1987286..738e06d 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -263,6 +263,7 @@
     "platform_notification_messages.h",
     "plugin_list.cc",
     "plugin_list.h",
+    "possibly_associated_interface_ptr.h",
     "presentation/presentation_struct_traits.cc",
     "presentation/presentation_struct_traits.h",
     "process_type.cc",
diff --git a/content/common/media/cdm_host_files.cc b/content/common/media/cdm_host_files.cc
index c3ce8e6..49c5562 100644
--- a/content/common/media/cdm_host_files.cc
+++ b/content/common/media/cdm_host_files.cc
@@ -134,8 +134,9 @@
   return cdm_host_files;
 }
 
-bool CdmHostFiles::InitVerification(base::NativeLibrary cdm_adapter_library,
-                                    const base::FilePath& cdm_adapter_path) {
+CdmHostFiles::Status CdmHostFiles::InitVerification(
+    base::NativeLibrary cdm_adapter_library,
+    const base::FilePath& cdm_adapter_path) {
   DVLOG(1) << __func__;
   DCHECK(cdm_adapter_library);
 
@@ -161,7 +162,7 @@
   if (!scoped_cdm_library.is_valid()) {
     LOG(ERROR) << "Failed to load CDM (error: " << error.ToString() << ")";
     CloseAllFiles();
-    return true;
+    return Status::kCdmLoadFailed;
   }
   cdm_library = scoped_cdm_library.get();
 #endif
@@ -173,7 +174,7 @@
   if (!init_verification_func) {
     LOG(ERROR) << "Function " << kInitVerificationFuncName << " not found.";
     CloseAllFiles();
-    return true;
+    return Status::kGetFunctionFailed;
   }
 
   // Fills |cdm_host_files| with common and CDM specific files for
@@ -199,12 +200,12 @@
   if (!init_verification_func(cdm_host_files_ptr, cdm_host_files.size())) {
     DVLOG(1) << "Failed to verify CDM host.";
     CloseAllFiles();
-    return false;
+    return Status::kInitVerificationFailed;
   }
 
   // Close all files not passed to the CDM.
   CloseAllFiles();
-  return true;
+  return Status::kSuccess;
 }
 
 #if defined(POSIX_WITH_ZYGOTE)
diff --git a/content/common/media/cdm_host_files.h b/content/common/media/cdm_host_files.h
index 48311c1f..a1f5849 100644
--- a/content/common/media/cdm_host_files.h
+++ b/content/common/media/cdm_host_files.h
@@ -59,15 +59,23 @@
   static std::unique_ptr<CdmHostFiles> Create(
       const base::FilePath& cdm_adapter_path);
 
+  // Status of CDM host verification.
+  // Note: Reported to UMA. Do not change the values.
+  enum class Status {
+    kNotCalled = 0,
+    kSuccess = 1,
+    kCdmLoadFailed = 2,
+    kGetFunctionFailed = 3,
+    kInitVerificationFailed = 4,
+    kStatusCount
+  };
+
   // Initializes the verification of CDM files by calling the function exported
   // by the CDM. If unexpected error happens, all files will be closed.
   // Otherwise, the PlatformFiles are passed to the CDM which will close the
   // files later.
-  // Only returns false if the CDM returns false (when there's an immediate
-  // failure). Otherwise always returns true for backward compatibility, e.g.
-  // when using an old CDM which doesn't implement the verification API.
-  bool InitVerification(base::NativeLibrary cdm_adapter_library,
-                        const base::FilePath& cdm_adapter_path);
+  Status InitVerification(base::NativeLibrary cdm_adapter_library,
+                          const base::FilePath& cdm_adapter_path);
 
  private:
 #if defined(POSIX_WITH_ZYGOTE)
diff --git a/content/renderer/possibly_associated_interface_ptr.h b/content/common/possibly_associated_interface_ptr.h
similarity index 90%
rename from content/renderer/possibly_associated_interface_ptr.h
rename to content/common/possibly_associated_interface_ptr.h
index 9e30892b..4d1bf57e 100644
--- a/content/renderer/possibly_associated_interface_ptr.h
+++ b/content/common/possibly_associated_interface_ptr.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_POSSIBLY_ASSOCIATED_INTERFACE_PTR_H_
-#define CONTENT_RENDERER_POSSIBLY_ASSOCIATED_INTERFACE_PTR_H_
+#ifndef CONTENT_COMMON_POSSIBLY_ASSOCIATED_INTERFACE_PTR_H_
+#define CONTENT_COMMON_POSSIBLY_ASSOCIATED_INTERFACE_PTR_H_
 
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
@@ -54,4 +54,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_RENDERER_POSSIBLY_ASSOCIATED_INTERFACE_PTR_H_
+#endif  // CONTENT_COMMON_POSSIBLY_ASSOCIATED_INTERFACE_PTR_H_
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index 52614ef5..8fed870 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -13,13 +13,15 @@
     int32_t in_routing_id,
     int32_t in_request_id,
     uint32_t in_options,
-    std::unique_ptr<ResourceRequest> in_url_request,
+    StartLoaderCallback in_start_loader_callback,
+    const ResourceRequest& in_url_request,
     scoped_refptr<base::SingleThreadTaskRunner> in_task_runner)
     : url_loader_factory(in_url_loader_factory),
       routing_id(in_routing_id),
       request_id(in_request_id),
       options(in_options),
-      url_request(std::move(in_url_request)),
+      start_loader_callback(std::move(in_start_loader_callback)),
+      url_request(in_url_request),
       task_runner(std::move(in_task_runner)) {}
 
 ThrottlingURLLoader::StartInfo::~StartInfo() = default;
@@ -55,14 +57,29 @@
     int32_t routing_id,
     int32_t request_id,
     uint32_t options,
-    std::unique_ptr<ResourceRequest> url_request,
+    const ResourceRequest& url_request,
     mojom::URLLoaderClient* client,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   std::unique_ptr<ThrottlingURLLoader> loader(new ThrottlingURLLoader(
       std::move(throttles), client, traffic_annotation));
-  loader->Start(factory, routing_id, request_id, options,
-                std::move(url_request), std::move(task_runner));
+  loader->Start(factory, routing_id, request_id, options, StartLoaderCallback(),
+                url_request, std::move(task_runner));
+  return loader;
+}
+
+// static
+std::unique_ptr<ThrottlingURLLoader> ThrottlingURLLoader::CreateLoaderAndStart(
+    StartLoaderCallback start_loader_callback,
+    std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
+    const ResourceRequest& url_request,
+    mojom::URLLoaderClient* client,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  std::unique_ptr<ThrottlingURLLoader> loader(new ThrottlingURLLoader(
+      std::move(throttles), client, net::MutableNetworkTrafficAnnotationTag()));
+  loader->Start(nullptr, 0, 0, mojom::kURLLoadOptionNone,
+                std::move(start_loader_callback), url_request,
+                std::move(task_runner));
   return loader;
 }
 
@@ -106,32 +123,61 @@
     int32_t routing_id,
     int32_t request_id,
     uint32_t options,
-    std::unique_ptr<ResourceRequest> url_request,
+    StartLoaderCallback start_loader_callback,
+    const ResourceRequest& url_request,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
   DCHECK(!cancelled_by_throttle_);
 
   if (throttle_) {
     bool deferred = false;
-    throttle_->WillStartRequest(url_request->url, url_request->load_flags,
-                                url_request->resource_type, &deferred);
+    throttle_->WillStartRequest(url_request.url, url_request.load_flags,
+                                url_request.resource_type, &deferred);
     if (cancelled_by_throttle_)
       return;
 
     if (deferred) {
       deferred_stage_ = DEFERRED_START;
-      start_info_ = base::MakeUnique<StartInfo>(factory, routing_id, request_id,
-                                                options, std::move(url_request),
-                                                std::move(task_runner));
+      start_info_ =
+          base::MakeUnique<StartInfo>(factory, routing_id, request_id, options,
+                                      std::move(start_loader_callback),
+                                      url_request, std::move(task_runner));
       return;
     }
   }
 
+  StartNow(factory, routing_id, request_id, options,
+           std::move(start_loader_callback), url_request,
+           std::move(task_runner));
+}
+
+void ThrottlingURLLoader::StartNow(
+    mojom::URLLoaderFactory* factory,
+    int32_t routing_id,
+    int32_t request_id,
+    uint32_t options,
+    StartLoaderCallback start_loader_callback,
+    const ResourceRequest& url_request,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   mojom::URLLoaderClientPtr client;
   client_binding_.Bind(mojo::MakeRequest(&client), std::move(task_runner));
-  factory->CreateLoaderAndStart(mojo::MakeRequest(&url_loader_), routing_id,
-                                request_id, options, *url_request,
-                                std::move(client), traffic_annotation_);
+
+  if (factory) {
+    DCHECK(!start_loader_callback);
+
+    mojom::URLLoaderAssociatedPtr url_loader;
+    auto url_loader_request = mojo::MakeRequest(&url_loader);
+    url_loader_ = std::move(url_loader);
+    factory->CreateLoaderAndStart(std::move(url_loader_request), routing_id,
+                                  request_id, options, url_request,
+                                  std::move(client), traffic_annotation_);
+  } else {
+    mojom::URLLoaderPtr url_loader;
+    auto url_loader_request = mojo::MakeRequest(&url_loader);
+    url_loader_ = std::move(url_loader);
+    std::move(start_loader_callback)
+        .Run(std::move(url_loader_request), std::move(client));
+  }
 }
 
 void ThrottlingURLLoader::OnReceiveResponse(
@@ -248,7 +294,7 @@
 
   deferred_stage_ = DEFERRED_NONE;
   client_binding_.Close();
-  url_loader_.reset();
+  url_loader_ = nullptr;
 
   forwarding_client_->OnComplete(request_complete_data);
 }
@@ -259,13 +305,10 @@
 
   switch (deferred_stage_) {
     case DEFERRED_START: {
-      mojom::URLLoaderClientPtr client;
-      client_binding_.Bind(
-          mojo::MakeRequest(&client), std::move(start_info_->task_runner));
-      start_info_->url_loader_factory->CreateLoaderAndStart(
-          mojo::MakeRequest(&url_loader_), start_info_->routing_id,
-          start_info_->request_id, start_info_->options,
-          *start_info_->url_request, std::move(client), traffic_annotation_);
+      StartNow(start_info_->url_loader_factory, start_info_->routing_id,
+               start_info_->request_id, start_info_->options,
+               std::move(start_info_->start_loader_callback),
+               start_info_->url_request, std::move(start_info_->task_runner));
 
       if (priority_info_) {
         auto priority_info = std::move(priority_info_);
diff --git a/content/common/throttling_url_loader.h b/content/common/throttling_url_loader.h
index 00f603c..11e34a2 100644
--- a/content/common/throttling_url_loader.h
+++ b/content/common/throttling_url_loader.h
@@ -7,9 +7,11 @@
 
 #include <memory>
 
+#include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/common/content_export.h"
+#include "content/common/possibly_associated_interface_ptr.h"
 #include "content/common/url_loader.mojom.h"
 #include "content/common/url_loader_factory.mojom.h"
 #include "content/public/common/url_loader_throttle.h"
@@ -33,18 +35,35 @@
  public:
   // |factory| and |client| must stay alive during the lifetime of the returned
   // object.
+  // Please note that the request may not start immediately since it could be
+  // deferred by throttles.
   static std::unique_ptr<ThrottlingURLLoader> CreateLoaderAndStart(
       mojom::URLLoaderFactory* factory,
       std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
       int32_t routing_id,
       int32_t request_id,
       uint32_t options,
-      std::unique_ptr<ResourceRequest> url_request,
+      const ResourceRequest& url_request,
       mojom::URLLoaderClient* client,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner =
           base::ThreadTaskRunnerHandle::Get());
 
+  using StartLoaderCallback =
+      base::OnceCallback<void(mojom::URLLoaderRequest request,
+                              mojom::URLLoaderClientPtr client)>;
+
+  // Similar to the method above, but uses a |start_loader_callback| instead of
+  // a mojom::URLLoaderFactory to start the loader. The callback must be safe
+  // to call during the lifetime of the returned object.
+  static std::unique_ptr<ThrottlingURLLoader> CreateLoaderAndStart(
+      StartLoaderCallback start_loader_callback,
+      std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
+      const ResourceRequest& url_request,
+      mojom::URLLoaderClient* client,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+          base::ThreadTaskRunnerHandle::Get());
+
   ~ThrottlingURLLoader() override;
 
   void FollowRedirect();
@@ -56,13 +75,25 @@
       mojom::URLLoaderClient* client,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation);
 
+  // Either of the two sets of arguments below is valid but not both:
+  // - |factory|, |routing_id|, |request_id| and |options|;
+  // - |start_loader_callback|.
   void Start(mojom::URLLoaderFactory* factory,
              int32_t routing_id,
              int32_t request_id,
              uint32_t options,
-             std::unique_ptr<ResourceRequest> url_request,
+             StartLoaderCallback start_loader_callback,
+             const ResourceRequest& url_request,
              scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
+  void StartNow(mojom::URLLoaderFactory* factory,
+                int32_t routing_id,
+                int32_t request_id,
+                uint32_t options,
+                StartLoaderCallback start_loader_callback,
+                const ResourceRequest& url_request,
+                scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
   // mojom::URLLoaderClient implementation:
   void OnReceiveResponse(const ResourceResponseHead& response_head,
                          const base::Optional<net::SSLInfo>& ssl_info,
@@ -97,14 +128,15 @@
   mojom::URLLoaderClient* forwarding_client_;
   mojo::Binding<mojom::URLLoaderClient> client_binding_;
 
-  mojom::URLLoaderAssociatedPtr url_loader_;
+  PossiblyAssociatedInterfacePtr<mojom::URLLoader> url_loader_;
 
   struct StartInfo {
     StartInfo(mojom::URLLoaderFactory* in_url_loader_factory,
               int32_t in_routing_id,
               int32_t in_request_id,
               uint32_t in_options,
-              std::unique_ptr<ResourceRequest> in_url_request,
+              StartLoaderCallback in_start_loader_callback,
+              const ResourceRequest& in_url_request,
               scoped_refptr<base::SingleThreadTaskRunner> in_task_runner);
     ~StartInfo();
 
@@ -112,7 +144,10 @@
     int32_t routing_id;
     int32_t request_id;
     uint32_t options;
-    std::unique_ptr<ResourceRequest> url_request;
+
+    StartLoaderCallback start_loader_callback;
+
+    ResourceRequest url_request;
     // |task_runner_| is used to set up |client_binding_|.
     scoped_refptr<base::SingleThreadTaskRunner> task_runner;
   };
diff --git a/content/common/throttling_url_loader_unittest.cc b/content/common/throttling_url_loader_unittest.cc
index fa0355a..cf9913c 100644
--- a/content/common/throttling_url_loader_unittest.cc
+++ b/content/common/throttling_url_loader_unittest.cc
@@ -208,11 +208,11 @@
   }
 
   void CreateLoaderAndStart() {
-    auto request = base::MakeUnique<ResourceRequest>();
-    request->url = GURL("http://example.org");
+    ResourceRequest request;
+    request.url = GURL("http://example.org");
     loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
-        factory_.factory_ptr().get(), std::move(throttles_), 0, 0, 0,
-        std::move(request), &client_,
+        factory_.factory_ptr().get(), std::move(throttles_), 0, 0, 0, request,
+        &client_,
         net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
     factory_.factory_ptr().FlushForTesting();
   }
diff --git a/content/ppapi_plugin/ppapi_thread.cc b/content/ppapi_plugin/ppapi_thread.cc
index 39c079d..bbdebce 100644
--- a/content/ppapi_plugin/ppapi_thread.cc
+++ b/content/ppapi_plugin/ppapi_thread.cc
@@ -362,6 +362,7 @@
   // Use a local instance of CdmHostFiles so that if we return early for any
   // error, all files will closed automatically.
   std::unique_ptr<CdmHostFiles> cdm_host_files;
+  CdmHostFiles::Status cdm_status = CdmHostFiles::Status::kNotCalled;
 
 #if defined(OS_WIN) || defined(OS_MACOSX)
   // Open CDM host files before the process is sandboxed.
@@ -377,8 +378,11 @@
   // On Windows, initialize CDM host verification unsandboxed. On other
   // platforms, this is called sandboxed below.
   if (cdm_host_files) {
-    DCHECK(IsCdm(path));
-    if (!cdm_host_files->InitVerification(library.get(), path)) {
+    DCHECK(!is_broker_ && IsCdm(path));
+    cdm_status = cdm_host_files->InitVerification(library.get(), path);
+    // Ignore other failures for backward compatibility, e.g. when using an old
+    // CDM which doesn't implement the verification API.
+    if (cdm_status == CdmHostFiles::Status::kInitVerificationFailed) {
       LOG(WARNING) << "CDM host verification failed.";
       // TODO(xhwang): Add a new load result if needed.
       ReportLoadResult(path, INIT_FAILED);
@@ -460,17 +464,26 @@
     CHECK(InitializeSandbox());
 #endif
 
-#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION) && !defined(OS_WIN)
+#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
+#if !defined(OS_WIN)
     // Now we are sandboxed, initialize CDM host verification.
     if (cdm_host_files) {
-      DCHECK(IsCdm(path));
-      if (!cdm_host_files->InitVerification(library.get(), path)) {
+      DCHECK(!is_broker_ && IsCdm(path));
+      cdm_status = cdm_host_files->InitVerification(library.get(), path);
+      // Ignore other failures for backward compatibility, e.g. when using an
+      // old CDM which doesn't implement the verification API.
+      if (cdm_status == CdmHostFiles::Status::kInitVerificationFailed) {
         LOG(WARNING) << "CDM host verification failed.";
         // TODO(xhwang): Add a new load result if needed.
         ReportLoadResult(path, INIT_FAILED);
         return;
       }
     }
+#endif  // !defined(OS_WIN)
+    if (!is_broker_ && IsCdm(path)) {
+      UMA_HISTOGRAM_ENUMERATION("Media.EME.CdmHostVerificationStatus",
+                                cdm_status, CdmHostFiles::Status::kStatusCount);
+    }
 #endif  // BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION) && !defined(OS_WIN)
 
     int32_t init_error = plugin_entry_points_.initialize_module(
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index cfedb98a..0326e861 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -86,7 +86,7 @@
 
         ],
         "resource_coordinator": [ "coordination_unit" ],
-        "video_capture": [ "capture" ]
+        "video_capture": [ "capture", "tests" ]
       }
     },
     "navigation:frame": {
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 2c4c7000..330e772 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -15,6 +15,7 @@
 #include "content/public/browser/navigation_ui_data.h"
 #include "content/public/browser/vpn_service_proxy.h"
 #include "content/public/common/sandbox_type.h"
+#include "content/public/common/url_loader_throttle.h"
 #include "media/audio/audio_manager.h"
 #include "media/base/cdm_factory.h"
 #include "media/media_features.h"
@@ -459,4 +460,10 @@
   return nullptr;
 }
 
+std::vector<std::unique_ptr<URLLoaderThrottle>>
+ContentBrowserClient::CreateURLLoaderThrottles(
+    const base::Callback<WebContents*()>& wc_getter) {
+  return std::vector<std::unique_ptr<URLLoaderThrottle>>();
+}
+
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index aa5e8ce9..758773b25 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -30,6 +30,7 @@
 #include "media/mojo/interfaces/remoting.mojom.h"
 #include "net/base/mime_util.h"
 #include "net/cookies/canonical_cookie.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/quota/quota_manager.h"
 #include "third_party/WebKit/public/platform/WebPageVisibilityState.h"
@@ -67,7 +68,6 @@
 }
 
 namespace service_manager {
-class BinderRegistry;
 class Service;
 struct BindSourceInfo;
 }
@@ -129,6 +129,7 @@
 class SpeechRecognitionManagerDelegate;
 class StoragePartition;
 class TracingDelegate;
+class URLLoaderThrottle;
 class VpnServiceProxy;
 class WebContents;
 class WebContentsViewDelegate;
@@ -796,6 +797,12 @@
   // params are used if this returns nullptr.
   virtual std::unique_ptr<base::TaskScheduler::InitParams>
   GetTaskSchedulerInitParams();
+
+  // Allows the embedder to register one or more URLLoaderThrottles for a
+  // URL request. This is used only when --enable-network-service is in effect.
+  // This is called on the IO thread.
+  virtual std::vector<std::unique_ptr<URLLoaderThrottle>>
+  CreateURLLoaderThrottles(const base::Callback<WebContents*()>& wc_getter);
 };
 
 }  // namespace content
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 4d0f3fe8..49baa59 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -15,6 +15,7 @@
 #include "content/public/common/file_chooser_params.h"
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_sender.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/WebKit/public/platform/WebPageVisibilityState.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
@@ -34,7 +35,6 @@
 }
 
 namespace service_manager {
-class BinderRegistry;
 class InterfaceProvider;
 }
 
diff --git a/content/public/common/simple_connection_filter.cc b/content/public/common/simple_connection_filter.cc
index 6ecfde4..da56a81b 100644
--- a/content/public/common/simple_connection_filter.cc
+++ b/content/public/common/simple_connection_filter.cc
@@ -5,7 +5,6 @@
 #include "content/public/common/simple_connection_filter.h"
 
 #include "services/service_manager/public/cpp/bind_source_info.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace content {
 
diff --git a/content/public/common/simple_connection_filter.h b/content/public/common/simple_connection_filter.h
index 9aca000f..c1a1fec 100644
--- a/content/public/common/simple_connection_filter.h
+++ b/content/public/common/simple_connection_filter.h
@@ -10,10 +10,7 @@
 
 #include "content/common/content_export.h"
 #include "content/public/common/connection_filter.h"
-
-namespace service_manager {
-class BinderRegistry;
-}
+#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace content {
 
diff --git a/content/public/gpu/content_gpu_client.h b/content/public/gpu/content_gpu_client.h
index e3126e8b..2f9fad53 100644
--- a/content/public/gpu/content_gpu_client.h
+++ b/content/public/gpu/content_gpu_client.h
@@ -7,16 +7,13 @@
 
 #include "base/metrics/field_trial.h"
 #include "content/public/common/content_client.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace gpu {
 class SyncPointManager;
 struct GpuPreferences;
 }
 
-namespace service_manager {
-class BinderRegistry;
-}
-
 namespace content {
 
 // Embedder API for participating in gpu logic.
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 2ff0b2c1..e9f20c0 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -17,6 +17,7 @@
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_sender.h"
 #include "ppapi/features/features.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/WebKit/public/platform/WebPageVisibilityState.h"
 #include "third_party/WebKit/public/web/WebNavigationPolicy.h"
 
@@ -38,7 +39,6 @@
 }
 
 namespace service_manager {
-class BinderRegistry;
 class InterfaceProvider;
 }
 
diff --git a/content/public/utility/content_utility_client.h b/content/public/utility/content_utility_client.h
index 0d552bf..25f262a 100644
--- a/content/public/utility/content_utility_client.h
+++ b/content/public/utility/content_utility_client.h
@@ -11,9 +11,9 @@
 #include "base/callback_forward.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/service_info.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace service_manager {
-class BinderRegistry;
 class InterfaceRegistry;
 }
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index f2f14ba..3da9f2ac 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -294,7 +294,6 @@
     "pepper/fullscreen_container.h",
     "peripheral_content_heuristic.cc",
     "peripheral_content_heuristic.h",
-    "possibly_associated_interface_ptr.h",
     "presentation/presentation_connection_proxy.cc",
     "presentation/presentation_connection_proxy.h",
     "presentation/presentation_dispatcher.cc",
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index e6fd186..68172ceb 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -392,7 +392,7 @@
   settings.resource_settings.use_gpu_memory_buffer_resources =
       compositor_deps->IsGpuMemoryBufferCompositorResourcesEnabled();
   settings.enable_color_correct_rasterization =
-      cmd.HasSwitch(switches::kEnableColorCorrectRendering);
+      base::FeatureList::IsEnabled(features::kColorCorrectRendering);
   settings.resource_settings.buffer_to_texture_target_map =
       compositor_deps->GetBufferToTextureTargetMap();
 
diff --git a/content/renderer/media/render_media_log.cc b/content/renderer/media/render_media_log.cc
index 66708ecf..0a3e4529 100644
--- a/content/renderer/media/render_media_log.cc
+++ b/content/renderer/media/render_media_log.cc
@@ -29,10 +29,8 @@
       event->type == media::MediaLogEvent::MEDIA_ERROR_LOG_ENTRY) {
     LOG(ERROR) << "MediaEvent: "
                << media::MediaLog::MediaEventToLogString(*event);
-  } else if (event->type != media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED &&
-             event->type != media::MediaLogEvent::PROPERTY_CHANGE &&
-             event->type != media::MediaLogEvent::WATCH_TIME_UPDATE &&
-             event->type != media::MediaLogEvent::NETWORK_ACTIVITY_SET) {
+  } else if (event->type != media::MediaLogEvent::PROPERTY_CHANGE &&
+             event->type != media::MediaLogEvent::WATCH_TIME_UPDATE) {
     MEDIA_EVENT_LOG_UTILITY << "MediaEvent: "
                             << media::MediaLog::MediaEventToLogString(*event);
   }
@@ -77,15 +75,6 @@
     base::AutoLock auto_lock(lock_);
 
     switch (event->type) {
-      case media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED:
-        // Keep track of the latest buffered extents properties to avoid sending
-        // thousands of events over IPC. See http://crbug.com/352585 for
-        // details.
-        last_buffered_extents_changed_event_.swap(event);
-        // SendQueuedMediaEvents() will enqueue the most recent event of this
-        // kind, if any, prior to sending the event batch.
-        break;
-
       case media::MediaLogEvent::DURATION_SET:
         // Similar to the extents changed message, this may fire many times for
         // badly muxed media. Suppress within our rate limits here.
@@ -178,11 +167,6 @@
     DCHECK(ipc_send_pending_);
     ipc_send_pending_ = false;
 
-    if (last_buffered_extents_changed_event_) {
-      queued_media_events_.push_back(*last_buffered_extents_changed_event_);
-      last_buffered_extents_changed_event_.reset();
-    }
-
     if (last_duration_changed_event_) {
       queued_media_events_.push_back(*last_duration_changed_event_);
       last_duration_changed_event_.reset();
diff --git a/content/renderer/media/render_media_log.h b/content/renderer/media/render_media_log.h
index f13e021..62c1256 100644
--- a/content/renderer/media/render_media_log.h
+++ b/content/renderer/media/render_media_log.h
@@ -70,7 +70,6 @@
   bool ipc_send_pending_;
 
   // Limits the number of events we send over IPC to one.
-  std::unique_ptr<media::MediaLogEvent> last_buffered_extents_changed_event_;
   std::unique_ptr<media::MediaLogEvent> last_duration_changed_event_;
 
   // Holds the earliest MEDIA_ERROR_LOG_ENTRY event added to this log. This is
diff --git a/content/renderer/media/render_media_log_unittest.cc b/content/renderer/media/render_media_log_unittest.cc
index e4db4d4..410c8df 100644
--- a/content/renderer/media/render_media_log_unittest.cc
+++ b/content/renderer/media/render_media_log_unittest.cc
@@ -101,29 +101,6 @@
   EXPECT_EQ(2, message_count());
 }
 
-TEST_F(RenderMediaLogTest, BufferedExtents) {
-  AddEvent(media::MediaLogEvent::LOAD);
-  AddEvent(media::MediaLogEvent::SEEK);
-
-  // This event is handled separately and should always appear last regardless
-  // of how many times we see it.
-  AddEvent(media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED);
-  AddEvent(media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED);
-  AddEvent(media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED);
-
-  EXPECT_EQ(0, message_count());
-  Advance(base::TimeDelta::FromMilliseconds(1000));
-  EXPECT_EQ(1, message_count());
-
-  // Verify contents. There should only be a single buffered extents changed
-  // event.
-  std::vector<media::MediaLogEvent> events = GetMediaLogEvents();
-  ASSERT_EQ(3u, events.size());
-  EXPECT_EQ(media::MediaLogEvent::LOAD, events[0].type);
-  EXPECT_EQ(media::MediaLogEvent::SEEK, events[1].type);
-  EXPECT_EQ(media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED, events[2].type);
-}
-
 TEST_F(RenderMediaLogTest, DurationChanged) {
   AddEvent(media::MediaLogEvent::LOAD);
   AddEvent(media::MediaLogEvent::SEEK);
diff --git a/content/renderer/mojo/blink_interface_registry_impl.cc b/content/renderer/mojo/blink_interface_registry_impl.cc
index 2f24cf9..9f53ede 100644
--- a/content/renderer/mojo/blink_interface_registry_impl.cc
+++ b/content/renderer/mojo/blink_interface_registry_impl.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "mojo/public/cpp/system/message_pipe.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace content {
 
diff --git a/content/renderer/mojo/blink_interface_registry_impl.h b/content/renderer/mojo/blink_interface_registry_impl.h
index 09a16e9..c955ac5 100644
--- a/content/renderer/mojo/blink_interface_registry_impl.h
+++ b/content/renderer/mojo/blink_interface_registry_impl.h
@@ -7,12 +7,10 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/WebKit/public/platform/InterfaceRegistry.h"
 
-namespace service_manager {
-class BinderRegistry;
-}
-
 namespace content {
 
 class BlinkInterfaceRegistryImpl : public blink::InterfaceRegistry {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 6298784f..9ed25956 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -116,7 +116,6 @@
 }
 
 namespace service_manager {
-class BinderRegistry;
 class InterfaceProvider;
 }
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 0cc1871..0577102e 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -489,11 +489,6 @@
 blink::WebFrameWidget* RenderWidget::CreateWebFrameWidget(
     RenderWidget* render_widget,
     blink::WebLocalFrame* frame) {
-  if (!frame->Parent()) {
-    // TODO(dcheng): The main frame widget currently has a special case.
-    // Eliminate this once WebView is no longer a WebWidget.
-    return blink::WebFrameWidget::Create(render_widget, frame->View(), frame);
-  }
   return blink::WebFrameWidget::Create(render_widget, frame);
 }
 
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index e0821e2..7884354 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -17,9 +17,9 @@
 #include "cc/blink/web_compositor_support_impl.h"
 #include "content/child/blink_platform_impl.h"
 #include "content/common/content_export.h"
+#include "content/common/possibly_associated_interface_ptr.h"
 #include "content/common/url_loader_factory.mojom.h"
 #include "content/renderer/origin_trials/web_trial_token_validator_impl.h"
-#include "content/renderer/possibly_associated_interface_ptr.h"
 #include "content/renderer/top_level_blame_context.h"
 #include "content/renderer/webpublicsuffixlist_impl.h"
 #include "services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h"
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index f496af7..6cfffb6 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -213,9 +213,7 @@
       command_line.AppendSwitch(switches::kDisableGpuRasterization);
     }
 
-    // Enable color correct rendering. If the virtual test suite didn't specify
-    // a color space, then use sRGB.
-    command_line.AppendSwitch(switches::kEnableColorCorrectRendering);
+    // If the virtual test suite didn't specify a color space, then force sRGB.
     if (!command_line.HasSwitch(switches::kForceColorProfile))
       command_line.AppendSwitchASCII(switches::kForceColorProfile, "srgb");
 
@@ -234,6 +232,13 @@
       *exit_code = 1;
       return true;
     }
+
+    // Enable additional base::Features. Note that there already may exist a
+    // list of enabled features from the virtual or physical test suite.
+    std::string enabled_features =
+        command_line.GetSwitchValueASCII(switches::kEnableFeatures);
+    enabled_features = "ColorCorrectRendering," + enabled_features;
+    command_line.AppendSwitchASCII(switches::kEnableFeatures, enabled_features);
   }
 
   content_client_.reset(base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 45103a4..4027e5722 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -801,6 +801,7 @@
     "//services/ui/gpu/interfaces",
     "//services/ui/public/cpp/gpu",
     "//services/video_capture/public/cpp",
+    "//services/video_capture/public/interfaces:constants",
     "//storage/browser",
     "//testing/gmock",
     "//testing/gtest",
@@ -967,6 +968,7 @@
       "../browser/webrtc/webrtc_internals_browsertest.cc",
       "../browser/webrtc/webrtc_ip_permissions_browsertest.cc",
       "../browser/webrtc/webrtc_media_recorder_browsertest.cc",
+      "../browser/webrtc/webrtc_video_capture_browsertest.cc",
       "../browser/webrtc/webrtc_webcam_browsertest.cc",
       "../browser/webrtc/webrtc_webcam_browsertest.h",
     ]
diff --git a/content/test/data/media/video_capture_test.html b/content/test/data/media/video_capture_test.html
new file mode 100644
index 0000000..faf8a28cf
--- /dev/null
+++ b/content/test/data/media/video_capture_test.html
@@ -0,0 +1,74 @@
+<html>
+<head>
+  <script type="text/javascript" src="webrtc_test_utilities.js"></script>
+  <script type="text/javascript">
+  $ = function(id) {
+    return document.getElementById(id);
+  };
+
+  const WIDTH = 320;
+  var CONSTRAINTS = { video: { width: { exact : WIDTH } } };
+  var hasReceivedTrackEndedEvent = false;
+
+  function startVideoCaptureAndVerifySize() {
+    console.log('Calling getUserMediaAndWaitForVideoRendering.');
+    navigator.webkitGetUserMedia(
+        CONSTRAINTS,
+        gotStreamCallback,
+        failedCallback);
+  }
+
+  function failedCallback(error) {
+    failTest('GetUserMedia call failed with code ' + error.code);
+  }
+
+  function gotStreamCallback(stream) {
+    var localView = $('local-view');
+    localView.src = URL.createObjectURL(stream);
+
+    var videoTracks = stream.getVideoTracks();
+    if (videoTracks.length == 0) {
+      failTest('Did not receive any video tracks');
+    }
+    var videoTrack = videoTracks[0];
+    videoTrack.onended = function() {
+      hasReceivedTrackEndedEvent = true;
+    };
+
+    detectVideoPlaying('local-view', function() {
+      if (localView.videoWidth == WIDTH) {
+        reportTestSuccess();
+      } else {
+        failTest('Video has unexpected width.');
+      }
+    });
+  }
+
+  function waitForVideoToTurnBlack() {
+    detectBlackVideo('local-view', function() {
+      reportTestSuccess();
+    });
+  }
+
+  function verifyHasReceivedTrackEndedEvent() {
+    if (hasReceivedTrackEndedEvent) {
+      reportTestSuccess();
+    } else {
+      failTest('Did not receive ended event from track.');
+    }
+  }
+
+  </script>
+</head>
+<body>
+  <table border="0">
+    <tr>
+      <td><video id="local-view" width="96" height="96" autoplay
+          style="display:none"></video></td>
+      <!-- The canvas is used to detect when video starts and stops. -->
+      <td><canvas id="local-view-canvas" width="96" height="96"
+          style="display:none"></canvas></td>
+    </tr>
+  </table>
+</body>
+</html>
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index f05de6ab..8748000 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -186,15 +186,15 @@
     self.Flaky('deqp/*', ['win', 'intel', 'd3d11'], bug=628395)
 
     # Passthrough command decoder / D3D11
-    self.Fail('conformance/textures/image_bitmap_from_video/*',
+    self.Skip('conformance/textures/image_bitmap_from_video/*',
         ['win', 'passthrough', 'd3d11'], bug=602688)
-    self.Fail('conformance/textures/video/*',
+    self.Skip('conformance/textures/video/*',
         ['win', 'passthrough', 'd3d11'], bug=602688)
-    self.Fail('conformance/textures/misc/texture-corner-case-videos.html',
+    self.Skip('conformance/textures/misc/texture-corner-case-videos.html',
         ['win', 'passthrough', 'd3d11'], bug=602688)
-    self.Fail('conformance2/textures/image_bitmap_from_video/*',
+    self.Skip('conformance2/textures/image_bitmap_from_video/*',
         ['win', 'passthrough', 'd3d11'], bug=602688)
-    self.Fail('conformance2/textures/video/*',
+    self.Skip('conformance2/textures/video/*',
         ['win', 'passthrough', 'd3d11'], bug=602688)
 
     self.Fail('conformance2/glsl3/no-attribute-vertex-shader.html',
diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc
index 43bf078..a3aa9a0 100644
--- a/content/test/layouttest_support.cc
+++ b/content/test/layouttest_support.cc
@@ -345,7 +345,7 @@
     cc::RendererSettings renderer_settings;
     base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
     renderer_settings.enable_color_correct_rendering =
-        cmd->HasSwitch(switches::kEnableColorCorrectRendering);
+        base::FeatureList::IsEnabled(features::kColorCorrectRendering);
     renderer_settings.allow_antialiasing &=
         !cmd->HasSwitch(cc::switches::kDisableCompositedAntialiasing);
     renderer_settings.highp_threshold_min = 2048;
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 458c5eb..bd989961 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -158,8 +158,8 @@
       "usb/mojo/device_manager_impl_unittest.cc",
       "usb/mojo/mock_permission_provider.cc",
       "usb/mojo/mock_permission_provider.h",
+      "usb/public/cpp/filter_utils_unittest.cc",
       "usb/usb_descriptors_unittest.cc",
-      "usb/usb_device_filter_unittest.cc",
       "usb/usb_device_handle_unittest.cc",
       "usb/usb_ids_unittest.cc",
       "usb/usb_service_unittest.cc",
@@ -171,6 +171,7 @@
       "//device/usb",
       "//device/usb:test_support",
       "//device/usb/mojo",
+      "//device/usb/public/cpp",
       "//device/usb/public/interfaces",
       "//net:test_support",
     ]
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index 25dd04f..f4e2b6c 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -49,8 +49,6 @@
     "test/fake_central.h",
     "test/fake_peripheral.cc",
     "test/fake_peripheral.h",
-    "test/fake_remote_gatt_service.cc",
-    "test/fake_remote_gatt_service.h",
   ]
 
   deps = [
diff --git a/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom b/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
index bba5e49..1e7b4bf 100644
--- a/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
+++ b/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
@@ -88,11 +88,4 @@
   // This method aims to simulate the response once all of these procedures
   // have completed or if there was an error during any of them.
   SetNextGATTDiscoveryResponse(string address, uint16 code) => (bool success);
-
-  // Adds a fake GATT Service with |service_uuid| to be found when
-  // discovering the peripheral's GATT Attributes. Runs its callback with
-  // the fake service's Id. Runs its callback with nullopt if it failed to
-  // add the fake service.
-  AddFakeService(string peripheral_address, UUID service_uuid)
-      => (string? service_id);
 };
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc
index a3e8cf6..74adb7b95 100644
--- a/device/bluetooth/test/fake_central.cc
+++ b/device/bluetooth/test/fake_central.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_discovery_filter.h"
@@ -76,19 +75,6 @@
   std::move(callback).Run(true);
 }
 
-void FakeCentral::AddFakeService(const std::string& peripheral_address,
-                                 const device::BluetoothUUID& service_uuid,
-                                 AddFakeServiceCallback callback) {
-  auto device_iter = devices_.find(peripheral_address);
-  if (device_iter == devices_.end()) {
-    std::move(callback).Run(base::nullopt);
-  }
-
-  FakePeripheral* fake_peripheral =
-      static_cast<FakePeripheral*>(device_iter->second.get());
-  std::move(callback).Run(fake_peripheral->AddFakeService(service_uuid));
-}
-
 std::string FakeCentral::GetAddress() const {
   NOTREACHED();
   return std::string();
diff --git a/device/bluetooth/test/fake_central.h b/device/bluetooth/test/fake_central.h
index 4786038..828cdb3 100644
--- a/device/bluetooth/test/fake_central.h
+++ b/device/bluetooth/test/fake_central.h
@@ -6,7 +6,6 @@
 
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "base/compiler_specific.h"
 #include "device/bluetooth/bluetooth_adapter.h"
@@ -38,9 +37,6 @@
       const std::string& address,
       uint16_t code,
       SetNextGATTDiscoveryResponseCallback callback) override;
-  void AddFakeService(const std::string& peripheral_address,
-                      const device::BluetoothUUID& service_uuid,
-                      AddFakeServiceCallback callback) override;
 
   // BluetoothAdapter overrides:
   std::string GetAddress() const override;
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc
index 3eba213..3a0e0e7 100644
--- a/device/bluetooth/test/fake_peripheral.cc
+++ b/device/bluetooth/test/fake_peripheral.cc
@@ -4,12 +4,7 @@
 
 #include "device/bluetooth/test/fake_peripheral.h"
 
-#include <utility>
-
 #include "base/memory/weak_ptr.h"
-#include "base/strings/string_number_conversions.h"
-#include "device/bluetooth/bluetooth_uuid.h"
-#include "device/bluetooth/test/fake_remote_gatt_service.h"
 
 namespace bluetooth {
 
@@ -47,22 +42,6 @@
   next_discovery_response_ = code;
 }
 
-std::string FakePeripheral::AddFakeService(
-    const device::BluetoothUUID& service_uuid) {
-  std::string new_service_id = base::SizeTToString(++last_service_id_);
-
-  GattServiceMap::iterator it;
-  bool inserted;
-
-  std::tie(it, inserted) = gatt_services_.emplace(
-      new_service_id,
-      base::MakeUnique<FakeRemoteGattService>(new_service_id, service_uuid,
-                                              true /* is_primary */, this));
-
-  DCHECK(inserted);
-  return it->second->GetIdentifier();
-}
-
 uint32_t FakePeripheral::GetBluetoothClass() const {
   NOTREACHED();
   return 0;
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h
index a23e86d..b42a4b2 100644
--- a/device/bluetooth/test/fake_peripheral.h
+++ b/device/bluetooth/test/fake_peripheral.h
@@ -12,7 +12,6 @@
 #include "base/optional.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/test/fake_central.h"
-#include "device/bluetooth/test/fake_remote_gatt_service.h"
 
 namespace bluetooth {
 
@@ -46,10 +45,6 @@
   // after IsGattDiscoveryComplete is called.
   void SetNextGATTDiscoveryResponse(uint16_t code);
 
-  // Adds a fake primary service with |service_uuid| to this peripheral.
-  // Returns the service's Id.
-  std::string AddFakeService(const device::BluetoothUUID& service_uuid);
-
   // BluetoothDevice overrides:
   uint32_t GetBluetoothClass() const override;
 #if defined(OS_CHROMEOS) || defined(OS_LINUX)
@@ -119,10 +114,6 @@
   // True when this Bluetooth interface is connected to the device.
   bool gatt_connected_;
 
-  // Keeps track of the last Id used to create a fake service. Incremented
-  // every time a new fake service is added.
-  size_t last_service_id_;
-
   // Used to simulate a GATT Discovery procedure.
   // Mutable because IsGattServicesDiscoveryComplete needs to set this but
   // is const.
diff --git a/device/bluetooth/test/fake_remote_gatt_service.cc b/device/bluetooth/test/fake_remote_gatt_service.cc
deleted file mode 100644
index 151c544..0000000
--- a/device/bluetooth/test/fake_remote_gatt_service.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 "device/bluetooth/test/fake_remote_gatt_service.h"
-
-#include <vector>
-
-#include "device/bluetooth/bluetooth_device.h"
-#include "device/bluetooth/bluetooth_uuid.h"
-
-namespace bluetooth {
-
-FakeRemoteGattService::FakeRemoteGattService(
-    const std::string& service_id,
-    const device::BluetoothUUID& service_uuid,
-    bool is_primary,
-    device::BluetoothDevice* device)
-    : service_id_(service_id),
-      service_uuid_(service_uuid),
-      is_primary_(is_primary),
-      device_(device) {}
-
-FakeRemoteGattService::~FakeRemoteGattService() {}
-
-std::string FakeRemoteGattService::GetIdentifier() const {
-  return service_id_;
-}
-
-device::BluetoothUUID FakeRemoteGattService::GetUUID() const {
-  return service_uuid_;
-}
-
-bool FakeRemoteGattService::IsPrimary() const {
-  return is_primary_;
-}
-
-device::BluetoothDevice* FakeRemoteGattService::GetDevice() const {
-  return device_;
-}
-
-std::vector<device::BluetoothRemoteGattCharacteristic*>
-FakeRemoteGattService::GetCharacteristics() const {
-  NOTREACHED();
-  return std::vector<device::BluetoothRemoteGattCharacteristic*>();
-}
-
-std::vector<device::BluetoothRemoteGattService*>
-FakeRemoteGattService::GetIncludedServices() const {
-  NOTREACHED();
-  return std::vector<device::BluetoothRemoteGattService*>();
-}
-
-device::BluetoothRemoteGattCharacteristic*
-FakeRemoteGattService::GetCharacteristic(const std::string& identifier) const {
-  NOTREACHED();
-  return nullptr;
-}
-
-}  // namespace bluetooth
diff --git a/device/bluetooth/test/fake_remote_gatt_service.h b/device/bluetooth/test/fake_remote_gatt_service.h
deleted file mode 100644
index fda3bfe6..0000000
--- a/device/bluetooth/test/fake_remote_gatt_service.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#ifndef DEVICE_BLUETOOTH_TEST_FAKE_REMOTE_GATT_SERVICE_H_
-#define DEVICE_BLUETOOTH_TEST_FAKE_REMOTE_GATT_SERVICE_H_
-
-#include <string>
-#include <vector>
-
-#include "device/bluetooth/bluetooth_remote_gatt_service.h"
-#include "device/bluetooth/bluetooth_uuid.h"
-
-namespace device {
-class BluetoothDevice;
-class BluetoothRemoteGattService;
-class BluetoothRemoteGattCharacteristic;
-}
-
-namespace bluetooth {
-
-// Implements device::BluetoothRemoteGattService. Meant to be used by
-// FakePeripheral to keep track of the service's state and attributes.
-class FakeRemoteGattService : public device::BluetoothRemoteGattService {
- public:
-  FakeRemoteGattService(const std::string& service_id,
-                        const device::BluetoothUUID& service_uuid,
-                        bool is_primary,
-                        device::BluetoothDevice* device);
-  ~FakeRemoteGattService() override;
-
-  // device::BluetoothGattService overrides:
-  std::string GetIdentifier() const override;
-  device::BluetoothUUID GetUUID() const override;
-  bool IsPrimary() const override;
-
-  // device::BluetoothRemoteGattService overrides:
-  device::BluetoothDevice* GetDevice() const override;
-  std::vector<device::BluetoothRemoteGattCharacteristic*> GetCharacteristics()
-      const override;
-  std::vector<device::BluetoothRemoteGattService*> GetIncludedServices()
-      const override;
-  device::BluetoothRemoteGattCharacteristic* GetCharacteristic(
-      const std::string& identifier) const override;
-
- private:
-  const std::string service_id_;
-  const device::BluetoothUUID service_uuid_;
-  const bool is_primary_;
-  device::BluetoothDevice* device_;
-};
-
-}  // namespace bluetooth
-
-#endif  // DEVICE_BLUETOOTH_TEST_FAKE_REMOTE_GATT_SERVICE_H_
diff --git a/device/usb/BUILD.gn b/device/usb/BUILD.gn
index 83ff8c11..5cb128f 100644
--- a/device/usb/BUILD.gn
+++ b/device/usb/BUILD.gn
@@ -26,8 +26,6 @@
     "usb_device.h",
     "usb_device_android.cc",
     "usb_device_android.h",
-    "usb_device_filter.cc",
-    "usb_device_filter.h",
     "usb_device_handle.cc",
     "usb_device_handle.h",
     "usb_device_handle_android.cc",
@@ -60,17 +58,13 @@
     "//base/third_party/dynamic_annotations",
     "//components/device_event_log",
     "//device/base",
+    "//device/usb/public/interfaces",
     "//net",
   ]
 
   public_deps = [
     "//base",
     "//url",
-
-    # Depend on the header generation target only to avoid the circular
-    # dependency caused by both using enums defined in the mojom headers and
-    # typemappings linking against this target.
-    "public/interfaces:interfaces__generator",
   ]
 
   if (use_udev) {
diff --git a/device/usb/mojo/BUILD.gn b/device/usb/mojo/BUILD.gn
index 25e916b..7d13939 100644
--- a/device/usb/mojo/BUILD.gn
+++ b/device/usb/mojo/BUILD.gn
@@ -17,6 +17,7 @@
   deps = [
     "//device/base",
     "//device/usb",
+    "//device/usb/public/cpp",
     "//device/usb/public/interfaces",
     "//mojo/common",
     "//mojo/public/cpp/bindings",
diff --git a/device/usb/mojo/device_manager_impl.cc b/device/usb/mojo/device_manager_impl.cc
index 31a08beb..539ea3a1 100644
--- a/device/usb/mojo/device_manager_impl.cc
+++ b/device/usb/mojo/device_manager_impl.cc
@@ -17,9 +17,9 @@
 #include "device/usb/mojo/device_impl.h"
 #include "device/usb/mojo/permission_provider.h"
 #include "device/usb/mojo/type_converters.h"
+#include "device/usb/public/cpp/filter_utils.h"
 #include "device/usb/public/interfaces/device.mojom.h"
 #include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
 #include "device/usb/usb_service.h"
 
 namespace device {
@@ -84,13 +84,13 @@
     mojom::UsbEnumerationOptionsPtr options,
     const GetDevicesCallback& callback,
     const std::vector<scoped_refptr<UsbDevice>>& devices) {
-  std::vector<UsbDeviceFilter> filters;
-  if (options && options->filters)
-    filters.swap(*options->filters);
+  std::vector<mojom::UsbDeviceFilterPtr> filters;
+  if (options)
+    filters.swap(options->filters);
 
   std::vector<mojom::UsbDeviceInfoPtr> device_infos;
   for (const auto& device : devices) {
-    if (UsbDeviceFilter::MatchesAny(*device, filters)) {
+    if (UsbDeviceFilterMatchesAny(filters, *device)) {
       if (permission_provider_ &&
           permission_provider_->HasDevicePermission(device)) {
         device_infos.push_back(mojom::UsbDeviceInfo::From(*device));
diff --git a/device/usb/mojo/device_manager_impl_unittest.cc b/device/usb/mojo/device_manager_impl_unittest.cc
index 0b41511..1cd3fa7 100644
--- a/device/usb/mojo/device_manager_impl_unittest.cc
+++ b/device/usb/mojo/device_manager_impl_unittest.cc
@@ -122,11 +122,11 @@
 
   UsbDeviceManagerPtr device_manager = ConnectToDeviceManager();
 
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_vendor_id = true;
+  filter->vendor_id = 0x1234;
   UsbEnumerationOptionsPtr options = mojom::UsbEnumerationOptions::New();
-  UsbDeviceFilter filter;
-  filter.vendor_id = 0x1234;
-  options->filters.emplace();
-  options->filters->push_back(filter);
+  options->filters.push_back(std::move(filter));
 
   std::set<std::string> guids;
   guids.insert(device0->guid());
diff --git a/device/usb/mojo/type_converters.h b/device/usb/mojo/type_converters.h
index cb9514c..fc26bb75 100644
--- a/device/usb/mojo/type_converters.h
+++ b/device/usb/mojo/type_converters.h
@@ -10,7 +10,6 @@
 #include "device/usb/public/interfaces/device.mojom.h"
 #include "device/usb/public/interfaces/device_manager.mojom.h"
 #include "device/usb/usb_descriptors.h"
-#include "device/usb/usb_device_filter.h"
 #include "device/usb/usb_device_handle.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
 
diff --git a/device/usb/public/cpp/BUILD.gn b/device/usb/public/cpp/BUILD.gn
new file mode 100644
index 0000000..717c2a9
--- /dev/null
+++ b/device/usb/public/cpp/BUILD.gn
@@ -0,0 +1,16 @@
+# 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.
+
+source_set("cpp") {
+  sources = [
+    "filter_utils.cc",
+    "filter_utils.h",
+  ]
+
+  deps = [
+    "//base",
+    "//device/usb",
+    "//device/usb/public/interfaces",
+  ]
+}
diff --git a/device/usb/public/cpp/filter_utils.cc b/device/usb/public/cpp/filter_utils.cc
new file mode 100644
index 0000000..021af336
--- /dev/null
+++ b/device/usb/public/cpp/filter_utils.cc
@@ -0,0 +1,59 @@
+// 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 "device/usb/public/cpp/filter_utils.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "device/usb/usb_device.h"
+
+namespace device {
+
+bool UsbDeviceFilterMatches(const mojom::UsbDeviceFilter& filter,
+                            const UsbDevice& device) {
+  if (filter.has_vendor_id) {
+    if (device.vendor_id() != filter.vendor_id)
+      return false;
+
+    if (filter.has_product_id && device.product_id() != filter.product_id)
+      return false;
+  }
+
+  if (filter.serial_number &&
+      device.serial_number() != base::UTF8ToUTF16(*filter.serial_number)) {
+    return false;
+  }
+
+  if (filter.has_class_code) {
+    for (const UsbConfigDescriptor& config : device.configurations()) {
+      for (const UsbInterfaceDescriptor& iface : config.interfaces) {
+        if (iface.interface_class == filter.class_code &&
+            (!filter.has_subclass_code ||
+             (iface.interface_subclass == filter.subclass_code &&
+              (!filter.has_protocol_code ||
+               iface.interface_protocol == filter.protocol_code)))) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  return true;
+}
+
+bool UsbDeviceFilterMatchesAny(
+    const std::vector<mojom::UsbDeviceFilterPtr>& filters,
+    const UsbDevice& device) {
+  if (filters.empty())
+    return true;
+
+  for (const auto& filter : filters) {
+    if (UsbDeviceFilterMatches(*filter, device))
+      return true;
+  }
+  return false;
+}
+
+}  // namespace device
diff --git a/device/usb/public/cpp/filter_utils.h b/device/usb/public/cpp/filter_utils.h
new file mode 100644
index 0000000..017917b
--- /dev/null
+++ b/device/usb/public/cpp/filter_utils.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_PUBLIC_CPP_FILTER_UTILS_H_
+#define DEVICE_USB_PUBLIC_CPP_FILTER_UTILS_H_
+
+#include <vector>
+
+#include "device/usb/public/interfaces/device_manager.mojom.h"
+
+namespace device {
+
+class UsbDevice;
+
+bool UsbDeviceFilterMatches(const mojom::UsbDeviceFilter& filter,
+                            const UsbDevice& device);
+
+bool UsbDeviceFilterMatchesAny(
+    const std::vector<mojom::UsbDeviceFilterPtr>& filters,
+    const UsbDevice& device);
+
+}  // namespace device
+
+#endif  // DEVICE_USB_PUBLIC_CPP_FILTER_UTILS_H_
diff --git a/device/usb/public/cpp/filter_utils_unittest.cc b/device/usb/public/cpp/filter_utils_unittest.cc
new file mode 100644
index 0000000..eeaf007
--- /dev/null
+++ b/device/usb/public/cpp/filter_utils_unittest.cc
@@ -0,0 +1,163 @@
+// 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.
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "device/usb/mock_usb_device.h"
+#include "device/usb/public/cpp/filter_utils.h"
+#include "device/usb/usb_descriptors.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+namespace {
+
+using testing::Return;
+
+class UsbFilterTest : public testing::Test {
+ public:
+  void SetUp() override {
+    UsbConfigDescriptor config(1, false, false, 0);
+    config.interfaces.emplace_back(1, 0, 0xff, 0x42, 0x01);
+
+    android_phone_ = new MockUsbDevice(0x18d1, 0x4ee2, "Google Inc.", "Nexus 5",
+                                       "ABC123", {config});
+  }
+
+ protected:
+  scoped_refptr<MockUsbDevice> android_phone_;
+};
+
+TEST_F(UsbFilterTest, MatchAny) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchVendorId) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_vendor_id = true;
+  filter->vendor_id = 0x18d1;
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchVendorIdNegative) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_vendor_id = true;
+  filter->vendor_id = 0x1d6b;
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchProductId) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_vendor_id = true;
+  filter->vendor_id = 0x18d1;
+  filter->has_product_id = true;
+  filter->product_id = 0x4ee2;
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchProductIdNegative) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_vendor_id = true;
+  filter->vendor_id = 0x18d1;
+  filter->has_product_id = true;
+  filter->product_id = 0x4ee1;
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceClass) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_class_code = true;
+  filter->class_code = 0xff;
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceClassNegative) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_class_code = true;
+  filter->class_code = 0xe0;
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceSubclass) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_class_code = true;
+  filter->class_code = 0xff;
+  filter->has_subclass_code = true;
+  filter->subclass_code = 0x42;
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceSubclassNegative) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_class_code = true;
+  filter->class_code = 0xff;
+  filter->has_subclass_code = true;
+  filter->subclass_code = 0x01;
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceProtocol) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_class_code = true;
+  filter->class_code = 0xff;
+  filter->has_subclass_code = true;
+  filter->subclass_code = 0x42;
+  filter->has_protocol_code = true;
+  filter->protocol_code = 0x01;
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceProtocolNegative) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->has_class_code = true;
+  filter->class_code = 0xff;
+  filter->has_subclass_code = true;
+  filter->subclass_code = 0x42;
+  filter->has_protocol_code = true;
+  filter->protocol_code = 0x02;
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchSerialNumber) {
+  auto filter = mojom::UsbDeviceFilter::New();
+  filter->serial_number = std::string("ABC123");
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  filter->has_vendor_id = true;
+  filter->vendor_id = 0x18d1;
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  filter->vendor_id = 0x18d2;
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  filter->vendor_id = 0x18d1;
+  filter->serial_number = std::string("DIFFERENT");
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchAnyEmptyList) {
+  std::vector<mojom::UsbDeviceFilterPtr> filters;
+  ASSERT_TRUE(UsbDeviceFilterMatchesAny(filters, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchesAnyVendorId) {
+  std::vector<mojom::UsbDeviceFilterPtr> filters;
+  filters.push_back(mojom::UsbDeviceFilter::New());
+  filters.back()->has_vendor_id = true;
+  filters.back()->vendor_id = 0x18d1;
+  ASSERT_TRUE(UsbDeviceFilterMatchesAny(filters, *android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchesAnyVendorIdNegative) {
+  std::vector<mojom::UsbDeviceFilterPtr> filters;
+  filters.push_back(mojom::UsbDeviceFilter::New());
+  filters.back()->has_vendor_id = true;
+  filters.back()->vendor_id = 0x1d6b;
+  ASSERT_FALSE(UsbDeviceFilterMatchesAny(filters, *android_phone_));
+}
+
+}  // namespace
+
+}  // namespace device
diff --git a/device/usb/public/interfaces/OWNERS b/device/usb/public/interfaces/OWNERS
index fda0d85..08850f4 100644
--- a/device/usb/public/interfaces/OWNERS
+++ b/device/usb/public/interfaces/OWNERS
@@ -1,8 +1,2 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
-
-per-file *_struct_traits*.*=set noparent
-per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
-
-per-file *.typemap=set noparent
-per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/device/usb/public/interfaces/device_manager.mojom b/device/usb/public/interfaces/device_manager.mojom
index 9e90681..4a4ec72 100644
--- a/device/usb/public/interfaces/device_manager.mojom
+++ b/device/usb/public/interfaces/device_manager.mojom
@@ -26,7 +26,7 @@
 };
 
 struct UsbEnumerationOptions {
-  array<UsbDeviceFilter>? filters;
+  array<UsbDeviceFilter> filters;
 };
 
 interface UsbDeviceManager {
diff --git a/device/usb/public/interfaces/device_manager.typemap b/device/usb/public/interfaces/device_manager.typemap
deleted file mode 100644
index 088ee80..0000000
--- a/device/usb/public/interfaces/device_manager.typemap
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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.
-
-mojom = "//device/usb/public/interfaces/device_manager.mojom"
-public_headers = [ "//device/usb/usb_device_filter.h" ]
-traits_headers =
-    [ "//device/usb/public/interfaces/device_manager_struct_traits.h" ]
-sources = [
-  "//device/usb/public/interfaces/device_manager_struct_traits.cc",
-]
-deps = [
-  "//device/usb",
-]
-type_mappings = [ "device.mojom.UsbDeviceFilter=device::UsbDeviceFilter" ]
diff --git a/device/usb/public/interfaces/device_manager_struct_traits.cc b/device/usb/public/interfaces/device_manager_struct_traits.cc
deleted file mode 100644
index 8198152e..0000000
--- a/device/usb/public/interfaces/device_manager_struct_traits.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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 "device/usb/public/interfaces/device_manager_struct_traits.h"
-
-namespace mojo {
-
-// static
-bool StructTraits<
-    device::mojom::UsbDeviceFilterDataView,
-    device::UsbDeviceFilter>::Read(device::mojom::UsbDeviceFilterDataView input,
-                                   device::UsbDeviceFilter* output) {
-  if (input.has_vendor_id())
-    output->vendor_id = input.vendor_id();
-  if (input.has_product_id())
-    output->product_id = input.product_id();
-  if (input.has_class_code())
-    output->interface_class = input.class_code();
-  if (input.has_subclass_code())
-    output->interface_subclass = input.subclass_code();
-  if (input.has_protocol_code())
-    output->interface_protocol = input.protocol_code();
-  return input.ReadSerialNumber(&output->serial_number);
-}
-
-}  // namespace mojo
diff --git a/device/usb/public/interfaces/device_manager_struct_traits.h b/device/usb/public/interfaces/device_manager_struct_traits.h
deleted file mode 100644
index 1028f66..0000000
--- a/device/usb/public/interfaces/device_manager_struct_traits.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef DEVICE_USB_PUBLIC_INTERFACES_DEVICE_MANAGER_STRUCT_TRAITS_H_
-#define DEVICE_USB_PUBLIC_INTERFACES_DEVICE_MANAGER_STRUCT_TRAITS_H_
-
-#include "device/usb/public/interfaces/device_manager.mojom.h"
-#include "device/usb/usb_device_filter.h"
-
-namespace mojo {
-
-template <>
-struct StructTraits<device::mojom::UsbDeviceFilterDataView,
-                    device::UsbDeviceFilter> {
-  static bool has_vendor_id(const device::UsbDeviceFilter& filter) {
-    return filter.vendor_id.has_value();
-  }
-
-  static uint16_t vendor_id(const device::UsbDeviceFilter& filter) {
-    return filter.vendor_id.value_or(0);
-  }
-
-  static bool has_product_id(const device::UsbDeviceFilter& filter) {
-    return filter.product_id.has_value();
-  }
-
-  static uint16_t product_id(const device::UsbDeviceFilter& filter) {
-    return filter.product_id.value_or(0);
-  }
-
-  static bool has_class_code(const device::UsbDeviceFilter& filter) {
-    return filter.interface_class.has_value();
-  }
-
-  static uint8_t class_code(const device::UsbDeviceFilter& filter) {
-    return filter.interface_class.value_or(0);
-  }
-
-  static bool has_subclass_code(const device::UsbDeviceFilter& filter) {
-    return filter.interface_subclass.has_value();
-  }
-
-  static uint8_t subclass_code(const device::UsbDeviceFilter& filter) {
-    return filter.interface_subclass.value_or(0);
-  }
-
-  static bool has_protocol_code(const device::UsbDeviceFilter& filter) {
-    return filter.interface_protocol.has_value();
-  }
-
-  static uint8_t protocol_code(const device::UsbDeviceFilter& filter) {
-    return filter.interface_protocol.value_or(0);
-  }
-
-  static const base::Optional<std::string>& serial_number(
-      const device::UsbDeviceFilter& filter) {
-    return filter.serial_number;
-  }
-
-  static bool Read(device::mojom::UsbDeviceFilterDataView input,
-                   device::UsbDeviceFilter* output);
-};
-
-}  // namespace mojo
-
-#endif  // DEVICE_USB_PUBLIC_INTERFACES_DEVICE_MANAGER_STRUCT_TRAITS_H_
diff --git a/device/usb/public/interfaces/typemaps.gni b/device/usb/public/interfaces/typemaps.gni
deleted file mode 100644
index 6938fc9a..0000000
--- a/device/usb/public/interfaces/typemaps.gni
+++ /dev/null
@@ -1,5 +0,0 @@
-# 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.
-
-typemaps = [ "//device/usb/public/interfaces/device_manager.typemap" ]
diff --git a/device/usb/usb_device_filter.cc b/device/usb/usb_device_filter.cc
deleted file mode 100644
index 3d637228..0000000
--- a/device/usb/usb_device_filter.cc
+++ /dev/null
@@ -1,101 +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.
-
-#include "device/usb/usb_device_filter.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "device/usb/usb_descriptors.h"
-#include "device/usb/usb_device.h"
-
-namespace device {
-
-namespace {
-
-const char kProductIdKey[] = "productId";
-const char kVendorIdKey[] = "vendorId";
-const char kInterfaceClassKey[] = "interfaceClass";
-const char kInterfaceSubclassKey[] = "interfaceSubclass";
-const char kInterfaceProtocolKey[] = "interfaceProtocol";
-
-}  // namespace
-
-UsbDeviceFilter::UsbDeviceFilter() = default;
-
-UsbDeviceFilter::UsbDeviceFilter(const UsbDeviceFilter& other) = default;
-
-UsbDeviceFilter::~UsbDeviceFilter() = default;
-
-bool UsbDeviceFilter::Matches(const UsbDevice& device) const {
-  if (vendor_id) {
-    if (device.vendor_id() != *vendor_id)
-      return false;
-
-    if (product_id && device.product_id() != *product_id)
-      return false;
-  }
-
-  if (serial_number &&
-      device.serial_number() != base::UTF8ToUTF16(*serial_number)) {
-    return false;
-  }
-
-  if (interface_class) {
-    for (const UsbConfigDescriptor& config : device.configurations()) {
-      for (const UsbInterfaceDescriptor& iface : config.interfaces) {
-        if (iface.interface_class == *interface_class &&
-            (!interface_subclass ||
-             (iface.interface_subclass == *interface_subclass &&
-              (!interface_protocol ||
-               iface.interface_protocol == *interface_protocol)))) {
-          return true;
-        }
-      }
-    }
-
-    return false;
-  }
-
-  return true;
-}
-
-std::unique_ptr<base::Value> UsbDeviceFilter::ToValue() const {
-  auto obj = base::MakeUnique<base::DictionaryValue>();
-
-  if (vendor_id) {
-    obj->SetInteger(kVendorIdKey, *vendor_id);
-    if (product_id)
-      obj->SetInteger(kProductIdKey, *product_id);
-  }
-
-  if (interface_class) {
-    obj->SetInteger(kInterfaceClassKey, *interface_class);
-    if (interface_subclass) {
-      obj->SetInteger(kInterfaceSubclassKey, *interface_subclass);
-      if (interface_protocol)
-        obj->SetInteger(kInterfaceProtocolKey, *interface_protocol);
-    }
-  }
-
-  return std::move(obj);
-}
-
-// static
-bool UsbDeviceFilter::MatchesAny(const UsbDevice& device,
-                                 const std::vector<UsbDeviceFilter>& filters) {
-  if (filters.empty())
-    return true;
-
-  for (const auto& filter : filters) {
-    if (filter.Matches(device))
-      return true;
-  }
-  return false;
-}
-
-}  // namespace device
diff --git a/device/usb/usb_device_filter.h b/device/usb/usb_device_filter.h
deleted file mode 100644
index 666f732..0000000
--- a/device/usb/usb_device_filter.h
+++ /dev/null
@@ -1,48 +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.
-
-#ifndef DEVICE_USB_USB_DEVICE_FILTER_H_
-#define DEVICE_USB_USB_DEVICE_FILTER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/optional.h"
-
-namespace base {
-class Value;
-}
-
-namespace device {
-
-class UsbDevice;
-
-struct UsbDeviceFilter {
-  UsbDeviceFilter();
-  UsbDeviceFilter(const UsbDeviceFilter& other);
-  ~UsbDeviceFilter();
-
-  // Returns true if |device| matches this filter.
-  bool Matches(const UsbDevice& device) const;
-  std::unique_ptr<base::Value> ToValue() const;
-
-  // Returns true if device matches any filter in |filters|, or if |filters| is
-  // empty.
-  static bool MatchesAny(const UsbDevice& device,
-                         const std::vector<UsbDeviceFilter>& filters);
-
-  base::Optional<uint16_t> vendor_id;
-  base::Optional<uint16_t> product_id;
-  base::Optional<uint8_t> interface_class;
-  base::Optional<uint8_t> interface_subclass;
-  base::Optional<uint8_t> interface_protocol;
-  base::Optional<std::string> serial_number;
-};
-
-}  // namespace device
-
-#endif  // DEVICE_USB_USB_DEVICE_FILTER_H_
diff --git a/device/usb/usb_device_filter_unittest.cc b/device/usb/usb_device_filter_unittest.cc
deleted file mode 100644
index dd7840e0..0000000
--- a/device/usb/usb_device_filter_unittest.cc
+++ /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.
-
-#include <string>
-#include <vector>
-
-#include "base/memory/ref_counted.h"
-#include "device/usb/mock_usb_device.h"
-#include "device/usb/usb_descriptors.h"
-#include "device/usb/usb_device_filter.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-namespace {
-
-using testing::Return;
-
-class UsbFilterTest : public testing::Test {
- public:
-  void SetUp() override {
-    UsbConfigDescriptor config(1, false, false, 0);
-    config.interfaces.emplace_back(1, 0, 0xff, 0x42, 0x01);
-
-    android_phone_ = new MockUsbDevice(0x18d1, 0x4ee2, "Google Inc.", "Nexus 5",
-                                       "ABC123", {config});
-  }
-
- protected:
-  scoped_refptr<MockUsbDevice> android_phone_;
-};
-
-TEST_F(UsbFilterTest, MatchAny) {
-  UsbDeviceFilter filter;
-  ASSERT_TRUE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchVendorId) {
-  UsbDeviceFilter filter;
-  filter.vendor_id = 0x18d1;
-  ASSERT_TRUE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchVendorIdNegative) {
-  UsbDeviceFilter filter;
-  filter.vendor_id = 0x1d6b;
-  ASSERT_FALSE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchProductId) {
-  UsbDeviceFilter filter;
-  filter.vendor_id = 0x18d1;
-  filter.product_id = 0x4ee2;
-  ASSERT_TRUE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchProductIdNegative) {
-  UsbDeviceFilter filter;
-  filter.vendor_id = 0x18d1;
-  filter.product_id = 0x4ee1;
-  ASSERT_FALSE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchInterfaceClass) {
-  UsbDeviceFilter filter;
-  filter.interface_class = 0xff;
-  ASSERT_TRUE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchInterfaceClassNegative) {
-  UsbDeviceFilter filter;
-  filter.interface_class = 0xe0;
-  ASSERT_FALSE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchInterfaceSubclass) {
-  UsbDeviceFilter filter;
-  filter.interface_class = 0xff;
-  filter.interface_subclass = 0x42;
-  ASSERT_TRUE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchInterfaceSubclassNegative) {
-  UsbDeviceFilter filter;
-  filter.interface_class = 0xff;
-  filter.interface_subclass = 0x01;
-  ASSERT_FALSE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchInterfaceProtocol) {
-  UsbDeviceFilter filter;
-  filter.interface_class = 0xff;
-  filter.interface_subclass = 0x42;
-  filter.interface_protocol = 0x01;
-  ASSERT_TRUE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchInterfaceProtocolNegative) {
-  UsbDeviceFilter filter;
-  filter.interface_class = 0xff;
-  filter.interface_subclass = 0x42;
-  filter.interface_protocol = 0x02;
-  ASSERT_FALSE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchSerialNumber) {
-  UsbDeviceFilter filter;
-  filter.serial_number = std::string("ABC123");
-  EXPECT_TRUE(filter.Matches(*android_phone_));
-  filter.vendor_id = 0x18d1;
-  EXPECT_TRUE(filter.Matches(*android_phone_));
-  filter.vendor_id = 0x18d2;
-  EXPECT_FALSE(filter.Matches(*android_phone_));
-  filter.vendor_id = 0x18d1;
-  filter.serial_number = std::string("DIFFERENT");
-  EXPECT_FALSE(filter.Matches(*android_phone_));
-}
-
-TEST_F(UsbFilterTest, MatchAnyEmptyList) {
-  std::vector<UsbDeviceFilter> filters;
-  ASSERT_TRUE(UsbDeviceFilter::MatchesAny(*android_phone_, filters));
-}
-
-TEST_F(UsbFilterTest, MatchesAnyVendorId) {
-  std::vector<UsbDeviceFilter> filters(1);
-  filters.back().vendor_id = 0x18d1;
-  ASSERT_TRUE(UsbDeviceFilter::MatchesAny(*android_phone_, filters));
-}
-
-TEST_F(UsbFilterTest, MatchesAnyVendorIdNegative) {
-  std::vector<UsbDeviceFilter> filters(1);
-  filters.back().vendor_id = 0x1d6b;
-  ASSERT_FALSE(UsbDeviceFilter::MatchesAny(*android_phone_, filters));
-}
-
-}  // namespace
-
-}  // namespace device
diff --git a/docs/speed/README.md b/docs/speed/README.md
index 7c7ac07c..7421f550 100644
--- a/docs/speed/README.md
+++ b/docs/speed/README.md
@@ -3,9 +3,11 @@
 ## Contact information
 
   * **Contact**: **speed@chromium.org**
-  * **Escalation**: rschoen@chromium.org (PM), benhenry@chromium.org (TPM),
-    parisa@chromium.org (eng director)
-  * **[File a bug](https://bugs.chromium.org/p/chromium/issues/entry?template=Speed%20Bug)**
+  * **Escalation**:
+    * benhenry@chromium.org (TPM)
+    * rschoen@chromium.org (PM)
+    * parisa@chromium.org (eng director)
+  * **File a bug**: [template](https://bugs.chromium.org/p/chromium/issues/entry?template=Speed%20Bug)
   * **Regression postmortem**: [template](https://docs.google.com/document/d/1fvfhFNOoUL9rB0XAEe1MYefyM_9yriR1IPjdxdm7PaQ/edit?disco=AAAABKdHwCg)
 
 ## User Docs
@@ -19,7 +21,7 @@
 
 ## Core Teams and Work
 
-  * **[Performance tracks](performance_tracks.md)**: Most of the performance
+  * **[Speed tracks](speed_tracks.md)**: Most of the speed 
     work on Chrome is organized into these tracks.
   * **[Chrome Speed Operations](chrome_speed_operations.md)**: provides the
     benchmarks, infrastructure, and releasing oversight to track regressions.
@@ -30,4 +32,4 @@
     * Performance dashboard, bisect, try jobs: speed-services-dev@chromium.org
   * **[Chrome Speed Metrics](https://docs.google.com/document/d/1wBT5fauGf8bqW2Wcg2A5Z-3_ZvgPhE8fbp1Xe6xfGRs/edit#heading=h.8ieoiiwdknwt)**: provides a set of high-quality metrics that represent real-world user experience, and exposes these metrics to both Chrome and Web Developers.
     * General discussion: progressive-web-metrics@chromium.org
-    * The actual metrics: [tracking](https://docs.google.com/spreadsheets/d/1gY5hkKPp8RNVqmOw1d-bo-f9EXLqtq4wa3Z7Q8Ek9Tk/edit#gid=0)
\ No newline at end of file
+    * The actual metrics: [tracking](https://docs.google.com/spreadsheets/d/1gY5hkKPp8RNVqmOw1d-bo-f9EXLqtq4wa3Z7Q8Ek9Tk/edit#gid=0)
diff --git a/docs/speed/adding_tests_bots.md b/docs/speed/adding_tests_bots.md
new file mode 100644
index 0000000..259a34f0
--- /dev/null
+++ b/docs/speed/adding_tests_bots.md
@@ -0,0 +1,11 @@
+# Adding tests and bots
+
+## Adding performance tests
+
+See the
+[new benchmark policy](https://docs.google.com/document/d/1ni2MIeVnlH4bTj4yvEDMVNxgL73PqK_O9_NUm3NW3BA/edit)
+
+## Adding hardware
+
+This documentation is under construction. In the meantime, please reach out to
+benchmarking-dev@chromium.org.
\ No newline at end of file
diff --git a/docs/speed/addressing_performance_regressions.md b/docs/speed/addressing_performance_regressions.md
index dd75ce1..f012a04 100644
--- a/docs/speed/addressing_performance_regressions.md
+++ b/docs/speed/addressing_performance_regressions.md
@@ -3,6 +3,8 @@
 The bisect bot just picked your CL as the culprit in a performance regression
 and assigned a bug to you! What should you do? Read on...
 
+[TOC]
+
 ## About our performance tests
 
 The [chromium.perf waterfall](perf_waterfall.md) is a continuous build which
@@ -12,7 +14,7 @@
 than what you tested locally before landing your CL.
 
 Each test has an owner, named in
-[this spreasheet](https://docs.google.com/spreadsheets/d/1xaAo0_SU3iDfGdqDJZX_jRV0QtkufwHUKH3kQKF3YQs/edit#gid=0),
+[this spreadsheet](https://docs.google.com/spreadsheets/d/1xaAo0_SU3iDfGdqDJZX_jRV0QtkufwHUKH3kQKF3YQs/edit#gid=0),
 who you can cc on a performance bug if you have questions.
 
 ## Understanding the bisect results
@@ -61,7 +63,7 @@
     * **How do I run that locally?** Follow the instructions under
       `To Run This Test`. But be aware that if it regressed on Android and
       you're developing on Windows, you may not be able to reproduce locally.
-      (See Debugging regressions below)
+      (See [Debugging regressions](#Debugging-regressions) below)
     * **What is this testing?** Generally the metric
       (`timeToFirstContentfulPaint_avg`) gives some information. If you're not
       familiar, you can cc the [benchmark owner](https://docs.google.com/spreadsheets/d/1xaAo0_SU3iDfGdqDJZX_jRV0QtkufwHUKH3kQKF3YQs/edit#gid=0)
@@ -178,7 +180,7 @@
       be measured well in benchmarks. If you believe your case falls into this
       category, you can show that end users are not affected via a finch trial.
       See the "End-user metrics" section of
-      [How does Chrome measure performance](how_does_chrome_measure_performance.md)
+      [How Chrome measures performance](how_does_chrome_measure_performance.md#End_user-metrics)
   * **Your change is a critical correctness or security fix.**
     It's true that sometimes something was "fast" because it was implemented
     incorrectly. In this case, a justification should clarify the performance
diff --git a/docs/speed/bisects.md b/docs/speed/bisects.md
new file mode 100644
index 0000000..91ced32
--- /dev/null
+++ b/docs/speed/bisects.md
@@ -0,0 +1,126 @@
+# Bisecting Performance Regressions
+
+[TOC]
+
+## What are performance bisects?
+
+The perf tests on chromium's continuous build are very long-running, so we
+cannot run them on every revision. Further, separate repositories like v8
+and skia sometimes roll multiple performance-sensitive changes into chromium
+at once. For these reasons, we need a tool that can bisect the root cause of
+performance regressions over a CL range, descending into third_party
+repositories as necessary. This is what the performance bisect bots do.
+
+The team is currently working on a new version of performance biscect called
+[pinpoint](https://docs.google.com/document/d/1FKPRNU2kbPJ15p6XHO0itCjYtfvCpGt2IHblriTX1tg/edit)
+
+## Starting a perf bisect
+
+Performance bisects are tightly integrated with the
+[Chrome Performance Dashboard](https://chromeperf.appspot.com/alerts) and
+[monorail](https://bugs.chromium.org/p/chromium/issues/list). Users kick off
+perf bisects on the perf dashboard and view results in monorail.
+
+You can kick off a perf bisect anywhere you see a performance graph on the perf
+dashboard (except for some tests which don't bisect, because they do not run on
+the [chromium.perf waterfall](https://build.chromium.org/p/chromium.perf/waterfall)).
+
+### To get to a graph, use one of the following methods:
+
+  * From a perf sheriff-filed bug, follow the link in #1 that looks like
+    `https://chromeperf.appspot.com/group_report?bug_id=XXXXXX`. Check the
+    boxes next to alerts in the table to display graphs.
+  * From the [alerts page](https://chromeperf.appspot.com/alerts), check the
+    box next to an alert and click the `Graph` button.
+  * From the [report page](https://chromeperf.appspot.com/report), use the menu
+    to navigate to the graph you want.
+
+### To kick off a bisect from the graph:
+
+![Bisecting on a performance graph](images/bisect_graph.png)
+![The bisect dialog](images/bisect_dialog.png)
+
+  1. Click on a data point in the graph.
+  2. In the tooltip that shows up, click the `BISECT` button.
+  3. Make sure to enter a Bug ID in the dialog that comes up.
+  4. Click the `START BISECT` button.
+
+### What are all the boxes in the form?
+
+  * **Bisect bot**: The name of the configuration in the perf lab to bisect on.
+    This has been prefilled to match the bot that generated the graph as
+    closely as possible.
+  * **Metric**: The metric of the performance test to bisect. This defaults to
+    the metric shown on the graph. It shows a list of other related metrics
+    (for example, if average page load time increased, the drop down will show
+    a list of individual pages which were measured).
+  * **Story filter**: This is a flag specific to
+    [telemetry](https://github.com/catapult-project/catapult/blob/master/telemetry/README.md).
+    It tells telemetry to only run a specific test case, instead of running all
+    the test cases in the suite. This dramatically reduces bisect time for
+    large test suites. The dashboard will prefill this box based on the graph
+    you clicked on. If you suspect that test cases in the benchmark are not
+    independent, you can try bisecting with this box cleared.
+  * **Bug ID**: The bug number in monorail. It's very important to fill in
+    this field, as this is where bisect results will be posted.
+  * **Earlier revision**: The chromium commit pos to start bisecting from. This
+    is prefilled by the dashboard to the start of the revision range for the
+    point you clicked on. You can set it to an earlier commit position to
+    bisect a larger range.
+  * **Later revision**: The chromium commit pos to bisect to. This is prefilled
+    by the dashboard to the end of the revision range for the point you clicked
+    on. You can set it to a later commit pos to bisect a larger range.
+  * **Launch on staging bots**: This is an internal feature, which allows the
+    bisect team to launch a bisect on a test configuration. You likely don't
+    want to check this box unless instructed by the bisect team.
+  * **Bisect mode**: use "mean" to bisect the mean value of the performance
+    test. See below for "return_code".
+
+## Bisecting test failures
+
+The perf bisect bots can also be used to bisect performance test failures.
+See details in [Triaging Data Stoppage Alerts](triaging_data_stoppage_alerts.md).
+
+## Interpreting the results
+
+The bisect bot will output a comment on the bug you input into the dialog when
+bisection is complete. See the
+[Understanding the Bisect Results](addressing_performance_regressions.md#Understanding-the-bisect-results)
+section of the Addressing Performance Regressions doc for details on how to
+interpret the results.
+
+## Getting more debugging data
+
+The bisect outputs some additional data which might be useful for really tough
+regressions or confusing results.
+
+### Traces
+
+Chrome traces are generated by most bisects and uploaded to cloud storage, but
+they're not very visible in the UI. We plan to address this in
+[pinpoint](https://docs.google.com/document/d/1FKPRNU2kbPJ15p6XHO0itCjYtfvCpGt2IHblriTX1tg/edit),
+but in the short term here are the steps to get the traces from a bisect:
+
+  1. The bisect comment should have a "Debug Info" link that looks like this:
+     `https://chromeperf.appspot.com/buildbucket_job_status/8980436717323504240`
+     Click it.
+  2. In the debug info, you should see a "Buildbot link" that looks like this:
+     `https://build.chromium.org/p/tryserver.chromium.perf/builders/android_nexus7_perf_bisect/builds/4097`
+     Click it.
+  3. There will be several steps on the buildbot status page named "Bisecting
+     Revision". Each has an annotation like "Revision: chromium@474894" so you
+     can tell which revision it ran. Pick the commit position you want the
+     trace from (usually the one at your CL and the one immediately before).
+     Click the arrow by "> Nested step(s) for: Bisecting revision..." on those
+     steps.
+  4. In the nested steps, you'll see several steps titled "Bisecting
+     revision.Performance Test X of Y". These are the actual perf test runs.
+     Click the "stdout" link for one of these steps.
+  5. In the output, do a text search for "View generated trace files online"
+     and you'll see a link to a trace that looks like this:
+     `https://console.developers.google.com/m/cloudstorage/b/chrome-telemetry-output/o/trace-file-id_0-2017-05-05_05-41-49-83206.html`
+
+Here are some screenshots showing what to click on:
+
+![Finding the Bisecting Revision Step](images/bisecting_revision_step.png)
+![Getting to the stdout](images/bisect_stdout.png)
\ No newline at end of file
diff --git a/docs/speed/help_improve_performance.md b/docs/speed/help_improve_performance.md
new file mode 100644
index 0000000..0003608
--- /dev/null
+++ b/docs/speed/help_improve_performance.md
@@ -0,0 +1,23 @@
+# Chrome is slow, what do I do?
+
+## How to file a bug
+
+  * **File a bug.** To file a bug, please use
+**[the speed bug template](https://bugs.chromium.org/p/chromium/issues/entry?template=Speed%20Bug)**.
+  * **Attach a trace.** The best way to get performance data to developers is to include a trace,
+    so please [follow these instructions](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool/recording-tracing-runs)
+    to record a trace and attach it to the bug.
+  * **Try TraceOnTap** the [TraceOnTap Extension](http://goto.google.com/traceontap)
+    makes it easier to record traces and attach them to bugs.
+    [Source code](https://github.com/catapult-project/catapult/tree/master/experimental/trace_on_tap)
+
+<!--- TODO: ## Bug SLO for perf bugs -->
+
+<!--- TODO: ## Bug Triage
+### High level
+### Best practices
+### 3 step approach to Speed Triage
+### Rotation Documentation
+### Bug Dashboards
+### Fixits
+-->
\ No newline at end of file
diff --git a/docs/speed/how_does_chrome_measure_performance.md b/docs/speed/how_does_chrome_measure_performance.md
index 6b61984..5f93a6e6 100644
--- a/docs/speed/how_does_chrome_measure_performance.md
+++ b/docs/speed/how_does_chrome_measure_performance.md
@@ -10,8 +10,8 @@
 like loading, memory, and power. Each track has high-level metrics associated
 with it.
 
-  * **[An overview of tracks](performance_tracks.md)**: lists the tracks and key contact points.
-  * **[Speed Launch Metrics](https://docs.google.com/document/d/1Ww487ZskJ-xBmJGwPO-XPz_QcJvw-kSNffm0nPhVpj8/edit):
+  * **[An overview of tracks](speed_tracks.md)**: lists the tracks and key contact points.
+  * **[Speed Launch Metrics](https://docs.google.com/document/d/1Ww487ZskJ-xBmJGwPO-XPz_QcJvw-kSNffm0nPhVpj8/edit)**:
     the important high-level metrics we measure for each track.
 
 ## Laboratory Metrics
@@ -26,11 +26,11 @@
 
   * **[What is the perf waterfall?](perf_waterfall.md)** An overview of the
     waterfall that runs the continuous build.
-  * **[How telemetry works](https://github.com/catapult-project/catapult/blob/master/telemetry/README.md):
+  * **[How telemetry works](https://github.com/catapult-project/catapult/blob/master/telemetry/README.md)**:
     An overview of telemetry, our performance testing harness.
-  * **[How perf bisects work](bisects.md): An overview of the bisect bots,
+  * **[How perf bisects work](bisects.md)**: An overview of the bisect bots,
     which narrow down regressions over a CL range to a specific commit.
-  * **Benchmarks*
+  * **Benchmarks**
     * **[Benchmark Policy](https://docs.google.com/document/d/1ni2MIeVnlH4bTj4yvEDMVNxgL73PqK_O9_NUm3NW3BA/edit)**:
       An overview of the benchmark harnesses available in Chrome, and how to
       find the right place to add a new test case.
@@ -60,4 +60,4 @@
 The **[Speed Launch Metrics](https://docs.google.com/document/d/1Ww487ZskJ-xBmJGwPO-XPz_QcJvw-kSNffm0nPhVpj8/edit)**
 doc explains metrics available in UMA for end user performance. If you want to
 test how your change impacts these metrics for end users, you'll probably want
-to **[Run a Finch Trial](http://goto.google.com/finch101)**.
\ No newline at end of file
+to **[Run a Finch Trial](http://goto.google.com/finch101)**.
diff --git a/docs/speed/images/bisect_dialog.png b/docs/speed/images/bisect_dialog.png
new file mode 100644
index 0000000..f2c4edb
--- /dev/null
+++ b/docs/speed/images/bisect_dialog.png
Binary files differ
diff --git a/docs/speed/images/bisect_graph.png b/docs/speed/images/bisect_graph.png
new file mode 100644
index 0000000..1db1be46
--- /dev/null
+++ b/docs/speed/images/bisect_graph.png
Binary files differ
diff --git a/docs/speed/images/bisect_stdout.png b/docs/speed/images/bisect_stdout.png
new file mode 100644
index 0000000..51b1970
--- /dev/null
+++ b/docs/speed/images/bisect_stdout.png
Binary files differ
diff --git a/docs/speed/images/bisecting_revision_step.png b/docs/speed/images/bisecting_revision_step.png
new file mode 100644
index 0000000..b1379baf
--- /dev/null
+++ b/docs/speed/images/bisecting_revision_step.png
Binary files differ
diff --git a/docs/speed/perf_trybots.md b/docs/speed/perf_trybots.md
new file mode 100644
index 0000000..181b9e3
--- /dev/null
+++ b/docs/speed/perf_trybots.md
@@ -0,0 +1,94 @@
+# Perf Try Bots
+
+[TOC]
+
+## What are perf try bots?
+
+Chrome has a performance lab with dozens of device and OS configurations. You
+can run performance tests on an unsubmitted CL on these devices using the
+perf try bots.
+
+## Supported platforms
+
+The platforms available in the lab change over time. To find the currently
+available platforms, run `tools/perf/run_benchmark try --help`.
+
+Example output:
+
+```
+> tools/perf/run_benchmark try --help
+usage: Run telemetry benchmarks on trybot. You can add all the benchmark options available except the --browser option
+       [-h] [--repo_path <repo path>] [--deps_revision <deps revision>]
+       <trybot name> <benchmark name>
+
+positional arguments:
+  <trybot name>         specify which bots to run telemetry benchmarks on.  Allowed values are:
+                        Mac Builder
+                        all
+                        all-android
+                        all-linux
+                        all-mac
+                        all-win
+                        android-fyi
+                        android-nexus5
+                        android-nexus5X
+                        android-nexus6
+                        android-nexus7
+                        android-one
+                        android-webview-arm64-aosp
+                        android-webview-nexus6-aosp
+                        linux
+                        mac-10-11
+                        mac-10-12
+                        mac-10-12-mini-8gb
+                        mac-air
+                        mac-pro
+                        mac-retina
+                        staging-android-nexus5X
+                        staging-linux
+                        staging-mac-10-12
+                        staging-win
+                        win
+                        win-8
+                        win-x64
+                        winx64-10
+                        winx64-high-dpi
+                        winx64-zen
+                        winx64ati
+                        winx64intel
+                        winx64nvidia
+
+```
+
+## Supported benchmarks
+
+All the telemetry benchmarks are supported by the perf trybots. To get a full
+list, run `tools/perf/run_benchmark list`.
+
+To learn more about the benchmark, you can read about the
+[system health benchmarks](https://docs.google.com/document/d/1BM_6lBrPzpMNMtcyi2NFKGIzmzIQ1oH3OlNG27kDGNU/edit?ts=57e92782),
+which test Chrome's performance at a high level, and the
+[benchmark harnesses](https://docs.google.com/spreadsheets/d/1ZdQ9OHqEjF5v8dqNjd7lGUjJnK6sgi8MiqO7eZVMgD0/edit#gid=0),
+which cover more specific areas.
+
+## Starting a perf try job
+
+Use this command line:
+
+`tools/perf/run_benchmark try <trybot_name> <benchmark_name>`
+
+See above for how to choose a trybot and benchmark.
+
+Run `tools/perf/run_benchmark try --help` for more information about available
+options.
+
+## Interpreting the results
+
+Perf trybots create a code review under the covers to hold the trybot results.
+The code review will list links to buildbot status pages for the try jobs.
+On each buildbot status page, you will see a "HTML Results" link. You can click
+it to see detailed information about the performance test results with and
+without your patch.
+
+**[Here is the documentation](https://github.com/catapult-project/catapult/blob/master/docs/metrics-results-ui.md)**
+on reading the results.
\ No newline at end of file
diff --git a/docs/speed/perf_waterfall.md b/docs/speed/perf_waterfall.md
new file mode 100644
index 0000000..a8df529
--- /dev/null
+++ b/docs/speed/perf_waterfall.md
@@ -0,0 +1,30 @@
+# chromium.perf Waterfall
+
+## Overview
+
+The [chromium.perf waterfall](http://build.chromium.org/p/chromium.perf/waterfall)
+continuously builds and runs our performance tests on real Android, Windows,
+Mac, and Linux hardware. Results are reported to the
+[Performance Dashboard](https://chromeperf.appspot.com/) for analysis. The
+[Perfbot Health Sheriffing Rotation](perf_bot_sheriffing.md) keeps the build
+green and files bugs on regressions. They maintain
+[Chrome's Core Principles](https://www.chromium.org/developers/core-principles)
+of speed:
+
+> "If you make a change that regresses measured performance, you will be
+> required to fix it or revert".
+
+## Contact
+
+  * You can reach the Chromium performance sheriffs at perf-sheriffs@chromium.org.
+  * Bugs on waterfall issues should have Component:
+    [Speed>Benchmarks>Waterfall](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=component%3ASpeed%3EBenchmarks%3EWaterfall+&colspec=ID+Pri+M+Stars+ReleaseBlock+Component+Status+Owner+Summary+OS+Modified&x=m&y=releaseblock&cells=ids).
+
+## Links
+
+  * [Perfbot Health Sheriffing Rotation](perf_bot_sheriffing.md)
+  * [Debugging an Android Perf Failure](perf_bot_sheriffing.md#Android-Device-failures)
+  * TODO: Page on how to repro failures locally
+  * TODO: Page on how to connect to bot in lab
+  * TODO: Page on how to hack on buildbot/recipe code
+  * TODO: Page on bringing up new hardware
\ No newline at end of file
diff --git a/docs/speed/speed_tracks.md b/docs/speed/speed_tracks.md
new file mode 100644
index 0000000..daf5e27a
--- /dev/null
+++ b/docs/speed/speed_tracks.md
@@ -0,0 +1,98 @@
+# Speed Tracks
+
+We've organized our focus into tracks to address the following concerns:
+
+ * Utilize domain expertise to manage incoming bugs and regression reports
+ * Have a clear escalation paths
+ * Organize optimization efforts as needed
+ * Identify opportunities for improvement and to manage projects
+
+## Tracks Organization
+
+### Memory
+
+This track deals with memory use by Chrome on all platforms. Primary focus is:
+
+ * Gracefully deal with situations where user is out of memory (OOM)
+ * Manage memory for idle and backgrounded tabs
+
+#### Links
+ 
+ * [Mailing List](https://groups.google.com/a/chromium.org/forum/#!forum/memory-dev)
+ * Performance-Memory [Bug
+   Queue](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Performance%3DMemory)
+ * [Docs](https://chromium.googlesource.com/chromium/src/+/master/docs/memory)
+
+### Power
+
+The Power track is concerned with improving power usage for our users.
+Collectively, our product has an impact on global greenhouse gas imitions and we
+want to do our best to make that efficient. If we can give users a good
+experience of not burning laps/hands or making fan noises, we feel like we did a
+good job.
+
+#### Links
+
+ * Performance-Power [Bug
+   Queue](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Performance%3DPower)
+
+### Loading
+
+The Loading track focuses on the time between click to the time when you can
+interact with a website.
+
+#### Links
+
+ * [Mailing
+   List](https://groups.google.com/a/chromium.org/forum/#!forum/loading-dev)
+ * Performance-Loading [Bug
+   Queue](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Performance%3DLoading)
+
+### Responsiveness
+
+Responsiveness track focuses on making sure all websites have smooth transitions
+by serving 60fps, and that the click to action time is not noticible.
+
+#### Links
+
+ * Performance-Responsiveness [Bug
+   Queue](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Performance%3DResponsiveness)
+
+### Binary Size
+
+Chrome has an update for you at least every six weeks. Since we do that for all
+of our users, we want to be nice to our users where downloading updates costs
+real money. We also don't want to hog all of the disk space on low end phones.
+So we focus attention on making sure we don't include bits in our update that
+are not necessary for users.
+
+#### Links
+
+ * [Mailing List](://groups.google.com/a/chromium.org/forum/#!forum/binary-size)
+ * Performance-Size [Bug
+   Queue](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Performance%3DSize)
+
+### Data Usage
+
+Data Usage is a focus on the question: Do the user see or need every byte
+downloaded? By looking at this, we can save user's cost of data, time to load,
+memory and power. 
+
+#### Links
+
+ * Performance-Data [Bug
+   Queue](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Performance%3DData)
+
+### Startup, Omnibox, Browser UI, etc.
+
+There are a handful of performance angles that don't fit into the tracks already
+mentioned. Historically, we've put these into a "Browser" bucket as that's
+descriptive of what's left over. These are things like making sure the Omnibox
+experience on Chrome is fast, making sure all of the Chrome UI, e.g. Settings,
+is fast and that the browser startup and session restore doesn't allow users to
+make coffee before they use the browser.
+
+#### Links
+
+ * Performance-Browser [Bug
+   Queue](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Performance%3DBrowser)
diff --git a/docs/webui_explainer.md b/docs/webui_explainer.md
index bd298d96..e9a9284 100644
--- a/docs/webui_explainer.md
+++ b/docs/webui_explainer.md
@@ -118,8 +118,8 @@
 ```c++
 // RenderFrameHostImpl::AllowBindings():
 if (bindings_flags & BINDINGS_POLICY_WEB_UI) {
-	ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings(
-			GetProcess()->GetID());
+  ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings(
+      GetProcess()->GetID());
 }
 ```
 
@@ -449,7 +449,7 @@
 ```c++
 // WebUIMessageHandler::RejectJavascriptCallback():
 CallJavascriptFunction("cr.webUIResponse", callback_id, base::Value(false),
-											 response);
+                       response);
 ```
 
 See also: [`ResolveJavascriptCallback`](#ResolveJavascriptCallback)
@@ -503,10 +503,10 @@
 ```c++
 // WebUIExtension::Install():
 v8::Local<v8::Object> chrome =
-		GetOrCreateChromeObject(isolate, context->Global());
+    GetOrCreateChromeObject(isolate, context->Global());
 chrome->Set(gin::StringToSymbol(isolate, "send"),
-						gin::CreateFunctionTemplate(
-								isolate, base::Bind(&WebUIExtension::Send))->GetFunction());
+            gin::CreateFunctionTemplate(
+                isolate, base::Bind(&WebUIExtension::Send))->GetFunction());
 ```
 
 The `chrome.send()` method takes a message name and argument list.
@@ -521,8 +521,8 @@
 ```c++
 // In the renderer (WebUIExtension::Send()):
 render_view->Send(new ViewHostMsg_WebUISend(render_view->GetRoutingID(),
-																						frame->GetDocument().Url(),
-																						message, *content));
+                                            frame->GetDocument().Url(),
+                                            message, *content));
 ```
 ```c++
 // In the browser (WebUIImpl::OnMessageReceived()):
diff --git a/extensions/browser/api/cast_channel/cast_channel_api.cc b/extensions/browser/api/cast_channel/cast_channel_api.cc
index 89273a0..bdbea33 100644
--- a/extensions/browser/api/cast_channel/cast_channel_api.cc
+++ b/extensions/browser/api/cast_channel/cast_channel_api.cc
@@ -164,15 +164,6 @@
   return browser_context_;
 }
 
-void CastChannelAPI::SetPingTimeoutTimerForTest(
-    std::unique_ptr<base::Timer> timer) {
-  injected_timeout_timer_ = std::move(timer);
-}
-
-std::unique_ptr<base::Timer> CastChannelAPI::GetInjectedTimeoutTimerForTest() {
-  return std::move(injected_timeout_timer_);
-}
-
 CastChannelAPI::~CastChannelAPI() {}
 
 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction()
@@ -297,7 +288,7 @@
         base::TimeDelta::FromMilliseconds(connect_info.timeout.get()
                                               ? *connect_info.timeout
                                               : kDefaultConnectTimeoutMillis),
-        liveness_timeout_ > base::TimeDelta(), api_->GetLogger(),
+        liveness_timeout_, ping_interval_, api_->GetLogger(),
         connect_info.capabilities.get() ? *connect_info.capabilities
                                         : CastDeviceCapability::NONE);
   }
@@ -309,20 +300,6 @@
           base::Bind(&CastChannelAPI::SendEvent, api_->AsWeakPtr(),
                      extension_->id()),
           socket, api_->GetLogger()));
-  if (socket->keep_alive()) {
-    // Wrap read delegate in a KeepAliveDelegate for timeout handling.
-    KeepAliveDelegate* keep_alive =
-        new KeepAliveDelegate(socket, api_->GetLogger(), std::move(delegate),
-                              ping_interval_, liveness_timeout_);
-    std::unique_ptr<base::Timer> injected_timer =
-        api_->GetInjectedTimeoutTimerForTest();
-    if (injected_timer) {
-      keep_alive->SetTimersForTest(base::MakeUnique<base::Timer>(false, false),
-                                   std::move(injected_timer));
-    }
-    delegate.reset(keep_alive);
-  }
-
   socket->Connect(std::move(delegate),
                   base::Bind(&CastChannelOpenFunction::OnOpen, this));
 }
diff --git a/extensions/browser/api/cast_channel/cast_channel_api.h b/extensions/browser/api/cast_channel/cast_channel_api.h
index 6ac7eff..baecc33 100644
--- a/extensions/browser/api/cast_channel/cast_channel_api.h
+++ b/extensions/browser/api/cast_channel/cast_channel_api.h
@@ -65,13 +65,6 @@
   // Returns the API browser context.
   content::BrowserContext* GetBrowserContext() const;
 
-  // Sets injected ping timeout timer for testing.
-  void SetPingTimeoutTimerForTest(std::unique_ptr<base::Timer> timer);
-
-  // Gets the injected ping timeout timer, if set.
-  // Returns a null scoped ptr if there is no injected timer.
-  std::unique_ptr<base::Timer> GetInjectedTimeoutTimerForTest();
-
   // Sends an event to the extension's EventRouter, if it exists.
   void SendEvent(const std::string& extension_id, std::unique_ptr<Event> event);
 
@@ -88,7 +81,6 @@
   content::BrowserContext* const browser_context_;
   scoped_refptr<cast_channel::Logger> logger_;
   std::unique_ptr<cast_channel::CastSocket> socket_for_test_;
-  std::unique_ptr<base::Timer> injected_timeout_timer_;
 
   DISALLOW_COPY_AND_ASSIGN(CastChannelAPI);
 };
diff --git a/extensions/browser/api/cast_channel/cast_channel_apitest.cc b/extensions/browser/api/cast_channel/cast_channel_apitest.cc
index b1547902..06d2c82 100644
--- a/extensions/browser/api/cast_channel/cast_channel_apitest.cc
+++ b/extensions/browser/api/cast_channel/cast_channel_apitest.cc
@@ -90,8 +90,6 @@
 
   void SetUpMockCastSocket() {
     extensions::CastChannelAPI* api = GetApi();
-    timeout_timer_ = new base::MockTimer(true, false);
-    api->SetPingTimeoutTimerForTest(base::WrapUnique(timeout_timer_));
 
     net::IPEndPoint ip_endpoint(net::IPAddress(192, 168, 1, 1), 8009);
     mock_cast_socket_ = new MockCastSocket;
@@ -187,7 +185,9 @@
   void FireTimeout() {
     content::BrowserThread::PostTask(
         content::BrowserThread::IO, FROM_HERE,
-        base::Bind(&base::MockTimer::Fire, base::Unretained(timeout_timer_)));
+        base::Bind(&CastTransport::Delegate::OnError,
+                   base::Unretained(message_delegate_),
+                   cast_channel::ChannelError::PING_TIMEOUT));
   }
 
   extensions::CastChannelOpenFunction* CreateOpenFunction(
@@ -207,7 +207,6 @@
   }
 
   MockCastSocket* mock_cast_socket_;
-  base::MockTimer* timeout_timer_;
   net::IPEndPoint ip_endpoint_;
   LastError last_error_;
   CastTransport::Delegate* message_delegate_;
diff --git a/extensions/browser/api/device_permissions_prompt.cc b/extensions/browser/api/device_permissions_prompt.cc
index 5826de3..eea7746 100644
--- a/extensions/browser/api/device_permissions_prompt.cc
+++ b/extensions/browser/api/device_permissions_prompt.cc
@@ -16,8 +16,8 @@
 #include "device/hid/hid_device_filter.h"
 #include "device/hid/hid_device_info.h"
 #include "device/hid/hid_service.h"
+#include "device/usb/public/cpp/filter_utils.h"
 #include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
 #include "device/usb/usb_ids.h"
 #include "device/usb/usb_service.h"
 #include "extensions/browser/api/device_permissions_manager.h"
@@ -33,7 +33,7 @@
 using device::HidDeviceFilter;
 using device::HidService;
 using device::UsbDevice;
-using device::UsbDeviceFilter;
+using device::mojom::UsbDeviceFilterPtr;
 using device::UsbService;
 
 namespace extensions {
@@ -73,10 +73,10 @@
       const Extension* extension,
       content::BrowserContext* context,
       bool multiple,
-      const std::vector<UsbDeviceFilter>& filters,
+      std::vector<UsbDeviceFilterPtr> filters,
       const DevicePermissionsPrompt::UsbDevicesCallback& callback)
       : Prompt(extension, context, multiple),
-        filters_(filters),
+        filters_(std::move(filters)),
         callback_(callback),
         service_observer_(this) {}
 
@@ -120,7 +120,7 @@
 
   // device::UsbService::Observer implementation:
   void OnDeviceAdded(scoped_refptr<UsbDevice> device) override {
-    if (!UsbDeviceFilter::MatchesAny(*device, filters_))
+    if (!UsbDeviceFilterMatchesAny(filters_, *device))
       return;
 
     std::unique_ptr<DeviceInfo> device_info(new UsbDeviceInfo(device));
@@ -151,7 +151,7 @@
     }
   }
 
-  std::vector<UsbDeviceFilter> filters_;
+  std::vector<UsbDeviceFilterPtr> filters_;
   DevicePermissionsPrompt::UsbDevicesCallback callback_;
   ScopedObserver<UsbService, UsbService::Observer> service_observer_;
 };
@@ -350,10 +350,10 @@
     const Extension* extension,
     content::BrowserContext* context,
     bool multiple,
-    const std::vector<UsbDeviceFilter>& filters,
+    std::vector<UsbDeviceFilterPtr> filters,
     const UsbDevicesCallback& callback) {
   prompt_ = new UsbDevicePermissionsPrompt(extension, context, multiple,
-                                           filters, callback);
+                                           std::move(filters), callback);
   ShowDialog();
 }
 
@@ -382,7 +382,7 @@
 DevicePermissionsPrompt::CreateUsbPromptForTest(const Extension* extension,
                                                 bool multiple) {
   return make_scoped_refptr(new UsbDevicePermissionsPrompt(
-      extension, nullptr, multiple, std::vector<UsbDeviceFilter>(),
+      extension, nullptr, multiple, std::vector<UsbDeviceFilterPtr>(),
       base::Bind(&NoopUsbCallback)));
 }
 
diff --git a/extensions/browser/api/device_permissions_prompt.h b/extensions/browser/api/device_permissions_prompt.h
index 40d29dc2..d2502f5 100644
--- a/extensions/browser/api/device_permissions_prompt.h
+++ b/extensions/browser/api/device_permissions_prompt.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
+#include "device/usb/public/interfaces/device_manager.mojom.h"
 
 namespace content {
 class BrowserContext;
@@ -25,7 +26,6 @@
 class HidDeviceFilter;
 class HidDeviceInfo;
 class UsbDevice;
-struct UsbDeviceFilter;
 }
 
 namespace extensions {
@@ -129,7 +129,7 @@
   void AskForUsbDevices(const Extension* extension,
                         content::BrowserContext* context,
                         bool multiple,
-                        const std::vector<device::UsbDeviceFilter>& filters,
+                        std::vector<device::mojom::UsbDeviceFilterPtr> filters,
                         const UsbDevicesCallback& callback);
 
   void AskForHidDevices(const Extension* extension,
diff --git a/extensions/browser/api/usb/BUILD.gn b/extensions/browser/api/usb/BUILD.gn
index 1f90d05..622efa0 100644
--- a/extensions/browser/api/usb/BUILD.gn
+++ b/extensions/browser/api/usb/BUILD.gn
@@ -28,6 +28,8 @@
     "//content/public/browser",
     "//content/public/common",
     "//device/usb",
+    "//device/usb/public/cpp",
+    "//device/usb/public/interfaces",
     "//extensions/common/api",
   ]
 
diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc
index c829565..2503c0c 100644
--- a/extensions/browser/api/usb/usb_api.cc
+++ b/extensions/browser/api/usb/usb_api.cc
@@ -16,6 +16,8 @@
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "device/base/device_client.h"
+#include "device/usb/public/cpp/filter_utils.h"
+#include "device/usb/public/interfaces/device_manager.mojom.h"
 #include "device/usb/usb_descriptors.h"
 #include "device/usb/usb_device_handle.h"
 #include "device/usb/usb_service.h"
@@ -50,11 +52,11 @@
 namespace SetInterfaceAlternateSetting = usb::SetInterfaceAlternateSetting;
 
 using content::BrowserThread;
+using device::mojom::UsbDeviceFilterPtr;
 using device::UsbConfigDescriptor;
 using device::UsbControlTransferRecipient;
 using device::UsbControlTransferType;
 using device::UsbDevice;
-using device::UsbDeviceFilter;
 using device::UsbDeviceHandle;
 using device::UsbEndpointDescriptor;
 using device::UsbInterfaceDescriptor;
@@ -369,18 +371,30 @@
   return output;
 }
 
-void ConvertDeviceFilter(const usb::DeviceFilter& input,
-                         UsbDeviceFilter* output) {
-  if (input.vendor_id)
+device::mojom::UsbDeviceFilterPtr ConvertDeviceFilter(
+    const usb::DeviceFilter& input) {
+  auto output = device::mojom::UsbDeviceFilter::New();
+  if (input.vendor_id) {
+    output->has_vendor_id = true;
     output->vendor_id = *input.vendor_id;
-  if (input.product_id)
+  }
+  if (input.product_id) {
+    output->has_product_id = true;
     output->product_id = *input.product_id;
-  if (input.interface_class)
-    output->interface_class = *input.interface_class;
-  if (input.interface_subclass)
-    output->interface_subclass = *input.interface_subclass;
-  if (input.interface_protocol)
-    output->interface_protocol = *input.interface_protocol;
+  }
+  if (input.interface_class) {
+    output->has_class_code = true;
+    output->class_code = *input.interface_class;
+  }
+  if (input.interface_subclass) {
+    output->has_subclass_code = true;
+    output->subclass_code = *input.interface_subclass;
+  }
+  if (input.interface_protocol) {
+    output->has_protocol_code = true;
+    output->protocol_code = *input.interface_protocol;
+  }
+  return output;
 }
 
 }  // namespace
@@ -590,16 +604,19 @@
   EXTENSION_FUNCTION_VALIDATE(parameters.get());
 
   if (parameters->options.filters) {
-    filters_.resize(parameters->options.filters->size());
-    for (size_t i = 0; i < parameters->options.filters->size(); ++i) {
-      ConvertDeviceFilter(parameters->options.filters->at(i), &filters_[i]);
-    }
+    filters_.reserve(parameters->options.filters->size());
+    for (const auto& filter : *parameters->options.filters)
+      filters_.push_back(ConvertDeviceFilter(filter));
   }
   if (parameters->options.vendor_id) {
-    filters_.resize(filters_.size() + 1);
-    filters_.back().vendor_id = *parameters->options.vendor_id;
-    if (parameters->options.product_id)
-      filters_.back().product_id = *parameters->options.product_id;
+    auto filter = device::mojom::UsbDeviceFilter::New();
+    filter->has_vendor_id = true;
+    filter->vendor_id = *parameters->options.vendor_id;
+    if (parameters->options.product_id) {
+      filter->has_product_id = true;
+      filter->product_id = *parameters->options.product_id;
+    }
+    filters_.push_back(std::move(filter));
   }
 
   UsbService* service = device::DeviceClient::Get()->GetUsbService();
@@ -617,7 +634,7 @@
   std::unique_ptr<base::ListValue> result(new base::ListValue());
   UsbGuidMap* guid_map = UsbGuidMap::Get(browser_context());
   for (const scoped_refptr<UsbDevice>& device : devices) {
-    if (UsbDeviceFilter::MatchesAny(*device, filters_) &&
+    if (UsbDeviceFilterMatchesAny(filters_, *device) &&
         HasDevicePermission(device)) {
       Device api_device;
       guid_map->GetApiDevice(device, &api_device);
@@ -648,12 +665,11 @@
     multiple = *parameters->options.multiple;
   }
 
-  std::vector<UsbDeviceFilter> filters;
+  std::vector<UsbDeviceFilterPtr> filters;
   if (parameters->options.filters) {
-    filters.resize(parameters->options.filters->size());
-    for (size_t i = 0; i < parameters->options.filters->size(); ++i) {
-      ConvertDeviceFilter(parameters->options.filters->at(i), &filters[i]);
-    }
+    filters.reserve(parameters->options.filters->size());
+    for (const auto& filter : *parameters->options.filters)
+      filters.push_back(ConvertDeviceFilter(filter));
   }
 
   prompt_ = ExtensionsAPIClient::Get()->CreateDevicePermissionsPrompt(
@@ -663,7 +679,7 @@
   }
 
   prompt_->AskForUsbDevices(
-      extension(), browser_context(), multiple, filters,
+      extension(), browser_context(), multiple, std::move(filters),
       base::Bind(&UsbGetUserSelectedDevicesFunction::OnDevicesChosen, this));
   return RespondLater();
 }
diff --git a/extensions/browser/api/usb/usb_api.h b/extensions/browser/api/usb/usb_api.h
index ef2086d5..17ad3538 100644
--- a/extensions/browser/api/usb/usb_api.h
+++ b/extensions/browser/api/usb/usb_api.h
@@ -14,8 +14,8 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "device/usb/public/interfaces/device_manager.mojom.h"
 #include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
 #include "device/usb/usb_device_handle.h"
 #include "extensions/browser/api/api_resource_manager.h"
 #include "extensions/browser/extension_function.h"
@@ -102,7 +102,7 @@
   void OnGetDevicesComplete(
       const std::vector<scoped_refptr<device::UsbDevice>>& devices);
 
-  std::vector<device::UsbDeviceFilter> filters_;
+  std::vector<device::mojom::UsbDeviceFilterPtr> filters_;
 
   DISALLOW_COPY_AND_ASSIGN(UsbGetDevicesFunction);
 };
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 1e0b269..c1edae4 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -293,6 +293,7 @@
       "//crypto",
       "//device/bluetooth",
       "//device/usb",
+      "//device/usb/public/cpp",
       "//extensions:extensions_resources",
       "//extensions/common/api",
       "//extensions/strings",
diff --git a/extensions/common/api/printer_provider/usb_printer_manifest_data.cc b/extensions/common/api/printer_provider/usb_printer_manifest_data.cc
index fc5c466..3812a32 100644
--- a/extensions/common/api/printer_provider/usb_printer_manifest_data.cc
+++ b/extensions/common/api/printer_provider/usb_printer_manifest_data.cc
@@ -5,16 +5,16 @@
 #include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "device/usb/public/cpp/filter_utils.h"
+#include "device/usb/public/interfaces/device_manager.mojom.h"
 #include "device/usb/usb_device.h"
-#include "device/usb/usb_device_filter.h"
 #include "extensions/common/api/extensions_manifest_types.h"
 #include "extensions/common/manifest_constants.h"
 
-using device::UsbDeviceFilter;
-
 namespace extensions {
 
 UsbPrinterManifestData::UsbPrinterManifestData() {
@@ -48,30 +48,37 @@
       return nullptr;
     }
 
-    UsbDeviceFilter output;
-    output.vendor_id = input.vendor_id;
+    auto output = device::mojom::UsbDeviceFilter::New();
+    output->has_vendor_id = true;
+    output->vendor_id = input.vendor_id;
 
-    if (input.product_id)
-      output.product_id = *input.product_id;
+    if (input.product_id) {
+      output->has_product_id = true;
+      output->product_id = *input.product_id;
+    }
 
     if (input.interface_class) {
-      output.interface_class = *input.interface_class;
+      output->has_class_code = true;
+      output->class_code = *input.interface_class;
       if (input.interface_subclass) {
-        output.interface_subclass = *input.interface_subclass;
-        if (input.interface_protocol)
-          output.interface_protocol = *input.interface_protocol;
+        output->has_subclass_code = true;
+        output->subclass_code = *input.interface_subclass;
+        if (input.interface_protocol) {
+          output->has_protocol_code = true;
+          output->protocol_code = *input.interface_protocol;
+        }
       }
     }
 
-    result->filters_.push_back(output);
+    result->filters_.push_back(std::move(output));
   }
   return result;
 }
 
 bool UsbPrinterManifestData::SupportsDevice(
-    const scoped_refptr<device::UsbDevice>& device) const {
+    const device::UsbDevice& device) const {
   for (const auto& filter : filters_) {
-    if (filter.Matches(*device))
+    if (UsbDeviceFilterMatches(*filter, device))
       return true;
   }
 
diff --git a/extensions/common/api/printer_provider/usb_printer_manifest_data.h b/extensions/common/api/printer_provider/usb_printer_manifest_data.h
index b4d3650..486c761 100644
--- a/extensions/common/api/printer_provider/usb_printer_manifest_data.h
+++ b/extensions/common/api/printer_provider/usb_printer_manifest_data.h
@@ -7,11 +7,11 @@
 
 #include <vector>
 
+#include "device/usb/public/interfaces/device_manager.mojom.h"
 #include "extensions/common/extension.h"
 
 namespace device {
 class UsbDevice;
-struct UsbDeviceFilter;
 }
 
 namespace extensions {
@@ -32,14 +32,12 @@
       const base::Value& value,
       base::string16* error);
 
-  bool SupportsDevice(const scoped_refptr<device::UsbDevice>& device) const;
-
-  const std::vector<device::UsbDeviceFilter>& filters() const {
-    return filters_;
-  }
+  bool SupportsDevice(const device::UsbDevice& device) const;
 
  private:
-  std::vector<device::UsbDeviceFilter> filters_;
+  FRIEND_TEST_ALL_PREFIXES(UsbPrinterManifestTest, Filters);
+
+  std::vector<device::mojom::UsbDeviceFilterPtr> filters_;
 };
 
 }  // namespace extensions
diff --git a/extensions/common/api/printer_provider/usb_printer_manifest_unittest.cc b/extensions/common/api/printer_provider/usb_printer_manifest_unittest.cc
index cd35af1..35b339d 100644
--- a/extensions/common/api/printer_provider/usb_printer_manifest_unittest.cc
+++ b/extensions/common/api/printer_provider/usb_printer_manifest_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "device/usb/usb_device_filter.h"
 #include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
 #include "extensions/common/manifest_test.h"
 #include "extensions/common/value_builder.h"
@@ -21,19 +20,27 @@
   const UsbPrinterManifestData* manifest_data =
       UsbPrinterManifestData::Get(extension.get());
   ASSERT_TRUE(manifest_data);
-  EXPECT_EQ(2u, manifest_data->filters().size());
-  EXPECT_TRUE(DictionaryBuilder()
-                  .Set("vendorId", 1)
-                  .Set("productId", 2)
-                  .Build()
-                  ->Equals(manifest_data->filters()[0].ToValue().get()));
-  EXPECT_TRUE(DictionaryBuilder()
-                  .Set("vendorId", 1)
-                  .Set("interfaceClass", 2)
-                  .Set("interfaceSubclass", 3)
-                  .Set("interfaceProtocol", 4)
-                  .Build()
-                  ->Equals(manifest_data->filters()[1].ToValue().get()));
+  ASSERT_EQ(2u, manifest_data->filters_.size());
+
+  {
+    const device::mojom::UsbDeviceFilter& filter = *manifest_data->filters_[0];
+    EXPECT_TRUE(filter.has_vendor_id);
+    EXPECT_EQ(1, filter.vendor_id);
+    EXPECT_TRUE(filter.has_product_id);
+    EXPECT_EQ(2, filter.product_id);
+  }
+
+  {
+    const device::mojom::UsbDeviceFilter& filter = *manifest_data->filters_[1];
+    EXPECT_TRUE(filter.has_vendor_id);
+    EXPECT_EQ(1, filter.vendor_id);
+    EXPECT_TRUE(filter.has_class_code);
+    EXPECT_EQ(2, filter.class_code);
+    EXPECT_TRUE(filter.has_subclass_code);
+    EXPECT_EQ(3, filter.subclass_code);
+    EXPECT_TRUE(filter.has_protocol_code);
+    EXPECT_EQ(4, filter.protocol_code);
+  }
 }
 
 TEST_F(UsbPrinterManifestTest, InvalidFilter) {
diff --git a/extensions/utility/utility_handler.cc b/extensions/utility/utility_handler.cc
index ff3299d..d63e9ec 100644
--- a/extensions/utility/utility_handler.cc
+++ b/extensions/utility/utility_handler.cc
@@ -18,7 +18,6 @@
 #include "extensions/utility/unpacker.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/zlib/google/zip.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_switches.h"
diff --git a/extensions/utility/utility_handler.h b/extensions/utility/utility_handler.h
index c2a70e92..d04b3cb 100644
--- a/extensions/utility/utility_handler.h
+++ b/extensions/utility/utility_handler.h
@@ -5,9 +5,7 @@
 #ifndef EXTENSIONS_UTILITY_UTILITY_HANDLER_H_
 #define EXTENSIONS_UTILITY_UTILITY_HANDLER_H_
 
-namespace service_manager {
-class BinderRegistry;
-}
+#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace extensions {
 
diff --git a/gpu/command_buffer/service/gl_context_virtual.cc b/gpu/command_buffer/service/gl_context_virtual.cc
index 2582d00..9615f23 100644
--- a/gpu/command_buffer/service/gl_context_virtual.cc
+++ b/gpu/command_buffer/service/gl_context_virtual.cc
@@ -100,6 +100,10 @@
   return shared_context_->GetYUVToRGBConverter();
 }
 
+void GLContextVirtual::ForceReleaseVirtuallyCurrent() {
+  shared_context_->OnReleaseVirtuallyCurrent(this);
+}
+
 GLContextVirtual::~GLContextVirtual() {
   Destroy();
 }
diff --git a/gpu/command_buffer/service/gl_context_virtual.h b/gpu/command_buffer/service/gl_context_virtual.h
index d8c7c91..7af79b8 100644
--- a/gpu/command_buffer/service/gl_context_virtual.h
+++ b/gpu/command_buffer/service/gl_context_virtual.h
@@ -47,6 +47,7 @@
   bool WasAllocatedUsingRobustnessExtension() override;
   void SetUnbindFboOnMakeCurrent() override;
   gl::YUVToRGBConverter* GetYUVToRGBConverter() override;
+  void ForceReleaseVirtuallyCurrent() override;
 
  protected:
   ~GLContextVirtual() override;
diff --git a/gpu/ipc/service/gpu_command_buffer_stub.cc b/gpu/ipc/service/gpu_command_buffer_stub.cc
index 8c5705e8..cf8634c 100644
--- a/gpu/ipc/service/gpu_command_buffer_stub.cc
+++ b/gpu/ipc/service/gpu_command_buffer_stub.cc
@@ -791,10 +791,10 @@
     // is in an indeterminate state, since the GLStateRestorer was not
     // initialized at the time the GLContextVirtual was made current. In
     // the case that this command decoder is the next one to be
-    // processed, force a "full" MakeCurrent to be performed. Note that
-    // GpuChannel's initialization of the gpu::Capabilities expects the
-    // context to be left current.
-    context->ReleaseCurrent(surface_.get());
+    // processed, force a "full virtual" MakeCurrent to be performed.
+    // Note that GpuChannel's initialization of the gpu::Capabilities
+    // expects the context to be left current.
+    context->ForceReleaseVirtuallyCurrent();
     if (!context->MakeCurrent(surface_.get())) {
       LOG(ERROR) << "Failed to make context current after initialization.";
       return false;
diff --git a/ios/chrome/browser/upgrade/upgrade_center.mm b/ios/chrome/browser/upgrade/upgrade_center.mm
index 611868da..cc1657e 100644
--- a/ios/chrome/browser/upgrade/upgrade_center.mm
+++ b/ios/chrome/browser/upgrade/upgrade_center.mm
@@ -218,7 +218,7 @@
   // Stores the clients of the upgrade center. These objectiveC objects are not
   // retained.
   __strong NSHashTable<id<UpgradeCenterClientProtocol>>* clients_;
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
   BOOL inCallback_;
 #endif
 }
@@ -295,7 +295,7 @@
 }
 
 - (void)unregisterClient:(id<UpgradeCenterClientProtocol>)client {
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
   DCHECK(!inCallback_);
 #endif
   [clients_ removeObject:client];
@@ -375,13 +375,13 @@
 
 - (void)showUpgradeInfoBars {
 // Add an infobar on all the open tabs.
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
   inCallback_ = YES;
 #endif
   upgradeInfoBarIsVisible_ = YES;
   for (id<UpgradeCenterClientProtocol> upgradeClient in clients_)
     [upgradeClient showUpgrade:self];
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
   inCallback_ = NO;
 #endif
 
diff --git a/ios/net/cookies/cookie_store_ios_persistent_unittest.mm b/ios/net/cookies/cookie_store_ios_persistent_unittest.mm
index 1a41211..85706f6 100644
--- a/ios/net/cookies/cookie_store_ios_persistent_unittest.mm
+++ b/ios/net/cookies/cookie_store_ios_persistent_unittest.mm
@@ -30,6 +30,7 @@
   static const bool preserves_trailing_dots = true;
   static const bool filters_schemes = false;
   static const bool has_path_prefix_bug = false;
+  static const bool forbids_setting_empty_name = false;
   static const int creation_time_granularity_in_ms = 0;
   static const int enforces_prefixes = true;
   static const bool enforce_strict_secure = false;
@@ -125,4 +126,4 @@
   EXPECT_EQ("a=b", callback.cookie_line());
 }
 
-}  // namespace net
\ No newline at end of file
+}  // namespace net
diff --git a/ios/net/cookies/cookie_store_ios_unittest.mm b/ios/net/cookies/cookie_store_ios_unittest.mm
index 329e846..514738b 100644
--- a/ios/net/cookies/cookie_store_ios_unittest.mm
+++ b/ios/net/cookies/cookie_store_ios_unittest.mm
@@ -33,6 +33,7 @@
   static const bool preserves_trailing_dots = false;
   static const bool filters_schemes = false;
   static const bool has_path_prefix_bug = true;
+  static const bool forbids_setting_empty_name = true;
   static const int creation_time_granularity_in_ms = 1000;
 
   base::MessageLoop loop_;
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index 01c9601a..34f53ef4 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: 6d5844642b004594ba1607916032616f85c7c045
+Revision: b452b1e7fb85cc9aeef2df2b6a44cbe0e8594560
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/ios/web_view/test/BUILD.gn b/ios/web_view/test/BUILD.gn
index 8b22400c..4b55e70 100644
--- a/ios/web_view/test/BUILD.gn
+++ b/ios/web_view/test/BUILD.gn
@@ -16,10 +16,10 @@
 test("ios_web_view_inttests") {
   testonly = true
   sources = [
-    "chrome_web_view_kvo_inttest.mm",
-    "chrome_web_view_restorable_state_inttest.mm",
-    "chrome_web_view_test.h",
-    "chrome_web_view_test.mm",
+    "web_view_kvo_inttest.mm",
+    "web_view_restorable_state_inttest.mm",
+    "web_view_test.h",
+    "web_view_test.mm",
   ]
 
   deps = [
diff --git a/ios/web_view/test/chrome_web_view_kvo_inttest.mm b/ios/web_view/test/web_view_kvo_inttest.mm
similarity index 96%
rename from ios/web_view/test/chrome_web_view_kvo_inttest.mm
rename to ios/web_view/test/web_view_kvo_inttest.mm
index 4cbd0c2..03d6b667 100644
--- a/ios/web_view/test/chrome_web_view_kvo_inttest.mm
+++ b/ios/web_view/test/web_view_kvo_inttest.mm
@@ -8,8 +8,8 @@
 #include "base/strings/stringprintf.h"
 #import "base/strings/sys_string_conversions.h"
 #import "ios/testing/wait_util.h"
-#import "ios/web_view/test/chrome_web_view_test.h"
 #import "ios/web_view/test/observer.h"
+#import "ios/web_view/test/web_view_test.h"
 #import "ios/web_view/test/web_view_test_util.h"
 #import "net/base/mac/url_conversions.h"
 #include "testing/gtest_mac.h"
@@ -23,10 +23,10 @@
 
 // Tests that the KVO compliant properties of CWVWebView correctly report
 // changes.
-typedef ios_web_view::ChromeWebViewTest ChromeWebViewKvoTest;
+typedef ios_web_view::WebViewTest WebViewKvoTest;
 
 // Tests that CWVWebView correctly reports |canGoBack| and |canGoForward| state.
-TEST_F(ChromeWebViewKvoTest, CanGoBackForward) {
+TEST_F(WebViewKvoTest, CanGoBackForward) {
   Observer* back_observer = [[Observer alloc] init];
   [back_observer setObservedObject:web_view_ keyPath:@"canGoBack"];
 
@@ -84,7 +84,7 @@
 }
 
 // Tests that CWVWebView correctly reports current |title|.
-TEST_F(ChromeWebViewKvoTest, Title) {
+TEST_F(WebViewKvoTest, Title) {
   Observer* observer = [[Observer alloc] init];
   [observer setObservedObject:web_view_ keyPath:@"title"];
 
@@ -113,7 +113,7 @@
 }
 
 // Tests that CWVWebView correctly reports |isLoading| value.
-TEST_F(ChromeWebViewKvoTest, Loading) {
+TEST_F(WebViewKvoTest, Loading) {
   Observer* observer = [[Observer alloc] init];
   [observer setObservedObject:web_view_ keyPath:@"loading"];
 
@@ -141,7 +141,7 @@
 }
 
 // Tests that CWVWebView correctly reports |visibleURL| and |lastCommittedURL|.
-TEST_F(ChromeWebViewKvoTest, URLs) {
+TEST_F(WebViewKvoTest, URLs) {
   Observer* last_committed_url_observer = [[Observer alloc] init];
   [last_committed_url_observer setObservedObject:web_view_
                                          keyPath:@"lastCommittedURL"];
diff --git a/ios/web_view/test/chrome_web_view_restorable_state_inttest.mm b/ios/web_view/test/web_view_restorable_state_inttest.mm
similarity index 92%
rename from ios/web_view/test/chrome_web_view_restorable_state_inttest.mm
rename to ios/web_view/test/web_view_restorable_state_inttest.mm
index 0c682e0..ab20268 100644
--- a/ios/web_view/test/chrome_web_view_restorable_state_inttest.mm
+++ b/ios/web_view/test/web_view_restorable_state_inttest.mm
@@ -4,7 +4,7 @@
 
 #import <ChromeWebView/ChromeWebView.h>
 
-#import "ios/web_view/test/chrome_web_view_test.h"
+#import "ios/web_view/test/web_view_test.h"
 #import "ios/web_view/test/web_view_test_util.h"
 #include "testing/gtest_mac.h"
 
@@ -34,8 +34,8 @@
 
 // Tests encodeRestorableStateWithCoder: and decodeRestorableStateWithCoder:
 // methods.
-typedef ios_web_view::ChromeWebViewTest ChromeWebViewRestorableStateTest;
-TEST_F(ChromeWebViewRestorableStateTest, EncodeDecode) {
+typedef ios_web_view::WebViewTest WebViewRestorableStateTest;
+TEST_F(WebViewRestorableStateTest, EncodeDecode) {
   // Load 2 URLs to create non-default state.
   ASSERT_FALSE([web_view_ lastCommittedURL]);
   ASSERT_FALSE([web_view_ visibleURL]);
diff --git a/ios/web_view/test/chrome_web_view_test.h b/ios/web_view/test/web_view_test.h
similarity index 85%
rename from ios/web_view/test/chrome_web_view_test.h
rename to ios/web_view/test/web_view_test.h
index d6e73534..6ae8988b 100644
--- a/ios/web_view/test/chrome_web_view_test.h
+++ b/ios/web_view/test/web_view_test.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_VIEW_TEST_CHROME_WEB_VIEW_TEST_H_
-#define IOS_WEB_VIEW_TEST_CHROME_WEB_VIEW_TEST_H_
+#ifndef IOS_WEB_VIEW_TEST_WEB_VIEW_TEST_H_
+#define IOS_WEB_VIEW_TEST_WEB_VIEW_TEST_H_
 
 #include <memory>
 #include <string>
@@ -26,10 +26,10 @@
 // A test fixture for testing CWVWebView. A test server is also created to
 // support loading content. The server supports the urls returned by the GetUrl*
 // methods below.
-class ChromeWebViewTest : public PlatformTest {
+class WebViewTest : public PlatformTest {
  protected:
-  ChromeWebViewTest();
-  ~ChromeWebViewTest() override;
+  WebViewTest();
+  ~WebViewTest() override;
 
   // Returns URL to an html page with title set to |title|.
   GURL GetUrlForPageWithTitle(const std::string& title);
@@ -56,4 +56,4 @@
 
 }  // namespace ios_web_view
 
-#endif  // IOS_WEB_VIEW_TEST_CHROME_WEB_VIEW_TEST_H_
+#endif  // IOS_WEB_VIEW_TEST_WEB_VIEW_TEST_H_
diff --git a/ios/web_view/test/chrome_web_view_test.mm b/ios/web_view/test/web_view_test.mm
similarity index 89%
rename from ios/web_view/test/chrome_web_view_test.mm
rename to ios/web_view/test/web_view_test.mm
index 039eee4..e0728e5 100644
--- a/ios/web_view/test/chrome_web_view_test.mm
+++ b/ios/web_view/test/web_view_test.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web_view/test/chrome_web_view_test.h"
+#import "ios/web_view/test/web_view_test.h"
 
 #import <ChromeWebView/ChromeWebView.h>
 #import <Foundation/Foundation.h>
@@ -96,30 +96,30 @@
 
 namespace ios_web_view {
 
-ChromeWebViewTest::ChromeWebViewTest()
+WebViewTest::WebViewTest()
     : web_view_(test::CreateWebView()),
       test_server_(base::MakeUnique<net::EmbeddedTestServer>(
           net::test_server::EmbeddedTestServer::TYPE_HTTP)) {
   test_server_->RegisterRequestHandler(base::Bind(&TestRequestHandler));
 }
 
-ChromeWebViewTest::~ChromeWebViewTest() = default;
+WebViewTest::~WebViewTest() = default;
 
-void ChromeWebViewTest::SetUp() {
+void WebViewTest::SetUp() {
   PlatformTest::SetUp();
   ASSERT_TRUE(test_server_->Start());
 }
 
-GURL ChromeWebViewTest::GetUrlForPageWithTitle(const std::string& title) {
+GURL WebViewTest::GetUrlForPageWithTitle(const std::string& title) {
   return GetUrlForPageWithTitleAndBody(title, std::string());
 }
 
-GURL ChromeWebViewTest::GetUrlForPageWithHtmlBody(const std::string& html) {
+GURL WebViewTest::GetUrlForPageWithHtmlBody(const std::string& html) {
   return GetUrlForPageWithTitleAndBody(std::string(), html);
 }
 
-GURL ChromeWebViewTest::GetUrlForPageWithTitleAndBody(const std::string& title,
-                                                      const std::string& body) {
+GURL WebViewTest::GetUrlForPageWithTitleAndBody(const std::string& title,
+                                                const std::string& body) {
   GURL url = test_server_->GetURL(kPageHtmlPath);
 
   // Encode |title| and |body| in url query in order to build the server
diff --git a/media/base/media_log.cc b/media/base/media_log.cc
index abba917..df26788f 100644
--- a/media/base/media_log.cc
+++ b/media/base/media_log.cc
@@ -65,16 +65,10 @@
       return "VIDEO_SIZE_SET";
     case MediaLogEvent::DURATION_SET:
       return "DURATION_SET";
-    case MediaLogEvent::TOTAL_BYTES_SET:
-      return "TOTAL_BYTES_SET";
-    case MediaLogEvent::NETWORK_ACTIVITY_SET:
-      return "NETWORK_ACTIVITY_SET";
     case MediaLogEvent::ENDED:
       return "ENDED";
     case MediaLogEvent::TEXT_ENDED:
       return "TEXT_ENDED";
-    case MediaLogEvent::BUFFERED_EXTENTS_CHANGED:
-      return "BUFFERED_EXTENTS_CHANGED";
     case MediaLogEvent::MEDIA_ERROR_LOG_ENTRY:
       return "MEDIA_ERROR_LOG_ENTRY";
     case MediaLogEvent::MEDIA_INFO_LOG_ENTRY:
@@ -269,20 +263,6 @@
   return event;
 }
 
-std::unique_ptr<MediaLogEvent> MediaLog::CreateBufferedExtentsChangedEvent(
-    int64_t start,
-    int64_t current,
-    int64_t end) {
-  std::unique_ptr<MediaLogEvent> event(
-      CreateEvent(MediaLogEvent::BUFFERED_EXTENTS_CHANGED));
-  // These values are headed to JS where there is no int64_t so we use a double
-  // and accept loss of precision above 2^53 bytes (8 Exabytes).
-  event->params.SetDouble("buffer_start", start);
-  event->params.SetDouble("buffer_current", current);
-  event->params.SetDouble("buffer_end", end);
-  return event;
-}
-
 std::unique_ptr<MediaLogEvent> MediaLog::CreateBufferingStateChangedEvent(
     const std::string& property,
     BufferingState state) {
diff --git a/media/base/media_log.h b/media/base/media_log.h
index 9899018..d138fd6 100644
--- a/media/base/media_log.h
+++ b/media/base/media_log.h
@@ -89,11 +89,6 @@
   std::unique_ptr<MediaLogEvent> CreatePipelineErrorEvent(PipelineStatus error);
   std::unique_ptr<MediaLogEvent> CreateVideoSizeSetEvent(size_t width,
                                                          size_t height);
-  std::unique_ptr<MediaLogEvent> CreateBufferedExtentsChangedEvent(
-      int64_t start,
-      int64_t current,
-      int64_t end);
-
   std::unique_ptr<MediaLogEvent> CreateBufferingStateChangedEvent(
       const std::string& property,
       BufferingState state);
diff --git a/media/base/media_log_event.h b/media/base/media_log_event.h
index 73d81dc..9b7be60 100644
--- a/media/base/media_log_event.h
+++ b/media/base/media_log_event.h
@@ -65,8 +65,6 @@
     // These take a single parameter based upon the name of the event and of
     // the appropriate type. e.g. DURATION_SET: "duration" of type TimeDelta.
     DURATION_SET,
-    TOTAL_BYTES_SET,
-    NETWORK_ACTIVITY_SET,
 
     // Audio/Video stream playback has ended.
     ENDED,
@@ -74,12 +72,6 @@
     // Text stream playback has ended.
     TEXT_ENDED,
 
-    // The extents of the sliding buffer have changed.
-    // params: "buffer_start": <first buffered byte>.
-    //         "buffer_current": <current offset>.
-    //         "buffer_end": <last buffered byte>.
-    BUFFERED_EXTENTS_CHANGED,
-
     // Error log reported by media code such as reasons of playback error.
     MEDIA_ERROR_LOG_ENTRY,
     // params: "error": Error string describing the error detected.
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 073decac..0b3c60a 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1748,9 +1748,6 @@
     SetNetworkState(WebMediaPlayer::kNetworkStateLoading);
   if (ready_state_ == ReadyState::kReadyStateHaveFutureData && !is_downloading)
     SetReadyState(WebMediaPlayer::kReadyStateHaveEnoughData);
-  media_log_->AddEvent(
-      media_log_->CreateBooleanEvent(MediaLogEvent::NETWORK_ACTIVITY_SET,
-                                     "is_downloading_data", is_downloading));
 }
 
 void WebMediaPlayerImpl::OnSurfaceCreated(int surface_id) {
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc
index 6995a52..262f919 100644
--- a/mojo/edk/embedder/entrypoints.cc
+++ b/mojo/edk/embedder/entrypoints.cc
@@ -58,14 +58,6 @@
   return g_core->CancelWatch(watcher_handle, context);
 }
 
-MojoResult MojoAllocMessageImpl(uint32_t num_bytes,
-                                const MojoHandle* handles,
-                                uint32_t num_handles,
-                                MojoAllocMessageFlags flags,
-                                MojoMessageHandle* message) {
-  return g_core->AllocMessage(num_bytes, handles, num_handles, flags, message);
-}
-
 MojoResult MojoCreateMessageImpl(uintptr_t context,
                                  const MojoMessageOperationThunks* thunks,
                                  MojoMessageHandle* message) {
@@ -105,35 +97,15 @@
 }
 
 MojoResult MojoWriteMessageImpl(MojoHandle message_pipe_handle,
-                                const void* bytes,
-                                uint32_t num_bytes,
-                                const MojoHandle* handles,
-                                uint32_t num_handles,
+                                MojoMessageHandle message,
                                 MojoWriteMessageFlags flags) {
-  return g_core->WriteMessage(message_pipe_handle, bytes, num_bytes, handles,
-                              num_handles, flags);
-}
-
-MojoResult MojoWriteMessageNewImpl(MojoHandle message_pipe_handle,
-                                   MojoMessageHandle message,
-                                   MojoWriteMessageFlags flags) {
-  return g_core->WriteMessageNew(message_pipe_handle, message, flags);
+  return g_core->WriteMessage(message_pipe_handle, message, flags);
 }
 
 MojoResult MojoReadMessageImpl(MojoHandle message_pipe_handle,
-                               void* bytes,
-                               uint32_t* num_bytes,
-                               MojoHandle* handles,
-                               uint32_t* num_handles,
+                               MojoMessageHandle* message,
                                MojoReadMessageFlags flags) {
-  return g_core->ReadMessage(
-      message_pipe_handle, bytes, num_bytes, handles, num_handles, flags);
-}
-
-MojoResult MojoReadMessageNewImpl(MojoHandle message_pipe_handle,
-                                  MojoMessageHandle* message,
-                                  MojoReadMessageFlags flags) {
-  return g_core->ReadMessageNew(message_pipe_handle, message, flags);
+  return g_core->ReadMessage(message_pipe_handle, message, flags);
 }
 
 MojoResult MojoFuseMessagePipesImpl(MojoHandle handle0, MojoHandle handle1) {
@@ -283,9 +255,6 @@
                                     MojoCancelWatchImpl,
                                     MojoArmWatcherImpl,
                                     MojoFuseMessagePipesImpl,
-                                    MojoWriteMessageNewImpl,
-                                    MojoReadMessageNewImpl,
-                                    MojoAllocMessageImpl,
                                     MojoCreateMessageImpl,
                                     MojoFreeMessageImpl,
                                     MojoSerializeMessageImpl,
diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc
index a04a960..bf8933a 100644
--- a/mojo/edk/js/core.cc
+++ b/mojo/edk/js/core.cc
@@ -135,7 +135,7 @@
                             MojoReadMessageFlags flags) {
   MojoMessageHandle message;
   MojoResult result =
-      MojoReadMessageNew(handle.value(), &message, MOJO_READ_MESSAGE_FLAG_NONE);
+      MojoReadMessage(handle.value(), &message, MOJO_READ_MESSAGE_FLAG_NONE);
   if (result != MOJO_RESULT_OK) {
     gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
     dictionary.Set("result", result);
@@ -435,8 +435,6 @@
             .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE)
 
             .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE)
-            .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD",
-                      MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)
 
             .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE",
                       MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE)
diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn
index b3b87392..21c9bfc4 100644
--- a/mojo/edk/js/tests/BUILD.gn
+++ b/mojo/edk/js/tests/BUILD.gn
@@ -15,10 +15,44 @@
 group("tests") {
   testonly = true
   deps = [
+    ":mojo_js_integration_tests",
     ":mojo_js_unittests",
   ]
 }
 
+test("mojo_js_integration_tests") {
+  deps = [
+    ":js_to_cpp_bindings",
+    "//base/test:test_support",
+    "//gin:gin_test",
+    "//mojo/common",
+    "//mojo/edk/js",
+    "//mojo/edk/test:run_all_unittests",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//mojo/public/js:bindings",
+  ]
+
+  sources = [
+    "js_to_cpp_tests.cc",
+  ]
+
+  data = [
+    "js_to_cpp_tests.js",
+  ]
+
+  configs += [ "//v8:external_startup_data" ]
+}
+
+mojom("js_to_cpp_bindings") {
+  sources = [
+    "js_to_cpp.mojom",
+  ]
+
+  # TODO(crbug.com/699569): Convert to use the new JS bindings.
+  use_new_js_bindings = false
+}
+
 test("mojo_js_unittests") {
   deps = [
     "//base",
diff --git a/third_party/WebKit/Source/core/mojo/tests/JsToCpp.mojom b/mojo/edk/js/tests/js_to_cpp.mojom
similarity index 100%
rename from third_party/WebKit/Source/core/mojo/tests/JsToCpp.mojom
rename to mojo/edk/js/tests/js_to_cpp.mojom
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc
new file mode 100644
index 0000000..0ce95e8
--- /dev/null
+++ b/mojo/edk/js/tests/js_to_cpp_tests.cc
@@ -0,0 +1,459 @@
+// 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.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/at_exit.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "gin/array_buffer.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/v8_initializer.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/edk/js/mojo_runner_delegate.h"
+#include "mojo/edk/js/tests/js_to_cpp.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/wait.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+// Global value updated by some checks to prevent compilers from optimizing
+// reads out of existence.
+uint32_t g_waste_accumulator = 0;
+
+namespace {
+
+// Negative numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const int8_t kExpectedInt8Value = -65;
+const int16_t kExpectedInt16Value = -16961;
+const int32_t kExpectedInt32Value = -1145258561;
+const int64_t kExpectedInt64Value = -77263311946305LL;
+
+// Positive numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const uint8_t kExpectedUInt8Value = 65;
+const uint16_t kExpectedUInt16Value = 16961;
+const uint32_t kExpectedUInt32Value = 1145258561;
+const uint64_t kExpectedUInt64Value = 77263311946305LL;
+
+// Double/float values, including special case constants.
+const double kExpectedDoubleVal = 3.14159265358979323846;
+const double kExpectedDoubleInf = std::numeric_limits<double>::infinity();
+const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN();
+const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal);
+const float kExpectedFloatInf = std::numeric_limits<float>::infinity();
+const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN();
+
+// NaN has the property that it is not equal to itself.
+#define EXPECT_NAN(x) EXPECT_NE(x, x)
+
+void CheckDataPipe(ScopedDataPipeConsumerHandle data_pipe_handle) {
+  std::string buffer;
+  bool result = common::BlockingCopyToString(std::move(data_pipe_handle),
+                                             &buffer);
+  EXPECT_TRUE(result);
+  EXPECT_EQ(64u, buffer.size());
+  for (int i = 0; i < 64; ++i) {
+    EXPECT_EQ(i, buffer[i]);
+  }
+}
+
+void CheckMessagePipe(MessagePipeHandle message_pipe_handle) {
+  MojoResult result = Wait(message_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  result = ReadMessageRaw(message_pipe_handle, &bytes, &handles, 0);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(64u, bytes.size());
+  for (int i = 0; i < 64; ++i) {
+    EXPECT_EQ(255 - i, bytes[i]);
+  }
+}
+
+js_to_cpp::EchoArgsPtr BuildSampleEchoArgs() {
+  js_to_cpp::EchoArgsPtr args(js_to_cpp::EchoArgs::New());
+  args->si64 = kExpectedInt64Value;
+  args->si32 = kExpectedInt32Value;
+  args->si16 = kExpectedInt16Value;
+  args->si8 = kExpectedInt8Value;
+  args->ui64 = kExpectedUInt64Value;
+  args->ui32 = kExpectedUInt32Value;
+  args->ui16 = kExpectedUInt16Value;
+  args->ui8 = kExpectedUInt8Value;
+  args->float_val = kExpectedFloatVal;
+  args->float_inf = kExpectedFloatInf;
+  args->float_nan = kExpectedFloatNan;
+  args->double_val = kExpectedDoubleVal;
+  args->double_inf = kExpectedDoubleInf;
+  args->double_nan = kExpectedDoubleNan;
+  args->name.emplace("coming");
+  args->string_array.emplace(3);
+  (*args->string_array)[0] = "one";
+  (*args->string_array)[1] = "two";
+  (*args->string_array)[2] = "three";
+  return args;
+}
+
+void CheckSampleEchoArgs(js_to_cpp::EchoArgsPtr arg) {
+  EXPECT_EQ(kExpectedInt64Value, arg->si64);
+  EXPECT_EQ(kExpectedInt32Value, arg->si32);
+  EXPECT_EQ(kExpectedInt16Value, arg->si16);
+  EXPECT_EQ(kExpectedInt8Value, arg->si8);
+  EXPECT_EQ(kExpectedUInt64Value, arg->ui64);
+  EXPECT_EQ(kExpectedUInt32Value, arg->ui32);
+  EXPECT_EQ(kExpectedUInt16Value, arg->ui16);
+  EXPECT_EQ(kExpectedUInt8Value, arg->ui8);
+  EXPECT_EQ(kExpectedFloatVal, arg->float_val);
+  EXPECT_EQ(kExpectedFloatInf, arg->float_inf);
+  EXPECT_NAN(arg->float_nan);
+  EXPECT_EQ(kExpectedDoubleVal, arg->double_val);
+  EXPECT_EQ(kExpectedDoubleInf, arg->double_inf);
+  EXPECT_NAN(arg->double_nan);
+  EXPECT_EQ(std::string("coming"), *arg->name);
+  EXPECT_EQ(std::string("one"), (*arg->string_array)[0]);
+  EXPECT_EQ(std::string("two"), (*arg->string_array)[1]);
+  EXPECT_EQ(std::string("three"), (*arg->string_array)[2]);
+  CheckDataPipe(std::move(arg->data_handle));
+  CheckMessagePipe(arg->message_handle.get());
+}
+
+void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
+  if (list.is_null())
+    return;
+  CheckSampleEchoArgs(std::move(list->item));
+  CheckSampleEchoArgsList(list->next);
+}
+
+// More forgiving checks are needed in the face of potentially corrupt
+// messages. The values don't matter so long as all accesses are within
+// bounds.
+void CheckCorruptedString(const std::string& arg) {
+  for (size_t i = 0; i < arg.size(); ++i)
+    g_waste_accumulator += arg[i];
+}
+
+void CheckCorruptedString(const base::Optional<std::string>& arg) {
+  if (!arg)
+    return;
+  CheckCorruptedString(*arg);
+}
+
+void CheckCorruptedStringArray(
+    const base::Optional<std::vector<std::string>>& string_array) {
+  if (!string_array)
+    return;
+  for (size_t i = 0; i < string_array->size(); ++i)
+    CheckCorruptedString((*string_array)[i]);
+}
+
+void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) {
+  unsigned char buffer[100];
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  MojoResult result = MojoReadData(
+      data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE);
+  if (result != MOJO_RESULT_OK)
+    return;
+  for (uint32_t i = 0; i < buffer_size; ++i)
+    g_waste_accumulator += buffer[i];
+}
+
+void CheckCorruptedMessagePipe(MojoHandle message_pipe_handle) {
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  MojoResult result = ReadMessageRaw(MessagePipeHandle(message_pipe_handle),
+                                     &bytes, &handles, 0);
+  if (result != MOJO_RESULT_OK)
+    return;
+  for (uint32_t i = 0; i < bytes.size(); ++i)
+    g_waste_accumulator += bytes[i];
+}
+
+void CheckCorruptedEchoArgs(const js_to_cpp::EchoArgsPtr& arg) {
+  if (arg.is_null())
+    return;
+  CheckCorruptedString(arg->name);
+  CheckCorruptedStringArray(arg->string_array);
+  if (arg->data_handle.is_valid())
+    CheckCorruptedDataPipe(arg->data_handle.get().value());
+  if (arg->message_handle.is_valid())
+    CheckCorruptedMessagePipe(arg->message_handle.get().value());
+}
+
+void CheckCorruptedEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
+  if (list.is_null())
+    return;
+  CheckCorruptedEchoArgs(list->item);
+  CheckCorruptedEchoArgsList(list->next);
+}
+
+// Base Provider implementation class. It's expected that tests subclass and
+// override the appropriate Provider functions. When test is done quit the
+// run_loop().
+class CppSideConnection : public js_to_cpp::CppSide {
+ public:
+  CppSideConnection()
+      : run_loop_(nullptr),
+        js_side_(nullptr),
+        mishandled_messages_(0),
+        binding_(this) {}
+  ~CppSideConnection() override {}
+
+  void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
+  base::RunLoop* run_loop() { return run_loop_; }
+
+  void set_js_side(js_to_cpp::JsSide* js_side) { js_side_ = js_side; }
+  js_to_cpp::JsSide* js_side() { return js_side_; }
+
+  void Bind(InterfaceRequest<js_to_cpp::CppSide> request) {
+    binding_.Bind(std::move(request));
+    // Keep the pipe open even after validation errors.
+    binding_.EnableTestingMode();
+  }
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { NOTREACHED(); }
+
+  void TestFinished() override { NOTREACHED(); }
+
+  void PingResponse() override { mishandled_messages_ += 1; }
+
+  void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+  void BitFlipResponse(
+      js_to_cpp::EchoArgsListPtr list,
+      js_to_cpp::ForTestingAssociatedPtrInfo not_used) override {
+    mishandled_messages_ += 1;
+  }
+
+  void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+ protected:
+  base::RunLoop* run_loop_;
+  js_to_cpp::JsSide* js_side_;
+  int mishandled_messages_;
+  mojo::Binding<js_to_cpp::CppSide> binding_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CppSideConnection);
+};
+
+// Trivial test to verify a message sent from JS is received.
+class PingCppSideConnection : public CppSideConnection {
+ public:
+  PingCppSideConnection() : got_message_(false) {}
+  ~PingCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->Ping(); }
+
+  void PingResponse() override {
+    got_message_ = true;
+    run_loop()->Quit();
+  }
+
+  bool DidSucceed() {
+    return got_message_ && !mishandled_messages_;
+  }
+
+ private:
+  bool got_message_;
+  DISALLOW_COPY_AND_ASSIGN(PingCppSideConnection);
+};
+
+// Test that parameters are passed with correct values.
+class EchoCppSideConnection : public CppSideConnection {
+ public:
+  EchoCppSideConnection() :
+      message_count_(0),
+      termination_seen_(false) {
+  }
+  ~EchoCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override {
+    js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
+  }
+
+  void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
+    const js_to_cpp::EchoArgsPtr& special_arg = list->item;
+    message_count_ += 1;
+    EXPECT_EQ(-1, special_arg->si64);
+    EXPECT_EQ(-1, special_arg->si32);
+    EXPECT_EQ(-1, special_arg->si16);
+    EXPECT_EQ(-1, special_arg->si8);
+    EXPECT_EQ(std::string("going"), *special_arg->name);
+    CheckSampleEchoArgsList(list->next);
+  }
+
+  void TestFinished() override {
+    termination_seen_ = true;
+    run_loop()->Quit();
+  }
+
+  bool DidSucceed() {
+    return termination_seen_ &&
+        !mishandled_messages_ &&
+        message_count_ == kExpectedMessageCount;
+  }
+
+ private:
+  static const int kExpectedMessageCount = 10;
+  int message_count_;
+  bool termination_seen_;
+  DISALLOW_COPY_AND_ASSIGN(EchoCppSideConnection);
+};
+
+// Test that corrupted messages don't wreak havoc.
+class BitFlipCppSideConnection : public CppSideConnection {
+ public:
+  BitFlipCppSideConnection() : termination_seen_(false) {}
+  ~BitFlipCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); }
+
+  void BitFlipResponse(
+      js_to_cpp::EchoArgsListPtr list,
+      js_to_cpp::ForTestingAssociatedPtrInfo not_used) override {
+    CheckCorruptedEchoArgsList(list);
+  }
+
+  void TestFinished() override {
+    termination_seen_ = true;
+    run_loop()->Quit();
+  }
+
+  bool DidSucceed() {
+    return termination_seen_;
+  }
+
+ private:
+  bool termination_seen_;
+  DISALLOW_COPY_AND_ASSIGN(BitFlipCppSideConnection);
+};
+
+// Test that severely random messages don't wreak havoc.
+class BackPointerCppSideConnection : public CppSideConnection {
+ public:
+  BackPointerCppSideConnection() : termination_seen_(false) {}
+  ~BackPointerCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); }
+
+  void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
+    CheckCorruptedEchoArgsList(list);
+  }
+
+  void TestFinished() override {
+    termination_seen_ = true;
+    run_loop()->Quit();
+  }
+
+  bool DidSucceed() {
+    return termination_seen_;
+  }
+
+ private:
+  bool termination_seen_;
+  DISALLOW_COPY_AND_ASSIGN(BackPointerCppSideConnection);
+};
+
+}  // namespace
+
+class JsToCppTest : public testing::Test {
+ public:
+  JsToCppTest() {}
+
+  void RunTest(const std::string& test, CppSideConnection* cpp_side) {
+    cpp_side->set_run_loop(&run_loop_);
+
+    js_to_cpp::JsSidePtr js_side;
+    auto js_side_proxy = MakeRequest(&js_side);
+
+    cpp_side->set_js_side(js_side.get());
+    js_to_cpp::CppSidePtr cpp_side_ptr;
+    cpp_side->Bind(MakeRequest(&cpp_side_ptr));
+
+    js_side->SetCppSide(std::move(cpp_side_ptr));
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+    gin::V8Initializer::LoadV8Snapshot();
+    gin::V8Initializer::LoadV8Natives();
+#endif
+
+    gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+                                   gin::IsolateHolder::kStableV8Extras,
+                                   gin::ArrayBufferAllocator::SharedInstance());
+    gin::IsolateHolder instance(base::ThreadTaskRunnerHandle::Get());
+    MojoRunnerDelegate delegate;
+    gin::ShellRunner runner(&delegate, instance.isolate());
+    delegate.Start(&runner, js_side_proxy.PassMessagePipe().release().value(),
+                   test);
+
+    run_loop_.Run();
+  }
+
+ private:
+  base::ShadowingAtExitManager at_exit_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(JsToCppTest);
+};
+
+TEST_F(JsToCppTest, Ping) {
+  PingCppSideConnection cpp_side_connection;
+  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, Echo) {
+  EchoCppSideConnection cpp_side_connection;
+  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BitFlip) {
+  // These tests generate a lot of expected validation errors. Suppress logging.
+  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
+
+  BitFlipCppSideConnection cpp_side_connection;
+  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BackPointer) {
+  // These tests generate a lot of expected validation errors. Suppress logging.
+  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
+
+  BackPointerCppSideConnection cpp_side_connection;
+  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
diff --git a/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.js b/mojo/edk/js/tests/js_to_cpp_tests.js
similarity index 74%
rename from third_party/WebKit/Source/core/mojo/tests/JsToCppTest.js
rename to mojo/edk/js/tests/js_to_cpp_tests.js
index 139687a..6ffce09 100644
--- a/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.js
+++ b/mojo/edk/js/tests/js_to_cpp_tests.js
@@ -2,18 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-(function () {
+define('mojo/edk/js/tests/js_to_cpp_tests', [
+  'console',
+  'mojo/edk/js/tests/js_to_cpp.mojom',
+  'mojo/public/js/bindings',
+  'mojo/public/js/connector',
+  'mojo/public/js/core',
+], function (console, jsToCpp, bindings, connector, core) {
   var retainedJsSide;
+  var retainedJsSideStub;
   var sampleData;
   var sampleMessage;
   var BAD_VALUE = 13;
   var DATA_PIPE_PARAMS = {
+    flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
     elementNumBytes: 1,
     capacityNumBytes: 64
   };
 
   function JsSideConnection() {
-    this.binding = new mojo.Binding(jsToCpp.JsSide, this);
+    this.binding = new bindings.Binding(jsToCpp.JsSide, this);
   }
 
   JsSideConnection.prototype.setCppSide = function(cppSide) {
@@ -47,13 +55,13 @@
       arg.si8 = BAD_VALUE;
 
     for (i = 0; i < numIterations; ++i) {
-      dataPipe1 = Mojo.createDataPipe(DATA_PIPE_PARAMS);
-      dataPipe2 = Mojo.createDataPipe(DATA_PIPE_PARAMS);
-      messagePipe1 = Mojo.createMessagePipe();
-      messagePipe2 = Mojo.createMessagePipe();
+      dataPipe1 = core.createDataPipe(DATA_PIPE_PARAMS);
+      dataPipe2 = core.createDataPipe(DATA_PIPE_PARAMS);
+      messagePipe1 = core.createMessagePipe();
+      messagePipe2 = core.createMessagePipe();
 
-      arg.dataHandle = dataPipe1.consumer;
-      arg.messageHandle = messagePipe1.handle1;
+      arg.data_handle = dataPipe1.consumerHandle;
+      arg.message_handle = messagePipe1.handle1;
 
       specialArg = new jsToCpp.EchoArgs();
       specialArg.si64 = -1;
@@ -61,19 +69,20 @@
       specialArg.si16 = -1;
       specialArg.si8 = -1;
       specialArg.name = 'going';
-      specialArg.dataHandle = dataPipe2.consumer;
-      specialArg.messageHandle = messagePipe2.handle1;
+      specialArg.data_handle = dataPipe2.consumerHandle;
+      specialArg.message_handle = messagePipe2.handle1;
 
       writeDataPipe(dataPipe1, sampleData);
       writeDataPipe(dataPipe2, sampleData);
       writeMessagePipe(messagePipe1, sampleMessage);
       writeMessagePipe(messagePipe2, sampleMessage);
+
       this.cppSide_.echoResponse(createEchoArgsList(specialArg, arg));
 
-      dataPipe1.producer.close();
-      dataPipe2.producer.close();
-      messagePipe1.handle0.close();
-      messagePipe2.handle0.close();
+      core.close(dataPipe1.producerHandle);
+      core.close(dataPipe2.producerHandle);
+      core.close(messagePipe1.handle0);
+      core.close(messagePipe2.handle0);
     }
     this.cppSide_.testFinished();
   };
@@ -82,7 +91,7 @@
     var iteration = 0;
     var dataPipe;
     var messagePipe;
-    var proto = mojo.internal.Connector.prototype;
+    var proto = connector.Connector.prototype;
     var stopSignalled = false;
 
     proto.realAccept = proto.accept;
@@ -101,13 +110,13 @@
     };
 
     while (!stopSignalled) {
-      messagePipe = Mojo.createMessagePipe();
+      messagePipe = core.createMessagePipe();
       writeMessagePipe(messagePipe, sampleMessage);
-      arg.messageHandle = messagePipe.handle1;
+      arg.message_handle = messagePipe.handle1;
 
       this.cppSide_.bitFlipResponse(createEchoArgsList(arg), null);
 
-      messagePipe.handle0.close();
+      core.close(messagePipe.handle0);
       iteration += 1;
     }
 
@@ -120,7 +129,7 @@
     var iteration = 0;
     var dataPipe;
     var messagePipe;
-    var proto = mojo.internal.Connector.prototype;
+    var proto = connector.Connector.prototype;
     var stopSignalled = false;
 
     proto.realAccept = proto.accept;
@@ -137,13 +146,13 @@
     };
 
     while (!stopSignalled) {
-      messagePipe = Mojo.createMessagePipe();
+      messagePipe = core.createMessagePipe();
       writeMessagePipe(messagePipe, sampleMessage);
-      arg.messageHandle = messagePipe.handle1;
+      arg.message_handle = messagePipe.handle1;
 
       this.cppSide_.backPointerResponse(createEchoArgsList(arg));
 
-      messagePipe.handle0.close();
+      core.close(messagePipe.handle0);
       iteration += 1;
     }
 
@@ -153,9 +162,10 @@
   };
 
   function writeDataPipe(pipe, data) {
-    var writeResult = pipe.producer.writeData(data);
+    var writeResult = core.writeData(
+      pipe.producerHandle, data, core.WRITE_DATA_FLAG_ALL_OR_NONE);
 
-    if (writeResult.result != Mojo.RESULT_OK) {
+    if (writeResult.result != core.RESULT_OK) {
       console.log('ERROR: Data pipe write result was ' + writeResult.result);
       return false;
     }
@@ -167,8 +177,8 @@
   }
 
   function writeMessagePipe(pipe, arrayBuffer) {
-    var result = pipe.handle0.writeMessage(arrayBuffer, []);
-    if (result != Mojo.RESULT_OK) {
+    var result = core.writeMessage(pipe.handle0, arrayBuffer, [], 0);
+    if (result != core.RESULT_OK) {
       console.log('ERROR: Message pipe write result was ' + result);
       return false;
     }
@@ -202,4 +212,4 @@
     retainedJsSide = new JsSideConnection;
     retainedJsSide.binding.bind(jsSideRequestHandle);
   };
-})();
+});
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index 1595add..a9a1d90f 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -462,53 +462,6 @@
                       ready_signals_states);
 }
 
-MojoResult Core::AllocMessage(uint32_t num_bytes,
-                              const MojoHandle* handles,
-                              uint32_t num_handles,
-                              MojoAllocMessageFlags flags,
-                              MojoMessageHandle* message_handle) {
-  if (!message_handle)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  if (num_handles == 0) {  // Fast path: no handles.
-    std::unique_ptr<ports::UserMessageEvent> message;
-    MojoResult rv = UserMessageImpl::CreateEventForNewSerializedMessage(
-        num_bytes, nullptr, 0, &message);
-    if (rv != MOJO_RESULT_OK)
-      return rv;
-
-    *message_handle = reinterpret_cast<MojoMessageHandle>(message.release());
-    return MOJO_RESULT_OK;
-  }
-
-  if (!handles)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  if (num_handles > kMaxHandlesPerMessage)
-    return MOJO_RESULT_RESOURCE_EXHAUSTED;
-
-  // If serialization fails below this point, one or more handles may be closed.
-  // This requires an active RequestContext.
-  RequestContext request_context;
-
-  std::vector<Dispatcher::DispatcherInTransit> dispatchers;
-  MojoResult acquire_result =
-      AcquireDispatchersForTransit(handles, num_handles, &dispatchers);
-  if (acquire_result != MOJO_RESULT_OK)
-    return acquire_result;
-
-  DCHECK_EQ(num_handles, dispatchers.size());
-  std::unique_ptr<ports::UserMessageEvent> message;
-  MojoResult rv = UserMessageImpl::CreateEventForNewSerializedMessage(
-      num_bytes, dispatchers.data(), num_handles, &message);
-
-  ReleaseDispatchersForTransit(dispatchers, rv == MOJO_RESULT_OK);
-  if (rv == MOJO_RESULT_OK)
-    *message_handle = reinterpret_cast<MojoMessageHandle>(message.release());
-
-  return rv;
-}
-
 MojoResult Core::CreateMessage(uintptr_t context,
                                const MojoMessageOperationThunks* thunks,
                                MojoMessageHandle* message_handle) {
@@ -572,19 +525,20 @@
       return MOJO_RESULT_RESOURCE_EXHAUSTED;
 
     *buffer = message->user_payload();
+  } else if (buffer) {
+    *buffer = nullptr;
   }
 
-  if (flags & MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES)
-    return MOJO_RESULT_OK;
-
   uint32_t max_num_handles = 0;
   if (num_handles) {
     max_num_handles = *num_handles;
     *num_handles = static_cast<uint32_t>(message->num_handles());
   }
 
-  if (message->num_handles() > max_num_handles)
+  if (message->num_handles() > max_num_handles ||
+      message->num_handles() > kMaxHandlesPerMessage) {
     return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
 
   return message->ExtractSerializedHandles(
       UserMessageImpl::ExtractBadHandlePolicy::kAbort, handles);
@@ -649,32 +603,8 @@
 }
 
 MojoResult Core::WriteMessage(MojoHandle message_pipe_handle,
-                              const void* bytes,
-                              uint32_t num_bytes,
-                              const MojoHandle* handles,
-                              uint32_t num_handles,
+                              MojoMessageHandle message_handle,
                               MojoWriteMessageFlags flags) {
-  if (num_bytes && !bytes)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-
-  MojoMessageHandle message_handle;
-  MojoResult rv = AllocMessage(num_bytes, handles, num_handles,
-                               MOJO_ALLOC_MESSAGE_FLAG_NONE, &message_handle);
-  if (rv != MOJO_RESULT_OK)
-    return rv;
-
-  if (num_bytes) {
-    auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
-                        ->GetMessage<UserMessageImpl>();
-    memcpy(message->user_payload(), bytes, num_bytes);
-  }
-
-  return WriteMessageNew(message_pipe_handle, message_handle, flags);
-}
-
-MojoResult Core::WriteMessageNew(MojoHandle message_pipe_handle,
-                                 MojoMessageHandle message_handle,
-                                 MojoWriteMessageFlags flags) {
   RequestContext request_context;
   if (!message_handle)
     return MOJO_RESULT_INVALID_ARGUMENT;
@@ -687,73 +617,18 @@
 }
 
 MojoResult Core::ReadMessage(MojoHandle message_pipe_handle,
-                             void* bytes,
-                             uint32_t* num_bytes,
-                             MojoHandle* handles,
-                             uint32_t* num_handles,
+                             MojoMessageHandle* message_handle,
                              MojoReadMessageFlags flags) {
-  DCHECK((!num_handles || !*num_handles || handles) &&
-         (!num_bytes || !*num_bytes || bytes));
-  RequestContext request_context;
-  auto dispatcher = GetDispatcher(message_pipe_handle);
-  if (!dispatcher)
-    return MOJO_RESULT_INVALID_ARGUMENT;
-  std::unique_ptr<ports::UserMessageEvent> message_event;
-  const uint32_t max_payload_size = num_bytes ? *num_bytes : 0;
-  const uint32_t max_num_handles = num_handles ? *num_handles : 0;
-  auto discard_policy = flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD
-                            ? Dispatcher::ReadMessageDiscardPolicy::kMayDiscard
-                            : Dispatcher::ReadMessageDiscardPolicy::kNoDiscard;
-  MojoResult rv =
-      dispatcher->ReadMessage(Dispatcher::ReadMessageSizePolicy::kLimitedSize,
-                              discard_policy, max_payload_size, max_num_handles,
-                              &message_event, num_bytes, num_handles);
-  if (rv != MOJO_RESULT_OK)
-    return rv;
-
-  // Some tests use fake message pipe dispatchers which return null events.
-  //
-  // TODO(rockot): Fix the tests, because this is weird.
-  if (!message_event)
-    return MOJO_RESULT_OK;
-
-  auto* message = message_event->GetMessage<UserMessageImpl>();
-  DCHECK(message->IsSerialized());
-  if (message->num_handles()) {
-    DCHECK_LE(message->num_handles(), max_num_handles);
-    MojoResult extract_result = message->ExtractSerializedHandles(
-        UserMessageImpl::ExtractBadHandlePolicy::kAbort, handles);
-    if (extract_result != MOJO_RESULT_OK)
-      return extract_result;
-  }
-  if (message->user_payload_size()) {
-    DCHECK_LE(message->user_payload_size(), max_payload_size);
-    memcpy(bytes, message->user_payload(), message->user_payload_size());
-  }
-  return MOJO_RESULT_OK;
-}
-
-MojoResult Core::ReadMessageNew(MojoHandle message_pipe_handle,
-                                MojoMessageHandle* message_handle,
-                                MojoReadMessageFlags flags) {
   RequestContext request_context;
   auto dispatcher = GetDispatcher(message_pipe_handle);
   if (!dispatcher || !message_handle)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
   std::unique_ptr<ports::UserMessageEvent> message_event;
-  MojoResult rv =
-      dispatcher->ReadMessage(Dispatcher::ReadMessageSizePolicy::kAnySize,
-                              Dispatcher::ReadMessageDiscardPolicy::kNoDiscard,
-                              0, 0, &message_event, nullptr, nullptr);
+  MojoResult rv = dispatcher->ReadMessage(&message_event);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
-  // If there's nowhere to store the message handle and discard is allowed by
-  // the caller, we simply drop the message event.
-  if (flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD && !message_handle)
-    return MOJO_RESULT_OK;
-
   *message_handle =
       reinterpret_cast<MojoMessageHandle>(message_event.release());
   return MOJO_RESULT_OK;
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index 5dd074e..ca95c63a 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -190,11 +190,6 @@
                         uintptr_t* ready_contexts,
                         MojoResult* ready_results,
                         MojoHandleSignalsState* ready_signals_states);
-  MojoResult AllocMessage(uint32_t num_bytes,
-                          const MojoHandle* handles,
-                          uint32_t num_handles,
-                          MojoAllocMessageFlags flags,
-                          MojoMessageHandle* message_handle);
   MojoResult CreateMessage(uintptr_t context,
                            const MojoMessageOperationThunks* thunks,
                            MojoMessageHandle* message_handle);
@@ -218,23 +213,11 @@
       MojoHandle* message_pipe_handle0,
       MojoHandle* message_pipe_handle1);
   MojoResult WriteMessage(MojoHandle message_pipe_handle,
-                          const void* bytes,
-                          uint32_t num_bytes,
-                          const MojoHandle* handles,
-                          uint32_t num_handles,
+                          MojoMessageHandle message_handle,
                           MojoWriteMessageFlags flags);
-  MojoResult WriteMessageNew(MojoHandle message_pipe_handle,
-                             MojoMessageHandle message_handle,
-                             MojoWriteMessageFlags flags);
   MojoResult ReadMessage(MojoHandle message_pipe_handle,
-                         void* bytes,
-                         uint32_t* num_bytes,
-                         MojoHandle* handles,
-                         uint32_t* num_handles,
+                         MojoMessageHandle* message_handle,
                          MojoReadMessageFlags flags);
-  MojoResult ReadMessageNew(MojoHandle message_pipe_handle,
-                            MojoMessageHandle* message_handle,
-                            MojoReadMessageFlags flags);
   MojoResult FuseMessagePipes(MojoHandle handle0, MojoHandle handle1);
   MojoResult NotifyBadMessage(MojoMessageHandle message_handle,
                               const char* error,
diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc
index 86e484f3..05a7f45 100644
--- a/mojo/edk/system/core_test_base.cc
+++ b/mojo/edk/system/core_test_base.cc
@@ -49,13 +49,7 @@
   }
 
   MojoResult ReadMessage(
-      ReadMessageSizePolicy size_policy,
-      ReadMessageDiscardPolicy discard_policy,
-      uint32_t max_payload_size,
-      uint32_t max_num_handles,
-      std::unique_ptr<ports::UserMessageEvent>* message_event,
-      uint32_t* actual_payload_size,
-      uint32_t* actual_num_handles) override {
+      std::unique_ptr<ports::UserMessageEvent>* message_event) override {
     info_->IncrementReadMessageCallCount();
     return MOJO_RESULT_OK;
   }
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index 60057e3..13d67f89 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -30,6 +30,28 @@
 
 using CoreTest = test::CoreTestBase;
 
+void UnusedGetSerializedSize(uintptr_t context,
+                             size_t* num_bytes,
+                             size_t* num_handles) {}
+void UnusedSerializeHandles(uintptr_t context, MojoHandle* handles) {}
+void UnusedSerializePayload(uintptr_t context, void* buffer) {}
+void IgnoreDestroyMessage(uintptr_t context) {}
+
+const MojoMessageOperationThunks kUnusedMessageThunks = {
+    sizeof(MojoMessageOperationThunks),
+    &UnusedGetSerializedSize,
+    &UnusedSerializeHandles,
+    &UnusedSerializePayload,
+    &IgnoreDestroyMessage,
+};
+
+MojoMessageHandle CreateTestMessageHandle(Core* core, uintptr_t context) {
+  MojoMessageHandle handle;
+  CHECK_EQ(MOJO_RESULT_OK,
+           core->CreateMessage(context, &kUnusedMessageThunks, &handle));
+  return handle;
+}
+
 TEST_F(CoreTest, GetTimeTicksNow) {
   const MojoTimeTicks start = core()->GetTimeTicksNow();
   ASSERT_NE(static_cast<MojoTimeTicks>(0), start)
@@ -53,15 +75,16 @@
   MojoMessageHandle message;
   ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(42, nullptr, &message));
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessageNew(h, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
+            core()->WriteMessage(h, CreateTestMessageHandle(core(), 42),
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
   ASSERT_EQ(1u, info.GetWriteMessageCallCount());
 
   ASSERT_EQ(0u, info.GetReadMessageCallCount());
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessageNew(h, &message, MOJO_READ_MESSAGE_FLAG_NONE));
+            core()->ReadMessage(h, &message, MOJO_READ_MESSAGE_FLAG_NONE));
   ASSERT_EQ(1u, info.GetReadMessageCallCount());
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessageNew(h, &message, MOJO_READ_MESSAGE_FLAG_NONE));
+            core()->ReadMessage(h, &message, MOJO_READ_MESSAGE_FLAG_NONE));
   ASSERT_EQ(2u, info.GetReadMessageCallCount());
 
   ASSERT_EQ(0u, info.GetWriteDataCallCount());
@@ -125,8 +148,8 @@
   // |num_handles|.
   {
     ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-              core()->WriteMessageNew(MOJO_HANDLE_INVALID, 0,
-                                      MOJO_WRITE_MESSAGE_FLAG_NONE));
+              core()->WriteMessage(MOJO_HANDLE_INVALID, 0,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
   }
 
   // |ReadMessageNew()|:
@@ -134,12 +157,12 @@
   // |num_handles|.
   {
     ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-              core()->ReadMessageNew(MOJO_HANDLE_INVALID, nullptr,
-                                     MOJO_READ_MESSAGE_FLAG_NONE));
+              core()->ReadMessage(MOJO_HANDLE_INVALID, nullptr,
+                                  MOJO_READ_MESSAGE_FLAG_NONE));
     MockHandleInfo info;
     MojoHandle h = CreateMockHandle(&info);
     ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-              core()->ReadMessageNew(h, nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
+              core()->ReadMessage(h, nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
     // Checked by |Core|, shouldn't go through to the dispatcher.
     ASSERT_EQ(0u, info.GetReadMessageCallCount());
     ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h));
@@ -168,24 +191,23 @@
 
   // Try to read anyway.
   MojoMessageHandle message;
-  ASSERT_EQ(
-      MOJO_RESULT_SHOULD_WAIT,
-      core()->ReadMessageNew(h[0], &message, MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            core()->ReadMessage(h[0], &message, MOJO_READ_MESSAGE_FLAG_NONE));
 
   // Write to |h[1]|.
   const uintptr_t kTestMessageContext = 123;
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->CreateMessage(kTestMessageContext, nullptr, &message));
-  ASSERT_EQ(MOJO_RESULT_OK, core()->WriteMessageNew(
-                                h[1], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h[1], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // Wait for |h[0]| to become readable.
   EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h[0]),
                                        MOJO_HANDLE_SIGNAL_READABLE, &hss[0]));
 
   // Read from |h[0]|.
-  ASSERT_EQ(MOJO_RESULT_OK, core()->ReadMessageNew(
-                                h[0], &message, MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h[0], &message, MOJO_READ_MESSAGE_FLAG_NONE));
   uintptr_t context;
   ASSERT_EQ(MOJO_RESULT_OK, core()->ReleaseMessageContext(message, &context));
   ASSERT_EQ(kTestMessageContext, context);
@@ -200,8 +222,8 @@
   // Write to |h[0]|.
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->CreateMessage(kTestMessageContext, nullptr, &message));
-  ASSERT_EQ(MOJO_RESULT_OK, core()->WriteMessageNew(
-                                h[0], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h[0], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // Close |h[0]|.
   ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h[0]));
@@ -224,8 +246,8 @@
             hss[1].satisfiable_signals);
 
   // Discard a message from |h[1]|.
-  ASSERT_EQ(MOJO_RESULT_OK, core()->ReadMessageNew(
-                                h[1], &message, MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h[1], &message, MOJO_READ_MESSAGE_FLAG_NONE));
   ASSERT_EQ(MOJO_RESULT_OK, core()->FreeMessage(message));
 
   // |h[1]| is no longer readable (and will never be).
@@ -237,9 +259,8 @@
   // Try writing to |h[1]|.
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->CreateMessage(kTestMessageContext, nullptr, &message));
-  ASSERT_EQ(
-      MOJO_RESULT_FAILED_PRECONDITION,
-      core()->WriteMessageNew(h[1], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->WriteMessage(h[1], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h[1]));
 }
@@ -256,9 +277,8 @@
   MojoMessageHandle message;
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->CreateMessage(kTestMessageContext, nullptr, &message));
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessageNew(h_passing[0], message,
-                                    MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->WriteMessage(h_passing[0], message,
+                                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
   hss = kEmptyMojoHandleSignalsState;
   EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]),
                                        MOJO_HANDLE_SIGNAL_READABLE, &hss));
@@ -266,9 +286,8 @@
             hss.satisfied_signals);
   ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
   MojoMessageHandle message_handle;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessageNew(h_passing[1], &message_handle,
-                                   MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->ReadMessage(h_passing[1], &message_handle,
+                                                MOJO_READ_MESSAGE_FLAG_NONE));
   uintptr_t context;
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->ReleaseMessageContext(message_handle, &context));
diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc
index f5cd9d3..027c518 100644
--- a/mojo/edk/system/dispatcher.cc
+++ b/mojo/edk/system/dispatcher.cc
@@ -47,13 +47,7 @@
 }
 
 MojoResult Dispatcher::ReadMessage(
-    ReadMessageSizePolicy size_policy,
-    ReadMessageDiscardPolicy discard_policy,
-    uint32_t max_payload_size,
-    uint32_t max_num_handles,
-    std::unique_ptr<ports::UserMessageEvent>* message,
-    uint32_t* actual_payload_size,
-    uint32_t* actual_num_handles) {
+    std::unique_ptr<ports::UserMessageEvent>* message) {
   return MOJO_RESULT_INVALID_ARGUMENT;
 }
 
diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h
index 8275202..60eff86 100644
--- a/mojo/edk/system/dispatcher.h
+++ b/mojo/edk/system/dispatcher.h
@@ -63,35 +63,6 @@
     PLATFORM_HANDLE = -1,
   };
 
-  // Transitional enum so that ReadMessage can service both MojoReadMessage and
-  // MojoReadMessageNew. To be deleted once MojoReadMessageNew replaces
-  // MojoReadMessage. This is used to decide whether ReadMessage should restrict
-  // the size of the next message it reads, according to its other arguments.
-  enum class ReadMessageSizePolicy {
-    // ReadMessage ignores its sizing arguments and reads any available message.
-    kAnySize,
-
-    // ReadMessage only reads the next available message if it fits within the
-    // size constraints given.
-    kLimitedSize,
-  };
-
-  // Transitional enum so that ReadMessage can service both MojoReadMessage and
-  // MojoReadMessageNew. To be deleted once MojoReadMessageNew replaces
-  // MojoReadMessage. If the selected |ReadMessageSizePolicy| is |kLimitedSize|,
-  // this chooses how to proceed when the provided size constraints are exceeded
-  // by the next available message. If |ReadMessageSizePolicy| is |kAnySize|
-  // this argument is ignored.
-  enum class ReadMessageDiscardPolicy {
-    // Never discard a message. ReadMessage will return an appropriate error
-    // code if the next available message exceeds the given size constraints.
-    kNoDiscard,
-
-    // Discards the next available message if it exceeds the given size
-    // constraints.
-    kMayDiscard,
-  };
-
   // All Dispatchers must minimally implement these methods.
 
   virtual Type GetType() const = 0;
@@ -115,13 +86,7 @@
       MojoWriteMessageFlags flags);
 
   virtual MojoResult ReadMessage(
-      ReadMessageSizePolicy size_policy,
-      ReadMessageDiscardPolicy discard_policy,
-      uint32_t max_payload_size,
-      uint32_t max_num_handles,
-      std::unique_ptr<ports::UserMessageEvent>* message,
-      uint32_t* actual_payload_size,
-      uint32_t* actual_num_handles);
+      std::unique_ptr<ports::UserMessageEvent>* message);
 
   ///////////// Shared buffer API /////////////
 
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
index 2c313e9..e18998f 100644
--- a/mojo/edk/system/message_pipe_dispatcher.cc
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -165,26 +165,35 @@
 }
 
 MojoResult MessagePipeDispatcher::ReadMessage(
-    ReadMessageSizePolicy size_policy,
-    ReadMessageDiscardPolicy discard_policy,
-    uint32_t max_payload_size,
-    uint32_t max_num_handles,
-    std::unique_ptr<ports::UserMessageEvent>* message,
-    uint32_t* actual_payload_size,
-    uint32_t* actual_num_handles) {
+    std::unique_ptr<ports::UserMessageEvent>* message) {
   // We can't read from a port that's closed or in transit!
   if (port_closed_ || in_transit_)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
-  MojoResult rv = UserMessageImpl::ReadMessageEventFromPort(
-      port_, size_policy, discard_policy, max_payload_size, max_num_handles,
-      message, actual_payload_size, actual_num_handles);
+  int rv = node_controller_->node()->GetMessage(port_, message, nullptr);
+  if (rv != ports::OK && rv != ports::ERROR_PORT_PEER_CLOSED) {
+    if (rv == ports::ERROR_PORT_UNKNOWN ||
+        rv == ports::ERROR_PORT_STATE_UNEXPECTED)
+      return MOJO_RESULT_INVALID_ARGUMENT;
+
+    NOTREACHED();
+    return MOJO_RESULT_UNKNOWN;
+  }
+
+  if (!*message) {
+    // No message was available in queue.
+    if (rv == ports::OK)
+      return MOJO_RESULT_SHOULD_WAIT;
+    // Peer is closed and there are no more messages to read.
+    DCHECK_EQ(rv, ports::ERROR_PORT_PEER_CLOSED);
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
 
   // We may need to update anyone watching our signals in case we just read the
   // last available message.
   base::AutoLock lock(signal_lock_);
   watchers_.NotifyState(GetHandleSignalsStateNoLock());
-  return rv;
+  return MOJO_RESULT_OK;
 }
 
 HandleSignalsState
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index 24cf42f..8d8f045 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -49,13 +49,8 @@
   MojoResult Close() override;
   MojoResult WriteMessage(std::unique_ptr<ports::UserMessageEvent> message,
                           MojoWriteMessageFlags flags) override;
-  MojoResult ReadMessage(ReadMessageSizePolicy size_policy,
-                         ReadMessageDiscardPolicy discard_policy,
-                         uint32_t max_payload_size,
-                         uint32_t max_num_handles,
-                         std::unique_ptr<ports::UserMessageEvent>* message,
-                         uint32_t* actual_payload_size,
-                         uint32_t* actual_num_handles) override;
+  MojoResult ReadMessage(
+      std::unique_ptr<ports::UserMessageEvent>* message) override;
   HandleSignalsState GetHandleSignalsState() const override;
   MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher,
                            uintptr_t context) override;
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc
index fb7c6365..eb15d96 100644
--- a/mojo/edk/system/message_pipe_unittest.cc
+++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -53,8 +53,8 @@
                          uint32_t* num_bytes,
                          bool may_discard = false) {
     MojoMessageHandle message_handle;
-    MojoResult rv = MojoReadMessageNew(message_pipe_handle, &message_handle,
-                                       MOJO_READ_MESSAGE_FLAG_NONE);
+    MojoResult rv = MojoReadMessage(message_pipe_handle, &message_handle,
+                                    MOJO_READ_MESSAGE_FLAG_NONE);
     if (rv != MOJO_RESULT_OK)
       return rv;
 
diff --git a/mojo/edk/system/message_unittest.cc b/mojo/edk/system/message_unittest.cc
index b9b4398..8078b01 100644
--- a/mojo/edk/system/message_unittest.cc
+++ b/mojo/edk/system/message_unittest.cc
@@ -143,24 +143,22 @@
 };
 
 TEST_F(MessageTest, InvalidMessageObjects) {
-  // null message
   ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
             MojoFreeMessage(MOJO_MESSAGE_HANDLE_INVALID));
 
-  // null message
   ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
             MojoGetSerializedMessageContents(
                 MOJO_MESSAGE_HANDLE_INVALID, nullptr, nullptr, nullptr, nullptr,
                 MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
 
-  // null message
   ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
             MojoSerializeMessage(MOJO_MESSAGE_HANDLE_INVALID));
 
-  // Non-zero num_handles with null handles array.
-  ASSERT_EQ(
-      MOJO_RESULT_INVALID_ARGUMENT,
-      MojoAllocMessage(0, nullptr, 1, MOJO_ALLOC_MESSAGE_FLAG_NONE, nullptr));
+  MojoMessageHandle message_handle;
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoCreateMessage(0, nullptr, &message_handle));
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoCreateMessage(1234, nullptr, nullptr));
 }
 
 TEST_F(MessageTest, SendLocalMessageWithContext) {
@@ -172,14 +170,14 @@
   MojoHandle a, b;
   CreateMessagePipe(&a, &b);
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessageNew(
+            MojoWriteMessage(
                 a, TestMessageBase::MakeMessageHandle(std::move(message)),
                 MOJO_WRITE_MESSAGE_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
 
   MojoMessageHandle read_message_handle;
-  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &read_message_handle,
-                                               MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(b, &read_message_handle,
+                                            MOJO_READ_MESSAGE_FLAG_NONE));
   message = TestMessageBase::UnwrapMessageHandle<NeverSerializedMessage>(
       &read_message_handle);
   EXPECT_EQ(original_message, message.get());
@@ -217,8 +215,8 @@
 TEST_F(MessageTest, SerializeSimpleMessageNoHandlesWithContext) {
   RUN_CHILD_ON_PIPE(ReceiveMessageNoHandles, h)
   auto message = base::MakeUnique<SimpleMessage>(kTestMessageWithContext1);
-  MojoWriteMessageNew(h, TestMessageBase::MakeMessageHandle(std::move(message)),
-                      MOJO_WRITE_MESSAGE_FLAG_NONE);
+  MojoWriteMessage(h, TestMessageBase::MakeMessageHandle(std::move(message)),
+                   MOJO_WRITE_MESSAGE_FLAG_NONE);
   END_CHILD()
 }
 
@@ -235,8 +233,8 @@
   auto message = base::MakeUnique<SimpleMessage>(kTestMessageWithContext1);
   mojo::MessagePipe pipe;
   message->AddMessagePipe(std::move(pipe.handle0));
-  MojoWriteMessageNew(h, TestMessageBase::MakeMessageHandle(std::move(message)),
-                      MOJO_WRITE_MESSAGE_FLAG_NONE);
+  MojoWriteMessage(h, TestMessageBase::MakeMessageHandle(std::move(message)),
+                   MOJO_WRITE_MESSAGE_FLAG_NONE);
   EXPECT_EQ(kTestMessageWithContext2,
             MojoTestBase::ReadMessage(pipe.handle1.get().value()));
   END_CHILD()
@@ -261,8 +259,8 @@
   message->AddMessagePipe(std::move(pipes[1].handle0));
   message->AddMessagePipe(std::move(pipes[2].handle0));
   message->AddMessagePipe(std::move(pipes[3].handle0));
-  MojoWriteMessageNew(h, TestMessageBase::MakeMessageHandle(std::move(message)),
-                      MOJO_WRITE_MESSAGE_FLAG_NONE);
+  MojoWriteMessage(h, TestMessageBase::MakeMessageHandle(std::move(message)),
+                   MOJO_WRITE_MESSAGE_FLAG_NONE);
   EXPECT_EQ(kTestMessageWithContext1,
             MojoTestBase::ReadMessage(pipes[0].handle1.get().value()));
   EXPECT_EQ(kTestMessageWithContext2,
@@ -292,14 +290,14 @@
   MojoHandle a, b;
   CreateMessagePipe(&a, &b);
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessageNew(
+            MojoWriteMessage(
                 a, TestMessageBase::MakeMessageHandle(std::move(message)),
                 MOJO_WRITE_MESSAGE_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
 
   MojoMessageHandle read_message_handle;
-  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &read_message_handle,
-                                               MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(b, &read_message_handle,
+                                            MOJO_READ_MESSAGE_FLAG_NONE));
   message =
       TestMessageBase::UnwrapMessageHandle<SimpleMessage>(&read_message_handle);
   EXPECT_EQ(original_message, message.get());
@@ -328,7 +326,7 @@
   MojoHandle a, b;
   CreateMessagePipe(&a, &b);
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessageNew(
+            MojoWriteMessage(
                 a, TestMessageBase::MakeMessageHandle(std::move(message)),
                 MOJO_WRITE_MESSAGE_FLAG_NONE));
   MojoClose(a);
@@ -349,14 +347,14 @@
   MojoHandle a, b;
   CreateMessagePipe(&a, &b);
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessageNew(
+            MojoWriteMessage(
                 a, TestMessageBase::MakeMessageHandle(std::move(message)),
                 MOJO_WRITE_MESSAGE_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
 
   MojoMessageHandle message_handle;
-  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &message_handle,
-                                               MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoReadMessage(b, &message_handle, MOJO_READ_MESSAGE_FLAG_NONE));
   EXPECT_FALSE(message_was_destroyed);
 
   // Not a serialized message, so we can't get serialized contents.
@@ -383,8 +381,8 @@
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
 
   MojoMessageHandle message_handle;
-  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &message_handle,
-                                               MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoReadMessage(b, &message_handle, MOJO_READ_MESSAGE_FLAG_NONE));
   uintptr_t context;
   EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
             MojoReleaseMessageContext(message_handle, &context));
@@ -441,10 +439,6 @@
                 MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoGetSerializedMessageContents(
-                message_handle, &buffer, &num_bytes, nullptr, nullptr,
-                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES));
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoGetSerializedMessageContents(
                 message_handle, &buffer, &num_bytes, &extracted_handle,
                 &num_handles, MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
   EXPECT_EQ(std::string(kTestMessageWithContext1).size(), num_bytes);
@@ -482,13 +476,13 @@
   // message object from a pipe.
   MessagePipe pipe;
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessageNew(pipe.handle0->value(), message_handle,
-                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+            MojoWriteMessage(pipe.handle0->value(), message_handle,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_OK,
             WaitForSignals(pipe.handle1->value(), MOJO_HANDLE_SIGNAL_READABLE));
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessageNew(pipe.handle1->value(), &message_handle,
-                               MOJO_READ_MESSAGE_FLAG_NONE));
+            MojoReadMessage(pipe.handle1->value(), &message_handle,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
             MojoSerializeMessage(message_handle));
 
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index 0087cc5..141acb5 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -1371,7 +1371,7 @@
       ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE));
       MojoMessageHandle message;
       ASSERT_EQ(MOJO_RESULT_OK,
-                MojoReadMessageNew(a, &message, MOJO_READ_MESSAGE_FLAG_NONE));
+                ::MojoReadMessage(a, &message, MOJO_READ_MESSAGE_FLAG_NONE));
       EXPECT_EQ(MOJO_RESULT_OK,
                 MojoNotifyBadMessage(message, kFirstErrorMessage.data(),
                                      kFirstErrorMessage.size()));
@@ -1380,7 +1380,7 @@
       // Read a message from the pipe we sent to child2 and flag it as bad.
       ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE));
       ASSERT_EQ(MOJO_RESULT_OK,
-                MojoReadMessageNew(c, &message, MOJO_READ_MESSAGE_FLAG_NONE));
+                ::MojoReadMessage(c, &message, MOJO_READ_MESSAGE_FLAG_NONE));
       EXPECT_EQ(MOJO_RESULT_OK,
                 MojoNotifyBadMessage(message, kSecondErrorMessage.data(),
                                      kSecondErrorMessage.size()));
diff --git a/mojo/edk/system/user_message_impl.cc b/mojo/edk/system/user_message_impl.cc
index 72334e09..727a4d2 100644
--- a/mojo/edk/system/user_message_impl.cc
+++ b/mojo/edk/system/user_message_impl.cc
@@ -172,93 +172,6 @@
 
 }  // namespace
 
-// A MessageFilter used by UserMessageImpl::ReadMessageEventFromPort to
-// determine whether a message should actually be consumed yet.
-class UserMessageImpl::ReadMessageFilter : public ports::MessageFilter {
- public:
-  // Creates a new ReadMessageFilter which captures and potentially modifies
-  // various (unowned) local state within
-  // UserMessageImpl::ReadMessageEventFromPort.
-  ReadMessageFilter(Dispatcher::ReadMessageSizePolicy size_policy,
-                    Dispatcher::ReadMessageDiscardPolicy discard_policy,
-                    uint32_t max_payload_size,
-                    uint32_t max_num_handles,
-                    uint32_t* actual_payload_size,
-                    uint32_t* actual_num_handles,
-                    bool* exceeds_size_limit_flag,
-                    bool* invalid_message_flag)
-      : size_policy_(size_policy),
-        discard_policy_(discard_policy),
-        max_payload_size_(max_payload_size),
-        max_num_handles_(max_num_handles),
-        actual_payload_size_(actual_payload_size),
-        actual_num_handles_(actual_num_handles),
-        exceeds_size_limit_flag_(exceeds_size_limit_flag),
-        invalid_message_flag_(invalid_message_flag) {}
-
-  ~ReadMessageFilter() override {}
-
-  // ports::MessageFilter:
-  bool Match(const ports::UserMessageEvent& event) override {
-    const auto* message = event.GetMessage<UserMessageImpl>();
-    if (!message->IsSerialized()) {
-      // Not a serialized message, so there's nothing to validate or filter
-      // against. We only ensure that the caller expected a message object and
-      // not a specific serialized buffer size.
-      if (size_policy_ != Dispatcher::ReadMessageSizePolicy::kAnySize) {
-        *invalid_message_flag_ = true;
-        return false;
-      }
-      return true;
-    }
-
-    // All messages which reach this filter have already had a basic level of
-    // validation applied by UserMessageImpl::CreateFromChannelMessage()
-    // so we know there is at least well-formed header.
-    DCHECK(message->header_);
-    auto* header = static_cast<MessageHeader*>(message->header_);
-
-    base::CheckedNumeric<uint32_t> checked_bytes_available =
-        message->user_payload_size();
-    if (!checked_bytes_available.IsValid()) {
-      *invalid_message_flag_ = true;
-      return true;
-    }
-    const uint32_t bytes_available = checked_bytes_available.ValueOrDie();
-    const uint32_t bytes_to_read = std::min(max_payload_size_, bytes_available);
-    if (actual_payload_size_)
-      *actual_payload_size_ = bytes_available;
-
-    const uint32_t handles_available = header->num_dispatchers;
-    const uint32_t handles_to_read =
-        std::min(max_num_handles_, handles_available);
-    if (actual_num_handles_)
-      *actual_num_handles_ = handles_available;
-
-    if ((handles_to_read < handles_available ||
-         bytes_to_read < bytes_available) &&
-        size_policy_ == Dispatcher::ReadMessageSizePolicy::kLimitedSize) {
-      *exceeds_size_limit_flag_ = true;
-      return discard_policy_ ==
-             Dispatcher::ReadMessageDiscardPolicy::kMayDiscard;
-    }
-
-    return true;
-  }
-
- private:
-  const Dispatcher::ReadMessageSizePolicy size_policy_;
-  const Dispatcher::ReadMessageDiscardPolicy discard_policy_;
-  const uint32_t max_payload_size_;
-  const uint32_t max_num_handles_;
-  uint32_t* const actual_payload_size_;
-  uint32_t* const actual_num_handles_;
-  bool* const exceeds_size_limit_flag_;
-  bool* const invalid_message_flag_;
-
-  DISALLOW_COPY_AND_ASSIGN(ReadMessageFilter);
-};
-
 // static
 const ports::UserMessage::TypeInfo UserMessageImpl::kUserMessageTypeInfo = {};
 
@@ -339,58 +252,6 @@
 }
 
 // static
-MojoResult UserMessageImpl::ReadMessageEventFromPort(
-    const ports::PortRef& port,
-    Dispatcher::ReadMessageSizePolicy size_policy,
-    Dispatcher::ReadMessageDiscardPolicy discard_policy,
-    uint32_t max_payload_size,
-    uint32_t max_num_handles,
-    std::unique_ptr<ports::UserMessageEvent>* out_event,
-    uint32_t* actual_payload_size,
-    uint32_t* actual_num_handles) {
-  bool exceeds_size_limit = false;
-  bool invalid_message = false;
-  ReadMessageFilter filter(size_policy, discard_policy, max_payload_size,
-                           max_num_handles, actual_payload_size,
-                           actual_num_handles, &exceeds_size_limit,
-                           &invalid_message);
-  std::unique_ptr<ports::UserMessageEvent> message_event;
-  int rv = internal::g_core->GetNodeController()->node()->GetMessage(
-      port, &message_event, &filter);
-  if (invalid_message)
-    return MOJO_RESULT_NOT_FOUND;
-
-  if (rv != ports::OK && rv != ports::ERROR_PORT_PEER_CLOSED) {
-    if (rv == ports::ERROR_PORT_UNKNOWN ||
-        rv == ports::ERROR_PORT_STATE_UNEXPECTED)
-      return MOJO_RESULT_INVALID_ARGUMENT;
-
-    NOTREACHED();
-    return MOJO_RESULT_UNKNOWN;
-  }
-
-  if (exceeds_size_limit) {
-    // |size_policy| is |kLimitedSize| and the next available message exceeded
-    // the constraints specified by |max_payload_size| and/or |max_num_handles|.
-    // The message may or may not have been read off the pipe, depending on the
-    // given value of |discard_policy|.
-    return MOJO_RESULT_RESOURCE_EXHAUSTED;
-  }
-
-  if (!message_event) {
-    // No message was available in queue.
-    if (rv == ports::OK)
-      return MOJO_RESULT_SHOULD_WAIT;
-    // Peer is closed and there are no more messages to read.
-    DCHECK_EQ(rv, ports::ERROR_PORT_PEER_CLOSED);
-    return MOJO_RESULT_FAILED_PRECONDITION;
-  }
-
-  *out_event = std::move(message_event);
-  return MOJO_RESULT_OK;
-}
-
-// static
 Channel::MessagePtr UserMessageImpl::FinalizeEventMessage(
     std::unique_ptr<ports::UserMessageEvent> message_event) {
   auto* message = message_event->GetMessage<UserMessageImpl>();
diff --git a/mojo/edk/system/user_message_impl.h b/mojo/edk/system/user_message_impl.h
index ce851b46..461e4e2 100644
--- a/mojo/edk/system/user_message_impl.h
+++ b/mojo/edk/system/user_message_impl.h
@@ -78,17 +78,6 @@
       void* payload,
       size_t payload_size);
 
-  // Attempts to read a message from the given |port|.
-  static MojoResult ReadMessageEventFromPort(
-      const ports::PortRef& port,
-      Dispatcher::ReadMessageSizePolicy size_policy,
-      Dispatcher::ReadMessageDiscardPolicy discard_policy,
-      uint32_t max_payload_size,
-      uint32_t max_num_handles,
-      std::unique_ptr<ports::UserMessageEvent>* out_event,
-      uint32_t* actual_payload_size,
-      uint32_t* actual_num_handles);
-
   // Extracts the serialized Channel::Message from the UserMessageEvent in
   // |event|. |event| must have a serialized UserMessageImpl instance attached.
   // |message_event| is serialized into the front of the message payload before
@@ -147,8 +136,6 @@
                                       MojoHandle* handles);
 
  private:
-  class ReadMessageFilter;
-
   // Creates an unserialized UserMessageImpl with an associated |context| and
   // |thunks|. If the message is ever going to be routed to another node (see
   // |WillBeRoutedExternally()| below), it will be serialized at that time using
diff --git a/mojo/public/c/system/message_pipe.h b/mojo/public/c/system/message_pipe.h
index 68316d2..58f224b 100644
--- a/mojo/public/c/system/message_pipe.h
+++ b/mojo/public/c/system/message_pipe.h
@@ -15,9 +15,7 @@
 #include "mojo/public/c/system/system_export.h"
 #include "mojo/public/c/system/types.h"
 
-// |MojoMessageHandle|: Used to refer to message objects created by
-//     |MojoAllocMessage()| and transferred by |MojoWriteMessageNew()| or
-//     |MojoReadMessageNew()|.
+// |MojoMessageHandle|: Used to refer to message objects.
 
 typedef uintptr_t MojoMessageHandle;
 
@@ -69,50 +67,27 @@
 // |MojoReadMessageFlags|: Used to specify different modes to
 // |MojoReadMessage()|.
 //   |MOJO_READ_MESSAGE_FLAG_NONE| - No flags; default mode.
-//   |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| - If the message is unable to be read
-//       for whatever reason (e.g., the caller-supplied buffer is too small),
-//       discard the message (i.e., simply dequeue it).
 
 typedef uint32_t MojoReadMessageFlags;
 
 #ifdef __cplusplus
 const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE = 0;
-const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD = 1 << 0;
 #else
 #define MOJO_READ_MESSAGE_FLAG_NONE ((MojoReadMessageFlags)0)
-#define MOJO_READ_MESSAGE_FLAG_MAY_DISCARD ((MojoReadMessageFlags)1 << 0)
-#endif
-
-// |MojoAllocMessageFlags|: Used to specify different options for
-// |MojoAllocMessage()|.
-//   |MOJO_ALLOC_MESSAGE_FLAG_NONE| - No flags; default mode.
-
-typedef uint32_t MojoAllocMessageFlags;
-
-#ifdef __cplusplus
-const MojoAllocMessageFlags MOJO_ALLOC_MESSAGE_FLAG_NONE = 0;
-#else
-#define MOJO_ALLOC_MESSAGE_FLAG_NONE ((MojoAllocMessageFlags)0)
 #endif
 
 // |MojoGetSerializedMessageContentsFlags|: Used to specify different options
 // |MojoGetSerializedMessageContents()|.
 //   |MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE| - No flags; default mode.
-//   |MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES| - Don't attempt
-//       to extract any handles from the serialized message.
 
 typedef uint32_t MojoGetSerializedMessageContentsFlags;
 
 #ifdef __cplusplus
 const MojoGetSerializedMessageContentsFlags
     MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE = 0;
-const MojoGetSerializedMessageContentsFlags
-    MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES = 1 << 0;
 #else
 #define MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE \
   ((MojoGetSerializedMessageContentsFlags)0)
-#define MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES \
-  ((MojoGetSerializedMessageContentsFlags)1 << 0)
 #endif
 
 #ifdef __cplusplus
@@ -192,112 +167,41 @@
     MojoHandle* message_pipe_handle0,                    // Out.
     MojoHandle* message_pipe_handle1);                   // Out.
 
-// Writes a message to the message pipe endpoint given by |message_pipe_handle|,
-// with message data specified by |bytes| of size |num_bytes| and attached
-// handles specified by |handles| of count |num_handles|, and options specified
-// by |flags|. If there is no message data, |bytes| may be null, in which case
-// |num_bytes| must be zero. If there are no attached handles, |handles| may be
-// null, in which case |num_handles| must be zero.
+// Writes a message to the message pipe endpoint given by |message_pipe_handle|.
 //
-// If handles are attached, the handles will no longer be valid (on success the
-// receiver will receive equivalent, but logically different, handles). Handles
-// to be sent should not be in simultaneous use (e.g., on another thread).
+// Note that regardless of success or failure, |message| is freed by this call
+// and therefore invalidated.
 //
 // Returns:
 //   |MOJO_RESULT_OK| on success (i.e., the message was enqueued).
-//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
-//       |message_pipe_handle| is not a valid handle, or some of the
-//       requirements above are not satisfied).
-//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if some system limit has been reached, or
-//       the number of handles to send is too large (TODO(vtl): reconsider the
-//       latter case).
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |message_pipe_handle| or |message| is
+//       invalid.
 //   |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed.
 //       Note that closing an endpoint is not necessarily synchronous (e.g.,
 //       across processes), so this function may succeed even if the other
 //       endpoint has been closed (in which case the message would be dropped).
-//   |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
-//   |MOJO_RESULT_BUSY| if some handle to be sent is currently in use.
-//
-// DEPRECATED: Use |MojoWriteMessageNew()|.
-MOJO_SYSTEM_EXPORT MojoResult
-    MojoWriteMessage(MojoHandle message_pipe_handle,
-                     const void* bytes,  // Optional.
-                     uint32_t num_bytes,
-                     const MojoHandle* handles,  // Optional.
-                     uint32_t num_handles,
-                     MojoWriteMessageFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
+                                               MojoMessageHandle message,
+                                               MojoWriteMessageFlags flags);
 
-// Writes a message to the message pipe endpoint given by |message_pipe_handle|.
-//
-// |message|: A message object allocated by |MojoAllocMessage()| or
-//     |MojoCreateMessage()|. Ownership of the message is passed into Mojo.
-//
-// Returns results corresponding to |MojoWriteMessage()| above.
-MOJO_SYSTEM_EXPORT MojoResult
-    MojoWriteMessageNew(MojoHandle message_pipe_handle,
-                        MojoMessageHandle message,
-                        MojoWriteMessageFlags);
-
-// Reads the next message from a message pipe, or indicates the size of the
-// message if it cannot fit in the provided buffers. The message will be read
-// in its entirety or not at all; if it is not, it will remain enqueued unless
-// the |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| flag was passed. At most one
-// message will be consumed from the queue, and the return value will indicate
-// whether a message was successfully read.
-//
-// |num_bytes| and |num_handles| are optional in/out parameters that on input
-// must be set to the sizes of the |bytes| and |handles| arrays, and on output
-// will be set to the actual number of bytes or handles contained in the
-// message (even if the message was not retrieved due to being too large).
-// Either |num_bytes| or |num_handles| may be null if the message is not
-// expected to contain the corresponding type of data, but such a call would
-// fail with |MOJO_RESULT_RESOURCE_EXHAUSTED| if the message in fact did
-// contain that type of data.
-//
-// |bytes| and |handles| will receive the contents of the message, if it is
-// retrieved. Either or both may be null, in which case the corresponding size
-// parameter(s) must also be set to zero or passed as null.
-//
-// Returns:
-//   |MOJO_RESULT_OK| on success (i.e., a message was actually read).
-//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid.
-//   |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed.
-//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if the message was too large to fit in the
-//       provided buffer(s). The message will have been left in the queue or
-//       discarded, depending on flags.
-//   |MOJO_RESULT_SHOULD_WAIT| if no message was available to be read.
-//   |MOJO_RESULT_NOT_FOUND| if a message was read from the pipe but could not
-//       be extracted into the provided buffers because it was not already in a
-//       serialized form.
-//
-// DEPRECATED: Use |MojoReadMessageNew()|.
-MOJO_SYSTEM_EXPORT MojoResult
-    MojoReadMessage(MojoHandle message_pipe_handle,
-                    void* bytes,            // Optional out.
-                    uint32_t* num_bytes,    // Optional in/out.
-                    MojoHandle* handles,    // Optional out.
-                    uint32_t* num_handles,  // Optional in/out.
-                    MojoReadMessageFlags flags);
-
-// Reads the next message from a message pipe and returns a message containing
-// the message bytes. The returned message must eventually be freed using
+// Reads the next message from a message pipe and returns a message as an opaque
+// message handle. The returned message must eventually be destroyed using
 // |MojoFreeMessage()|.
 //
 // Message payload and handles can be accessed using
 // |MojoGetSerializedMessageContents()|.
 //
-// |message| must be non-null unless |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| is set
-//     in |flags|.
+// |message| must be non-null.
 //
-// Return values correspond to the return values for |MojoReadMessage()| above.
-// On success (MOJO_RESULT_OK), |*message| will contain a handle to a message
-// object which may be passed to |MojoGetSerializedMessageContents()| or
-// |MojoReleaseMessageContext()|. The caller owns the message object and is
-// responsible for freeing it via |MojoFreeMessage()| or
-// |MojoReleaseMessageContext()|.
-MOJO_SYSTEM_EXPORT MojoResult MojoReadMessageNew(MojoHandle message_pipe_handle,
-                                                 MojoMessageHandle* message,
-                                                 MojoReadMessageFlags flags);
+// Returns:
+//   |MOJO_RESULT_OK| on success (i.e., a message was actually read).
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed
+//       and there are no more messages to read.
+//   |MOJO_RESULT_SHOULD_WAIT| if no message was available to be read.
+MOJO_SYSTEM_EXPORT MojoResult MojoReadMessage(MojoHandle message_pipe_handle,
+                                              MojoMessageHandle* message,
+                                              MojoReadMessageFlags flags);
 
 // Fuses two message pipe endpoints together. Given two pipes:
 //
@@ -323,60 +227,26 @@
 MOJO_SYSTEM_EXPORT MojoResult
     MojoFuseMessagePipes(MojoHandle handle0, MojoHandle handle1);
 
-// Allocates a new message whose ownership may be passed to
-// |MojoWriteMessageNew()|. Use |MojoGetSerializedMessageContents()| to retrieve
-// the address of the mutable message payload.
-//
-// |num_bytes|: The size of the message payload in bytes.
-// |handles|: An array of handles to transfer in the message. This takes
-//     ownership of and invalidates all contained handles. Must be null if and
-//     only if |num_handles| is 0.
-// |num_handles|: The number of handles contained in |handles|.
-// |flags|: Must be |MOJO_CREATE_MESSAGE_FLAG_NONE|.
-// |message|: The address of a handle to be filled with the allocated message's
-//     handle. Must be non-null.
-//
-// Returns:
-//   |MOJO_RESULT_OK| if the message was successfully allocated. In this case
-//       |*message| will be populated with a handle to an allocated message
-//       with a buffer large enough to hold |num_bytes| contiguous bytes.
-//   |MOJO_RESULT_INVALID_ARGUMENT| if one or more handles in |handles| was
-//       invalid, or |handles| was null with a non-zero |num_handles|.
-//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if allocation failed because either
-//       |num_bytes| or |num_handles| exceeds an implementation-defined maximum.
-//   |MOJO_RESULT_BUSY| if one or more handles in |handles| cannot be sent at
-//       the time of this call.
-//
-// Only upon successful message allocation will all handles in |handles| be
-// transferred into the message and invalidated.
-//
-// DEPRECATED: Use |MojoCreateMessage()|.
-MOJO_SYSTEM_EXPORT MojoResult
-MojoAllocMessage(uint32_t num_bytes,
-                 const MojoHandle* handles,
-                 uint32_t num_handles,
-                 MojoAllocMessageFlags flags,
-                 MojoMessageHandle* message);  // Out
-
 // Creates a new message object which may be sent over a message pipe via
-// |MojoWriteMessageNew()|. Returns a handle to the new message object in
+// |MojoWriteMessage()|. Returns a handle to the new message object in
 // |*message|.
 //
 // |context| is an arbitrary context value to associate with this message, and
 // |thunks| is a set of functions which may be called on |context| to perform
 // various operations. See |MojoMessageOperationThunks| for details.
 //
-// Unlike |MojoAllocMessage()| above, this does not assume that the message
-// object will require serialization. Typically a caller will use |context| as
-// an opaque pointer to some arbitrary heap object which is effectively owned by
-// the newly created message once this returns. In this way, messages can be
-// sent over a message pipe to a peer endpoint in the same process as the sender
-// without ever performing a serialization step.
+// Typically a caller will use |context| as an opaque pointer to some heap
+// object which is effectively owned by the newly created message once this
+// returns. In this way, messages can be sent over a message pipe to a peer
+// endpoint in the same process as the sender without ever performing a
+// serialization step.
 //
-// If the message does need to cross a process boundary to reach its
-// destination, it will be serialized lazily when necessary. To accomplish this,
-// the system will invoke the serialization helper functions from |thunks| on
-// |context| to obtain a serialized representation of the message.
+// If the message does need to cross a process boundary or is otherwise
+// forced to serialize (see |MojoSerializeMessage()| below), it will be
+// serialized by invoking the serialization helper functions from |thunks| on
+// |context| to obtain a serialized representation of the message. Note that
+// because the need to serialize may occur at any time and on any thread,
+// functions in |thunks| must be safe to invoke accordingly.
 //
 // If |thunks| is null, the created message cannot be serialized. Subsequent
 // calls to |MojoSerializeMessage()| on the created message, or any attempt to
@@ -393,12 +263,12 @@
                   const struct MojoMessageOperationThunks* thunks,
                   MojoMessageHandle* message);
 
-// Frees a message allocated by |MojoAllocMessage()| or |MojoReadMessageNew()|.
+// Destroys a message object created by |MojoCreateMessage()| or
+// |MojoReadMessage()|.
 //
-// |message|: The message to free. This must correspond to a message previously
-//     allocated by |MojoAllocMessage()| or |MojoReadMessageNew()|. Note that if
-//     the message has already been passed to |MojoWriteMessageNew()| it should
-//     NOT also be freed with this API.
+// |message|: The message to destroy. Note that if a message has been written
+//     to a message pipe using |MojoWriteMessage()|, it is neither necessary nor
+//     valid to attempt to destroy it.
 //
 // Returns:
 //   |MOJO_RESULT_OK| if |message| was valid and has been freed.
@@ -445,10 +315,6 @@
 //     null if |num_handles| is null or |*num_handles| is 0.
 // |flags|: Flags to affect the behavior of this API.
 //
-// If |MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES| is set in
-// |flags|, |num_handles| and |handles| arguments are ignored and only payload-
-// related outputs are updated.
-//
 // Returns:
 //   |MOJO_RESULT_OK| if |message| is a serialized message and the provided
 //       handle storage is sufficient to contain all handles attached to the
@@ -505,8 +371,7 @@
 // terminate, a process, etc.) The embedder may not be notified if the calling
 // process has lost its connection to the source process.
 //
-// |message|: The message to report as bad. This must have come from a call to
-//     |MojoReadMessageNew()|.
+// |message|: The message to report as bad.
 // |error|: An error string which may provide the embedder with context when
 //     notified of this error.
 // |error_num_bytes|: The length of |error| in bytes.
diff --git a/mojo/public/c/system/tests/core_perftest.cc b/mojo/public/c/system/tests/core_perftest.cc
index 54b9d81..80508bc0 100644
--- a/mojo/public/c/system/tests/core_perftest.cc
+++ b/mojo/public/c/system/tests/core_perftest.cc
@@ -147,7 +147,7 @@
     CorePerftest* self = static_cast<CorePerftest*>(closure);
     MojoMessageHandle message;
     MojoResult result =
-        MojoReadMessageNew(self->h0_, &message, MOJO_READ_MESSAGE_FLAG_NONE);
+        MojoReadMessage(self->h0_, &message, MOJO_READ_MESSAGE_FLAG_NONE);
     ALLOW_UNUSED_LOCAL(result);
     assert(result == MOJO_RESULT_SHOULD_WAIT);
   }
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
index 7f99097..bd8e989 100644
--- a/mojo/public/c/system/tests/core_unittest.cc
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -44,10 +44,10 @@
   // Message pipe:
   h0 = MOJO_HANDLE_INVALID;
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoWriteMessageNew(h0, MOJO_MESSAGE_HANDLE_INVALID,
-                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+            MojoWriteMessage(h0, MOJO_MESSAGE_HANDLE_INVALID,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoReadMessageNew(h0, nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
+            MojoReadMessage(h0, nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
 
   // Data pipe:
   buffer_size = static_cast<uint32_t>(sizeof(buffer));
@@ -94,14 +94,14 @@
   // Try to read.
   MojoMessageHandle message;
   EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
-            MojoReadMessageNew(h0, &message, MOJO_READ_MESSAGE_FLAG_NONE));
+            MojoReadMessage(h0, &message, MOJO_READ_MESSAGE_FLAG_NONE));
 
   // Write to |h1|.
   const uintptr_t kTestMessageContext = 1234;
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoCreateMessage(kTestMessageContext, nullptr, &message));
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessageNew(h1, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
+            MojoWriteMessage(h1, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // |h0| should be readable.
   size_t result_index = 1;
@@ -117,7 +117,7 @@
 
   // Read from |h0|.
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessageNew(h0, &message, MOJO_READ_MESSAGE_FLAG_NONE));
+            MojoReadMessage(h0, &message, MOJO_READ_MESSAGE_FLAG_NONE));
   uintptr_t context;
   EXPECT_EQ(MOJO_RESULT_OK, MojoReleaseMessageContext(message, &context));
   EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c
index de16790..c5b4c5d 100644
--- a/mojo/public/c/system/tests/core_unittest_pure_c.c
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -58,10 +58,10 @@
   MojoMessageHandle message;
   EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(42, NULL, &message));
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessageNew(handle0, message, MOJO_WRITE_DATA_FLAG_NONE));
+            MojoWriteMessage(handle0, message, MOJO_WRITE_DATA_FLAG_NONE));
 
-  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(handle1, &message,
-                                               MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(handle1, &message,
+                                             MOJO_READ_MESSAGE_FLAG_NONE));
   uintptr_t context;
   EXPECT_EQ(MOJO_RESULT_OK, MojoReleaseMessageContext(message, &context));
   EXPECT_EQ(42, context);
diff --git a/mojo/public/c/system/thunks.cc b/mojo/public/c/system/thunks.cc
index 64344928..11713cc 100644
--- a/mojo/public/c/system/thunks.cc
+++ b/mojo/public/c/system/thunks.cc
@@ -38,25 +38,17 @@
 }
 
 MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
-                            const void* bytes,
-                            uint32_t num_bytes,
-                            const MojoHandle* handles,
-                            uint32_t num_handles,
+                            MojoMessageHandle message_handle,
                             MojoWriteMessageFlags flags) {
   assert(g_thunks.WriteMessage);
-  return g_thunks.WriteMessage(message_pipe_handle, bytes, num_bytes, handles,
-                               num_handles, flags);
+  return g_thunks.WriteMessage(message_pipe_handle, message_handle, flags);
 }
 
 MojoResult MojoReadMessage(MojoHandle message_pipe_handle,
-                           void* bytes,
-                           uint32_t* num_bytes,
-                           MojoHandle* handles,
-                           uint32_t* num_handles,
+                           MojoMessageHandle* message_handle,
                            MojoReadMessageFlags flags) {
   assert(g_thunks.ReadMessage);
-  return g_thunks.ReadMessage(message_pipe_handle, bytes, num_bytes, handles,
-                              num_handles, flags);
+  return g_thunks.ReadMessage(message_pipe_handle, message_handle, flags);
 }
 
 MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options,
@@ -180,30 +172,6 @@
   return g_thunks.FuseMessagePipes(handle0, handle1);
 }
 
-MojoResult MojoWriteMessageNew(MojoHandle message_pipe_handle,
-                               MojoMessageHandle message,
-                               MojoWriteMessageFlags flags) {
-  assert(g_thunks.WriteMessageNew);
-  return g_thunks.WriteMessageNew(message_pipe_handle, message, flags);
-}
-
-MojoResult MojoReadMessageNew(MojoHandle message_pipe_handle,
-                              MojoMessageHandle* message,
-                              MojoReadMessageFlags flags) {
-  assert(g_thunks.ReadMessageNew);
-  return g_thunks.ReadMessageNew(message_pipe_handle, message, flags);
-}
-
-MojoResult MojoAllocMessage(uint32_t num_bytes,
-                            const MojoHandle* handles,
-                            uint32_t num_handles,
-                            MojoAllocMessageFlags flags,
-                            MojoMessageHandle* message) {
-  assert(g_thunks.AllocMessage);
-  return g_thunks.AllocMessage(
-      num_bytes, handles, num_handles, flags, message);
-}
-
 MojoResult MojoCreateMessage(uintptr_t context,
                              const MojoMessageOperationThunks* thunks,
                              MojoMessageHandle* message) {
diff --git a/mojo/public/c/system/thunks.h b/mojo/public/c/system/thunks.h
index ba921cf..d17b09ec 100644
--- a/mojo/public/c/system/thunks.h
+++ b/mojo/public/c/system/thunks.h
@@ -30,16 +30,10 @@
       MojoHandle* message_pipe_handle0,
       MojoHandle* message_pipe_handle1);
   MojoResult (*WriteMessage)(MojoHandle message_pipe_handle,
-                             const void* bytes,
-                             uint32_t num_bytes,
-                             const MojoHandle* handles,
-                             uint32_t num_handles,
+                             MojoMessageHandle message_handle,
                              MojoWriteMessageFlags flags);
   MojoResult (*ReadMessage)(MojoHandle message_pipe_handle,
-                            void* bytes,
-                            uint32_t* num_bytes,
-                            MojoHandle* handles,
-                            uint32_t* num_handles,
+                            MojoMessageHandle* message_handle,
                             MojoReadMessageFlags flags);
   MojoResult (*CreateDataPipe)(const struct MojoCreateDataPipeOptions* options,
                                MojoHandle* data_pipe_producer_handle,
@@ -91,17 +85,6 @@
                            MojoResult* ready_results,
                            MojoHandleSignalsState* ready_signals_states);
   MojoResult (*FuseMessagePipes)(MojoHandle handle0, MojoHandle handle1);
-  MojoResult (*WriteMessageNew)(MojoHandle message_pipe_handle,
-                                MojoMessageHandle message,
-                                MojoWriteMessageFlags flags);
-  MojoResult (*ReadMessageNew)(MojoHandle message_pipe_handle,
-                               MojoMessageHandle* message,
-                               MojoReadMessageFlags flags);
-  MojoResult (*AllocMessage)(uint32_t num_bytes,
-                             const MojoHandle* handles,
-                             uint32_t num_handles,
-                             MojoAllocMessageFlags flags,
-                             MojoMessageHandle* message);
   MojoResult (*CreateMessage)(uintptr_t context,
                               const struct MojoMessageOperationThunks* thunks,
                               MojoMessageHandle* message);
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 43bf15b..2f3727e 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -60,6 +60,7 @@
     "lib/binding_state.cc",
     "lib/binding_state.h",
     "lib/bindings_internal.h",
+    "lib/buffer.cc",
     "lib/buffer.h",
     "lib/connector.cc",
     "lib/control_message_handler.cc",
@@ -78,10 +79,6 @@
     "lib/map_serialization.h",
     "lib/may_auto_lock.h",
     "lib/message.cc",
-    "lib/message_buffer.cc",
-    "lib/message_buffer.h",
-    "lib/message_builder.cc",
-    "lib/message_builder.h",
     "lib/message_header_validator.cc",
     "lib/message_internal.h",
     "lib/multiplex_router.cc",
diff --git a/mojo/public/cpp/bindings/lib/buffer.cc b/mojo/public/cpp/bindings/lib/buffer.cc
new file mode 100644
index 0000000..308156ad
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/buffer.cc
@@ -0,0 +1,52 @@
+// 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 "mojo/public/cpp/bindings/lib/buffer.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+
+namespace mojo {
+namespace internal {
+
+Buffer::Buffer() = default;
+
+Buffer::Buffer(void* data, size_t size) : data_(data), size_(size), cursor_(0) {
+  DCHECK(IsAligned(data_));
+}
+
+Buffer::Buffer(Buffer&& other) {
+  *this = std::move(other);
+}
+
+Buffer::~Buffer() = default;
+
+Buffer& Buffer::operator=(Buffer&& other) {
+  data_ = other.data_;
+  size_ = other.size_;
+  cursor_ = other.cursor_;
+  other.Reset();
+  return *this;
+}
+
+void* Buffer::Allocate(size_t num_bytes) {
+  const size_t block_start = cursor_;
+  cursor_ += Align(num_bytes);
+  if (cursor_ > size_ || cursor_ < block_start) {
+    NOTREACHED();
+    cursor_ = block_start;
+    return nullptr;
+  }
+  DCHECK_LE(cursor_, size_);
+  return reinterpret_cast<char*>(data_) + block_start;
+}
+
+void Buffer::Reset() {
+  data_ = nullptr;
+  size_ = 0;
+  cursor_ = 0;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h
index 213a4459..2b6cbcc 100644
--- a/mojo/public/cpp/bindings/lib/buffer.h
+++ b/mojo/public/cpp/bindings/lib/buffer.h
@@ -6,60 +6,51 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
 
 #include <stddef.h>
+#include <stdint.h>
 
-#include "base/logging.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/bindings_export.h"
 
 namespace mojo {
 namespace internal {
 
 // Buffer provides an interface to allocate memory blocks which are 8-byte
-// aligned and zero-initialized. It doesn't own the underlying memory. Users
-// must ensure that the memory stays valid while using the allocated blocks from
-// Buffer.
-class Buffer {
+// aligned. It doesn't own the underlying memory. Users must ensure that the
+// memory stays valid while using the allocated blocks from Buffer.
+//
+// A Buffer may be moved around. A moved-from Buffer is reset and may no longer
+// be used to Allocate memory unless re-Initialized.
+class MOJO_CPP_BINDINGS_EXPORT Buffer {
  public:
-  Buffer() {}
+  // Constructs an invalid Buffer. May not call Allocate().
+  Buffer();
 
-  // The memory must have been zero-initialized. |data| must be 8-byte
-  // aligned.
-  void Initialize(void* data, size_t size) {
-    DCHECK(IsAligned(data));
+  // Constructs a Buffer which can Allocate() blocks starting at |data|, up to
+  // a total of |size| bytes. |data| is not owned.
+  Buffer(void* data, size_t size);
 
-    data_ = data;
-    size_ = size;
-    cursor_ = reinterpret_cast<uintptr_t>(data);
-    data_end_ = cursor_ + size;
-  }
+  Buffer(Buffer&& other);
+  ~Buffer();
 
-  size_t size() const { return size_; }
+  Buffer& operator=(Buffer&& other);
 
   void* data() const { return data_; }
+  size_t size() const { return size_; }
+  size_t cursor() const { return cursor_; }
+
+  bool is_valid() const { return data_ != nullptr; }
 
   // Allocates |num_bytes| from the buffer and returns a pointer to the start of
-  // the allocated block.
-  // The resulting address is 8-byte aligned, and the content of the memory is
-  // zero-filled.
-  void* Allocate(size_t num_bytes) {
-    num_bytes = Align(num_bytes);
-    uintptr_t result = cursor_;
-    cursor_ += num_bytes;
-    if (cursor_ > data_end_ || cursor_ < result) {
-      NOTREACHED();
-      cursor_ -= num_bytes;
-      return nullptr;
-    }
+  // the allocated block. The resulting address is 8-byte aligned.
+  void* Allocate(size_t num_bytes);
 
-    return reinterpret_cast<void*>(result);
-  }
+  // Resets the buffer to an invalid state. Can no longer be used to Allocate().
+  void Reset();
 
  private:
   void* data_ = nullptr;
   size_t size_ = 0;
-
-  uintptr_t cursor_ = 0;
-  uintptr_t data_end_ = 0;
+  size_t cursor_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(Buffer);
 };
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.cc b/mojo/public/cpp/bindings/lib/control_message_handler.cc
index 1b7bb78..2160a78 100644
--- a/mojo/public/cpp/bindings/lib/control_message_handler.cc
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc
@@ -10,9 +10,9 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/lib/message_builder.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
 #include "mojo/public/cpp/bindings/lib/validation_util.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
 
 namespace mojo {
@@ -118,16 +118,15 @@
   size_t size =
       PrepareToSerialize<interface_control::RunResponseMessageParamsDataView>(
           response_params_ptr, &context_);
-  MessageBuilder builder(interface_control::kRunMessageId,
-                         Message::kFlagIsResponse, size, 0);
-  builder.message()->set_request_id(message->request_id());
-
+  Message response_message(interface_control::kRunMessageId,
+                           Message::kFlagIsResponse, size, 0);
+  response_message.set_request_id(message->request_id());
   interface_control::internal::RunResponseMessageParams_Data* response_params =
       nullptr;
   Serialize<interface_control::RunResponseMessageParamsDataView>(
-      response_params_ptr, builder.buffer(), &response_params, &context_);
-  ignore_result(responder->Accept(builder.message()));
-
+      response_params_ptr, response_message.payload_buffer(), &response_params,
+      &context_);
+  ignore_result(responder->Accept(&response_message));
   return true;
 }
 
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
index d082b49..672d620 100644
--- a/mojo/public/cpp/bindings/lib/control_message_proxy.cc
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
@@ -12,7 +12,6 @@
 #include "base/callback_helpers.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "mojo/public/cpp/bindings/lib/message_builder.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
 #include "mojo/public/cpp/bindings/lib/validation_util.h"
 #include "mojo/public/cpp/bindings/message.h"
@@ -74,48 +73,41 @@
                     interface_control::RunInputPtr input_ptr,
                     const RunCallback& callback) {
   SerializationContext context;
-
   auto params_ptr = interface_control::RunMessageParams::New();
   params_ptr->input = std::move(input_ptr);
-  size_t size = PrepareToSerialize<interface_control::RunMessageParamsDataView>(
-      params_ptr, &context);
-  MessageBuilder builder(interface_control::kRunMessageId,
-                         Message::kFlagExpectsResponse, size, 0);
-
+  const size_t size =
+      PrepareToSerialize<interface_control::RunMessageParamsDataView>(
+          params_ptr, &context);
+  Message message(interface_control::kRunMessageId,
+                  Message::kFlagExpectsResponse, size, 0);
   interface_control::internal::RunMessageParams_Data* params = nullptr;
   Serialize<interface_control::RunMessageParamsDataView>(
-      params_ptr, builder.buffer(), &params, &context);
+      params_ptr, message.payload_buffer(), &params, &context);
   std::unique_ptr<MessageReceiver> responder =
       base::MakeUnique<RunResponseForwardToCallback>(callback);
-  ignore_result(
-      receiver->AcceptWithResponder(builder.message(), std::move(responder)));
+  ignore_result(receiver->AcceptWithResponder(&message, std::move(responder)));
 }
 
 Message ConstructRunOrClosePipeMessage(
     interface_control::RunOrClosePipeInputPtr input_ptr) {
   SerializationContext context;
-
   auto params_ptr = interface_control::RunOrClosePipeMessageParams::New();
   params_ptr->input = std::move(input_ptr);
-
-  size_t size = PrepareToSerialize<
+  const size_t size = PrepareToSerialize<
       interface_control::RunOrClosePipeMessageParamsDataView>(params_ptr,
                                                               &context);
-  MessageBuilder builder(interface_control::kRunOrClosePipeMessageId, 0, size,
-                         0);
-
+  Message message(interface_control::kRunOrClosePipeMessageId, 0, size, 0);
   interface_control::internal::RunOrClosePipeMessageParams_Data* params =
       nullptr;
   Serialize<interface_control::RunOrClosePipeMessageParamsDataView>(
-      params_ptr, builder.buffer(), &params, &context);
-  return std::move(*builder.message());
+      params_ptr, message.payload_buffer(), &params, &context);
+  return message;
 }
 
 void SendRunOrClosePipeMessage(
     MessageReceiverWithResponder* receiver,
     interface_control::RunOrClosePipeInputPtr input_ptr) {
   Message message(ConstructRunOrClosePipeMessage(std::move(input_ptr)));
-
   ignore_result(receiver->Accept(&message));
 }
 
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
index 725a193..3a540268 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -6,15 +6,13 @@
 
 #include <stdlib.h>
 
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+
 namespace mojo {
 namespace internal {
 
-FixedBufferForTesting::FixedBufferForTesting(size_t size) {
-  size = internal::Align(size);
-  // Use calloc here to ensure all message memory is zero'd out.
-  void* ptr = calloc(size, 1);
-  Initialize(ptr, size);
-}
+FixedBufferForTesting::FixedBufferForTesting(size_t size)
+    : FixedBufferForTesting(nullptr, Align(size)) {}
 
 FixedBufferForTesting::~FixedBufferForTesting() {
   free(data());
@@ -22,9 +20,13 @@
 
 void* FixedBufferForTesting::Leak() {
   void* ptr = data();
-  Initialize(nullptr, 0);
+  Reset();
   return ptr;
 }
 
+FixedBufferForTesting::FixedBufferForTesting(std::nullptr_t,
+                                             size_t aligned_size)
+    : Buffer(calloc(aligned_size, 1), aligned_size) {}
+
 }  // namespace internal
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
index 070b0c8c..8fa3ff5d 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.h
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -5,9 +5,8 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
 
-#include <stddef.h>
+#include <cstddef>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/buffer.h"
@@ -17,8 +16,7 @@
 
 // FixedBufferForTesting owns its buffer. The Leak method may be used to steal
 // the underlying memory.
-class MOJO_CPP_BINDINGS_EXPORT FixedBufferForTesting
-    : NON_EXPORTED_BASE(public Buffer) {
+class MOJO_CPP_BINDINGS_EXPORT FixedBufferForTesting : public Buffer {
  public:
   explicit FixedBufferForTesting(size_t size);
   ~FixedBufferForTesting();
@@ -30,6 +28,8 @@
   void* Leak();
 
  private:
+  FixedBufferForTesting(std::nullptr_t, size_t aligned_size);
+
   DISALLOW_COPY_AND_ASSIGN(FixedBufferForTesting);
 };
 
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index 445d0f2..36c6b86a 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -33,44 +33,206 @@
   message.NotifyBadMessage(error);
 }
 
+template <typename HeaderType>
+void AllocateHeaderFromBuffer(internal::Buffer* buffer, HeaderType** header) {
+  *header = static_cast<HeaderType*>(buffer->Allocate(sizeof(HeaderType)));
+  (*header)->num_bytes = sizeof(HeaderType);
+}
+
+// An internal serialization context used to initialize new serialized messages.
+struct MessageInfo {
+  MessageInfo(size_t total_size, std::vector<ScopedHandle> handles)
+      : total_size(total_size), handles(std::move(handles)) {}
+  const size_t total_size;
+  std::vector<ScopedHandle> handles;
+  internal::Buffer payload_buffer;
+};
+
+void GetSerializedSizeFromMessageInfo(uintptr_t context,
+                                      size_t* num_bytes,
+                                      size_t* num_handles) {
+  auto* info = reinterpret_cast<MessageInfo*>(context);
+  *num_bytes = info->total_size;
+  *num_handles = info->handles.size();
+}
+
+void SerializeHandlesFromMessageInfo(uintptr_t context, MojoHandle* handles) {
+  auto* info = reinterpret_cast<MessageInfo*>(context);
+  for (size_t i = 0; i < info->handles.size(); ++i)
+    handles[i] = info->handles[i].release().value();
+}
+
+void SerializePayloadFromMessageInfo(uintptr_t context, void* storage) {
+  auto* info = reinterpret_cast<MessageInfo*>(context);
+  info->payload_buffer = internal::Buffer(storage, info->total_size);
+}
+
+void DestroyMessageInfo(uintptr_t context) {
+  // MessageInfo is always stack-allocated, so there's nothing to do here.
+}
+
+const MojoMessageOperationThunks kMessageInfoThunks{
+    sizeof(MojoMessageOperationThunks),
+    &GetSerializedSizeFromMessageInfo,
+    &SerializeHandlesFromMessageInfo,
+    &SerializePayloadFromMessageInfo,
+    &DestroyMessageInfo,
+};
+
+size_t ComputeHeaderSize(uint32_t flags, size_t payload_interface_id_count) {
+  if (payload_interface_id_count > 0) {
+    // Version 2
+    return sizeof(internal::MessageHeaderV2);
+  } else if (flags &
+             (Message::kFlagExpectsResponse | Message::kFlagIsResponse)) {
+    // Version 1
+    return sizeof(internal::MessageHeaderV1);
+  } else {
+    // Version 0
+    return sizeof(internal::MessageHeader);
+  }
+}
+
+size_t ComputeTotalSize(uint32_t flags,
+                        size_t payload_size,
+                        size_t payload_interface_id_count) {
+  const size_t header_size =
+      ComputeHeaderSize(flags, payload_interface_id_count);
+  if (payload_interface_id_count > 0) {
+    return internal::Align(
+        header_size + internal::Align(payload_size) +
+        internal::ArrayDataTraits<uint32_t>::GetStorageSize(
+            static_cast<uint32_t>(payload_interface_id_count)));
+  }
+  return internal::Align(header_size + payload_size);
+}
+
+void WriteMessageHeader(uint32_t name,
+                        uint32_t flags,
+                        size_t payload_interface_id_count,
+                        internal::Buffer* payload_buffer) {
+  if (payload_interface_id_count > 0) {
+    // Version 2
+    internal::MessageHeaderV2* header;
+    AllocateHeaderFromBuffer(payload_buffer, &header);
+    header->version = 2;
+    header->name = name;
+    header->flags = flags;
+    // The payload immediately follows the header.
+    header->payload.Set(header + 1);
+  } else if (flags &
+             (Message::kFlagExpectsResponse | Message::kFlagIsResponse)) {
+    // Version 1
+    internal::MessageHeaderV1* header;
+    AllocateHeaderFromBuffer(payload_buffer, &header);
+    header->version = 1;
+    header->name = name;
+    header->flags = flags;
+  } else {
+    internal::MessageHeader* header;
+    AllocateHeaderFromBuffer(payload_buffer, &header);
+    header->version = 0;
+    header->name = name;
+    header->flags = flags;
+  }
+}
+
+void CreateSerializedMessageObject(uint32_t name,
+                                   uint32_t flags,
+                                   size_t payload_size,
+                                   size_t payload_interface_id_count,
+                                   std::vector<ScopedHandle> handles,
+                                   ScopedMessageHandle* out_handle,
+                                   internal::Buffer* out_buffer) {
+  internal::Buffer buffer;
+  MessageInfo info(
+      ComputeTotalSize(flags, payload_size, payload_interface_id_count),
+      std::move(handles));
+  ScopedMessageHandle handle;
+  MojoResult rv = mojo::CreateMessage(reinterpret_cast<uintptr_t>(&info),
+                                      &kMessageInfoThunks, &handle);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  DCHECK(handle.is_valid());
+
+  // Force the message object to be serialized immediately. MessageInfo thunks
+  // are invoked here to serialize the handles and allocate enough space for
+  // header and user payload. |info.payload_buffer| is initialized with a Buffer
+  // that can be used to write the header and payload data.
+  rv = MojoSerializeMessage(handle->value());
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  DCHECK(info.payload_buffer.is_valid());
+
+  // Make sure we zero the memory first!
+  memset(info.payload_buffer.data(), 0, info.total_size);
+  WriteMessageHeader(name, flags, payload_interface_id_count,
+                     &info.payload_buffer);
+
+  *out_handle = std::move(handle);
+  *out_buffer = std::move(info.payload_buffer);
+}
+
 }  // namespace
 
-Message::Message() {
+Message::Message() = default;
+
+Message::Message(Message&& other) = default;
+
+Message::Message(uint32_t name,
+                 uint32_t flags,
+                 size_t payload_size,
+                 size_t payload_interface_id_count,
+                 std::vector<ScopedHandle> handles) {
+  CreateSerializedMessageObject(name, flags, payload_size,
+                                payload_interface_id_count, std::move(handles),
+                                &handle_, &payload_buffer_);
+  data_ = payload_buffer_.data();
+  data_size_ = payload_buffer_.size();
+  transferable_ = true;
 }
 
-Message::Message(Message&& other)
-    : buffer_(std::move(other.buffer_)),
-      handles_(std::move(other.handles_)),
-      associated_endpoint_handles_(
-          std::move(other.associated_endpoint_handles_)) {}
+Message::Message(ScopedMessageHandle handle) {
+  DCHECK(handle.is_valid());
 
-Message::~Message() {}
+  // Extract any serialized handles if possible.
+  uint32_t num_bytes;
+  void* buffer;
+  uint32_t num_handles = 0;
+  MojoResult rv = MojoGetSerializedMessageContents(
+      handle->value(), &buffer, &num_bytes, nullptr, &num_handles,
+      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  if (rv == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    handles_.resize(num_handles);
+    rv = MojoGetSerializedMessageContents(
+        handle->value(), &buffer, &num_bytes,
+        reinterpret_cast<MojoHandle*>(handles_.data()), &num_handles,
+        MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  } else {
+    // No handles, so it's safe to retransmit this message if the caller really
+    // wants to.
+    transferable_ = true;
+  }
 
-Message& Message::operator=(Message&& other) {
-  Reset();
-  std::swap(other.buffer_, buffer_);
-  std::swap(other.handles_, handles_);
-  std::swap(other.associated_endpoint_handles_, associated_endpoint_handles_);
-  return *this;
+  if (rv != MOJO_RESULT_OK) {
+    // Failed to deserialize handles. Leave the Message uninitialized.
+    return;
+  }
+
+  handle_ = std::move(handle);
+  data_ = buffer;
+  data_size_ = num_bytes;
 }
 
+Message::~Message() = default;
+
+Message& Message::operator=(Message&& other) = default;
+
 void Message::Reset() {
+  handle_.reset();
   handles_.clear();
   associated_endpoint_handles_.clear();
-  buffer_.reset();
-}
-
-void Message::Initialize(size_t capacity, bool zero_initialized) {
-  DCHECK(!buffer_);
-  buffer_.reset(new internal::MessageBuffer(capacity, zero_initialized));
-}
-
-void Message::InitializeFromMojoMessage(ScopedMessageHandle message,
-                                        uint32_t num_bytes,
-                                        std::vector<ScopedHandle>* handles) {
-  DCHECK(!buffer_);
-  buffer_.reset(new internal::MessageBuffer(std::move(message), num_bytes));
-  handles_.swap(*handles);
+  data_ = nullptr;
+  data_size_ = 0;
+  transferable_ = false;
 }
 
 const uint8_t* Message::payload() const {
@@ -112,48 +274,57 @@
   return array_pointer ? array_pointer->storage() : nullptr;
 }
 
+void Message::AttachHandles(std::vector<ScopedHandle> handles) {
+  if (handles.empty())
+    return;
+
+  // Sanity-check the current serialized message state. We must have a valid
+  // message handle and it must have no serialized handles.
+  DCHECK(handle_.is_valid());
+  DCHECK(payload_buffer_.is_valid());
+  void* buffer;
+  uint32_t num_bytes;
+  uint32_t num_handles = 0;
+  MojoResult rv = MojoGetSerializedMessageContents(
+      handle_->value(), &buffer, &num_bytes, nullptr, &num_handles,
+      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+  MessageInfo new_info(data_size_, std::move(handles));
+  ScopedMessageHandle new_handle;
+  rv = mojo::CreateMessage(reinterpret_cast<uintptr_t>(&new_info),
+                           &kMessageInfoThunks, &new_handle);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  DCHECK(new_handle.is_valid());
+
+  rv = MojoSerializeMessage(new_handle->value());
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  DCHECK(new_info.payload_buffer.is_valid());
+
+  // Copy the existing payload into the new message.
+  void* storage = new_info.payload_buffer.Allocate(payload_buffer_.cursor());
+  memcpy(storage, data_, data_size_);
+
+  handle_ = std::move(new_handle);
+  payload_buffer_ = std::move(new_info.payload_buffer);
+  data_ = payload_buffer_.data();
+  data_size_ = payload_buffer_.size();
+  transferable_ = true;
+}
+
 ScopedMessageHandle Message::TakeMojoMessage() {
   // If there are associated endpoints transferred,
   // SerializeAssociatedEndpointHandles() must be called before this method.
   DCHECK(associated_endpoint_handles_.empty());
-
-  if (handles_.empty())  // Fast path for the common case: No handles.
-    return buffer_->TakeMessage();
-
-  // Allocate a new message with space for the handles, then copy the buffer
-  // contents into it.
-  //
-  // TODO(rockot): We could avoid this copy by extending GetSerializedSize()
-  // behavior to collect handles. It's unoptimized for now because it's much
-  // more common to have messages with no handles.
-  ScopedMessageHandle new_message;
-  MojoResult rv = AllocMessage(
-      data_num_bytes(),
-      handles_.empty() ? nullptr
-                       : reinterpret_cast<const MojoHandle*>(handles_.data()),
-      handles_.size(),
-      MOJO_ALLOC_MESSAGE_FLAG_NONE,
-      &new_message);
-  CHECK_EQ(rv, MOJO_RESULT_OK);
-
-  // The handles are now owned by the message object.
-  for (auto& handle : handles_)
-    ignore_result(handle.release());
-  handles_.clear();
-
-  void* new_buffer = nullptr;
-  rv = GetMessageBuffer(new_message.get(), &new_buffer);
-  CHECK_EQ(rv, MOJO_RESULT_OK);
-
-  memcpy(new_buffer, data(), data_num_bytes());
-  buffer_.reset();
-
-  return new_message;
+  DCHECK(transferable_);
+  auto handle = std::move(handle_);
+  Reset();
+  return handle;
 }
 
 void Message::NotifyBadMessage(const std::string& error) {
-  DCHECK(buffer_);
-  buffer_->NotifyBadMessage(error);
+  DCHECK(handle_.is_valid());
+  mojo::NotifyBadMessage(handle_.get(), error);
 }
 
 void Message::SerializeAssociatedEndpointHandles(
@@ -163,9 +334,10 @@
 
   DCHECK_GE(version(), 2u);
   DCHECK(header_v2()->payload_interface_ids.is_null());
+  DCHECK(payload_buffer_.is_valid());
 
   size_t size = associated_endpoint_handles_.size();
-  auto* data = internal::Array_Data<uint32_t>::New(size, buffer());
+  auto* data = internal::Array_Data<uint32_t>::New(size, &payload_buffer_);
   header_v2()->payload_interface_ids.Set(data);
 
   for (size_t i = 0; i < size; ++i) {
@@ -239,23 +411,13 @@
 }
 
 MojoResult ReadMessage(MessagePipeHandle handle, Message* message) {
-  ScopedMessageHandle mojo_message;
+  ScopedMessageHandle message_handle;
   MojoResult rv =
-      ReadMessageNew(handle, &mojo_message, MOJO_READ_MESSAGE_FLAG_NONE);
+      ReadMessageNew(handle, &message_handle, MOJO_READ_MESSAGE_FLAG_NONE);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
-  uint32_t num_bytes = 0;
-  void* buffer;
-  std::vector<ScopedHandle> handles;
-  rv = GetSerializedMessageContents(
-      mojo_message.get(), &buffer, &num_bytes, &handles,
-      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
-  if (rv != MOJO_RESULT_OK)
-    return rv;
-
-  message->InitializeFromMojoMessage(
-      std::move(mojo_message), num_bytes, &handles);
+  *message = Message(std::move(message_handle));
   return MOJO_RESULT_OK;
 }
 
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.cc b/mojo/public/cpp/bindings/lib/message_buffer.cc
deleted file mode 100644
index cc12ef6..0000000
--- a/mojo/public/cpp/bindings/lib/message_buffer.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/bindings/lib/message_buffer.h"
-
-#include <limits>
-
-#include "mojo/public/cpp/bindings/lib/serialization_util.h"
-
-namespace mojo {
-namespace internal {
-
-MessageBuffer::MessageBuffer(size_t capacity, bool zero_initialized) {
-  DCHECK_LE(capacity, std::numeric_limits<uint32_t>::max());
-
-  MojoResult rv = AllocMessage(capacity, nullptr, 0,
-                               MOJO_ALLOC_MESSAGE_FLAG_NONE, &message_);
-  CHECK_EQ(rv, MOJO_RESULT_OK);
-
-  void* buffer = nullptr;
-  if (capacity != 0) {
-    rv = GetMessageBuffer(message_.get(), &buffer);
-    CHECK_EQ(rv, MOJO_RESULT_OK);
-
-    if (zero_initialized)
-      memset(buffer, 0, capacity);
-  }
-  Initialize(buffer, capacity);
-}
-
-MessageBuffer::MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes) {
-  message_ = std::move(message);
-
-  void* buffer = nullptr;
-  if (num_bytes != 0) {
-    MojoResult rv = GetMessageBuffer(message_.get(), &buffer);
-    CHECK_EQ(rv, MOJO_RESULT_OK);
-  }
-  Initialize(buffer, num_bytes);
-}
-
-MessageBuffer::~MessageBuffer() {}
-
-void MessageBuffer::NotifyBadMessage(const std::string& error) {
-  DCHECK(message_.is_valid());
-  MojoResult result = mojo::NotifyBadMessage(message_.get(), error);
-  DCHECK_EQ(result, MOJO_RESULT_OK);
-}
-
-}  // namespace internal
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.h b/mojo/public/cpp/bindings/lib/message_buffer.h
deleted file mode 100644
index 96d5140..0000000
--- a/mojo/public/cpp/bindings/lib/message_buffer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
-
-#include <stdint.h>
-
-#include <utility>
-
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
-#include "mojo/public/cpp/system/message.h"
-
-namespace mojo {
-namespace internal {
-
-// A fixed-size Buffer using a Mojo message object for storage.
-class MessageBuffer : public Buffer {
- public:
-  // Initializes this buffer to carry a fixed byte capacity and no handles.
-  MessageBuffer(size_t capacity, bool zero_initialized);
-
-  // Initializes this buffer from an existing Mojo MessageHandle.
-  MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes);
-
-  ~MessageBuffer();
-
-  ScopedMessageHandle TakeMessage() { return std::move(message_); }
-
-  void NotifyBadMessage(const std::string& error);
-
- private:
-  ScopedMessageHandle message_;
-
-  DISALLOW_COPY_AND_ASSIGN(MessageBuffer);
-};
-
-}  // namespace internal
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc
deleted file mode 100644
index 6806a73..0000000
--- a/mojo/public/cpp/bindings/lib/message_builder.cc
+++ /dev/null
@@ -1,69 +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 "mojo/public/cpp/bindings/lib/message_builder.h"
-
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
-#include "mojo/public/cpp/bindings/lib/message_internal.h"
-
-namespace mojo {
-namespace internal {
-
-template <typename Header>
-void Allocate(Buffer* buf, Header** header) {
-  *header = static_cast<Header*>(buf->Allocate(sizeof(Header)));
-  (*header)->num_bytes = sizeof(Header);
-}
-
-MessageBuilder::MessageBuilder(uint32_t name,
-                               uint32_t flags,
-                               size_t payload_size,
-                               size_t payload_interface_id_count) {
-  if (payload_interface_id_count > 0) {
-    // Version 2
-    InitializeMessage(
-        sizeof(MessageHeaderV2) + Align(payload_size) +
-        ArrayDataTraits<uint32_t>::GetStorageSize(
-            static_cast<uint32_t>(payload_interface_id_count)));
-
-    MessageHeaderV2* header;
-    Allocate(message_.buffer(), &header);
-    header->version = 2;
-    header->name = name;
-    header->flags = flags;
-    // The payload immediately follows the header.
-    header->payload.Set(header + 1);
-  } else if (flags &
-             (Message::kFlagExpectsResponse | Message::kFlagIsResponse)) {
-    // Version 1
-    InitializeMessage(sizeof(MessageHeaderV1) + payload_size);
-
-    MessageHeaderV1* header;
-    Allocate(message_.buffer(), &header);
-    header->version = 1;
-    header->name = name;
-    header->flags = flags;
-  } else {
-    InitializeMessage(sizeof(MessageHeader) + payload_size);
-
-    MessageHeader* header;
-    Allocate(message_.buffer(), &header);
-    header->version = 0;
-    header->name = name;
-    header->flags = flags;
-  }
-}
-
-MessageBuilder::~MessageBuilder() {
-}
-
-void MessageBuilder::InitializeMessage(size_t size) {
-  message_.Initialize(static_cast<uint32_t>(Align(size)),
-                      true /* zero_initialized */);
-}
-
-}  // namespace internal
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_builder.h b/mojo/public/cpp/bindings/lib/message_builder.h
deleted file mode 100644
index 8a4d5c4..0000000
--- a/mojo/public/cpp/bindings/lib/message_builder.h
+++ /dev/null
@@ -1,45 +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 MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/message.h"
-
-namespace mojo {
-
-class Message;
-
-namespace internal {
-
-class Buffer;
-
-class MOJO_CPP_BINDINGS_EXPORT MessageBuilder {
- public:
-  MessageBuilder(uint32_t name,
-                 uint32_t flags,
-                 size_t payload_size,
-                 size_t payload_interface_id_count);
-  ~MessageBuilder();
-
-  Buffer* buffer() { return message_.buffer(); }
-  Message* message() { return &message_; }
-
- private:
-  void InitializeMessage(size_t size);
-
-  Message message_;
-
-  DISALLOW_COPY_AND_ASSIGN(MessageBuilder);
-};
-
-}  // namespace internal
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
index d451c05..d39b991 100644
--- a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
@@ -6,7 +6,6 @@
 
 #include "base/logging.h"
 #include "mojo/public/cpp/bindings/interface_id.h"
-#include "mojo/public/cpp/bindings/lib/message_builder.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
 #include "mojo/public/cpp/bindings/lib/serialization_context.h"
 #include "mojo/public/cpp/bindings/lib/validation_context.h"
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
index 1029c2c4..50db139 100644
--- a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
@@ -9,8 +9,8 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/lib/message_builder.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/interfaces/bindings/pipe_control_messages.mojom.h"
 
 namespace mojo {
@@ -25,14 +25,12 @@
 
   size_t size = internal::PrepareToSerialize<
       pipe_control::RunOrClosePipeMessageParamsDataView>(params_ptr, &context);
-  internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId, 0,
-                                   size, 0);
-
+  Message message(pipe_control::kRunOrClosePipeMessageId, 0, size, 0);
   pipe_control::internal::RunOrClosePipeMessageParams_Data* params = nullptr;
   internal::Serialize<pipe_control::RunOrClosePipeMessageParamsDataView>(
-      params_ptr, builder.buffer(), &params, &context);
-  builder.message()->set_interface_id(kInvalidInterfaceId);
-  return std::move(*builder.message());
+      params_ptr, message.payload_buffer(), &params, &context);
+  message.set_interface_id(kInvalidInterfaceId);
+  return message;
 }
 
 }  // namespace
diff --git a/mojo/public/cpp/bindings/lib/serialization.h b/mojo/public/cpp/bindings/lib/serialization.h
index 2a7d288d..f479e9ed 100644
--- a/mojo/public/cpp/bindings/lib/serialization.h
+++ b/mojo/public/cpp/bindings/lib/serialization.h
@@ -50,8 +50,7 @@
     DCHECK(IsAligned(result_buffer));
   }
 
-  Buffer buffer;
-  buffer.Initialize(result_buffer, size);
+  Buffer buffer(result_buffer, size);
   typename MojomTypeTraits<MojomType>::Data* data = nullptr;
   Serialize<MojomType>(*input, &buffer, &data, &context);
 
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index 1ec0119..07d0a7c 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -17,7 +17,7 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/lib/message_buffer.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
 #include "mojo/public/cpp/bindings/lib/message_internal.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "mojo/public/cpp/system/message.h"
@@ -38,11 +38,37 @@
   static const uint32_t kFlagIsResponse = 1 << 1;
   static const uint32_t kFlagIsSync = 1 << 2;
 
+  // Constructs an uninitialized Message object.
   Message();
+
+  // Constructs a new serialized Message object with optional handles attached.
+  // This message is fully functional and may be exchanged for a
+  // ScopedMessageHandle for transit over a message pipe. See TakeMojoMessage().
+  //
+  // If |handles| are not known at Message construction time, they may be
+  // attached later by calling |AttachHandles()|. See the note on that method
+  // regarding performance considerations.
+  Message(uint32_t name,
+          uint32_t flags,
+          size_t payload_size,
+          size_t payload_interface_id_count,
+          std::vector<ScopedHandle> handles = std::vector<ScopedHandle>());
+
+  // Constructs a new serialized Message object from an existing
+  // ScopedMessageHandle; e.g., one read from a message pipe.
+  //
+  // If the message had any handles attached, they will be extracted and
+  // retrievable via |handles()|. Such messages may NOT be sent back over
+  // another message pipe, but are otherwise safe to inspect and pass around.
+  Message(ScopedMessageHandle handle);
+
+  // See the move-assignment operator below.
   Message(Message&& other);
 
   ~Message();
 
+  // Moves |other| into a new Message object. The moved-from Message becomes
+  // invalid and is effectively in a default-constructed state after this call.
   Message& operator=(Message&& other);
 
   // Resets the Message to an uninitialized state. Upon reset, the Message
@@ -51,51 +77,38 @@
   void Reset();
 
   // Indicates whether this Message is uninitialized.
-  bool IsNull() const { return !buffer_; }
+  bool IsNull() const { return !handle_.is_valid(); }
 
-  // Initializes a Message with enough space for |capacity| bytes.
-  void Initialize(size_t capacity, bool zero_initialized);
-
-  // Initializes a Message from an existing Mojo MessageHandle.
-  void InitializeFromMojoMessage(ScopedMessageHandle message,
-                                 uint32_t num_bytes,
-                                 std::vector<ScopedHandle>* handles);
-
-  uint32_t data_num_bytes() const {
-    return static_cast<uint32_t>(buffer_->size());
-  }
+  uint32_t data_num_bytes() const { return static_cast<uint32_t>(data_size_); }
 
   // Access the raw bytes of the message.
-  const uint8_t* data() const {
-    return static_cast<const uint8_t*>(buffer_->data());
-  }
-
-  uint8_t* mutable_data() { return static_cast<uint8_t*>(buffer_->data()); }
+  const uint8_t* data() const { return static_cast<const uint8_t*>(data_); }
+  uint8_t* mutable_data() { return static_cast<uint8_t*>(data_); }
 
   // Access the header.
   const internal::MessageHeader* header() const {
-    return static_cast<const internal::MessageHeader*>(buffer_->data());
+    return static_cast<const internal::MessageHeader*>(data_);
   }
   internal::MessageHeader* header() {
-    return static_cast<internal::MessageHeader*>(buffer_->data());
+    return static_cast<internal::MessageHeader*>(data_);
   }
 
   const internal::MessageHeaderV1* header_v1() const {
     DCHECK_GE(version(), 1u);
-    return static_cast<const internal::MessageHeaderV1*>(buffer_->data());
+    return static_cast<const internal::MessageHeaderV1*>(data_);
   }
   internal::MessageHeaderV1* header_v1() {
     DCHECK_GE(version(), 1u);
-    return static_cast<internal::MessageHeaderV1*>(buffer_->data());
+    return static_cast<internal::MessageHeaderV1*>(data_);
   }
 
   const internal::MessageHeaderV2* header_v2() const {
     DCHECK_GE(version(), 2u);
-    return static_cast<const internal::MessageHeaderV2*>(buffer_->data());
+    return static_cast<const internal::MessageHeaderV2*>(data_);
   }
   internal::MessageHeaderV2* header_v2() {
     DCHECK_GE(version(), 2u);
-    return static_cast<internal::MessageHeaderV2*>(buffer_->data());
+    return static_cast<internal::MessageHeaderV2*>(data_);
   }
 
   uint32_t version() const { return header()->version; }
@@ -120,7 +133,10 @@
   uint32_t payload_num_interface_ids() const;
   const uint32_t* payload_interface_ids() const;
 
-  // Access the handles.
+  internal::Buffer* payload_buffer() { return &payload_buffer_; }
+
+  // Access the handles of a received message. Note that these are unused on
+  // outgoing messages.
   const std::vector<ScopedHandle>* handles() const { return &handles_; }
   std::vector<ScopedHandle>* mutable_handles() { return &handles_; }
 
@@ -133,8 +149,10 @@
     return &associated_endpoint_handles_;
   }
 
-  // Access the underlying Buffer interface.
-  internal::Buffer* buffer() { return buffer_.get(); }
+  // Attaches handles to this Message. Note that this requires the underlying
+  // message object to be reallocated and the payload to be copied into a new
+  // buffer.
+  void AttachHandles(std::vector<ScopedHandle> handles);
 
   // Takes a scoped MessageHandle which may be passed to |WriteMessageNew()| for
   // transmission. Note that this invalidates this Message object, taking
@@ -156,12 +174,26 @@
       AssociatedGroupController* group_controller);
 
  private:
-  void CloseHandles();
+  ScopedMessageHandle handle_;
 
-  std::unique_ptr<internal::MessageBuffer> buffer_;
+  // Pointer to raw serialized message data, including header. This is only
+  // valid when |handle_| is a valid handle to a serialized message object.
+  void* data_ = nullptr;
+  size_t data_size_ = 0;
+
   std::vector<ScopedHandle> handles_;
   std::vector<ScopedInterfaceEndpointHandle> associated_endpoint_handles_;
 
+  // Indicates whether this Message object is transferable, i.e. can be sent
+  // elsewhere. In general this is true unless |handle_| is invalid or
+  // serialized handles have been extracted from the serialized message object
+  // identified by |handle_|.
+  bool transferable_ = false;
+
+  // A Buffer which may be used to allocated blocks of data within the message
+  // payload. May be invalid if there is no capacity remaining in the payload.
+  internal::Buffer payload_buffer_;
+
   DISALLOW_COPY_AND_ASSIGN(Message);
 };
 
diff --git a/mojo/public/cpp/bindings/tests/bindings_perftest.cc b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
index 65b3c8c1..aee51ef2 100644
--- a/mojo/public/cpp/bindings/tests/bindings_perftest.cc
+++ b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
@@ -12,7 +12,6 @@
 #include "base/time/time.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
-#include "mojo/public/cpp/bindings/lib/message_builder.h"
 #include "mojo/public/cpp/bindings/lib/multiplex_router.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/test_support/test_support.h"
@@ -154,8 +153,8 @@
       }
     }
 
-    internal::MessageBuilder builder(count, 0, 8, 0);
-    bool result = sender_->Accept(builder.message());
+    Message reply(count, 0, 8, 0);
+    bool result = sender_->Accept(&reply);
     DCHECK(result);
     return true;
   }
@@ -174,8 +173,8 @@
     quit_closure_ = run_loop.QuitClosure();
 
     start_time_ = base::TimeTicks::Now();
-    internal::MessageBuilder builder(0, 0, 8, 0);
-    bool result = sender_->Accept(builder.message());
+    Message message(0, 0, 8, 0);
+    bool result = sender_->Accept(&message);
     DCHECK(result);
 
     run_loop.Run();
@@ -264,9 +263,8 @@
     receiver.Reset();
     base::TimeTicks start_time = base::TimeTicks::Now();
     for (size_t j = 0; j < kIterations[i]; ++j) {
-      internal::MessageBuilder builder(0, 0, 8, 0);
-      bool result =
-          router->SimulateReceivingMessageForTesting(builder.message());
+      Message message(0, 0, 8, 0);
+      bool result = router->SimulateReceivingMessageForTesting(&message);
       DCHECK(result);
     }
 
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index 8757d72..63fc1efa 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -16,7 +16,7 @@
 #include "base/run_loop.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/tests/message_queue.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -97,12 +97,14 @@
 
   void TearDown() override {}
 
-  void AllocMessage(const char* text, Message* message) {
-    size_t payload_size = strlen(text) + 1;  // Plus null terminator.
-    internal::MessageBuilder builder(1, 0, payload_size, 0);
-    memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
-
-    *message = std::move(*builder.message());
+  Message CreateMessage(
+      const char* text,
+      std::vector<ScopedHandle> handles = std::vector<ScopedHandle>()) {
+    const size_t size = strlen(text) + 1;  // Plus null terminator.
+    Message message(1, 0, size, 0);
+    memcpy(message.payload_buffer()->Allocate(size), text, size);
+    message.AttachHandles(std::move(handles));
+    return message;
   }
 
  protected:
@@ -120,10 +122,7 @@
                        base::ThreadTaskRunnerHandle::Get());
 
   const char kText[] = "hello world";
-
-  Message message;
-  AllocMessage(kText, &message);
-
+  Message message = CreateMessage(kText);
   connector0.Accept(&message);
 
   base::RunLoop run_loop;
@@ -149,10 +148,7 @@
                        base::ThreadTaskRunnerHandle::Get());
 
   const char kText[] = "hello world";
-
-  Message message;
-  AllocMessage(kText, &message);
-
+  Message message = CreateMessage(kText);
   connector0.Accept(&message);
 
   MessageAccumulator accumulator;
@@ -181,10 +177,7 @@
   connector1.set_incoming_receiver(&accumulator);
 
   const char kText[] = "hello world";
-
-  Message message;
-  AllocMessage(kText, &message);
-
+  Message message = CreateMessage(kText);
   connector0.Accept(&message);
 
   run_loop.Run();
@@ -206,11 +199,8 @@
                        base::ThreadTaskRunnerHandle::Get());
 
   const char* kText[] = {"hello", "world"};
-
   for (size_t i = 0; i < arraysize(kText); ++i) {
-    Message message;
-    AllocMessage(kText[i], &message);
-
+    Message message = CreateMessage(kText[i]);
     connector0.Accept(&message);
   }
 
@@ -241,11 +231,8 @@
                        base::ThreadTaskRunnerHandle::Get());
 
   const char* kText[] = {"hello", "world"};
-
   for (size_t i = 0; i < arraysize(kText); ++i) {
-    Message message;
-    AllocMessage(kText[i], &message);
-
+    Message message = CreateMessage(kText[i]);
     connector0.Accept(&message);
   }
 
@@ -271,9 +258,7 @@
                        base::ThreadTaskRunnerHandle::Get());
 
   const char kText[] = "hello world";
-
-  Message message;
-  AllocMessage(kText, &message);
+  Message message = CreateMessage(kText);
 
   // Close the other end of the pipe.
   handle1_.reset();
@@ -304,18 +289,13 @@
 
   const char kText[] = "hello world";
 
-  Message message1;
-  AllocMessage(kText, &message1);
-
   MessagePipe pipe;
-  message1.mutable_handles()->emplace_back(
-      ScopedHandle::From(std::move(pipe.handle0)));
+  std::vector<ScopedHandle> handles;
+  handles.emplace_back(ScopedHandle::From(std::move(pipe.handle0)));
+  Message message1 = CreateMessage(kText, std::move(handles));
 
   connector0.Accept(&message1);
 
-  // The message should have been transferred, releasing the handles.
-  EXPECT_TRUE(message1.handles()->empty());
-
   base::RunLoop run_loop;
   MessageAccumulator accumulator(run_loop.QuitClosure());
   connector1.set_incoming_receiver(&accumulator);
@@ -343,9 +323,7 @@
                                Connector::SINGLE_THREADED_SEND,
                                base::ThreadTaskRunnerHandle::Get());
 
-  Message message2;
-  AllocMessage(kText, &message2);
-
+  Message message2 = CreateMessage(kText);
   connector_received.Accept(&message2);
   base::RunLoop run_loop2;
   MessageAccumulator accumulator2(run_loop2.QuitClosure());
@@ -377,10 +355,7 @@
                     base::ThreadTaskRunnerHandle::Get());
 
   const char kText[] = "hello world";
-
-  Message message;
-  AllocMessage(kText, &message);
-
+  Message message = CreateMessage(kText);
   connector0.Accept(&message);
 
   ConnectorDeletingMessageAccumulator accumulator(&connector1);
@@ -406,11 +381,8 @@
                        base::ThreadTaskRunnerHandle::Get());
 
   const char* kText[] = {"hello", "world"};
-
   for (size_t i = 0; i < arraysize(kText); ++i) {
-    Message message;
-    AllocMessage(kText[i], &message);
-
+    Message message = CreateMessage(kText[i]);
     connector0.Accept(&message);
   }
 
@@ -458,10 +430,7 @@
                  run_loop2.QuitClosure()));
 
   const char kText[] = "hello world";
-
-  Message message;
-  AllocMessage(kText, &message);
-
+  Message message = CreateMessage(kText);
   connector0.Accept(&message);
   connector0.RaiseError();
 
@@ -512,10 +481,9 @@
   const char kText[] = "hello world";
 
   // Queue up two messages.
-  Message message;
-  AllocMessage(kText, &message);
+  Message message = CreateMessage(kText);
   connector0.Accept(&message);
-  AllocMessage(kText, &message);
+  message = CreateMessage(kText);
   connector0.Accept(&message);
 
   base::RunLoop run_loop;
@@ -552,10 +520,9 @@
   const char kText[] = "hello world";
 
   // Queue up two messages.
-  Message message;
-  AllocMessage(kText, &message);
+  Message message = CreateMessage(kText);
   connector0.Accept(&message);
-  AllocMessage(kText, &message);
+  message = CreateMessage(kText);
   connector0.Accept(&message);
 
   base::RunLoop run_loop;
diff --git a/mojo/public/cpp/bindings/tests/router_test_util.cc b/mojo/public/cpp/bindings/tests/router_test_util.cc
index 9bab1cb..4a0b4cd 100644
--- a/mojo/public/cpp/bindings/tests/router_test_util.cc
+++ b/mojo/public/cpp/bindings/tests/router_test_util.cc
@@ -8,7 +8,7 @@
 #include <stdint.h>
 #include <string.h>
 
-#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/tests/message_queue.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -17,10 +17,8 @@
 
 void AllocRequestMessage(uint32_t name, const char* text, Message* message) {
   size_t payload_size = strlen(text) + 1;  // Plus null terminator.
-  internal::MessageBuilder builder(name, Message::kFlagExpectsResponse,
-                                   payload_size, 0);
-  memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
-  *message = std::move(*builder.message());
+  *message = Message(name, Message::kFlagExpectsResponse, payload_size, 0);
+  memcpy(message->payload_buffer()->Allocate(payload_size), text, payload_size);
 }
 
 void AllocResponseMessage(uint32_t name,
@@ -28,11 +26,9 @@
                           uint64_t request_id,
                           Message* message) {
   size_t payload_size = strlen(text) + 1;  // Plus null terminator.
-  internal::MessageBuilder builder(name, Message::kFlagIsResponse, payload_size,
-                                   0);
-  builder.message()->set_request_id(request_id);
-  memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
-  *message = std::move(*builder.message());
+  *message = Message(name, Message::kFlagIsResponse, payload_size, 0);
+  message->set_request_id(request_id);
+  memcpy(message->payload_buffer()->Allocate(payload_size), text, payload_size);
 }
 
 MessageAccumulator::MessageAccumulator(MessageQueue* queue,
diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc
index f271341..d498f049 100644
--- a/mojo/public/cpp/bindings/tests/validation_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -23,6 +24,7 @@
 #include "mojo/public/cpp/bindings/message_header_validator.h"
 #include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
 #include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/message.h"
 #include "mojo/public/cpp/test_support/test_support.h"
 #include "mojo/public/interfaces/bindings/tests/validation_test_associated_interfaces.mojom.h"
 #include "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.h"
@@ -32,6 +34,36 @@
 namespace test {
 namespace {
 
+void GetSerializedRawMessageSize(uintptr_t context,
+                                 size_t* num_bytes,
+                                 size_t* num_handles) {
+  *num_bytes = *reinterpret_cast<size_t*>(context);
+  *num_handles = 0;
+}
+static void IgnoreSerializeHandles(uintptr_t, MojoHandle*) {}
+static void IgnoreSerializePayload(uintptr_t, void*) {}
+static void IgnoreDestroy(uintptr_t) {}
+
+const MojoMessageOperationThunks kRawMessageThunks{
+    sizeof(MojoMessageOperationThunks),
+    &GetSerializedRawMessageSize,
+    &IgnoreSerializeHandles,
+    &IgnoreSerializePayload,
+    &IgnoreDestroy,
+};
+
+Message CreateRawMessage(size_t size) {
+  ScopedMessageHandle handle;
+  MojoResult rv = CreateMessage(reinterpret_cast<uintptr_t>(&size),
+                                &kRawMessageThunks, &handle);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  DCHECK(handle.is_valid());
+
+  rv = MojoSerializeMessage(handle->value());
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  return Message(std::move(handle));
+}
+
 template <typename T>
 void Append(std::vector<uint8_t>* data_vector, T data) {
   size_t pos = data_vector->size();
@@ -150,8 +182,7 @@
     return false;
   }
 
-  message->Initialize(static_cast<uint32_t>(data.size()),
-                      false /* zero_initialized */);
+  *message = CreateRawMessage(data.size());
   if (!data.empty())
     memcpy(message->mutable_data(), &data[0], data.size());
   message->mutable_handles()->resize(num_handles);
diff --git a/mojo/public/cpp/system/message.h b/mojo/public/cpp/system/message.h
index e06d6a7..ceb66cc 100644
--- a/mojo/public/cpp/system/message.h
+++ b/mojo/public/cpp/system/message.h
@@ -62,24 +62,6 @@
   return MOJO_RESULT_OK;
 }
 
-inline MojoResult AllocMessage(size_t num_bytes,
-                               const MojoHandle* handles,
-                               size_t num_handles,
-                               MojoAllocMessageFlags flags,
-                               ScopedMessageHandle* handle) {
-  DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max());
-  DCHECK_LE(num_handles, std::numeric_limits<uint32_t>::max());
-  MojoMessageHandle raw_handle;
-  MojoResult rv = MojoAllocMessage(static_cast<uint32_t>(num_bytes), handles,
-                                   static_cast<uint32_t>(num_handles), flags,
-                                   &raw_handle);
-  if (rv != MOJO_RESULT_OK)
-    return rv;
-
-  handle->reset(MessageHandle(raw_handle));
-  return MOJO_RESULT_OK;
-}
-
 inline MojoResult GetSerializedMessageContents(
     MessageHandle message,
     void** buffer,
@@ -104,13 +86,6 @@
       reinterpret_cast<MojoHandle*>(handles->data()), &num_handles, flags);
 }
 
-inline MojoResult GetMessageBuffer(MessageHandle message, void** buffer) {
-  uint32_t num_bytes;
-  return GetSerializedMessageContents(
-      message, buffer, &num_bytes, nullptr,
-      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES);
-}
-
 inline MojoResult NotifyBadMessage(MessageHandle message,
                                    const base::StringPiece& error) {
   DCHECK(message.is_valid());
diff --git a/mojo/public/cpp/system/message_pipe.cc b/mojo/public/cpp/system/message_pipe.cc
index 1254e2e..3f66b92 100644
--- a/mojo/public/cpp/system/message_pipe.cc
+++ b/mojo/public/cpp/system/message_pipe.cc
@@ -81,8 +81,8 @@
     return MOJO_RESULT_ABORTED;
   }
 
-  return MojoWriteMessageNew(message_pipe.value(),
-                             message_handle.release().value(), flags);
+  return MojoWriteMessage(message_pipe.value(),
+                          message_handle.release().value(), flags);
 }
 
 MojoResult ReadMessageRaw(MessagePipeHandle message_pipe,
@@ -90,7 +90,7 @@
                           std::vector<ScopedHandle>* handles,
                           MojoReadMessageFlags flags) {
   ScopedMessageHandle message_handle;
-  int rv = ReadMessageNew(message_pipe, &message_handle, flags);
+  MojoResult rv = ReadMessageNew(message_pipe, &message_handle, flags);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
diff --git a/mojo/public/cpp/system/message_pipe.h b/mojo/public/cpp/system/message_pipe.h
index 7a421746..ae0e2c7 100644
--- a/mojo/public/cpp/system/message_pipe.h
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -91,17 +91,17 @@
 inline MojoResult WriteMessageNew(MessagePipeHandle message_pipe,
                                   ScopedMessageHandle message,
                                   MojoWriteMessageFlags flags) {
-  return MojoWriteMessageNew(
-      message_pipe.value(), message.release().value(), flags);
+  return MojoWriteMessage(message_pipe.value(), message.release().value(),
+                          flags);
 }
 
-// Reads from a message pipe. See |MojoReadMessageNew()| for complete
+// Reads from a message pipe. See |MojoReadMessage()| for complete
 // documentation.
 inline MojoResult ReadMessageNew(MessagePipeHandle message_pipe,
                                  ScopedMessageHandle* message,
                                  MojoReadMessageFlags flags) {
   MojoMessageHandle raw_message;
-  MojoResult rv = MojoReadMessageNew(message_pipe.value(), &raw_message, flags);
+  MojoResult rv = MojoReadMessage(message_pipe.value(), &raw_message, flags);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
diff --git a/mojo/public/cpp/test_support/lib/test_utils.cc b/mojo/public/cpp/test_support/lib/test_utils.cc
index b59e3116..e5eca0ae 100644
--- a/mojo/public/cpp/test_support/lib/test_utils.cc
+++ b/mojo/public/cpp/test_support/lib/test_utils.cc
@@ -47,7 +47,7 @@
 bool DiscardMessage(const MessagePipeHandle& handle) {
   MojoMessageHandle message;
   int rv =
-      MojoReadMessageNew(handle.value(), &message, MOJO_READ_MESSAGE_FLAG_NONE);
+      MojoReadMessage(handle.value(), &message, MOJO_READ_MESSAGE_FLAG_NONE);
   if (rv != MOJO_RESULT_OK)
     return false;
   MojoFreeMessage(message);
diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js
index b2c4ee27..b973144 100644
--- a/mojo/public/js/core.js
+++ b/mojo/public/js/core.js
@@ -74,7 +74,6 @@
  * See core.h for more information.
  */
 var READ_MESSAGE_FLAG_NONE;
-var READ_MESSAGE_FLAG_MAY_DISCARD;
 
 /**
  * MojoCreateDataPipeOptions: Used to specify creation parameters for a data
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 1166acc1..e605df00 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -20,7 +20,6 @@
   "//device/bluetooth/public/interfaces/typemaps.gni",
   "//device/gamepad/public/interfaces/typemaps.gni",
   "//device/generic_sensor/public/interfaces/typemaps.gni",
-  "//device/usb/public/interfaces/typemaps.gni",
   "//extensions/common/typemaps.gni",
   "//gpu/ipc/common/typemaps.gni",
   "//media/capture/mojo/typemaps.gni",
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
index b748016..1aaba401 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -33,14 +33,15 @@
 {%- endmacro %}
 
 {%- macro build_message(struct, input_pattern, struct_display_name,
-                        serialization_context) -%}
+                        serialization_context, message) -%}
   {{struct_macros.serialize(struct, struct_display_name, input_pattern,
-                            "params", "builder.buffer()",
+                            "params", "%s.payload_buffer()"|format(message),
                             serialization_context)}}
-  ({{serialization_context}})->handles.Swap(
-      builder.message()->mutable_handles());
   ({{serialization_context}})->associated_endpoint_handles.swap(
-      *builder.message()->mutable_associated_endpoint_handles());
+      *{{message}}.mutable_associated_endpoint_handles());
+  std::vector<mojo::ScopedHandle> handles_to_attach;
+  ({{serialization_context}})->handles.Swap(&handles_to_attach);
+  {{message}}.AttachHandles(std::move(handles_to_attach));
 {%- endmacro %}
 
 {#--- Begin #}
@@ -161,14 +162,12 @@
   mojo::internal::SerializationContext serialization_context;
   {{struct_macros.get_serialized_size(params_struct, "param_%s",
                                       "&serialization_context")}}
-
-  mojo::internal::MessageBuilder builder(
+  mojo::Message message(
       {{message_name}},
       mojo::Message::kFlagIsSync | mojo::Message::kFlagExpectsResponse,
       size, serialization_context.associated_endpoint_count);
-
   {{build_message(params_struct, "param_%s", params_description,
-                  "&serialization_context")}}
+                  "&serialization_context", "message")}}
 
   bool result = false;
   std::unique_ptr<mojo::MessageReceiver> responder(
@@ -177,8 +176,7 @@
 {%-     for param in method.response_parameters -%}
           , param_{{param.name}}
 {%-     endfor %}));
-  ignore_result(receiver_->AcceptWithResponder(builder.message(),
-                                               std::move(responder)));
+  ignore_result(receiver_->AcceptWithResponder(&message, std::move(responder)));
   return result;
 }
 {%-   endif %}
@@ -188,29 +186,25 @@
   mojo::internal::SerializationContext serialization_context;
   {{struct_macros.get_serialized_size(params_struct, "in_%s",
                                       "&serialization_context")}}
-
 {%- if method.response_parameters != None %}
   constexpr uint32_t kFlags = mojo::Message::kFlagExpectsResponse;
 {%- else %}
   constexpr uint32_t kFlags = 0;
 {%- endif %}
-  mojo::internal::MessageBuilder builder(
-      {{message_name}}, kFlags, size,
-      serialization_context.associated_endpoint_count);
-
+  mojo::Message message({{message_name}}, kFlags, size,
+                        serialization_context.associated_endpoint_count);
   {{build_message(params_struct, "in_%s", params_description,
-                  "&serialization_context")}}
+                  "&serialization_context", "message")}}
 
 {%- if method.response_parameters != None %}
   std::unique_ptr<mojo::MessageReceiver> responder(
       new {{class_name}}_{{method.name}}_ForwardToCallback(
           std::move(callback)));
-  ignore_result(receiver_->AcceptWithResponder(builder.message(),
-                                               std::move(responder)));
+  ignore_result(receiver_->AcceptWithResponder(&message, std::move(responder)));
 {%- else %}
   // This return value may be ignored as false implies the Connector has
   // encountered an error, which will be visible through other means.
-  ignore_result(receiver_->Accept(builder.message()));
+  ignore_result(receiver_->Accept(&message));
 {%- endif %}
 }
 {%- endfor %}
@@ -277,17 +271,14 @@
   mojo::internal::SerializationContext serialization_context;
   {{struct_macros.get_serialized_size(response_params_struct, "in_%s",
                                       "&serialization_context")}}
-
   uint32_t flags = (is_sync_ ? mojo::Message::kFlagIsSync : 0) |
                    mojo::Message::kFlagIsResponse;
-  mojo::internal::MessageBuilder builder(
-      {{message_name}}, flags, size,
-      serialization_context.associated_endpoint_count);
-  builder.message()->set_request_id(request_id_);
-
+  mojo::Message message({{message_name}}, flags, size,
+                        serialization_context.associated_endpoint_count);
   {{build_message(response_params_struct, "in_%s", params_description,
-                  "&serialization_context")}}
-  ignore_result(responder_->Accept(builder.message()));
+                  "&serialization_context", "message")}}
+  message.set_request_id(request_id_);
+  ignore_result(responder_->Accept(&message));
   // TODO(darin): Accept() returning false indicates a malformed message, and
   // that may be good reason to close the connection. However, we don't have a
   // way to do that from here. We should add a way.
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
index cbbe1be..2a0f849 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -25,7 +25,6 @@
 #include <utility>
 
 #include "base/logging.h"
-#include "mojo/public/cpp/bindings/lib/message_builder.h"
 #include "mojo/public/cpp/bindings/lib/serialization_util.h"
 #include "mojo/public/cpp/bindings/lib/validate_params.h"
 #include "mojo/public/cpp/bindings/lib/validation_context.h"
diff --git a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.cc b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.cc
index 421e1f3..d05e6df 100644
--- a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.cc
+++ b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.cc
@@ -10,6 +10,10 @@
 
 namespace nacl_io {
 
+// This is not further implemented.
+// PNaCl is on a path to deprecation, and WebAssembly is
+// the focused technology.
+
 GoogleDriveFs::GoogleDriveFs() {}
 
 Error GoogleDriveFs::Init(const FsInitArgs& args) {
diff --git a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.h b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.h
index 179cb8d2..fc38652 100644
--- a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.h
+++ b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs.h
@@ -12,6 +12,10 @@
 
 namespace nacl_io {
 
+// This is not further implemented.
+// PNaCl is on a path to deprecation, and WebAssembly is
+// the focused technology.
+
 class GoogleDriveFs : public Filesystem {
  protected:
   GoogleDriveFs();
diff --git a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc
index 506ffd2..988f290 100644
--- a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc
+++ b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.cc
@@ -13,6 +13,10 @@
 
 namespace nacl_io {
 
+// This is not further implemented.
+// PNaCl is on a path to deprecation, and WebAssembly is
+// the focused technology.
+
 GoogleDriveFsNode::GoogleDriveFsNode(GoogleDriveFs* googledrivefs)
     : Node(googledrivefs) {}
 
diff --git a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.h b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.h
index e212e89..57aa41ac 100644
--- a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.h
+++ b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_node.h
@@ -14,6 +14,10 @@
 
 namespace nacl_io {
 
+// This is not further implemented.
+// PNaCl is on a path to deprecation, and WebAssembly is
+// the focused technology.
+
 class GoogleDriveFsNode : public Node {
  public:
   GoogleDriveFsNode(GoogleDriveFs* googledrivefs);
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index 6c81a55..8e3158a 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -50,10 +50,12 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "net/base/url_util.h"
 #include "net/cookies/cookie_util.h"
 #include "net/cookies/parsed_cookie.h"
 #include "url/gurl.h"
 #include "url/url_canon.h"
+#include "url/url_util.h"
 
 using base::Time;
 using base::TimeDelta;
@@ -228,21 +230,22 @@
                                                          creation_time,
                                                          server_time);
 
-  CookiePrefix prefix = CanonicalCookie::GetCookiePrefix(parsed_cookie.Name());
-  bool is_cookie_valid =
-      CanonicalCookie::IsCookiePrefixValid(prefix, url, parsed_cookie);
-  CanonicalCookie::RecordCookiePrefixMetrics(prefix, is_cookie_valid);
+  CookiePrefix prefix = GetCookiePrefix(parsed_cookie.Name());
+  bool is_cookie_valid = IsCookiePrefixValid(prefix, url, parsed_cookie);
+  RecordCookiePrefixMetrics(prefix, is_cookie_valid);
   if (!is_cookie_valid) {
     VLOG(kVlogSetCookies)
         << "Create() failed because the cookie violated prefix rules.";
     return nullptr;
   }
 
-  return base::WrapUnique(new CanonicalCookie(
+  std::unique_ptr<CanonicalCookie> cc(base::MakeUnique<CanonicalCookie>(
       parsed_cookie.Name(), parsed_cookie.Value(), cookie_domain, cookie_path,
       creation_time, cookie_expires, creation_time, parsed_cookie.IsSecure(),
       parsed_cookie.IsHttpOnly(), parsed_cookie.SameSite(),
       parsed_cookie.Priority()));
+  DCHECK(cc->IsCanonical());
+  return cc;
 }
 
 bool CanonicalCookie::IsEquivalentForSecureCookieMatching(
@@ -399,6 +402,51 @@
   return Priority() < other.Priority();
 }
 
+bool CanonicalCookie::IsCanonical() const {
+  // Not checking domain against ParsedCookie as it may have come purely
+  // from the URL.
+  if (ParsedCookie::ParseTokenString(name_) != name_ ||
+      ParsedCookie::ParseValueString(value_) != value_ ||
+      ParsedCookie::ParseValueString(path_) != path_ ||
+      !ParsedCookie::IsValidCookieAttributeValue(name_) ||
+      !ParsedCookie::IsValidCookieAttributeValue(value_) ||
+      !ParsedCookie::IsValidCookieAttributeValue(path_)) {
+    return false;
+  }
+
+  if (!last_access_date_.is_null() && creation_date_.is_null())
+    return false;
+
+  url::CanonHostInfo canon_host_info;
+  std::string canonical_domain(CanonicalizeHost(domain_, &canon_host_info));
+  // TODO(rdsmith): This specifically allows for empty domains.  The spec
+  // suggests this is invalid (if a domain attribute is empty, the cookie's
+  // domain is set to the canonicalized request host; see
+  // https://tools.ietf.org/html/rfc6265#section-5.3).  However, it is
+  // needed for Chrome extension cookies.
+  // See http://crbug.com/730633 for more information.
+  if (canonical_domain != domain_)
+    return false;
+
+  if (path_.empty() || path_[0] != '/')
+    return false;
+
+  switch (GetCookiePrefix(name_)) {
+    case COOKIE_PREFIX_HOST:
+      if (!secure_ || path_ != "/" || domain_.empty() || domain_[0] == '.')
+        return false;
+      break;
+    case COOKIE_PREFIX_SECURE:
+      if (!secure_)
+        return false;
+      break;
+    default:
+      break;
+  }
+
+  return true;
+}
+
 // static
 CanonicalCookie::CookiePrefix CanonicalCookie::GetCookiePrefix(
     const std::string& name) {
diff --git a/net/cookies/canonical_cookie.h b/net/cookies/canonical_cookie.h
index a2b366d..1d3b3ed5 100644
--- a/net/cookies/canonical_cookie.h
+++ b/net/cookies/canonical_cookie.h
@@ -144,6 +144,21 @@
   // FullCompare() are also sorted with respect to PartialCompare().
   bool FullCompare(const CanonicalCookie& other) const;
 
+  // Return whether this object is a valid CanonicalCookie().  Invalid
+  // cookies may be constructed by the detailed constructor.
+  // A cookie is considered canonical if-and-only-if:
+  // * It can be created by CanonicalCookie::Create, or
+  // * It is identical to a cookie created by CanonicalCookie::Create except
+  //   that the creation time is null, or
+  // * It can be derived from a cookie created by CanonicalCookie::Create by
+  //   entry into and retrieval from a cookie store (specifically, this means
+  //   by the setting of an creation time in place of a null creation time, and
+  //   the setting of a last access time).
+  // An additional requirement on a CanonicalCookie is that if the last
+  // access time is non-null, the creation time must also be non-null and
+  // greater than the last access time.
+  bool IsCanonical() const;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(CanonicalCookieTest, TestPrefixHistograms);
 
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index 402846d..aaa9c69d 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -44,6 +44,16 @@
   EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie2->SameSite());
 }
 
+TEST(CanonicalCookie, SpaceInName) {
+  GURL url("http://www.example.com/test/foo.html");
+  base::Time creation_time = base::Time::Now();
+  CookieOptions options;
+  std::unique_ptr<CanonicalCookie> cookie(
+      CanonicalCookie::Create(url, "A C=2", creation_time, options));
+  EXPECT_TRUE(cookie.get());
+  EXPECT_EQ("A C", cookie->Name());
+}
+
 TEST(CanonicalCookieTest, Create) {
   // Test creating cookies from a cookie string.
   GURL url("http://www.example.com/test/foo.html");
@@ -596,6 +606,264 @@
   EXPECT_TRUE(https_cookie_secure.get());
 }
 
+TEST(CanonicalCookieTest, IsCanonical) {
+  // Base correct template.
+  EXPECT_TRUE(CanonicalCookie("A", "B", "x.y", "/path", base::Time(),
+                              base::Time(), base::Time(), false, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Newline in name.
+  EXPECT_FALSE(CanonicalCookie("A\n", "B", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Carriage return in name.
+  EXPECT_FALSE(CanonicalCookie("A\r", "B", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Null character in name.
+  EXPECT_FALSE(CanonicalCookie(std::string("A\0Z", 3), "B", "x.y", "/path",
+                               base::Time(), base::Time(), base::Time(), false,
+                               false, CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Name begins with whitespace.
+  EXPECT_FALSE(CanonicalCookie(" A", "B", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Name ends with whitespace.
+  EXPECT_FALSE(CanonicalCookie("A ", "B", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Empty name.  (Note this is against the spec but compatible with other
+  // browsers.)
+  EXPECT_TRUE(CanonicalCookie("", "B", "x.y", "/path", base::Time(),
+                              base::Time(), base::Time(), false, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Space in name
+  EXPECT_TRUE(CanonicalCookie("A C", "B", "x.y", "/path", base::Time(),
+                              base::Time(), base::Time(), false, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Extra space suffixing name.
+  EXPECT_FALSE(CanonicalCookie("A ", "B", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // '=' character in name.
+  EXPECT_FALSE(CanonicalCookie("A=", "B", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Separator in name.
+  EXPECT_FALSE(CanonicalCookie("A;", "B", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // '=' character in value.
+  EXPECT_TRUE(CanonicalCookie("A", "B=", "x.y", "/path", base::Time(),
+                              base::Time(), base::Time(), false, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Separator in value.
+  EXPECT_FALSE(CanonicalCookie("A", "B;", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Separator in domain.
+  EXPECT_FALSE(CanonicalCookie("A", "B", ";x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Garbage in domain.
+  EXPECT_FALSE(CanonicalCookie("A", "B", "@:&", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Space in domain.
+  EXPECT_FALSE(CanonicalCookie("A", "B", "x.y ", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Empty domain.  (This is against cookie spec, but needed for Chrome's
+  // out-of-spec use of cookies for extensions; see http://crbug.com/730633.
+  EXPECT_TRUE(CanonicalCookie("A", "B", "", "/path", base::Time(), base::Time(),
+                              base::Time(), false, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Path does not start with a "/".
+  EXPECT_FALSE(CanonicalCookie("A", "B", "x.y", "path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Empty path.
+  EXPECT_FALSE(CanonicalCookie("A", "B", "x.y", "", base::Time(), base::Time(),
+                               base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Path suffixed with a space.
+  EXPECT_FALSE(CanonicalCookie("A", "B", "x.y", "/path ", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Path suffixed with separator.
+  EXPECT_FALSE(CanonicalCookie("A", "B", "x.y", "/path;", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Simple IPv4 address as domain.
+  EXPECT_TRUE(CanonicalCookie("A", "B", "1.2.3.4", "/path", base::Time(),
+                              base::Time(), base::Time(), false, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // NOn-canonical IPv4 address as domain.
+  EXPECT_FALSE(CanonicalCookie("A", "B", "01.2.03.4", "/path", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Null IPv6 address as domain.
+  EXPECT_TRUE(CanonicalCookie("A", "B", "[::]", "/path", base::Time(),
+                              base::Time(), base::Time(), false, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Localhost IPv6 address as domain.
+  EXPECT_TRUE(CanonicalCookie("A", "B", "[::1]", "/path", base::Time(),
+                              base::Time(), base::Time(), false, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Fully speced IPv6 address as domain.
+  EXPECT_FALSE(CanonicalCookie(
+                   "A", "B", "[2001:0DB8:AC10:FE01:0000:0000:0000:0000]",
+                   "/path", base::Time(), base::Time(), base::Time(), false,
+                   false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Zero abbreviated IPv6 address as domain.  Not canonical because of leading
+  // zeros & uppercase hex letters.
+  EXPECT_FALSE(CanonicalCookie("A", "B", "[2001:0DB8:AC10:FE01::]", "/path",
+                               base::Time(), base::Time(), base::Time(), false,
+                               false, CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Zero prefixes removed IPv6 address as domain.  Not canoncial because of
+  // uppercase hex letters.
+  EXPECT_FALSE(CanonicalCookie("A", "B", "[2001:DB8:AC10:FE01::]", "/path",
+                               base::Time(), base::Time(), base::Time(), false,
+                               false, CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Lowercased hex IPv6 address as domain.
+  EXPECT_TRUE(CanonicalCookie("A", "B", "[2001:db8:ac10:fe01::]", "/path",
+                              base::Time(), base::Time(), base::Time(), false,
+                              false, CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Properly formatted host cookie.
+  EXPECT_TRUE(CanonicalCookie("__Host-A", "B", "x.y", "/", base::Time(),
+                              base::Time(), base::Time(), true, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Insecure host cookie.
+  EXPECT_FALSE(CanonicalCookie("__Host-A", "B", "x.y", "/", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Host cookie with non-null path.
+  EXPECT_FALSE(CanonicalCookie("__Host-A", "B", "x.y", "/path", base::Time(),
+                               base::Time(), base::Time(), true, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Host cookie with empty domain.
+  EXPECT_FALSE(CanonicalCookie("__Host-A", "B", "", "/", base::Time(),
+                               base::Time(), base::Time(), true, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Host cookie with period prefixed domain.
+  EXPECT_FALSE(CanonicalCookie("__Host-A", "B", ".x.y", "/", base::Time(),
+                               base::Time(), base::Time(), true, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+
+  // Properly formatted secure cookie.
+  EXPECT_TRUE(CanonicalCookie("__Secure-A", "B", "x.y", "/", base::Time(),
+                              base::Time(), base::Time(), true, false,
+                              CookieSameSite::NO_RESTRICTION,
+                              COOKIE_PRIORITY_LOW)
+                  .IsCanonical());
+
+  // Insecure secure cookie.
+  EXPECT_FALSE(CanonicalCookie("__Secure-A", "B", "x.y", "/", base::Time(),
+                               base::Time(), base::Time(), false, false,
+                               CookieSameSite::NO_RESTRICTION,
+                               COOKIE_PRIORITY_LOW)
+                   .IsCanonical());
+}
+
 TEST(CanonicalCookieTest, TestPrefixHistograms) {
   base::HistogramTester histograms;
   const char kCookiePrefixHistogram[] = "Cookie.CookiePrefix";
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 3cd57005..0b60812a 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -109,6 +109,7 @@
   static const bool preserves_trailing_dots = true;
   static const bool filters_schemes = true;
   static const bool has_path_prefix_bug = false;
+  static const bool forbids_setting_empty_name = false;
   static const int creation_time_granularity_in_ms = 0;
 };
 
diff --git a/net/cookies/cookie_store_unittest.h b/net/cookies/cookie_store_unittest.h
index 8120bf0d..14aa3e9 100644
--- a/net/cookies/cookie_store_unittest.h
+++ b/net/cookies/cookie_store_unittest.h
@@ -1279,6 +1279,38 @@
       "a=val99", this->GetCookies(cs, GURL("http://chromium.org/path1")));
 }
 
+// Note that accepting an empty name is contrary to spec; see
+// https://tools.ietf.org/html/rfc6265#section-4.1.1.  However, we do it
+// for web compatibility; see http://inikulin.github.io/cookie-compat/
+// (specifically the "foo" and "=a" tests).  This test is present in Chromium
+// so that a flag is raised if this behavior is changed.
+// On IOS we use the system cookie store which has Safari's behavior, so
+// the test is skipped.
+TYPED_TEST_P(CookieStoreTest, EmptyName) {
+  if (TypeParam::forbids_setting_empty_name)
+    return;
+
+  GURL url_foo("http://www.foo.com/");
+  CookieStore* cs = this->GetCookieStore();
+
+  CookieOptions options;
+  if (!TypeParam::supports_http_only)
+    options.set_include_httponly();
+  EXPECT_TRUE(this->SetCookieWithOptions(cs, url_foo, "a", options));
+  CookieList list = this->GetAllCookiesForURL(cs, url_foo);
+  EXPECT_EQ(1u, list.size());
+  EXPECT_EQ("", list[0].Name());
+  EXPECT_EQ("a", list[0].Value());
+  EXPECT_EQ(1, this->DeleteAll(cs));
+
+  EXPECT_TRUE(this->SetCookieWithOptions(cs, url_foo, "=b", options));
+  list = this->GetAllCookiesForURL(cs, url_foo);
+  EXPECT_EQ(1u, list.size());
+  EXPECT_EQ("", list[0].Name());
+  EXPECT_EQ("b", list[0].Value());
+  EXPECT_EQ(1, this->DeleteAll(cs));
+}
+
 TYPED_TEST_P(CookieStoreTest, CookieOrdering) {
   // Put a random set of cookies into a store and make sure they're returned in
   // the right order.
@@ -1476,6 +1508,7 @@
                            TestSecure,
                            NetUtilCookieTest,
                            OverwritePersistentCookie,
+                           EmptyName,
                            CookieOrdering,
                            GetAllCookiesAsync,
                            DeleteCookieAsync,
diff --git a/net/cookies/parsed_cookie.cc b/net/cookies/parsed_cookie.cc
index ac3f87dd..8832afdb 100644
--- a/net/cookies/parsed_cookie.cc
+++ b/net/cookies/parsed_cookie.cc
@@ -124,16 +124,6 @@
   return c <= 31;
 }
 
-bool IsValidCookieAttributeValue(const std::string& value) {
-  // The greatest common denominator of cookie attribute values is
-  // <any CHAR except CTLs or ";"> according to RFC 6265.
-  for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
-    if (IsControlCharacter(*i) || *i == ';')
-      return false;
-  }
-  return true;
-}
-
 }  // namespace
 
 namespace net {
@@ -240,6 +230,7 @@
   return out;
 }
 
+// static
 std::string::const_iterator ParsedCookie::FindFirstTerminator(
     const std::string& s) {
   std::string::const_iterator end = s.end();
@@ -251,6 +242,7 @@
   return end;
 }
 
+// static
 bool ParsedCookie::ParseToken(std::string::const_iterator* it,
                               const std::string::const_iterator& end,
                               std::string::const_iterator* token_start,
@@ -287,6 +279,7 @@
   return true;
 }
 
+// static
 void ParsedCookie::ParseValue(std::string::const_iterator* it,
                               const std::string::const_iterator& end,
                               std::string::const_iterator* value_start,
@@ -313,6 +306,7 @@
   }
 }
 
+// static
 std::string ParsedCookie::ParseTokenString(const std::string& token) {
   std::string::const_iterator it = token.begin();
   std::string::const_iterator end = FindFirstTerminator(token);
@@ -323,6 +317,7 @@
   return std::string();
 }
 
+// static
 std::string ParsedCookie::ParseValueString(const std::string& value) {
   std::string::const_iterator it = value.begin();
   std::string::const_iterator end = FindFirstTerminator(value);
@@ -332,6 +327,17 @@
   return std::string(value_start, value_end);
 }
 
+// static
+bool ParsedCookie::IsValidCookieAttributeValue(const std::string& value) {
+  // The greatest common denominator of cookie attribute values is
+  // <any CHAR except CTLs or ";"> according to RFC 6265.
+  for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
+    if (IsControlCharacter(*i) || *i == ';')
+      return false;
+  }
+  return true;
+}
+
 // Parse all token/value pairs and populate pairs_.
 void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line) {
   pairs_.clear();
diff --git a/net/cookies/parsed_cookie.h b/net/cookies/parsed_cookie.h
index 5e6f615..8bc5e99 100644
--- a/net/cookies/parsed_cookie.h
+++ b/net/cookies/parsed_cookie.h
@@ -103,6 +103,9 @@
   static std::string ParseTokenString(const std::string& token);
   static std::string ParseValueString(const std::string& value);
 
+  // Is the string valid as the value of a cookie attribute?
+  static bool IsValidCookieAttributeValue(const std::string& value);
+
  private:
   void ParseTokenValuePairs(const std::string& cookie_line);
   void SetupAttributes();
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index dd17ae5..644000c 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -1822,11 +1822,14 @@
   // failure, restart this transaction.
   DCHECK(!reading_);
 
+  // Reset before invoking SetRequest() which can reset the request info sent to
+  // network transaction.
+  if (network_trans_)
+    network_trans_.reset();
+
   SetRequest(net_log_);
 
   entry_ = nullptr;
-  if (network_trans_)
-    network_trans_.reset();
 
   TransitionToState(STATE_GET_BACKEND);
   return OK;
@@ -2054,16 +2057,18 @@
 //-----------------------------------------------------------------------------
 
 void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log) {
+  net_log_ = net_log;
+
   // Reset the variables that might get set in this function. This is done
   // because this function can be invoked multiple times for a transaction.
   cache_entry_status_ = CacheEntryStatus::ENTRY_UNDEFINED;
   external_validation_.Reset();
   range_requested_ = false;
   partial_.reset();
+
+  request_ = initial_request_;
   custom_request_.reset();
 
-  net_log_ = net_log;
-  request_ = initial_request_;
   effective_load_flags_ = request_->load_flags;
   method_ = request_->method;
 
@@ -2148,6 +2153,8 @@
     if (method_ == "GET" && partial_->Init(request_->extra_headers)) {
       // We will be modifying the actual range requested to the server, so
       // let's remove the header here.
+      // Note that custom_request_ is a shallow copy so will keep the same
+      // pointer to upload data stream as in the original request.
       custom_request_.reset(new HttpRequestInfo(*request_));
       custom_request_->extra_headers.RemoveHeader(HttpRequestHeaders::kRange);
       request_ = custom_request_.get();
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index d2f0971..869708f 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -858,12 +858,13 @@
 
   cache.disk_cache()->set_soft_failures(true);
 
+  MockHttpRequest request(kSimpleGET_Transaction);
+
   // Now fail to read from the cache.
   auto c = base::MakeUnique<Context>();
   int rv = cache.CreateTransaction(&c->trans);
   ASSERT_THAT(rv, IsOk());
 
-  MockHttpRequest request(kSimpleGET_Transaction);
   rv = c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
   EXPECT_THAT(c->callback.GetResult(rv), IsOk());
 
@@ -7971,10 +7972,10 @@
   TestCompletionCallback callback;
 
   {
+    MockHttpRequest request(kSimpleGET_Transaction);
     std::unique_ptr<HttpTransaction> trans;
     ASSERT_THAT(cache.CreateTransaction(&trans), IsOk());
 
-    MockHttpRequest request(kSimpleGET_Transaction);
     int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
     EXPECT_THAT(callback.GetResult(rv), IsOk());
 
@@ -8006,11 +8007,11 @@
 
   ScopedMockTransaction transaction(kSimpleGET_Transaction);
   transaction.data = "";
+  MockHttpRequest request(transaction);
 
   std::unique_ptr<HttpTransaction> trans;
   ASSERT_THAT(cache.CreateTransaction(&trans), IsOk());
 
-  MockHttpRequest request(transaction);
   int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
   EXPECT_THAT(callback.GetResult(rv), IsOk());
 
@@ -8536,6 +8537,7 @@
 TEST(HttpCache, SetPriority) {
   MockHttpCache cache;
 
+  HttpRequestInfo info;
   std::unique_ptr<HttpTransaction> trans;
   ASSERT_THAT(cache.http_cache()->CreateTransaction(IDLE, &trans), IsOk());
 
@@ -8546,7 +8548,6 @@
   EXPECT_EQ(DEFAULT_PRIORITY,
             cache.network_layer()->last_create_transaction_priority());
 
-  HttpRequestInfo info;
   info.url = GURL(kSimpleGET_Transaction.url);
   TestCompletionCallback callback;
   EXPECT_EQ(ERR_IO_PENDING,
@@ -8572,6 +8573,7 @@
 // transaction passes on its argument to the underlying network transaction.
 TEST(HttpCache, SetWebSocketHandshakeStreamCreateHelper) {
   MockHttpCache cache;
+  HttpRequestInfo info;
 
   FakeWebSocketHandshakeStreamCreateHelper create_helper;
   std::unique_ptr<HttpTransaction> trans;
@@ -8579,7 +8581,6 @@
 
   EXPECT_FALSE(cache.network_layer()->last_transaction());
 
-  HttpRequestInfo info;
   info.url = GURL(kSimpleGET_Transaction.url);
   TestCompletionCallback callback;
   EXPECT_EQ(ERR_IO_PENDING,
diff --git a/net/http/http_stream_factory_impl_job_controller.cc b/net/http/http_stream_factory_impl_job_controller.cc
index 660ff8f..3e46fa94 100644
--- a/net/http/http_stream_factory_impl_job_controller.cc
+++ b/net/http/http_stream_factory_impl_job_controller.cc
@@ -107,9 +107,7 @@
   alternative_job_.reset();
   bound_job_ = nullptr;
   if (pac_request_) {
-    // TODO(mmenke):  Convert this to a DCHECK once https://crbug.com/723589 is
-    // resolved.
-    CHECK_EQ(STATE_RESOLVE_PROXY_COMPLETE, next_state_);
+    DCHECK_EQ(STATE_RESOLVE_PROXY_COMPLETE, next_state_);
     session_->proxy_service()->CancelPacRequest(pac_request_);
   }
   net_log_.EndEvent(NetLogEventType::HTTP_STREAM_JOB_CONTROLLER);
@@ -711,9 +709,7 @@
 }
 
 int HttpStreamFactoryImpl::JobController::DoResolveProxy() {
-  // TODO(mmenke):  Convert this to a DCHECK once https://crbug.com/723589 is
-  // resolved.
-  CHECK(!pac_request_);
+  DCHECK(!pac_request_);
   DCHECK(session_);
 
   next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
@@ -1245,9 +1241,7 @@
                                                                     int error) {
   // ReconsiderProxyAfterError() should only be called when the last job fails.
   DCHECK(!(alternative_job_ && main_job_));
-  // TODO(mmenke):  Convert this to a DCHECK once https://crbug.com/723589 is
-  // resolved.
-  CHECK(!pac_request_);
+  DCHECK(!pac_request_);
   DCHECK(session_);
 
   if (!job->should_reconsider_proxy())
diff --git a/net/http/http_transaction_test_util.cc b/net/http/http_transaction_test_util.cc
index 2d728cf..9b683ef 100644
--- a/net/http/http_transaction_test_util.cc
+++ b/net/http/http_transaction_test_util.cc
@@ -239,7 +239,12 @@
       done_reading_called_(false),
       weak_factory_(this) {}
 
-MockNetworkTransaction::~MockNetworkTransaction() {}
+MockNetworkTransaction::~MockNetworkTransaction() {
+  // Use request_ as in ~HttpNetworkTransaction to make sure its valid and not
+  // already freed by the consumer. See crbug.com/734037.
+  if (request_)
+    DCHECK(request_->load_flags >= 0);
+}
 
 int MockNetworkTransaction::Start(const HttpRequestInfo* request,
                                   const CompletionCallback& callback,
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_manager.cc b/services/resource_coordinator/coordination_unit/coordination_unit_manager.cc
index 4eca58767..714313c 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_manager.cc
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_manager.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "services/resource_coordinator/coordination_unit/coordination_unit_provider_impl.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service_context.h"
 
 namespace resource_coordinator {
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_manager.h b/services/resource_coordinator/coordination_unit/coordination_unit_manager.h
index 7d07f8b..4b10fcd 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_manager.h
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_manager.h
@@ -6,9 +6,9 @@
 #define SERVICES_RESOURCE_COORDINATOR_COORDINATION_UNIT_COORDINATION_UNIT_MANAGER_H_
 
 #include "base/macros.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace service_manager {
-class BinderRegistry;
 class ServiceContextRefFactory;
 }  // service_manager
 
diff --git a/services/service_manager/public/cpp/BUILD.gn b/services/service_manager/public/cpp/BUILD.gn
index 1e6a2f6..c015921 100644
--- a/services/service_manager/public/cpp/BUILD.gn
+++ b/services/service_manager/public/cpp/BUILD.gn
@@ -6,13 +6,11 @@
   output_name = "service_manager_cpp"
 
   sources = [
-    "binder_registry.cc",
     "binder_registry.h",
     "connect.h",
     "connector.cc",
     "connector.h",
     "export.h",
-    "interface_binder.cc",
     "interface_binder.h",
     "interface_provider.cc",
     "interface_provider.h",
diff --git a/services/service_manager/public/cpp/binder_registry.cc b/services/service_manager/public/cpp/binder_registry.cc
deleted file mode 100644
index d50de16f..0000000
--- a/services/service_manager/public/cpp/binder_registry.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/service_manager/public/cpp/binder_registry.h"
-
-#include "services/service_manager/public/cpp/identity.h"
-
-namespace service_manager {
-
-BinderRegistry::BinderRegistry() : weak_factory_(this) {}
-BinderRegistry::~BinderRegistry() {}
-
-void BinderRegistry::AddInterface(
-    const std::string& interface_name,
-    const base::Callback<void(mojo::ScopedMessagePipeHandle)>& callback,
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
-  SetInterfaceBinder(interface_name, base::MakeUnique<GenericCallbackBinder>(
-                                         callback, task_runner));
-}
-
-void BinderRegistry::AddInterface(
-    const std::string& interface_name,
-    const Binder& binder_callback,
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
-  SetInterfaceBinder(interface_name, base::MakeUnique<GenericCallbackBinder>(
-                                         binder_callback, task_runner));
-}
-
-void BinderRegistry::RemoveInterface(const std::string& interface_name) {
-  auto it = binders_.find(interface_name);
-  if (it != binders_.end())
-    binders_.erase(it);
-}
-
-bool BinderRegistry::CanBindInterface(const std::string& interface_name) const {
-  auto it = binders_.find(interface_name);
-  return it != binders_.end();
-}
-
-void BinderRegistry::BindInterface(
-    const BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  auto it = binders_.find(interface_name);
-  if (it != binders_.end()) {
-    it->second->BindInterface(source_info, interface_name,
-                              std::move(interface_pipe));
-  } else {
-    LOG(ERROR) << "Failed to locate a binder for interface: " << interface_name;
-  }
-}
-
-base::WeakPtr<BinderRegistry> BinderRegistry::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
-void BinderRegistry::SetInterfaceBinder(
-    const std::string& interface_name,
-    std::unique_ptr<InterfaceBinder> binder) {
-  RemoveInterface(interface_name);
-  binders_[interface_name] = std::move(binder);
-}
-
-}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/binder_registry.h b/services/service_manager/public/cpp/binder_registry.h
index 0cce9d98..e499b7a 100644
--- a/services/service_manager/public/cpp/binder_registry.h
+++ b/services/service_manager/public/cpp/binder_registry.h
@@ -20,34 +20,46 @@
 
 struct BindSourceInfo;
 
-class SERVICE_MANAGER_PUBLIC_CPP_EXPORT BinderRegistry {
+template <typename... BinderArgs>
+class BinderRegistryWithParams {
  public:
   using Binder = base::Callback<void(const BindSourceInfo&,
                                      const std::string&,
-                                     mojo::ScopedMessagePipeHandle)>;
+                                     mojo::ScopedMessagePipeHandle,
+                                     BinderArgs...)>;
 
-  BinderRegistry();
-  ~BinderRegistry();
+  BinderRegistryWithParams() : weak_factory_(this) {}
+  ~BinderRegistryWithParams() = default;
 
   template <typename Interface>
-  void AddInterface(
-      const base::Callback<void(const BindSourceInfo&,
-                                mojo::InterfaceRequest<Interface>)>& callback,
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner =
-          nullptr) {
+  void AddInterface(const base::Callback<void(const BindSourceInfo&,
+                                              mojo::InterfaceRequest<Interface>,
+                                              BinderArgs...)>& callback,
+                    const scoped_refptr<base::SingleThreadTaskRunner>&
+                        task_runner = nullptr) {
     SetInterfaceBinder(
         Interface::Name_,
-        base::MakeUnique<CallbackBinder<Interface>>(callback, task_runner));
+        base::MakeUnique<CallbackBinder<Interface, BinderArgs...>>(
+            callback, task_runner));
   }
   void AddInterface(
       const std::string& interface_name,
-      const base::Callback<void(mojo::ScopedMessagePipeHandle)>& callback,
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner = nullptr);
-
-  void AddInterface(
-      const std::string& interface_name,
-      const Binder& binder_callback,
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner = nullptr);
+      const base::Callback<void(mojo::ScopedMessagePipeHandle, BinderArgs...)>&
+          callback,
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner =
+          nullptr) {
+    SetInterfaceBinder(interface_name,
+                       base::MakeUnique<GenericCallbackBinder<BinderArgs...>>(
+                           callback, task_runner));
+  }
+  void AddInterface(const std::string& interface_name,
+                    const Binder& callback,
+                    const scoped_refptr<base::SingleThreadTaskRunner>&
+                        task_runner = nullptr) {
+    SetInterfaceBinder(interface_name,
+                       base::MakeUnique<GenericCallbackBinder<BinderArgs...>>(
+                           callback, task_runner));
+  }
 
   // Removes the specified interface from the registry. This has no effect on
   // bindings already completed.
@@ -55,34 +67,58 @@
   void RemoveInterface() {
     RemoveInterface(Interface::Name_);
   }
-  void RemoveInterface(const std::string& interface_name);
+  void RemoveInterface(const std::string& interface_name) {
+    auto it = binders_.find(interface_name);
+    if (it != binders_.end())
+      binders_.erase(it);
+  }
 
   // Returns true if an InterfaceBinder is registered for |interface_name|.
-  bool CanBindInterface(const std::string& interface_name) const;
+  bool CanBindInterface(const std::string& interface_name) const {
+    auto it = binders_.find(interface_name);
+    return it != binders_.end();
+  }
 
   // Completes binding the request for |interface_name| on |interface_pipe|, by
   // invoking the corresponding InterfaceBinder.
   void BindInterface(const BindSourceInfo& source_info,
                      const std::string& interface_name,
-                     mojo::ScopedMessagePipeHandle interface_pipe);
+                     mojo::ScopedMessagePipeHandle interface_pipe,
+                     BinderArgs... args) {
+    auto it = binders_.find(interface_name);
+    if (it != binders_.end()) {
+      it->second->BindInterface(source_info, interface_name,
+                                std::move(interface_pipe), args...);
+    } else {
+      LOG(ERROR) << "Failed to locate a binder for interface: "
+                 << interface_name;
+    }
+  }
 
-  base::WeakPtr<BinderRegistry> GetWeakPtr();
+  base::WeakPtr<BinderRegistryWithParams> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
 
  private:
   using InterfaceNameToBinderMap =
-      std::map<std::string, std::unique_ptr<InterfaceBinder>>;
+      std::map<std::string, std::unique_ptr<InterfaceBinder<BinderArgs...>>>;
 
   // Adds |binder| to the internal map.
-  void SetInterfaceBinder(const std::string& interface_name,
-                          std::unique_ptr<InterfaceBinder> binder);
+  void SetInterfaceBinder(
+      const std::string& interface_name,
+      std::unique_ptr<InterfaceBinder<BinderArgs...>> binder) {
+    RemoveInterface(interface_name);
+    binders_[interface_name] = std::move(binder);
+  }
 
   InterfaceNameToBinderMap binders_;
 
-  base::WeakPtrFactory<BinderRegistry> weak_factory_;
+  base::WeakPtrFactory<BinderRegistryWithParams> weak_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(BinderRegistry);
+  DISALLOW_COPY_AND_ASSIGN(BinderRegistryWithParams);
 };
 
+using BinderRegistry = BinderRegistryWithParams<>;
 
 }  // namespace service_manager
 
diff --git a/services/service_manager/public/cpp/interface_binder.cc b/services/service_manager/public/cpp/interface_binder.cc
deleted file mode 100644
index 3d4a032..0000000
--- a/services/service_manager/public/cpp/interface_binder.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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 "services/service_manager/public/cpp/interface_binder.h"
-
-#include "services/service_manager/public/cpp/bind_source_info.h"
-
-namespace service_manager {
-
-namespace {
-
-void BindCallbackAdapter(
-    const base::Callback<void(mojo::ScopedMessagePipeHandle)>& callback,
-    const BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle handle) {
-  callback.Run(std::move(handle));
-}
-
-}  // namespace
-
-GenericCallbackBinder::GenericCallbackBinder(
-    const BindCallback& callback,
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
-    : callback_(callback), task_runner_(task_runner) {}
-
-GenericCallbackBinder::GenericCallbackBinder(
-    const base::Callback<void(mojo::ScopedMessagePipeHandle)>& callback,
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
-    : callback_(base::Bind(&BindCallbackAdapter, callback)),
-      task_runner_(task_runner) {}
-
-GenericCallbackBinder::~GenericCallbackBinder() {}
-
-void GenericCallbackBinder::BindInterface(
-    const BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle handle) {
-  if (task_runner_) {
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&GenericCallbackBinder::RunCallback, callback_, source_info,
-                   interface_name, base::Passed(&handle)));
-    return;
-  }
-  RunCallback(callback_, source_info, interface_name, std::move(handle));
-}
-
-// static
-void GenericCallbackBinder::RunCallback(const BindCallback& callback,
-                                        const BindSourceInfo& source_info,
-                                        const std::string& interface_name,
-                                        mojo::ScopedMessagePipeHandle handle) {
-  callback.Run(source_info, interface_name, std::move(handle));
-}
-
-}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/interface_binder.h b/services/service_manager/public/cpp/interface_binder.h
index a53aa057..3daab552 100644
--- a/services/service_manager/public/cpp/interface_binder.h
+++ b/services/service_manager/public/cpp/interface_binder.h
@@ -11,11 +11,22 @@
 #include "base/bind.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/system/message_pipe.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
 
 namespace service_manager {
 
-struct BindSourceInfo;
+template <typename... BinderArgs>
+void BindCallbackAdapter(
+    const base::Callback<void(mojo::ScopedMessagePipeHandle, BinderArgs...)>&
+        callback,
+    const BindSourceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle handle,
+    BinderArgs... args) {
+  callback.Run(std::move(handle), args...);
+}
 
+template <typename... BinderArgs>
 class InterfaceBinder {
  public:
   virtual ~InterfaceBinder() {}
@@ -25,11 +36,12 @@
   // an implementation it must take ownership of the request handle.
   virtual void BindInterface(const BindSourceInfo& source_info,
                              const std::string& interface_name,
-                             mojo::ScopedMessagePipeHandle handle) = 0;
+                             mojo::ScopedMessagePipeHandle handle,
+                             BinderArgs... args) = 0;
 };
 
-template <typename Interface>
-class CallbackBinder : public InterfaceBinder {
+template <typename Interface, typename... BinderArgs>
+class CallbackBinder : public InterfaceBinder<BinderArgs...> {
  public:
   using BindCallback = base::Callback<void(const BindSourceInfo&,
                                            mojo::InterfaceRequest<Interface>)>;
@@ -43,21 +55,23 @@
   // InterfaceBinder:
   void BindInterface(const BindSourceInfo& source_info,
                      const std::string& interface_name,
-                     mojo::ScopedMessagePipeHandle handle) override {
+                     mojo::ScopedMessagePipeHandle handle,
+                     BinderArgs... args) override {
     mojo::InterfaceRequest<Interface> request(std::move(handle));
     if (task_runner_) {
       task_runner_->PostTask(
           FROM_HERE, base::BindOnce(&CallbackBinder::RunCallback, callback_,
-                                    source_info, std::move(request)));
+                                    source_info, std::move(request), args...));
     } else {
-      RunCallback(callback_, source_info, std::move(request));
+      RunCallback(callback_, source_info, std::move(request), args...);
     }
   }
 
   static void RunCallback(const BindCallback& callback,
                           const BindSourceInfo& source_info,
-                          mojo::InterfaceRequest<Interface> request) {
-    callback.Run(source_info, std::move(request));
+                          mojo::InterfaceRequest<Interface> request,
+                          BinderArgs... args) {
+    callback.Run(source_info, std::move(request), args...);
   }
 
   const BindCallback callback_;
@@ -65,30 +79,50 @@
   DISALLOW_COPY_AND_ASSIGN(CallbackBinder);
 };
 
-class GenericCallbackBinder : public InterfaceBinder {
+template <typename... BinderArgs>
+class GenericCallbackBinder : public InterfaceBinder<BinderArgs...> {
  public:
   using BindCallback = base::Callback<void(const BindSourceInfo&,
                                            const std::string&,
-                                           mojo::ScopedMessagePipeHandle)>;
+                                           mojo::ScopedMessagePipeHandle,
+                                           BinderArgs...)>;
 
   GenericCallbackBinder(
       const BindCallback& callback,
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+      : callback_(callback), task_runner_(task_runner) {}
   GenericCallbackBinder(
-      const base::Callback<void(mojo::ScopedMessagePipeHandle)>& callback,
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
-  ~GenericCallbackBinder() override;
+      const base::Callback<void(mojo::ScopedMessagePipeHandle, BinderArgs...)>&
+          callback,
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+      : callback_(base::Bind(&BindCallbackAdapter<BinderArgs...>, callback)),
+        task_runner_(task_runner) {}
+  ~GenericCallbackBinder() override {}
 
  private:
   // InterfaceBinder:
   void BindInterface(const BindSourceInfo& source_info,
                      const std::string& interface_name,
-                     mojo::ScopedMessagePipeHandle handle) override;
+                     mojo::ScopedMessagePipeHandle handle,
+                     BinderArgs... args) override {
+    if (task_runner_) {
+      task_runner_->PostTask(
+          FROM_HERE, base::Bind(&GenericCallbackBinder::RunCallback, callback_,
+                                source_info, interface_name,
+                                base::Passed(&handle), args...));
+      return;
+    }
+    RunCallback(callback_, source_info, interface_name, std::move(handle),
+                args...);
+  }
 
   static void RunCallback(const BindCallback& callback,
                           const BindSourceInfo& source_info,
                           const std::string& interface_name,
-                          mojo::ScopedMessagePipeHandle client_handle);
+                          mojo::ScopedMessagePipeHandle handle,
+                          BinderArgs... args) {
+    callback.Run(source_info, interface_name, std::move(handle), args...);
+  }
 
   const BindCallback callback_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/services/ui/display/screen_manager.h b/services/ui/display/screen_manager.h
index 017b40db..4cec5ee 100644
--- a/services/ui/display/screen_manager.h
+++ b/services/ui/display/screen_manager.h
@@ -8,12 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/ui/display/screen_manager_delegate.h"
 
-namespace service_manager {
-class BinderRegistry;
-}
-
 namespace display {
 
 class ScreenBase;
diff --git a/services/ui/input_devices/input_device_server.cc b/services/ui/input_devices/input_device_server.cc
index 5f5a5da..d1275f3 100644
--- a/services/ui/input_devices/input_device_server.cc
+++ b/services/ui/input_devices/input_device_server.cc
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "services/service_manager/public/cpp/bind_source_info.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/touchscreen_device.h"
 
diff --git a/services/ui/input_devices/input_device_server.h b/services/ui/input_devices/input_device_server.h
index c64bda2..2ce10100 100644
--- a/services/ui/input_devices/input_device_server.h
+++ b/services/ui/input_devices/input_device_server.h
@@ -8,12 +8,12 @@
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/ui/public/interfaces/input_devices/input_device_server.mojom.h"
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/input_device_event_observer.h"
 
 namespace service_manager {
-class BinderRegistry;
 struct BindSourceInfo;
 }
 
diff --git a/services/ui/input_devices/touch_device_server.cc b/services/ui/input_devices/touch_device_server.cc
index efd88ba..bf74e76 100644
--- a/services/ui/input_devices/touch_device_server.cc
+++ b/services/ui/input_devices/touch_device_server.cc
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "services/service_manager/public/cpp/bind_source_info.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/display/manager/chromeos/default_touch_transform_setter.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/touchscreen_device.h"
diff --git a/services/ui/input_devices/touch_device_server.h b/services/ui/input_devices/touch_device_server.h
index 6def2b0..a06f6120 100644
--- a/services/ui/input_devices/touch_device_server.h
+++ b/services/ui/input_devices/touch_device_server.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/ui/public/interfaces/input_devices/touch_device_server.mojom.h"
 
 namespace display {
@@ -15,7 +16,6 @@
 }
 
 namespace service_manager {
-class BinderRegistry;
 struct BindSourceInfo;
 }
 
diff --git a/services/ui/public/cpp/input_devices/input_device_controller.cc b/services/ui/public/cpp/input_devices/input_device_controller.cc
index 084236ac..0dea582 100644
--- a/services/ui/public/cpp/input_devices/input_device_controller.cc
+++ b/services/ui/public/cpp/input_devices/input_device_controller.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/touchscreen_device.h"
 #include "ui/events/keycodes/dom/dom_code.h"
diff --git a/services/ui/public/cpp/input_devices/input_device_controller.h b/services/ui/public/cpp/input_devices/input_device_controller.h
index 9164a1e..7fea004 100644
--- a/services/ui/public/cpp/input_devices/input_device_controller.h
+++ b/services/ui/public/cpp/input_devices/input_device_controller.h
@@ -8,10 +8,10 @@
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/ui/public/interfaces/input_devices/input_device_controller.mojom.h"
 
 namespace service_manager {
-class BinderRegistry;
 struct BindSourceInfo;
 }
 
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index be8833cb..789fef2c0 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -37,6 +37,8 @@
     "receiver_mojo_to_media_adapter.h",
     "service_impl.cc",
     "service_impl.h",
+    "testing_controls_impl.cc",
+    "testing_controls_impl.h",
   ]
 
   public_deps = [
diff --git a/services/video_capture/public/interfaces/BUILD.gn b/services/video_capture/public/interfaces/BUILD.gn
index 58034c9..36fc949 100644
--- a/services/video_capture/public/interfaces/BUILD.gn
+++ b/services/video_capture/public/interfaces/BUILD.gn
@@ -10,6 +10,7 @@
     "device_factory.mojom",
     "device_factory_provider.mojom",
     "receiver.mojom",
+    "testing_controls.mojom",
   ]
 
   deps = [
diff --git a/services/video_capture/public/interfaces/testing_controls.mojom b/services/video_capture/public/interfaces/testing_controls.mojom
new file mode 100644
index 0000000..fd5ac97
--- /dev/null
+++ b/services/video_capture/public/interfaces/testing_controls.mojom
@@ -0,0 +1,11 @@
+// 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.
+
+module video_capture.mojom;
+
+// APIs for allowing tests to control service behavior for the purpose of
+// integration testing.
+interface TestingControls  {
+  Crash();
+};
diff --git a/services/video_capture/service_impl.cc b/services/video_capture/service_impl.cc
index 16bfa06..5c4aa54 100644
--- a/services/video_capture/service_impl.cc
+++ b/services/video_capture/service_impl.cc
@@ -8,6 +8,7 @@
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/video_capture/device_factory_provider_impl.h"
 #include "services/video_capture/public/interfaces/constants.mojom.h"
+#include "services/video_capture/testing_controls_impl.h"
 
 namespace video_capture {
 
@@ -28,6 +29,10 @@
       // Unretained |this| is safe because |registry_| is owned by |this|.
       base::Bind(&ServiceImpl::OnDeviceFactoryProviderRequest,
                  base::Unretained(this)));
+  registry_.AddInterface<mojom::TestingControls>(
+      // Unretained |this| is safe because |registry_| is owned by |this|.
+      base::Bind(&ServiceImpl::OnTestingControlsRequest,
+                 base::Unretained(this)));
 }
 
 void ServiceImpl::OnBindInterface(
@@ -60,6 +65,15 @@
       std::move(request));
 }
 
+void ServiceImpl::OnTestingControlsRequest(
+    const service_manager::BindSourceInfo& source_info,
+    mojom::TestingControlsRequest request) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  mojo::MakeStrongBinding(
+      base::MakeUnique<TestingControlsImpl>(ref_factory_->CreateRef()),
+      std::move(request));
+}
+
 void ServiceImpl::SetShutdownDelayInSeconds(float seconds) {
   DCHECK(thread_checker_.CalledOnValidThread());
   shutdown_delay_in_seconds_ = seconds;
diff --git a/services/video_capture/service_impl.h b/services/video_capture/service_impl.h
index fc40952..34d7079 100644
--- a/services/video_capture/service_impl.h
+++ b/services/video_capture/service_impl.h
@@ -12,6 +12,7 @@
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/interfaces/device_factory_provider.mojom.h"
+#include "services/video_capture/public/interfaces/testing_controls.mojom.h"
 
 #if defined(OS_WIN)
 #include "base/win/scoped_com_initializer.h"
@@ -35,6 +36,9 @@
   void OnDeviceFactoryProviderRequest(
       const service_manager::BindSourceInfo& source_info,
       mojom::DeviceFactoryProviderRequest request);
+  void OnTestingControlsRequest(
+      const service_manager::BindSourceInfo& source_info,
+      mojom::TestingControlsRequest request);
   void SetShutdownDelayInSeconds(float seconds);
   void MaybeRequestQuitDelayed();
   void MaybeRequestQuit();
diff --git a/services/video_capture/service_manifest.json b/services/video_capture/service_manifest.json
index 62d76da..dcd36230 100644
--- a/services/video_capture/service_manifest.json
+++ b/services/video_capture/service_manifest.json
@@ -3,11 +3,14 @@
   "display_name": "Video Capture",
   "interface_provider_specs": {
     "service_manager:connector": {
-      "provides": {
-        "capture": [ "video_capture::mojom::DeviceFactoryProvider" ],
-        "tests": [ "*" ]
+      "provides" : {
+        "capture" : ["video_capture::mojom::DeviceFactoryProvider"],
+        "tests" : [
+          "video_capture::mojom::DeviceFactoryProvider",
+          "video_capture::mojom::TestingControls"
+        ]
       },
-      "requires": {
+      "requires" : {
         "service_manager": [ "service_manager:all_users" ]
       }
     }
diff --git a/services/video_capture/testing_controls_impl.cc b/services/video_capture/testing_controls_impl.cc
new file mode 100644
index 0000000..e89eb86
--- /dev/null
+++ b/services/video_capture/testing_controls_impl.cc
@@ -0,0 +1,19 @@
+// 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 "services/video_capture/testing_controls_impl.h"
+
+namespace video_capture {
+
+TestingControlsImpl::TestingControlsImpl(
+    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
+    : service_ref_(std::move(service_ref)) {}
+
+TestingControlsImpl::~TestingControlsImpl() = default;
+
+void TestingControlsImpl::Crash() {
+  CHECK(false) << "This is an intentional crash for the purpose of testing";
+}
+
+}  // namespace video_capture
diff --git a/services/video_capture/testing_controls_impl.h b/services/video_capture/testing_controls_impl.h
new file mode 100644
index 0000000..f0a17fe
--- /dev/null
+++ b/services/video_capture/testing_controls_impl.h
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_
+#define SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_
+
+#include "services/service_manager/public/cpp/service_context_ref.h"
+#include "services/video_capture/public/interfaces/testing_controls.mojom.h"
+
+namespace video_capture {
+
+class TestingControlsImpl : public mojom::TestingControls {
+ public:
+  TestingControlsImpl(
+      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  ~TestingControlsImpl() override;
+
+  // mojom::TestingControls implementation.
+  void Crash() override;
+
+ private:
+  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestingControlsImpl);
+};
+
+}  // namespace video_capture
+
+#endif  // SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 6f3f0b43..bd223c9 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -3539,23 +3539,11 @@
   "Linux Builder": {
     "additional_compile_targets": [
       "all"
-    ],
-    "scripts": [
-      {
-        "name": "check_gn_headers",
-        "script": "check_gn_headers.py"
-      }
     ]
   },
   "Linux Builder (dbg)": {
     "additional_compile_targets": [
       "all"
-    ],
-    "scripts": [
-      {
-        "name": "check_gn_headers",
-        "script": "check_gn_headers.py"
-      }
     ]
   },
   "Linux Tests": {
@@ -3967,6 +3955,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "mojo_js_integration_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "mojo_js_unittests"
       },
       {
@@ -4642,6 +4636,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "mojo_js_integration_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "mojo_js_unittests"
       },
       {
diff --git a/testing/buildbot/client.v8.chromium.json b/testing/buildbot/client.v8.chromium.json
index 09e30211..bb493a0 100644
--- a/testing/buildbot/client.v8.chromium.json
+++ b/testing/buildbot/client.v8.chromium.json
@@ -236,6 +236,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "mojo_js_integration_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "mojo_js_unittests"
       },
       {
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index d1275115..60b106c 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -143,6 +143,7 @@
 -WebContentsViewAuraTest.ScreenshotForSwappedOutRenderViews
 -WebRtcGetUserMediaOldConstraintsBrowserTest.TwoGetUserMediaWithFirstHdSecondVga
 -WebRtcGetUserMediaOldConstraintsBrowserTest.TwoGetUserMediaWithSecondVideoCropped
+-WebRtcVideoCaptureBrowserTest.RecoverFromCrashInVideoCaptureProcess
 -WebUIMojoTest.EndToEndPing
 -WheelScrollLatchingBrowserTest.WheelEventTarget
 -WorkerFetchTest.*
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 34e2f4c6..f305fcf 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -712,6 +712,10 @@
     "label": "//mojo/common:mojo_common_unittests",
     "type": "console_test_launcher",
   },
+  "mojo_js_integration_tests": {
+    "label": "//mojo/edk/js/tests:mojo_js_integration_tests",
+    "type": "console_test_launcher",
+  },
   "mojo_js_unittests": {
     "label": "//mojo/edk/js/tests:mojo_js_unittests",
     "type": "console_test_launcher",
@@ -918,10 +922,12 @@
     "script": "//third_party/catapult/devil/devil/android/tools/system_app.py",
     "args": [
       "remove",
-      "--package com.android.webview com.google.android.webview",
+      "--package",
+      "com.android.webview",
+      "com.google.android.webview",
       "-v",
       "--",
-      "//testing/scripts/run_telemetry_benchmark_as_googletest.py",
+      "../../testing/scripts/run_telemetry_benchmark_as_googletest.py",
       "../../tools/perf/run_benchmark",
     ],
   },
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index d5372222..b48b43c6 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -18269,10 +18269,8 @@
 crbug.com/591099 media/network-no-source-const-shadow.html [ Failure ]
 crbug.com/591099 media/no-autoplay-with-user-gesture-requirement.html [ Crash ]
 crbug.com/591099 media/remoteplayback/availability-callback-gc.html [ Crash ]
-crbug.com/591099 media/remoteplayback/prompt-throws-when-backend-disabled.html [ Crash Timeout ]
 crbug.com/591099 media/remoteplayback/prompt-twice-throws.html [ Crash ]
 crbug.com/591099 media/remoteplayback/watch-availability-throws-low-end-device.html [ Crash ]
-crbug.com/591099 media/remoteplayback/watch-availability-works-when-backend-disabled.html [ Crash ]
 crbug.com/591099 media/remove-from-document-before-load.html [ Crash ]
 crbug.com/591099 media/remove-from-document-config-controls-no-crash.html [ Crash Failure ]
 crbug.com/591099 media/remove-from-document.html [ Crash ]
@@ -23057,10 +23055,8 @@
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/video-overlay-cast-light-rendering.html [ Failure ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/video-overlay-play-button.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/remoteplayback/availability-callback-gc.html [ Crash ]
-crbug.com/591099 virtual/new-remote-playback-pipeline/media/remoteplayback/prompt-throws-when-backend-disabled.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/remoteplayback/prompt-twice-throws.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/remoteplayback/watch-availability-throws-low-end-device.html [ Crash ]
-crbug.com/591099 virtual/new-remote-playback-pipeline/media/remoteplayback/watch-availability-works-when-backend-disabled.html [ Crash ]
 crbug.com/591099 virtual/off-main-thread-fetch/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/extendable-message-event.https.html [ Crash Timeout ]
 crbug.com/591099 virtual/off-main-thread-fetch/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html [ Failure Pass ]
 crbug.com/591099 virtual/off-main-thread-fetch/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/SmokeTests b/third_party/WebKit/LayoutTests/SmokeTests
index 05f133bc..2ddbae26 100644
--- a/third_party/WebKit/LayoutTests/SmokeTests
+++ b/third_party/WebKit/LayoutTests/SmokeTests
@@ -974,6 +974,7 @@
 transforms/3d/hit-testing/composited-hit-test.html
 transforms/3d/hit-testing/rotated-hit-test-with-child.html
 transforms/container-transform-crash.html
+transforms/cssmatrix-crash.html
 transforms/focus-on-transformed-node.html
 transforms/no_transform_hit_testing.html
 transforms/topmost-becomes-bottomost-for-scrolling.html
diff --git a/third_party/WebKit/LayoutTests/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.html b/third_party/WebKit/LayoutTests/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.html
index 1aef3568..738e0f0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.html
@@ -8,7 +8,7 @@
 'use strict';
 promise_test(() => {
   return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryServices(),
       new DOMException(
         'Origin is not allowed to access any service. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/requestDevice/blocklisted-service-in-optionalServices.html b/third_party/WebKit/LayoutTests/bluetooth/requestDevice/blocklisted-service-in-optionalServices.html
index 2b9fef68..1a69b79 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/requestDevice/blocklisted-service-in-optionalServices.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/requestDevice/blocklisted-service-in-optionalServices.html
@@ -16,7 +16,7 @@
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['human_interface_device']
     })
-    .then(({device, fake_peripheral}) => {
+    .then(([device, fake_peripheral]) => {
       return fake_peripheral
         .setNextGATTConnectionResponse({code: HCI_SUCCESS})
         .then(() => device.gatt.connect())
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-before.js
index 1c93f99..3945d6f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-before.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-before.js
@@ -3,7 +3,7 @@
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['generic_access']})
-    .then(({device}) => {
+    .then(([device]) => {
       device.gatt.disconnect();
       return assert_promise_rejects_with_message(
         device.gatt.CALLS([
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-error.js
index 4dafb7b0..f79f856 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-error.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-error.js
@@ -1,7 +1,7 @@
 'use strict';
 promise_test(() => {
   return getEmptyHealthThermometerDevice()
-    .then(({device}) => {
+    .then(([device]) => {
       let promise = assert_promise_rejects_with_message(
         device.gatt.CALLS([
           getPrimaryService('health_thermometer')|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-success.js
index 722a04b..73d266a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-success.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-success.js
@@ -1,20 +1,22 @@
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gatt => {
       let promise = assert_promise_rejects_with_message(
-        device.gatt.CALLS([
-          getPrimaryService('health_thermometer')|
+        gatt.CALLS([
+          getPrimaryService('heart_rate')|
           getPrimaryServices()|
-          getPrimaryServices('health_thermometer')[UUID]
+          getPrimaryServices('heart_rate')[UUID]
         ]),
         new DOMException('GATT Server is disconnected. ' +
                          'Cannot retrieve services. ' +
                          '(Re)connect first with `device.gatt.connect`.',
                          'NetworkError'));
-      device.gatt.disconnect();
+      gatt.disconnect();
       return promise;
     });
 }, 'disconnect() called during a FUNCTION_NAME call that succeeds. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
index 3367d92..db410ed 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
@@ -1,18 +1,21 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => {
-      return device.gatt.CALLS([
-        getPrimaryService('health_thermometer')|
-        getPrimaryServices()|
-        getPrimaryServices('health_thermometer')[UUID]])
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      return gattServer
+        .CALLS([
+          getPrimaryService('heart_rate')|
+          getPrimaryServices()|
+          getPrimaryServices('heart_rate')[UUID]])
         // Convert to array if necessary.
         .then(s => {
           let services = [].concat(s);
-          device.gatt.disconnect();
-          return device.gatt.connect()
+          gattServer.disconnect();
+          return gattServer.connect()
             .then(() => services);
         });
     })
@@ -26,7 +29,7 @@
           'InvalidStateError');
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristic('measurement_interval'),
+            service.getCharacteristic('body_sensor_location'),
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
@@ -34,7 +37,7 @@
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristics('measurement_interval'),
+            service.getCharacteristics('body_sensor_location'),
             error));
       }
       return promises;
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnected-device.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnected-device.js
index e1d66cb..2ae7af2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnected-device.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnected-device.js
@@ -4,7 +4,7 @@
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['generic_access']
     })
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.CALLS([
         getPrimaryService('health_thermometer')|
         getPrimaryServices()|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/discovery-complete-no-permission-absent-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/discovery-complete-no-permission-absent-service.js
index cba08269..27f8bba 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/discovery-complete-no-permission-absent-service.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/discovery-complete-no-permission-absent-service.js
@@ -7,7 +7,7 @@
                                   'SecurityError');
   return getHealthThermometerDeviceWithServicesDiscovered({
       filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
         device.gatt.CALLS([
           getPrimaryService(glucose.alias)|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/discovery-complete-service-not-found.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/discovery-complete-service-not-found.js
index 39f8376..879b6cd 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/discovery-complete-service-not-found.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/discovery-complete-service-not-found.js
@@ -3,7 +3,7 @@
   return getHealthThermometerDeviceWithServicesDiscovered({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['glucose']})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.CALLS([
         getPrimaryService('glucose')|
         getPrimaryServices('glucose')[UUID]
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js
index 7ae3837..360ee82 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js
@@ -2,7 +2,7 @@
 promise_test(() => {
   let promise;
   return getEmptyHealthThermometerDevice()
-      .then(({device}) => {
+      .then(([device]) => {
         promise = assert_promise_rejects_with_message(
           device.gatt.CALLS([
             getPrimaryService('health_thermometer')|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js
index f03a204..f2bab5df 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js
@@ -1,21 +1,25 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => {
-      promise = assert_promise_rejects_with_message(
-        device.gatt.CALLS([
-          getPrimaryService('health_thermometer') |
-          getPrimaryServices() |
-          getPrimaryServices('health_thermometer')[UUID]]),
-        new DOMException(
-          'GATT Server is disconnected. Cannot retrieve services. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        promise = assert_promise_rejects_with_message(
+            gattServer.CALLS(
+                [getPrimaryService('health_thermometer') |
+                 getPrimaryServices() |
+                 getPrimaryServices('health_thermometer')[UUID]]),
+            new DOMException(
+                'GATT Server is disconnected. ' +
+                    'Cannot retrieve services. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        gattServer.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during a FUNCTION_NAME call that succeeds. ' +
    'Should not crash.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js
index e18bc2aa..26ca8da 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js
@@ -1,38 +1,37 @@
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
-      let services_first_connection;
-      return device.gatt.CALLS([
-          getPrimaryService('health_thermometer')|
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      let services1;
+      return gattServer
+        .CALLS([
+          getPrimaryService('heart_rate')|
           getPrimaryServices()|
-          getPrimaryServices('health_thermometer')[UUID]])
-        .then(services => services_first_connection = services)
-        .then(() => device.gatt.disconnect())
-        .then(() => device.gatt.connect())
-        .then(() => device.gatt.PREVIOUS_CALL)
-        .then(services_second_connection => [
-          services_first_connection,
-          services_second_connection
-        ]);
+          getPrimaryServices('heart_rate')[UUID]])
+        .then(services => services1 = services)
+        .then(() => gattServer.disconnect())
+        .then(() => gattServer.connect())
+        .then(() => gattServer.PREVIOUS_CALL)
+        .then(services2 => [services1, services2])
     })
-    .then(([services_first_connection, services_second_connection]) => {
+    .then(services_arrays => {
       // Convert to arrays if necessary.
-      services_first_connection = [].concat(services_first_connection);
-      services_second_connection = [].concat(services_second_connection);
+      for (let i = 0; i < services_arrays.length; i++) {
+        services_arrays[i] = [].concat(services_arrays[i]);
+      }
 
-      assert_equals(services_first_connection.length, services_second_connection.length);
+      for (let i = 1; i < services_arrays.length; i++) {
+        assert_equals(services_arrays[0].length, services_arrays[i].length);
+      }
 
-      let first_connection_set = new Set(services_first_connection);
-      let second_connection_set = new Set(services_second_connection);
-
-      // The two sets should be disjoint.
-      let common_services = services_first_connection.filter(
-        val => second_connection_set.has(val));
-      assert_equals(common_services.length, 0);
-
+      let base_set = new Set(services_arrays.shift());
+      for (let services of services_arrays) {
+        services.forEach(service => assert_false(base_set.has(service)));
+      }
     });
 }, 'Calls to FUNCTION_NAME after a disconnection should return a ' +
    'different object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js
index 10d440f..529bffb87 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js
@@ -1,32 +1,29 @@
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => Promise.all([
-      device.gatt.CALLS([
-        getPrimaryService('health_thermometer')|
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => Promise.all([
+      gattServer.CALLS([
+        getPrimaryService('heart_rate')|
         getPrimaryServices()|
-        getPrimaryServices('health_thermometer')[UUID]]),
-      device.gatt.PREVIOUS_CALL]))
-    .then(([services_first_call, services_second_call]) => {
+        getPrimaryServices('heart_rate')[UUID]]),
+      gattServer.PREVIOUS_CALL]))
+    .then(services_arrays => {
       // Convert to arrays if necessary.
-      services_first_call = [].concat(services_first_call);
-      services_second_call = [].concat(services_second_call);
+      for (let i = 0; i < services_arrays.length; i++) {
+        services_arrays[i] = [].concat(services_arrays[i]);
+      }
 
-      assert_equals(services_first_call.length, services_second_call.length);
+      for (let i = 1; i < services_arrays.length; i++) {
+        assert_equals(services_arrays[0].length, services_arrays[i].length);
+      }
 
-      let first_call_set = new Set(services_first_call);
-      assert_equals(services_first_call.length, first_call_set.size);
-      let second_call_set = new Set(services_second_call);
-      assert_equals(services_second_call.length, second_call_set.size);
-
-      services_first_call.forEach(service => {
-        assert_true(second_call_set.has(service))
-      });
-
-      services_second_call.forEach(service => {
-        assert_true(first_call_set.has(service));
-      });
+      let base_set = new Set(services_arrays[0]);
+      for (let services of services_arrays) {
+        services.forEach(service => assert_true(base_set.has(service)));
+      }
     });
 }, 'Calls to FUNCTION_NAME should return the same object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
index 5205b5c..32544f0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/invalid-service-name.js
@@ -1,7 +1,7 @@
 'use strict';
 promise_test(() => {
   return getHealthThermometerDevice()
-    .then(({device}) => {
+    .then(([device]) => {
       return assert_promise_rejects_with_message(
         device.gatt.CALLS([
           getPrimaryService('wrong_name')|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-absent-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-absent-service.js
index 061ff09..5be6047 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-absent-service.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-absent-service.js
@@ -7,7 +7,7 @@
                                   'SecurityError');
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
         device.gatt.CALLS([
           getPrimaryService(glucose.alias)|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
index 41286232..e2e95c2e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
@@ -1,7 +1,7 @@
 'use strict';
 promise_test(() => {
   return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
         device.gatt.CALLS([
           getPrimaryService('heart_rate')|
           getPrimaryServices()|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-present-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-present-service.js
index eb9dc48c..37bccfc3 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-present-service.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-present-service.js
@@ -5,16 +5,18 @@
                                   '\'optionalServices\' in requestDevice() ' +
                                   'options. https://goo.gl/HxfxSQ',
                                   'SecurityError');
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gatt => Promise.all([
       assert_promise_rejects_with_message(
-        device.gatt.CALLS([
+        gatt.CALLS([
           getPrimaryService(generic_access.alias)|
           getPrimaryServices(generic_access.alias)[UUID]
         ]), expected),
       assert_promise_rejects_with_message(
-        device.gatt.FUNCTION_NAME(generic_access.name), expected),
+        gatt.FUNCTION_NAME(generic_access.name), expected),
       assert_promise_rejects_with_message(
-        device.gatt.FUNCTION_NAME(generic_access.uuid), expected)]));
+        gatt.FUNCTION_NAME(generic_access.uuid), expected)]));
 }, 'Request for present service without permission. Reject with SecurityError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/service-not-found.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/service-not-found.js
index 888aee2..09c7d916 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/service-not-found.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/service-not-found.js
@@ -3,7 +3,7 @@
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['glucose']})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.CALLS([
         getPrimaryService('glucose')|
         getPrimaryServices('glucose')[UUID]
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
index 78067cb6..a5fb002 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
@@ -8,7 +8,7 @@
 'use strict';
 promise_test(() => {
   return getDiscoveredHealthThermometerDevice()
-    .then(({device, fake_peripheral}) => {
+    .then(([device, fake_peripheral]) => {
       return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
         .then(() => device.gatt.connect())
         .then(gatt => assert_true(gatt.connected));
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
index 7b735914..60156c1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
@@ -8,7 +8,7 @@
 'use strict';
 promise_test(() => {
   return getDiscoveredHealthThermometerDevice()
-    .then(({device, fake_peripheral}) => {
+    .then(([device, fake_peripheral]) => {
       return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
         .then(() => {
           // Don't return the promise and let |device| go out of scope
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
index 62f9d0b..d6d1a42 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
@@ -8,7 +8,7 @@
 'use strict';
 promise_test(() => {
   return getDiscoveredHealthThermometerDevice()
-    .then(({device, fake_peripheral}) => {
+    .then(([device, fake_peripheral]) => {
       return fake_peripheral
         .setNextGATTConnectionResponse({code: HCI_SUCCESS})
         .then(() => device.gatt.connect())
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
index f1aa39cc..c6f391c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/device-same-object.html
@@ -8,7 +8,7 @@
 'use strict';
 promise_test(() => {
   return getDiscoveredHealthThermometerDevice()
-    .then(({device, fake_peripheral}) => {
+    .then(([device, fake_peripheral]) => {
       return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
         .then(() => device.gatt.connect());
     })
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-before.html
index 86e704f..36e2de71 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-before.html
@@ -11,7 +11,7 @@
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['generic_access']})
-    .then(({device}) => {
+    .then(([device]) => {
       device.gatt.disconnect();
       return assert_promise_rejects_with_message(
         device.gatt.getPrimaryService('health_thermometer'),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.html
index b4fbe18..4eb61ea1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.html
@@ -9,7 +9,7 @@
 'use strict';
 promise_test(() => {
   return getEmptyHealthThermometerDevice()
-    .then(({device}) => {
+    .then(([device]) => {
       let promise = assert_promise_rejects_with_message(
         device.gatt.getPrimaryService('health_thermometer'),
         new DOMException('GATT Server is disconnected. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.html
index 9adb4ca6..802a7d6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.html
@@ -8,17 +8,19 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gatt => {
       let promise = assert_promise_rejects_with_message(
-        device.gatt.getPrimaryService('health_thermometer'),
+        gatt.getPrimaryService('heart_rate'),
         new DOMException('GATT Server is disconnected. ' +
                          'Cannot retrieve services. ' +
                          '(Re)connect first with `device.gatt.connect`.',
                          'NetworkError'));
-      device.gatt.disconnect();
+      gatt.disconnect();
       return promise;
     });
 }, 'disconnect() called during a getPrimaryService call that succeeds. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
index eb20948..0acff90 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
@@ -9,15 +9,18 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => {
-      return device.gatt.getPrimaryService('health_thermometer')
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      return gattServer
+        .getPrimaryService('heart_rate')
         // Convert to array if necessary.
         .then(s => {
           let services = [].concat(s);
-          device.gatt.disconnect();
-          return device.gatt.connect()
+          gattServer.disconnect();
+          return gattServer.connect()
             .then(() => services);
         });
     })
@@ -31,7 +34,7 @@
           'InvalidStateError');
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristic('measurement_interval'),
+            service.getCharacteristic('body_sensor_location'),
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
@@ -39,7 +42,7 @@
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristics('measurement_interval'),
+            service.getCharacteristics('body_sensor_location'),
             error));
       }
       return promises;
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnected-device.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnected-device.html
index 938b159b..a7061fa 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnected-device.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnected-device.html
@@ -12,7 +12,7 @@
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['generic_access']
     })
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryService('health_thermometer'),
       new DOMException('GATT Server is disconnected. ' +
                        'Cannot retrieve services. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.html
index 1659be5..cb05a7a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.html
@@ -15,7 +15,7 @@
                                   'SecurityError');
   return getHealthThermometerDeviceWithServicesDiscovered({
       filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
         device.gatt.getPrimaryService(glucose.alias), expected),
       assert_promise_rejects_with_message(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.html
index 962c713..f6978485 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.html
@@ -11,7 +11,7 @@
   return getHealthThermometerDeviceWithServicesDiscovered({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['glucose']})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryService('glucose'),
       new DOMException(
         'No Services matching UUID ' + glucose.uuid + ' found in Device.',
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html
index ae48249..a0c3a56 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html
@@ -10,7 +10,7 @@
 promise_test(() => {
   let promise;
   return getEmptyHealthThermometerDevice()
-      .then(({device}) => {
+      .then(([device]) => {
         promise = assert_promise_rejects_with_message(
           device.gatt.getPrimaryService('health_thermometer'),
           new DOMException(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
index d275cea..f03e923 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
@@ -9,19 +9,23 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => {
-      promise = assert_promise_rejects_with_message(
-        device.gatt.getPrimaryService('health_thermometer'),
-        new DOMException(
-          'GATT Server is disconnected. Cannot retrieve services. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        promise = assert_promise_rejects_with_message(
+            gattServer.getPrimaryService('health_thermometer'),
+            new DOMException(
+                'GATT Server is disconnected. ' +
+                    'Cannot retrieve services. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        gattServer.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during a getPrimaryService call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
index 69411a5b..37c33542 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
@@ -8,36 +8,35 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
-      let services_first_connection;
-      return device.gatt.getPrimaryService('health_thermometer')
-        .then(services => services_first_connection = services)
-        .then(() => device.gatt.disconnect())
-        .then(() => device.gatt.connect())
-        .then(() => device.gatt.getPrimaryService('health_thermometer'))
-        .then(services_second_connection => [
-          services_first_connection,
-          services_second_connection
-        ]);
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      let services1;
+      return gattServer
+        .getPrimaryService('heart_rate')
+        .then(services => services1 = services)
+        .then(() => gattServer.disconnect())
+        .then(() => gattServer.connect())
+        .then(() => gattServer.getPrimaryService('heart_rate'))
+        .then(services2 => [services1, services2])
     })
-    .then(([services_first_connection, services_second_connection]) => {
+    .then(services_arrays => {
       // Convert to arrays if necessary.
-      services_first_connection = [].concat(services_first_connection);
-      services_second_connection = [].concat(services_second_connection);
+      for (let i = 0; i < services_arrays.length; i++) {
+        services_arrays[i] = [].concat(services_arrays[i]);
+      }
 
-      assert_equals(services_first_connection.length, services_second_connection.length);
+      for (let i = 1; i < services_arrays.length; i++) {
+        assert_equals(services_arrays[0].length, services_arrays[i].length);
+      }
 
-      let first_connection_set = new Set(services_first_connection);
-      let second_connection_set = new Set(services_second_connection);
-
-      // The two sets should be disjoint.
-      let common_services = services_first_connection.filter(
-        val => second_connection_set.has(val));
-      assert_equals(common_services.length, 0);
-
+      let base_set = new Set(services_arrays.shift());
+      for (let services of services_arrays) {
+        services.forEach(service => assert_false(base_set.has(service)));
+      }
     });
 }, 'Calls to getPrimaryService after a disconnection should return a ' +
    'different object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
index 3783693f2..f046b52 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
@@ -8,31 +8,28 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => Promise.all([
-      device.gatt.getPrimaryService('health_thermometer'),
-      device.gatt.getPrimaryService('health_thermometer')]))
-    .then(([services_first_call, services_second_call]) => {
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => Promise.all([
+      gattServer.getPrimaryService('heart_rate'),
+      gattServer.getPrimaryService('heart_rate')]))
+    .then(services_arrays => {
       // Convert to arrays if necessary.
-      services_first_call = [].concat(services_first_call);
-      services_second_call = [].concat(services_second_call);
+      for (let i = 0; i < services_arrays.length; i++) {
+        services_arrays[i] = [].concat(services_arrays[i]);
+      }
 
-      assert_equals(services_first_call.length, services_second_call.length);
+      for (let i = 1; i < services_arrays.length; i++) {
+        assert_equals(services_arrays[0].length, services_arrays[i].length);
+      }
 
-      let first_call_set = new Set(services_first_call);
-      assert_equals(services_first_call.length, first_call_set.size);
-      let second_call_set = new Set(services_second_call);
-      assert_equals(services_second_call.length, second_call_set.size);
-
-      services_first_call.forEach(service => {
-        assert_true(second_call_set.has(service))
-      });
-
-      services_second_call.forEach(service => {
-        assert_true(first_call_set.has(service));
-      });
+      let base_set = new Set(services_arrays[0]);
+      for (let services of services_arrays) {
+        services.forEach(service => assert_true(base_set.has(service)));
+      }
     });
 }, 'Calls to getPrimaryService should return the same object.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
index 722be1a..591757d1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-invalid-service-name.html
@@ -9,7 +9,7 @@
 'use strict';
 promise_test(() => {
   return getHealthThermometerDevice()
-    .then(({device}) => {
+    .then(([device]) => {
       return assert_promise_rejects_with_message(
         device.gatt.getPrimaryService('wrong_name'),
         new DOMException(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-absent-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-absent-service.html
index 88362fe..04fa5a1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-absent-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-absent-service.html
@@ -15,7 +15,7 @@
                                   'SecurityError');
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
         device.gatt.getPrimaryService(glucose.alias), expected),
       assert_promise_rejects_with_message(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
index 7301f20..d02d25d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
@@ -9,7 +9,7 @@
 'use strict';
 promise_test(() => {
   return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
         device.gatt.getPrimaryService('heart_rate'),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.html
index d5e841c..94fad05 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.html
@@ -13,15 +13,17 @@
                                   '\'optionalServices\' in requestDevice() ' +
                                   'options. https://goo.gl/HxfxSQ',
                                   'SecurityError');
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gatt => Promise.all([
       assert_promise_rejects_with_message(
-        device.gatt.getPrimaryService(generic_access.alias), expected),
+        gatt.getPrimaryService(generic_access.alias), expected),
       assert_promise_rejects_with_message(
-        device.gatt.getPrimaryService(generic_access.name), expected),
+        gatt.getPrimaryService(generic_access.name), expected),
       assert_promise_rejects_with_message(
-        device.gatt.getPrimaryService(generic_access.uuid), expected)]));
+        gatt.getPrimaryService(generic_access.uuid), expected)]));
 }, 'Request for present service without permission. Reject with SecurityError.');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-service-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-service-not-found.html
index d7290ba..ed44846d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-service-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-service-not-found.html
@@ -11,7 +11,7 @@
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['glucose']})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryService('glucose'),
       new DOMException(
         'No Services matching UUID ' + glucose.uuid + ' found in Device.',
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
index a544178..e23fc14 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
@@ -2,19 +2,19 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
-promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
-      return Promise.all([
-          device.gatt.getPrimaryService(generic_access.alias),
-          device.gatt.getPrimaryService(generic_access.name),
-          device.gatt.getPrimaryService(generic_access.uuid)])
+promise_test(function() {
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => {
+      return device.gatt.connect()
+        .then(gattServer => Promise.all([
+          gattServer.getPrimaryService(generic_access.alias),
+          gattServer.getPrimaryService(generic_access.name),
+          gattServer.getPrimaryService(generic_access.uuid)]))
         .then(services => {
           services.forEach(service => {
             assert_equals(service.uuid, generic_access.uuid,
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.html
index 6989bf26..898cd7c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.html
@@ -2,62 +2,49 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
-"use strict";
-var firstIframe = true;
-var iframe1 = document.createElement('iframe');
-iframe1.src ='../../../resources/bluetooth/health-thermometer-two-iframes.html';
-iframe1.id = 'iframe1';
-var iframe2 = document.createElement('iframe');
-iframe2.src = '../../../resources/bluetooth/health-thermometer-two-iframes.html';
-iframe2.id = 'iframe2';
-async_test(test => {
-  window.onmessage = messageEvent => test.step(() => {
-    if (messageEvent.data === 'Ready') {
-      if (firstIframe) {
+  "use strict";
+  var firstIframe = true;
+  var iframe1 = document.createElement('iframe');
+  iframe1.src = '../../../resources/bluetooth/heart-rate-two-iframes.html';
+  iframe1.id = 'iframe1';
+  var iframe2 = document.createElement('iframe');
+  iframe2.src = '../../../resources/bluetooth/heart-rate-two-iframes.html';
+  iframe2.id = 'iframe2';
+  async_test(test => {
+    window.onmessage = messageEvent => test.step(() => {
+      if (messageEvent.data === 'Ready') {
+        if (firstIframe) {
+          callWithKeyDown(() => {
+            iframe1.contentWindow.postMessage('Iframe1RequestAndConnect', '*');
+          });
+        } else {
+          callWithKeyDown(() => {
+            iframe2.contentWindow.postMessage('Iframe2RequestAndConnect', '*');
+          });
+        }
+        firstIframe = false;
+      } else if (messageEvent.data === 'Iframe1Connected') {
         callWithKeyDown(() => {
-          iframe1.contentWindow.postMessage('Iframe1RequestAndConnect', '*');
+          iframe1.contentWindow.postMessage('Iframe1TryAccessGenericAccessService', '*');
         });
-      } else {
+      } else if (messageEvent.data === 'Iframe1AccessGenericAccessServiceFailed') {
+        document.body.appendChild(iframe2);
+      } else if (messageEvent.data === 'Iframe2Connected') {
         callWithKeyDown(() => {
-          iframe2.contentWindow.postMessage('Iframe2RequestAndConnect', '*');
+          iframe1.contentWindow.postMessage('TestIframe1HasGenericAccessService', '*');
         });
       }
-      firstIframe = false;
-    } else if (messageEvent.data === 'Iframe1Connected') {
-      callWithKeyDown(() => {
-        iframe1.contentWindow.postMessage('Iframe1TryAccessGenericAccessService', '*');
-      });
-    } else if (messageEvent.data === 'Iframe1AccessGenericAccessServiceFailed') {
-      document.body.appendChild(iframe2);
-    } else if (messageEvent.data === 'Iframe2Connected') {
-      callWithKeyDown(() => {
-        iframe1.contentWindow.postMessage('TestIframe1HasGenericAccessService', '*');
-      });
-    }
-    else if (messageEvent.data === 'DoneTest') {
-      test.done();
-    } else {
-      assert_unreached('iframe sent invalid data: ' + messageEvent.data);
-    }
-  });
-
-  return setUpPreconnectedDevice({
-      address: '09:09:09:09:09:09',
-      name: 'Health Thermometer',
-      knownServiceUUIDs: ['generic_access', 'health_thermometer'],
-    })
-    .then(fake_peripheral => {
-      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
-        .then(() => fake_peripheral.addFakeService({uuid: 'generic_access'}))
-        .then(() => fake_peripheral.addFakeService({uuid: 'health_thermometer'}))
-        .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
-          code: HCI_SUCCESS}))
-    })
-    .then(() => {
-      document.body.appendChild(iframe1);
+      else if (messageEvent.data === 'DoneTest') {
+        test.done();
+      } else {
+        assert_unreached('iframe sent invalid data: ' + messageEvent.data);
+      }
     });
-}, 'Two iframes in the same origin should be able to access each other\'s services');
+
+    setBluetoothFakeAdapter('HeartRateAdapter')
+      .then(() => {
+        document.body.appendChild(iframe1);
+      });
+  }, 'Two iframes in the same origin should be able to access each other\'s services');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
index 39d834d..65c8b2f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
@@ -2,16 +2,17 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return getHIDDevice({
-      filters: [{services: ['battery_service']}],
-      optionalServices: ['human_interface_device']})
-    .then(({device}) => assert_promise_rejects_with_message(
-      device.gatt.getPrimaryServices('human_interface_device'),
+  return setBluetoothFakeAdapter('BlocklistTestAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['device_information']}],
+      optionalServices: ['human_interface_device']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => assert_promise_rejects_with_message(
+      gattServer.getPrimaryServices('human_interface_device'),
+
       new DOMException('Origin is not allowed to access the service. ' +
                        'Tip: Add the service UUID to \'optionalServices\' ' +
                        'in requestDevice() options. https://goo.gl/HxfxSQ',
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
index f7d2592a..e894ab5 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
@@ -2,27 +2,27 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return getHIDDevice({
-      filters: [{services: ['battery_service']}],
+  return setBluetoothFakeAdapter('BlocklistTestAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['device_information']}],
       optionalServices: [
-        'generic_access',
-        'human_interface_device'
-      ]})
-    .then(({device}) => device.gatt.getPrimaryServices())
+        blocklist_test_service_uuid, 'device_information', 'generic_access',
+       'heart_rate', 'human_interface_device']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => gattServer.getPrimaryServices())
     .then(services => {
-      assert_equals(services.length, 2);
-      let uuid_set = new Set(services.map(s => s.uuid));
-
-      assert_equals(uuid_set.size, 2);
-      assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
-      assert_true(uuid_set.has(BluetoothUUID.getService('battery_service')));
-      assert_false(
-        uuid_set.has(BluetoothUUID.getService('human_interface_device')));
+      assert_equals(services.length, 4);
+      assert_equals(services[0].uuid,
+                    BluetoothUUID.getService(blocklist_test_service_uuid));
+      assert_equals(services[1].uuid,
+                    BluetoothUUID.getService('device_information'));
+      assert_equals(services[2].uuid,
+                    BluetoothUUID.getService('generic_access'));
+      assert_equals(services[3].uuid,
+                    BluetoothUUID.getService('heart_rate'));
     });
 }, 'Request for services. Does not return blocklisted service.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.html
index f5354b89..fa7df561 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.html
@@ -11,7 +11,7 @@
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['generic_access']})
-    .then(({device}) => {
+    .then(([device]) => {
       device.gatt.disconnect();
       return assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices('health_thermometer'),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.html
index 2c19bda2..2b6213db 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-before.html
@@ -11,7 +11,7 @@
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['generic_access']})
-    .then(({device}) => {
+    .then(([device]) => {
       device.gatt.disconnect();
       return assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices(),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.html
index 01017a2..a29f4a6f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.html
@@ -9,7 +9,7 @@
 'use strict';
 promise_test(() => {
   return getEmptyHealthThermometerDevice()
-    .then(({device}) => {
+    .then(([device]) => {
       let promise = assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices('health_thermometer'),
         new DOMException('GATT Server is disconnected. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.html
index 1e11f89..aeecb4c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.html
@@ -9,7 +9,7 @@
 'use strict';
 promise_test(() => {
   return getEmptyHealthThermometerDevice()
-    .then(({device}) => {
+    .then(([device]) => {
       let promise = assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices(),
         new DOMException('GATT Server is disconnected. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.html
index a6c7211..e05bbe0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.html
@@ -8,17 +8,19 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gatt => {
       let promise = assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices('health_thermometer'),
+        gatt.getPrimaryServices('heart_rate'),
         new DOMException('GATT Server is disconnected. ' +
                          'Cannot retrieve services. ' +
                          '(Re)connect first with `device.gatt.connect`.',
                          'NetworkError'));
-      device.gatt.disconnect();
+      gatt.disconnect();
       return promise;
     });
 }, 'disconnect() called during a getPrimaryServices call that succeeds. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.html
index 523c668..fd9a868 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.html
@@ -8,17 +8,19 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gatt => {
       let promise = assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices(),
+        gatt.getPrimaryServices(),
         new DOMException('GATT Server is disconnected. ' +
                          'Cannot retrieve services. ' +
                          '(Re)connect first with `device.gatt.connect`.',
                          'NetworkError'));
-      device.gatt.disconnect();
+      gatt.disconnect();
       return promise;
     });
 }, 'disconnect() called during a getPrimaryServices call that succeeds. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
index 26b6589f..19ccb5a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
@@ -9,15 +9,18 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => {
-      return device.gatt.getPrimaryServices('health_thermometer')
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      return gattServer
+        .getPrimaryServices('heart_rate')
         // Convert to array if necessary.
         .then(s => {
           let services = [].concat(s);
-          device.gatt.disconnect();
-          return device.gatt.connect()
+          gattServer.disconnect();
+          return gattServer.connect()
             .then(() => services);
         });
     })
@@ -31,7 +34,7 @@
           'InvalidStateError');
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristic('measurement_interval'),
+            service.getCharacteristic('body_sensor_location'),
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
@@ -39,7 +42,7 @@
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristics('measurement_interval'),
+            service.getCharacteristics('body_sensor_location'),
             error));
       }
       return promises;
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
index 62a1165..1506c3d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
@@ -9,15 +9,18 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => {
-      return device.gatt.getPrimaryServices()
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      return gattServer
+        .getPrimaryServices()
         // Convert to array if necessary.
         .then(s => {
           let services = [].concat(s);
-          device.gatt.disconnect();
-          return device.gatt.connect()
+          gattServer.disconnect();
+          return gattServer.connect()
             .then(() => services);
         });
     })
@@ -31,7 +34,7 @@
           'InvalidStateError');
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristic('measurement_interval'),
+            service.getCharacteristic('body_sensor_location'),
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
@@ -39,7 +42,7 @@
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristics('measurement_interval'),
+            service.getCharacteristics('body_sensor_location'),
             error));
       }
       return promises;
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.html
index 202df7ec..48474bb5 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.html
@@ -12,7 +12,7 @@
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['generic_access']
     })
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryServices('health_thermometer'),
       new DOMException('GATT Server is disconnected. ' +
                        'Cannot retrieve services. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnected-device.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnected-device.html
index e327868..9f7a190c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnected-device.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnected-device.html
@@ -12,7 +12,7 @@
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['generic_access']
     })
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryServices(),
       new DOMException('GATT Server is disconnected. ' +
                        'Cannot retrieve services. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.html
index 2d341428..85737f76 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.html
@@ -15,7 +15,7 @@
                                   'SecurityError');
   return getHealthThermometerDeviceWithServicesDiscovered({
       filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices(glucose.alias), expected),
       assert_promise_rejects_with_message(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.html
index 84e5bcef..0682b61a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.html
@@ -11,7 +11,7 @@
   return getHealthThermometerDeviceWithServicesDiscovered({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['glucose']})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryServices('glucose'),
       new DOMException(
         'No Services matching UUID ' + glucose.uuid + ' found in Device.',
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html
index ae626b2..d78d076 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html
@@ -10,7 +10,7 @@
 promise_test(() => {
   let promise;
   return getEmptyHealthThermometerDevice()
-      .then(({device}) => {
+      .then(([device]) => {
         promise = assert_promise_rejects_with_message(
           device.gatt.getPrimaryServices('health_thermometer'),
           new DOMException(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html
index 4d50a5c..1beb3147 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html
@@ -10,7 +10,7 @@
 promise_test(() => {
   let promise;
   return getEmptyHealthThermometerDevice()
-      .then(({device}) => {
+      .then(([device]) => {
         promise = assert_promise_rejects_with_message(
           device.gatt.getPrimaryServices(),
           new DOMException(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
index 58f9f534..ff3978c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
@@ -9,19 +9,23 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => {
-      promise = assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices('health_thermometer'),
-        new DOMException(
-          'GATT Server is disconnected. Cannot retrieve services. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        promise = assert_promise_rejects_with_message(
+            gattServer.getPrimaryServices('health_thermometer'),
+            new DOMException(
+                'GATT Server is disconnected. ' +
+                    'Cannot retrieve services. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        gattServer.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during a getPrimaryServices call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
index 05983aa..66813e4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
@@ -9,19 +9,23 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => {
-      promise = assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices(),
-        new DOMException(
-          'GATT Server is disconnected. Cannot retrieve services. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        promise = assert_promise_rejects_with_message(
+            gattServer.getPrimaryServices(),
+            new DOMException(
+                'GATT Server is disconnected. ' +
+                    'Cannot retrieve services. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        gattServer.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during a getPrimaryServices call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
index 6bb3339..e903a517 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
@@ -8,36 +8,35 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
-      let services_first_connection;
-      return device.gatt.getPrimaryServices('health_thermometer')
-        .then(services => services_first_connection = services)
-        .then(() => device.gatt.disconnect())
-        .then(() => device.gatt.connect())
-        .then(() => device.gatt.getPrimaryServices('health_thermometer'))
-        .then(services_second_connection => [
-          services_first_connection,
-          services_second_connection
-        ]);
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      let services1;
+      return gattServer
+        .getPrimaryServices('heart_rate')
+        .then(services => services1 = services)
+        .then(() => gattServer.disconnect())
+        .then(() => gattServer.connect())
+        .then(() => gattServer.getPrimaryServices('heart_rate'))
+        .then(services2 => [services1, services2])
     })
-    .then(([services_first_connection, services_second_connection]) => {
+    .then(services_arrays => {
       // Convert to arrays if necessary.
-      services_first_connection = [].concat(services_first_connection);
-      services_second_connection = [].concat(services_second_connection);
+      for (let i = 0; i < services_arrays.length; i++) {
+        services_arrays[i] = [].concat(services_arrays[i]);
+      }
 
-      assert_equals(services_first_connection.length, services_second_connection.length);
+      for (let i = 1; i < services_arrays.length; i++) {
+        assert_equals(services_arrays[0].length, services_arrays[i].length);
+      }
 
-      let first_connection_set = new Set(services_first_connection);
-      let second_connection_set = new Set(services_second_connection);
-
-      // The two sets should be disjoint.
-      let common_services = services_first_connection.filter(
-        val => second_connection_set.has(val));
-      assert_equals(common_services.length, 0);
-
+      let base_set = new Set(services_arrays.shift());
+      for (let services of services_arrays) {
+        services.forEach(service => assert_false(base_set.has(service)));
+      }
     });
 }, 'Calls to getPrimaryServices after a disconnection should return a ' +
    'different object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
index 230c7485..1afe652 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
@@ -8,36 +8,35 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => {
-      let services_first_connection;
-      return device.gatt.getPrimaryServices()
-        .then(services => services_first_connection = services)
-        .then(() => device.gatt.disconnect())
-        .then(() => device.gatt.connect())
-        .then(() => device.gatt.getPrimaryServices())
-        .then(services_second_connection => [
-          services_first_connection,
-          services_second_connection
-        ]);
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => {
+      let services1;
+      return gattServer
+        .getPrimaryServices()
+        .then(services => services1 = services)
+        .then(() => gattServer.disconnect())
+        .then(() => gattServer.connect())
+        .then(() => gattServer.getPrimaryServices())
+        .then(services2 => [services1, services2])
     })
-    .then(([services_first_connection, services_second_connection]) => {
+    .then(services_arrays => {
       // Convert to arrays if necessary.
-      services_first_connection = [].concat(services_first_connection);
-      services_second_connection = [].concat(services_second_connection);
+      for (let i = 0; i < services_arrays.length; i++) {
+        services_arrays[i] = [].concat(services_arrays[i]);
+      }
 
-      assert_equals(services_first_connection.length, services_second_connection.length);
+      for (let i = 1; i < services_arrays.length; i++) {
+        assert_equals(services_arrays[0].length, services_arrays[i].length);
+      }
 
-      let first_connection_set = new Set(services_first_connection);
-      let second_connection_set = new Set(services_second_connection);
-
-      // The two sets should be disjoint.
-      let common_services = services_first_connection.filter(
-        val => second_connection_set.has(val));
-      assert_equals(common_services.length, 0);
-
+      let base_set = new Set(services_arrays.shift());
+      for (let services of services_arrays) {
+        services.forEach(service => assert_false(base_set.has(service)));
+      }
     });
 }, 'Calls to getPrimaryServices after a disconnection should return a ' +
    'different object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
index 5f56fe0..e6231c3 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
@@ -8,31 +8,28 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => Promise.all([
-      device.gatt.getPrimaryServices('health_thermometer'),
-      device.gatt.getPrimaryServices('health_thermometer')]))
-    .then(([services_first_call, services_second_call]) => {
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => Promise.all([
+      gattServer.getPrimaryServices('heart_rate'),
+      gattServer.getPrimaryServices('heart_rate')]))
+    .then(services_arrays => {
       // Convert to arrays if necessary.
-      services_first_call = [].concat(services_first_call);
-      services_second_call = [].concat(services_second_call);
+      for (let i = 0; i < services_arrays.length; i++) {
+        services_arrays[i] = [].concat(services_arrays[i]);
+      }
 
-      assert_equals(services_first_call.length, services_second_call.length);
+      for (let i = 1; i < services_arrays.length; i++) {
+        assert_equals(services_arrays[0].length, services_arrays[i].length);
+      }
 
-      let first_call_set = new Set(services_first_call);
-      assert_equals(services_first_call.length, first_call_set.size);
-      let second_call_set = new Set(services_second_call);
-      assert_equals(services_second_call.length, second_call_set.size);
-
-      services_first_call.forEach(service => {
-        assert_true(second_call_set.has(service))
-      });
-
-      services_second_call.forEach(service => {
-        assert_true(first_call_set.has(service));
-      });
+      let base_set = new Set(services_arrays[0]);
+      for (let services of services_arrays) {
+        services.forEach(service => assert_true(base_set.has(service)));
+      }
     });
 }, 'Calls to getPrimaryServices should return the same object.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
index 1685f6f..00f859b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
@@ -8,31 +8,28 @@
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => Promise.all([
-      device.gatt.getPrimaryServices(),
-      device.gatt.getPrimaryServices()]))
-    .then(([services_first_call, services_second_call]) => {
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => Promise.all([
+      gattServer.getPrimaryServices(),
+      gattServer.getPrimaryServices()]))
+    .then(services_arrays => {
       // Convert to arrays if necessary.
-      services_first_call = [].concat(services_first_call);
-      services_second_call = [].concat(services_second_call);
+      for (let i = 0; i < services_arrays.length; i++) {
+        services_arrays[i] = [].concat(services_arrays[i]);
+      }
 
-      assert_equals(services_first_call.length, services_second_call.length);
+      for (let i = 1; i < services_arrays.length; i++) {
+        assert_equals(services_arrays[0].length, services_arrays[i].length);
+      }
 
-      let first_call_set = new Set(services_first_call);
-      assert_equals(services_first_call.length, first_call_set.size);
-      let second_call_set = new Set(services_second_call);
-      assert_equals(services_second_call.length, second_call_set.size);
-
-      services_first_call.forEach(service => {
-        assert_true(second_call_set.has(service))
-      });
-
-      services_second_call.forEach(service => {
-        assert_true(first_call_set.has(service));
-      });
+      let base_set = new Set(services_arrays[0]);
+      for (let services of services_arrays) {
+        services.forEach(service => assert_true(base_set.has(service)));
+      }
     });
 }, 'Calls to getPrimaryServices should return the same object.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
index ce17288..f8d010b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-invalid-service-name.html
@@ -9,7 +9,7 @@
 'use strict';
 promise_test(() => {
   return getHealthThermometerDevice()
-    .then(({device}) => {
+    .then(([device]) => {
       return assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices('wrong_name'),
         new DOMException(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.html
index ce19ff45..bbebc7a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.html
@@ -15,7 +15,7 @@
                                   'SecurityError');
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices(glucose.alias), expected),
       assert_promise_rejects_with_message(
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
index e6e5be02..c80c983 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
@@ -9,7 +9,7 @@
 'use strict';
 promise_test(() => {
   return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices('heart_rate'),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
index 45306589..c771e477 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
@@ -9,7 +9,7 @@
 'use strict';
 promise_test(() => {
   return getHealthThermometerDevice({acceptAllDevices: true})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
         device.gatt.getPrimaryServices(),
         new DOMException('Origin is not allowed to access any service. Tip: ' +
                          'Add the service UUID to \'optionalServices\' in ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.html
index 622bdaf..dd42f46 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.html
@@ -13,15 +13,17 @@
                                   '\'optionalServices\' in requestDevice() ' +
                                   'options. https://goo.gl/HxfxSQ',
                                   'SecurityError');
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gatt => Promise.all([
       assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices(generic_access.alias), expected),
+        gatt.getPrimaryServices(generic_access.alias), expected),
       assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices(generic_access.name), expected),
+        gatt.getPrimaryServices(generic_access.name), expected),
       assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices(generic_access.uuid), expected)]));
+        gatt.getPrimaryServices(generic_access.uuid), expected)]));
 }, 'Request for present service without permission. Reject with SecurityError.');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.html
index 79c7d89..cd9dce1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.html
@@ -11,7 +11,7 @@
   return getHealthThermometerDevice({
       filters: [{services: ['health_thermometer']}],
       optionalServices: ['glucose']})
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryServices('glucose'),
       new DOMException(
         'No Services matching UUID ' + glucose.uuid + ' found in Device.',
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
index 63ac04e..4b27bbb 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
@@ -2,26 +2,27 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
-promise_test(() => {
-  return getTwoHealthThermometerServicesDevice({
-    filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => Promise.all([
-      device.gatt.getPrimaryServices(health_thermometer.alias),
-      device.gatt.getPrimaryServices(health_thermometer.name),
-      device.gatt.getPrimaryServices(health_thermometer.uuid)]))
+promise_test(function() {
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => Promise.all([
+      gattServer.getPrimaryServices(heart_rate.alias),
+      gattServer.getPrimaryServices(heart_rate.name),
+      gattServer.getPrimaryServices(heart_rate.uuid)]))
     .then(services_arrays => {
       services_arrays.forEach(services => {
         assert_equals(services.length, 2);
-        services.forEach(service => {
-          assert_equals(service.uuid,
-                        BluetoothUUID.getService('health_thermometer'));
-          assert_true(service.isPrimary);
-        });
+        assert_equals(services[0].uuid,
+                      BluetoothUUID.getService('heart_rate'));
+        assert_equals(services[1].uuid,
+                      BluetoothUUID.getService('heart_rate'));
+        assert_true(services[0].isPrimary);
+        assert_true(services[1].isPrimary);
       });
     });
-}, 'Request for services. Should return right number of services.');
+}, 'Request for services. Should return right services');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
index 880108c..64c06041 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
@@ -2,27 +2,26 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(function() {
-  return getTwoHealthThermometerServicesDevice({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: ['generic_access']})
-    .then(({device}) => device.gatt.getPrimaryServices())
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}],
+      optionalServices: ['generic_access']}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => gattServer.getPrimaryServices())
     .then(services => {
-      // Expect three service instances.
       assert_equals(services.length, 3);
-      services.forEach(s => assert_true(s.isPrimary));
-
-      let uuid_set = new Set(services.map(s => s.uuid));
-      // Two of the expected services are 'health_thermometer', so
-      // only 2 unique UUIDs.
-      assert_equals(uuid_set.size, 2);
-
-      assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
-      assert_true(uuid_set.has(BluetoothUUID.getService('health_thermometer')));
+      assert_equals(services[0].uuid,
+                    BluetoothUUID.getService('generic_access'));
+      assert_equals(services[1].uuid,
+                    BluetoothUUID.getService('heart_rate'));
+      assert_equals(services[2].uuid,
+                    BluetoothUUID.getService('heart_rate'));
+      assert_true(services[0].isPrimary);
+      assert_true(services[1].isPrimary);
+      assert_true(services[2].isPrimary);
     });
 }, 'Find all services in a device.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found.html
index f621118..744fda8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found.html
@@ -8,7 +8,7 @@
 'use strict';
 promise_test(() => {
   return getEmptyHealthThermometerDevice()
-    .then(({device}) => assert_promise_rejects_with_message(
+    .then(([device]) => assert_promise_rejects_with_message(
       device.gatt.getPrimaryServices(),
       new DOMException('No Services found in device.', 'NotFoundError')));
 }, 'Request for services in a device with no services. Reject with ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/device-same-from-2-services.html b/third_party/WebKit/LayoutTests/bluetooth/service/device-same-from-2-services.html
index d1ef99d6..4056825 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/device-same-from-2-services.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/device-same-from-2-services.html
@@ -2,16 +2,16 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return getTwoHealthThermometerServicesDevice({
-     filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => device.gatt.getPrimaryServices('health_thermometer'))
-    .then(([service1, service2]) => {
-      assert_equals(service1.device, service2.device);
+  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => gattServer.getPrimaryServices('heart_rate'))
+    .then(services => {
+      assert_equals(services[0].device, services[1].device);
     });
 }, "Same parent device returned from multiple services.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/device-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/service/device-same-object.html
index d3f4808..91388573 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/device-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/device-same-object.html
@@ -2,14 +2,14 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script src="../../resources/bluetooth/web-bluetooth-test.js"></script>
-<script src="../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return getHealthThermometerDevice({
-      filters: [{services: ['health_thermometer']}]})
-    .then(({device}) => device.gatt.getPrimaryService('health_thermometer'))
+  return setBluetoothFakeAdapter('HeartRateAdapter')
+    .then(() => requestDeviceWithKeyDown({
+      filters: [{services: ['heart_rate']}]}))
+    .then(device => device.gatt.connect())
+    .then(gattServer => gattServer.getPrimaryService('heart_rate'))
     .then(service => {
       assert_equals(service.device, service.device);
     });
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt
new file mode 100644
index 0000000..22ad39c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt
@@ -0,0 +1,82 @@
+This is a testharness.js-based test.
+Found 78 tests; 54 PASS, 24 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS DOMMatrix stringifier: identity (2d) 
+PASS DOMMatrix stringifier: identity (3d) 
+PASS DOMMatrix stringifier: NaN (2d) 
+PASS DOMMatrix stringifier: NaN (3d) 
+PASS DOMMatrix stringifier: Infinity (2d) 
+PASS DOMMatrix stringifier: Infinity (3d) 
+PASS DOMMatrix stringifier: -Infinity (2d) 
+PASS DOMMatrix stringifier: -Infinity (3d) 
+PASS DOMMatrix stringifier: 1/3 (2d) 
+PASS DOMMatrix stringifier: 1/3 (3d) 
+PASS DOMMatrix stringifier: 1/300000 (2d) 
+PASS DOMMatrix stringifier: 1/300000 (3d) 
+PASS DOMMatrix stringifier: 1/300000000 (2d) 
+PASS DOMMatrix stringifier: 1/300000000 (3d) 
+PASS DOMMatrix stringifier: 100000 + (1/3) (2d) 
+PASS DOMMatrix stringifier: 100000 + (1/3) (3d) 
+PASS DOMMatrix stringifier: Math.pow(2, 53) + 1 (2d) 
+PASS DOMMatrix stringifier: Math.pow(2, 53) + 1 (3d) 
+PASS DOMMatrix stringifier: Math.pow(2, 53) + 2 (2d) 
+PASS DOMMatrix stringifier: Math.pow(2, 53) + 2 (3d) 
+PASS DOMMatrix stringifier: Number.MAX_VALUE (2d) 
+PASS DOMMatrix stringifier: Number.MAX_VALUE (3d) 
+PASS DOMMatrix stringifier: Number.MIN_VALUE (2d) 
+PASS DOMMatrix stringifier: Number.MIN_VALUE (3d) 
+PASS DOMMatrix stringifier: throwing getters (2d) 
+PASS DOMMatrix stringifier: throwing getters (3d) 
+PASS DOMMatrixReadOnly stringifier: identity (2d) 
+PASS DOMMatrixReadOnly stringifier: identity (3d) 
+PASS DOMMatrixReadOnly stringifier: NaN (2d) 
+PASS DOMMatrixReadOnly stringifier: NaN (3d) 
+PASS DOMMatrixReadOnly stringifier: Infinity (2d) 
+PASS DOMMatrixReadOnly stringifier: Infinity (3d) 
+PASS DOMMatrixReadOnly stringifier: -Infinity (2d) 
+PASS DOMMatrixReadOnly stringifier: -Infinity (3d) 
+PASS DOMMatrixReadOnly stringifier: 1/3 (2d) 
+PASS DOMMatrixReadOnly stringifier: 1/3 (3d) 
+PASS DOMMatrixReadOnly stringifier: 1/300000 (2d) 
+PASS DOMMatrixReadOnly stringifier: 1/300000 (3d) 
+PASS DOMMatrixReadOnly stringifier: 1/300000000 (2d) 
+PASS DOMMatrixReadOnly stringifier: 1/300000000 (3d) 
+PASS DOMMatrixReadOnly stringifier: 100000 + (1/3) (2d) 
+PASS DOMMatrixReadOnly stringifier: 100000 + (1/3) (3d) 
+PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 1 (2d) 
+PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 1 (3d) 
+PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 2 (2d) 
+PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 2 (3d) 
+PASS DOMMatrixReadOnly stringifier: Number.MAX_VALUE (2d) 
+PASS DOMMatrixReadOnly stringifier: Number.MAX_VALUE (3d) 
+PASS DOMMatrixReadOnly stringifier: Number.MIN_VALUE (2d) 
+PASS DOMMatrixReadOnly stringifier: Number.MIN_VALUE (3d) 
+PASS DOMMatrixReadOnly stringifier: throwing getters (2d) 
+PASS DOMMatrixReadOnly stringifier: throwing getters (3d) 
+PASS WebKitCSSMatrix stringifier: identity (2d) 
+FAIL WebKitCSSMatrix stringifier: identity (3d) self[constr].fromMatrix is not a function
+FAIL WebKitCSSMatrix stringifier: NaN (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,NaN'.
+FAIL WebKitCSSMatrix stringifier: NaN (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,NaN,0,1'.
+FAIL WebKitCSSMatrix stringifier: Infinity (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,Infinity'.
+FAIL WebKitCSSMatrix stringifier: Infinity (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,Infinity,0,1'.
+FAIL WebKitCSSMatrix stringifier: -Infinity (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,-Infinity'.
+FAIL WebKitCSSMatrix stringifier: -Infinity (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,-Infinity,0,1'.
+FAIL WebKitCSSMatrix stringifier: 1/3 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,0.3333333333333333'.
+FAIL WebKitCSSMatrix stringifier: 1/3 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,0.3333333333333333,0,1'.
+FAIL WebKitCSSMatrix stringifier: 1/300000 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,0.0000033333333333333333'.
+FAIL WebKitCSSMatrix stringifier: 1/300000 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,0.0000033333333333333333,0,1'.
+FAIL WebKitCSSMatrix stringifier: 1/300000000 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,3.3333333333333334e-9'.
+FAIL WebKitCSSMatrix stringifier: 1/300000000 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,3.3333333333333334e-9,0,1'.
+FAIL WebKitCSSMatrix stringifier: 100000 + (1/3) (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,100000.33333333333'.
+FAIL WebKitCSSMatrix stringifier: 100000 + (1/3) (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,100000.33333333333,0,1'.
+FAIL WebKitCSSMatrix stringifier: Math.pow(2, 53) + 1 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,9007199254740992'.
+FAIL WebKitCSSMatrix stringifier: Math.pow(2, 53) + 1 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,9007199254740992,0,1'.
+FAIL WebKitCSSMatrix stringifier: Math.pow(2, 53) + 2 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,9007199254740994'.
+FAIL WebKitCSSMatrix stringifier: Math.pow(2, 53) + 2 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,9007199254740994,0,1'.
+FAIL WebKitCSSMatrix stringifier: Number.MAX_VALUE (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,1.7976931348623157e+308'.
+FAIL WebKitCSSMatrix stringifier: Number.MAX_VALUE (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,1.7976931348623157e+308,0,1'.
+FAIL WebKitCSSMatrix stringifier: Number.MIN_VALUE (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,5e-324'.
+FAIL WebKitCSSMatrix stringifier: Number.MIN_VALUE (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,5e-324,0,1'.
+PASS WebKitCSSMatrix stringifier: throwing getters (2d) 
+FAIL WebKitCSSMatrix stringifier: throwing getters (3d) self[constr].fromMatrix is not a function
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/WebKitCSSMatrix-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/WebKitCSSMatrix-expected.txt
new file mode 100644
index 0000000..dd0272a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/WebKitCSSMatrix-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL Equivalence test assert_equals: interface object expected function "function DOMMatrix() { [native code] }" but got function "function WebKitCSSMatrix() { [native code] }"
+PASS Property descriptor for WebKitCSSMatrix 
+PASS Property descriptor for DOMMatrix 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
index 294eef2..7d0014c 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
@@ -7,7 +7,7 @@
 PASS Option.prototype.toString.call(new Option) is '[object HTMLOptionElement]'
 PASS WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix) is 'matrix(1, 0, 0, 1, 0, 0)'
 PASS WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix()) is 'matrix(1, 0, 0, 1, 0, 0)'
-PASS new WebKitCSSMatrix(null) threw exception SyntaxError: Failed to construct 'DOMMatrix': Failed to parse 'null'..
+PASS new WebKitCSSMatrix(null) threw exception SyntaxError: Failed to construct 'WebKitCSSMatrix': Failed to parse 'null'..
 FAIL new WebKitCSSMatrix(undefined) should throw an exception. Was matrix(1, 0, 0, 1, 0, 0).
 PASS XMLHttpRequest.prototype.toString.call(new XMLHttpRequest) is '[object XMLHttpRequest]'
 PASS XSLTProcessor.prototype.toString.call(new XSLTProcessor) is '[object XSLTProcessor]'
diff --git a/third_party/WebKit/LayoutTests/fast/dom/call-a-constructor-as-a-function-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/call-a-constructor-as-a-function-expected.txt
index d91c597..bcd6af3 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/call-a-constructor-as-a-function-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/call-a-constructor-as-a-function-expected.txt
@@ -22,7 +22,7 @@
 PASS Uint16Array() threw exception TypeError: Constructor Uint16Array requires 'new'.
 PASS Uint32Array() threw exception TypeError: Constructor Uint32Array requires 'new'.
 PASS Uint8Array() threw exception TypeError: Constructor Uint8Array requires 'new'.
-PASS WebKitCSSMatrix() threw exception TypeError: Failed to construct 'DOMMatrix': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
+PASS WebKitCSSMatrix() threw exception TypeError: Failed to construct 'WebKitCSSMatrix': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
 PASS WebSocket() threw exception TypeError: Failed to construct 'WebSocket': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
 PASS Worker() threw exception TypeError: Failed to construct 'Worker': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
 PASS XMLHttpRequest() threw exception TypeError: Failed to construct 'XMLHttpRequest': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota-expected.txt
new file mode 100644
index 0000000..b2222f9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota-expected.txt
@@ -0,0 +1,12 @@
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 124: InspectorTest.IndexedDB_callback3
+Tests quota reporting.
+
+Tree element found: true
+Clear storage view is visible: true
+0 B storage quota used out of -
+
+Running: Now with data
+9.5 MB storage quota used out of -
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota.html b/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota.html
new file mode 100644
index 0000000..20fdf24
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota.html
@@ -0,0 +1,62 @@
+<html>
+<head>
+<script src="../inspector-test.js"></script>
+<script src="../resources-test.js"></script>
+<script src="../console-test.js"></script>
+<script src="../indexeddb/indexeddb-test.js"></script>
+<script>
+async function test() {
+  var updateListener = null;
+
+  async function writeArray() {
+    var array = [];
+    for (var i = 0; i < 5000000; i++)
+      array.push(i % 10);
+    var mainFrameId = InspectorTest.resourceTreeModel.mainFrame.id;
+    await new Promise(resolve => InspectorTest.createDatabase(mainFrameId, 'Database1', resolve));
+    await new Promise(resolve => InspectorTest.createObjectStore(mainFrameId, 'Database1', 'Store1', 'id', true, resolve));
+    await new Promise(resolve => InspectorTest.addIDBValue(mainFrameId, 'Database1', 'Store1', {key: 1, value: array}, '', resolve));
+  }
+
+  async function dumpWhenMatches(view, predicate) {
+    await new Promise(resolve => {
+      function sniffer(usage, quota) {
+        if (usage !== null && (!predicate || predicate(usage, quota)))
+          resolve();
+        else
+          InspectorTest.addSniffer(clearStorageView, '_usageUpdatedForTest', sniffer);
+      }
+      sniffer(null);
+    });
+    // Quota will vary between setups, rather strip it altogether
+    var clean = view._quotaRow.innerHTML.replace(/\&nbsp;/g, ' ');
+    var quotaStripped = clean.replace(/(.*) \d+ .?B([^\d]*)/, '$1 -$2');
+    InspectorTest.addResult(quotaStripped);
+  }
+  UI.viewManager.showView('resources');
+
+  var parent = UI.panels.resources._sidebar._applicationTreeElement;
+  var clearStorageElement = parent.children().find(child => child.title === 'Clear storage');
+
+  InspectorTest.addResult('Tree element found: ' + !!clearStorageElement);
+  clearStorageElement.select();
+
+  var clearStorageView = UI.panels.resources.visibleView;
+  InspectorTest.addResult("Clear storage view is visible: " + (clearStorageView instanceof Resources.ClearStorageView));
+
+  clearStorageView._clearButton.click();
+  await dumpWhenMatches(clearStorageView, usage => usage === 0);
+
+  InspectorTest.markStep('Now with data');
+
+  await writeArray();
+  await dumpWhenMatches(clearStorageView, usage => usage > 5000000);
+
+  InspectorTest.completeTest();
+}
+</script>
+</head>
+<body onload="runTest()">
+  <p>Tests quota reporting.</p>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/help/release-note-expected.txt b/third_party/WebKit/LayoutTests/inspector/help/release-note-expected.txt
index fd12a4d..83b5e68 100644
--- a/third_party/WebKit/LayoutTests/inspector/help/release-note-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/help/release-note-expected.txt
@@ -1,7 +1,7 @@
 Test release note
 
 Last release note version seen:
-0
+1
 
 Dumping release note text:
 Highlights from Chrome 100 update
diff --git a/third_party/WebKit/LayoutTests/inspector/help/release-note-unit-expected.txt b/third_party/WebKit/LayoutTests/inspector/help/release-note-unit-expected.txt
index 149ad071..5f3e950 100644
--- a/third_party/WebKit/LayoutTests/inspector/help/release-note-unit-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/help/release-note-unit-expected.txt
@@ -13,3 +13,8 @@
 Last seen version: 5
 Did not show release note drawer
 
+Running: doNotShowReleaseNoteOnFreshProfile
+Last seen version: 0
+Did not show release note drawer
+Release note version in setting: 5
+
diff --git a/third_party/WebKit/LayoutTests/inspector/help/release-note-unit.html b/third_party/WebKit/LayoutTests/inspector/help/release-note-unit.html
index 2de5f28..9aef56a 100644
--- a/third_party/WebKit/LayoutTests/inspector/help/release-note-unit.html
+++ b/third_party/WebKit/LayoutTests/inspector/help/release-note-unit.html
@@ -42,6 +42,12 @@
             testMaybeShowInDrawer(lastSeenVersion);
             next();
         },
+        function doNotShowReleaseNoteOnFreshProfile(next) {
+            var lastSeenVersion = 0;
+            testMaybeShowInDrawer(lastSeenVersion);
+            InspectorTest.addResult(`Release note version in setting: ${Help.releaseNoteVersionSetting().get()}`);
+            next();
+        },
     ]);
 }
 </script>
diff --git a/third_party/WebKit/LayoutTests/inspector/help/release-note.html b/third_party/WebKit/LayoutTests/inspector/help/release-note.html
index f61ad816..235b48e 100644
--- a/third_party/WebKit/LayoutTests/inspector/help/release-note.html
+++ b/third_party/WebKit/LayoutTests/inspector/help/release-note.html
@@ -26,6 +26,7 @@
     InspectorTest.addSniffer(UI.viewManager, "showView", onShowView);
 
     InspectorTest.addResult("Last release note version seen:");
+    Help.releaseNoteVersionSetting().set(1);
     InspectorTest.addResult(Help.releaseNoteVersionSetting().get() + "\n");
     Help.showReleaseNoteIfNeeded();
 
diff --git a/third_party/WebKit/LayoutTests/media/remoteplayback/prompt-throws-when-backend-disabled.html b/third_party/WebKit/LayoutTests/media/remoteplayback/prompt-throws-when-backend-disabled.html
deleted file mode 100644
index 33948b9..0000000
--- a/third_party/WebKit/LayoutTests/media/remoteplayback/prompt-throws-when-backend-disabled.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE html>
-<html>
-<title>Test that calling prompt() when backend is disabled, throws an exception</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../media-file.js"></script>
-<script src="util.js"></script>
-<button id="button">Click me</button>
-<script>
-  // WPT: this test can't be moved to WPT because of its usage of:
-  // - user gesture;
-  // - internals.mediaPlayerRemoteRouteAvailabilityChanged
-  // - internals.runtimeFlags
-
-  function clickOnElem(e) {
-    var x = e.offsetLeft + e.offsetWidth / 2;
-    var y = e.offsetTop + e.offsetHeight / 2;
-    chrome.gpuBenchmarking.pointerActionSequence(
-    [
-      {
-        "source": "mouse",
-        "actions":
-        [
-          { "name": "pointerDown", "x": x, "y": y },
-          { "name": "pointerUp" }
-        ]
-      }
-    ]);
-  }
-
-  async_test(function(t)
-  {
-    disableRemotePlaybackBackendForTest(t);
-
-    var v = document.createElement('video');
-    v.src = findMediaFile('video', 'content/test');
-    document.body.appendChild(v);
-
-    internals.mediaPlayerRemoteRouteAvailabilityChanged(v, true);
-
-    var btn = document.getElementById('button');
-    btn.onclick = function() {
-      v.remote.prompt().then(t.unreached_func(),
-          t.step_func_done(function(e) {
-            assert_equals(e.name, 'NotSupportedError');
-            assert_equals(e.message, 'The RemotePlayback API is disabled on this platform.');
-          }));
-    }
-
-    clickOnElem(btn);
-  }, 'Test that calling prompt() when backend is disabled, throws an exception.');
-</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/media/remoteplayback/prompt-twice-throws.html b/third_party/WebKit/LayoutTests/media/remoteplayback/prompt-twice-throws.html
index 51776f9..5712a66 100644
--- a/third_party/WebKit/LayoutTests/media/remoteplayback/prompt-twice-throws.html
+++ b/third_party/WebKit/LayoutTests/media/remoteplayback/prompt-twice-throws.html
@@ -5,6 +5,7 @@
         <script src="../../resources/testharness.js"></script>
         <script src="../../resources/testharnessreport.js"></script>
         <script src="../media-file.js"></script>
+        <script src="util.js"></script>
     </head>
     <body>
         <button id="button">Click me</button>
@@ -12,6 +13,7 @@
             // WPT: this test can't be moved to WPT because of its usage of:
             // - user gesture;
             // - internals.mediaPlayerRemoteRouteAvailabilityChanged
+            // - internals.runtimeFlags
 
             function clickOnElem(e) {
                 eventSender.mouseMoveTo(
@@ -23,14 +25,17 @@
 
             async_test(function(test)
             {
-                var v = document.createElement('video');
-                v.src = findMediaFile('video', 'content/test');
-                document.body.appendChild(v);
+                // So that prompt() doesn't throw NotSupportedError.
+                enableRemotePlaybackBackendForTest(test);
 
-                internals.mediaPlayerRemoteRouteAvailabilityChanged(v, true);
+                var v = document.createElement('video');
+                v.src = findMediaFile('video', '../content/test');
+                document.body.appendChild(v);
 
                 var btn = document.getElementById('button');
                 btn.onclick = function() {
+                    internals.mediaPlayerRemoteRouteAvailabilityChanged(v, true);
+
                     v.remote.prompt();
 
                     btn.onclick = function() {
diff --git a/third_party/WebKit/LayoutTests/media/remoteplayback/util.js b/third_party/WebKit/LayoutTests/media/remoteplayback/util.js
index 10f1eb0..3502d1312 100644
--- a/third_party/WebKit/LayoutTests/media/remoteplayback/util.js
+++ b/third_party/WebKit/LayoutTests/media/remoteplayback/util.js
@@ -1,10 +1,10 @@
-function disableRemotePlaybackBackendForTest(t) {
+function enableRemotePlaybackBackendForTest(t) {
   var remotePlaybackBackendEnabledOldValue =
       internals.runtimeFlags.remotePlaybackBackendEnabled;
-  internals.runtimeFlags.remotePlaybackBackendEnabled = false;
+  internals.runtimeFlags.remotePlaybackBackendEnabled = true;
 
   t.add_cleanup(() => {
     internals.runtimeFlags.remotePlaybackBackendEnabled =
         remotePlaybackBackendEnabledOldValue;
   });
-}
\ No newline at end of file
+}
diff --git a/third_party/WebKit/LayoutTests/media/remoteplayback/watch-availability-works-when-backend-disabled.html b/third_party/WebKit/LayoutTests/media/remoteplayback/watch-availability-works-when-backend-disabled.html
deleted file mode 100644
index 853ebfc..0000000
--- a/third_party/WebKit/LayoutTests/media/remoteplayback/watch-availability-works-when-backend-disabled.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<title>Test that watchAvailability does not throw when backend is disabled.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/gc.js"></script>
-<script src="../media-file.js"></script>
-<script src="util.js"></script>
-<video></video>
-<script>
-  // WPT: this test can't be moved to WPT because of its usage of:
-  // - internals.runtimeFlags
-
-  async_test(function(t) {
-    disableRemotePlaybackBackendForTest(t);
-
-    var availability_callback_id = -1;
-    var v = document.querySelector('video');
-    v.src = findMediaFile('video', 'content/test');
-
-    function callback(available) {
-      assert_false(available);
-      v.remote.cancelWatchAvailability(availability_callback_id).then(
-         t.step_func_done(), t.unreached_func());
-    }
-
-    v.remote.watchAvailability(callback).then(t.step_func(
-        function(id) { assert_true(id >= 0); availability_callback_id = id; }),
-        t.unreached_func());
-  }, 'Test that watchAvailability doesn not throw when backend is disabled.');
-</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/paint/printing/print-box-shadow-expected.png b/third_party/WebKit/LayoutTests/paint/printing/print-box-shadow-expected.png
index 5325356..cae9229 100644
--- a/third_party/WebKit/LayoutTests/paint/printing/print-box-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/paint/printing/print-box-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/new-remote-playback-pipeline/media/controls/controls-cast-overlay-slow-fade-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/new-remote-playback-pipeline/media/controls/controls-cast-overlay-slow-fade-expected.txt
deleted file mode 100644
index 8b4c5f2..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/new-remote-playback-pipeline/media/controls/controls-cast-overlay-slow-fade-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Test that the overlay cast button fades at the right time (neither too soon nor too late). assert_true: button should exist expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
index 4289e61..b743349 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -7369,9 +7369,63 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix : DOMMatrixReadOnly
+interface WebKitCSSMatrix
     attribute @@toStringTag
+    getter a
+    getter b
+    getter c
+    getter d
+    getter e
+    getter f
+    getter m11
+    getter m12
+    getter m13
+    getter m14
+    getter m21
+    getter m22
+    getter m23
+    getter m24
+    getter m31
+    getter m32
+    getter m33
+    getter m34
+    getter m41
+    getter m42
+    getter m43
+    getter m44
     method constructor
+    method inverse
+    method multiply
+    method rotate
+    method rotateAxisAngle
+    method scale
+    method setMatrixValue
+    method skewX
+    method skewY
+    method toString
+    method translate
+    setter a
+    setter b
+    setter c
+    setter d
+    setter e
+    setter f
+    setter m11
+    setter m12
+    setter m13
+    setter m14
+    setter m21
+    setter m22
+    setter m23
+    setter m24
+    setter m31
+    setter m32
+    setter m33
+    setter m34
+    setter m41
+    setter m42
+    setter m43
+    setter m44
 interface WebKitMutationObserver
     attribute @@toStringTag
     method constructor
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
index f88813a..ff81a8a 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -7298,9 +7298,63 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix : DOMMatrixReadOnly
+interface WebKitCSSMatrix
     attribute @@toStringTag
+    getter a
+    getter b
+    getter c
+    getter d
+    getter e
+    getter f
+    getter m11
+    getter m12
+    getter m13
+    getter m14
+    getter m21
+    getter m22
+    getter m23
+    getter m24
+    getter m31
+    getter m32
+    getter m33
+    getter m34
+    getter m41
+    getter m42
+    getter m43
+    getter m44
     method constructor
+    method inverse
+    method multiply
+    method rotate
+    method rotateAxisAngle
+    method scale
+    method setMatrixValue
+    method skewX
+    method skewY
+    method toString
+    method translate
+    setter a
+    setter b
+    setter c
+    setter d
+    setter e
+    setter f
+    setter m11
+    setter m12
+    setter m13
+    setter m14
+    setter m21
+    setter m22
+    setter m23
+    setter m24
+    setter m31
+    setter m32
+    setter m33
+    setter m34
+    setter m41
+    setter m42
+    setter m43
+    setter m44
 interface WebKitMutationObserver
     attribute @@toStringTag
     method constructor
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
index fbff635..c8dea3d6 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
@@ -40,11 +40,6 @@
   name: 'heart_rate',
   uuid: '0000180d-0000-1000-8000-00805f9b34fb'
 };
-var health_thermometer = {
-  alias: 0x1809,
-  name: 'health_thermometer',
-  uuid: '00001809-0000-1000-8000-00805f9b34fb'
-};
 var body_sensor_location = {
   alias: 0x2a38,
   name: 'body_sensor_location',
@@ -456,94 +451,22 @@
      })]));
 }
 
-// Returns an object containing a BluetoothDevice discovered using |options|,
-// its corresponding FakePeripheral and FakeRemoteGATTServices.
+// Returns a BluetoothDevice discovered using |options| and its
+// corresponding FakePeripheral.
 // The simulated device is called 'Health Thermometer' it has two known service
-// UUIDs: 'generic_access' and 'health_thermometer' which correspond to two
-// services with the same UUIDs. The device has been connected to and its
-// services are ready to be discovered.
-// TODO(crbug.com/719816): Add characteristics and descriptors.
+// UUIDs: 'generic_access' and 'health_thermometer'. The device has been
+// connected to and its services have been discovered.
+// TODO(crbug.com/719816): Add services, characteristics and descriptors,
+// and discover all the attributes.
 function getHealthThermometerDevice(options) {
-  let device;
-  let fake_peripheral;
-  let fake_generic_access;
-  let fake_health_thermometer;
-
-  return getConnectedHealthThermometerDevice(options)
-    .then(result => {
-      ({
-        device,
-        fake_peripheral,
-        fake_generic_access,
-        fake_health_thermometer,
-      } = result);
-    })
-    .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
-      code: HCI_SUCCESS}))
-    .then(() => ({
-      device: device,
-      fake_peripheral: fake_peripheral,
-      fake_generic_access: fake_generic_access,
-      fake_health_thermometer1: fake_health_thermometer,
-    }));
-}
-
-// Similar to getHealthThermometerDevice except that the peripheral has
-// two 'health_thermometer' services.
-function getTwoHealthThermometerServicesDevice(options) {
-  let device;
-  let fake_peripheral;
-  let fake_generic_access;
-  let fake_health_thermometer1;
-  let fake_health_thermometer2;
-
-  return getConnectedHealthThermometerDevice(options)
-    .then(result => {
-      ({
-        device,
-        fake_peripheral,
-        fake_generic_access,
-        fake_health_thermometer: fake_health_thermometer1,
-      } = result);
-    })
-    .then(() => fake_peripheral.addFakeService({uuid: 'health_thermometer'}))
-    .then(s => fake_health_thermometer2 = s)
-    .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
-      code: HCI_SUCCESS}))
-    .then(() => ({
-      device: device,
-      fake_peripheral: fake_peripheral,
-      fake_generic_access: fake_generic_access,
-      fake_health_thermometer1: fake_health_thermometer1,
-      fake_health_thermometer2: fake_health_thermometer2
-    }));
-}
-
-// Similar to getHealthThermometerDevice except the GATT discovery
-// response has not been set yet so more attributes can still be added.
-function getConnectedHealthThermometerDevice(options) {
-  let device;
-  let fake_peripheral;
-  let fake_generic_access;
-  let fake_health_thermometer;
   return getDiscoveredHealthThermometerDevice(options)
-    .then(result => {
-      ({device, fake_peripheral} = result);
-    })
-    .then(() => fake_peripheral.setNextGATTConnectionResponse({
-      code: HCI_SUCCESS}))
-    .then(() => device.gatt.connect())
-    .then(() => fake_peripheral.addFakeService({uuid: 'generic_access'}))
-    .then(s => fake_generic_access = s)
-    .then(() => fake_peripheral.addFakeService({
-      uuid: 'health_thermometer'}))
-    .then(s => fake_health_thermometer = s)
-    .then(() => ({
-      device: device,
-      fake_peripheral: fake_peripheral,
-      fake_generic_access: fake_generic_access,
-      fake_health_thermometer: fake_health_thermometer,
-    }));
+    .then(([device, fake_peripheral]) => {
+      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
+        .then(() => device.gatt.connect())
+        .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
+          code: HCI_SUCCESS}))
+        .then(() => [device, fake_peripheral]);
+    });
 }
 
 // Returns the same device and fake peripheral as getHealthThermometerDevice()
@@ -581,10 +504,7 @@
         }))
         .then(() => requestDeviceWithKeyDown(options))
         .then(device => device.gatt.connect())
-        .then(gatt => ({
-          device: gatt.device,
-          fake_peripheral: fake_peripheral
-        }));
+        .then(gatt => [gatt.device, fake_peripheral]);
     });
 }
 
@@ -592,56 +512,15 @@
 // characteristics, or descriptors.
 function getEmptyHealthThermometerDevice(options) {
   return getDiscoveredHealthThermometerDevice(options)
-    .then(({device, fake_peripheral}) => {
+    .then(([device, fake_peripheral]) => {
       return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
         .then(() => device.gatt.connect())
         .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
           code: HCI_SUCCESS}))
-        .then(() => ({
-          device: device,
-          fake_peripheral: fake_peripheral
-        }));
+        .then(() => [device, fake_peripheral]);
     });
 }
 
-// Returns a BluetoothDevice discovered using |options| and its
-// corresponding FakePeripheral.
-// The simulated device is called 'HID Device' it has three known service
-// UUIDs: 'generic_access', 'battery_service', 'human_interface_device'. The
-// device has been connected to and its services are ready to be discovered.
-// TODO(crbug.com/719816): Add characteristics and descriptors.
-function getHIDDevice(options) {
-  return setUpPreconnectedDevice({
-      address: '10:10:10:10:10:10',
-      name: 'HID Device',
-      knownServiceUUIDs: [
-        'generic_access',
-        'battery_service',
-        'human_interface_device'
-      ],
-    })
-    .then(fake_peripheral => {
-      return requestDeviceWithKeyDown(options)
-        .then(device => {
-          return fake_peripheral.setNextGATTConnectionResponse({
-              code: HCI_SUCCESS})
-            .then(() => device.gatt.connect())
-            .then(() => fake_peripheral.addFakeService({
-              uuid: 'generic_access'}))
-            .then(() => fake_peripheral.addFakeService({
-              uuid: 'battery_service'}))
-            .then(() => fake_peripheral.addFakeService({
-              uuid: 'human_interface_device'}))
-            .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
-              code: HCI_SUCCESS}))
-            .then(() => ({
-              device: device,
-              fake_peripheral: fake_peripheral
-            }));
-        });
-    });
-};
-
 // Similar to getHealthThermometerDevice() except the device
 // is not connected and thus its services have not been
 // discovered.
@@ -654,9 +533,6 @@
   })
   .then(fake_peripheral => {
     return requestDeviceWithKeyDown(options)
-      .then(device => ({
-        device: device,
-        fake_peripheral: fake_peripheral
-      }));
+      .then(device => [device, fake_peripheral]);
   });
 }
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/health-thermometer-two-iframes.html b/third_party/WebKit/LayoutTests/resources/bluetooth/heart-rate-two-iframes.html
similarity index 61%
rename from third_party/WebKit/LayoutTests/resources/bluetooth/health-thermometer-two-iframes.html
rename to third_party/WebKit/LayoutTests/resources/bluetooth/heart-rate-two-iframes.html
index d5d38fd..91f7a98 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/health-thermometer-two-iframes.html
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/heart-rate-two-iframes.html
@@ -1,25 +1,25 @@
 <!DOCTYPE html>
 <script>
-'use strict';
-let device;
-window.onmessage = messageEvent => {
-  if (messageEvent.data === 'Iframe1RequestAndConnect') {
-    navigator.bluetooth.requestDevice({
-        filters: [{services: ['health_thermometer']}]
+'use restrict';
+  let device;
+  window.onmessage = messageEvent => {
+    if (messageEvent.data === 'Iframe1RequestAndConnect') {
+      navigator.bluetooth.requestDevice({
+        filters: [{services: ['heart_rate']}]
       })
       .then(device => device.gatt.connect())
       .then(gattServer => {
-        // iframe1 can access health_thermometer service.
-        return gattServer.getPrimaryService('health_thermometer');
+        // iframe1 can access heart_rate service.
+        return gattServer.getPrimaryService('heart_rate');
       }).then(() => {
         parent.postMessage('Iframe1Connected', '*');
       }).catch(err => {
         console.error(err);
         parent.postMessage('FAIL: ' + err, '*');
       });
-  } else if (messageEvent.data === 'Iframe1TryAccessGenericAccessService') {
-    navigator.bluetooth.requestDevice({
-        filters: [{services: ['health_thermometer']}]
+    } else if (messageEvent.data === 'Iframe1TryAccessGenericAccessService') {
+      navigator.bluetooth.requestDevice({
+        filters: [{services: ['heart_rate']}]
       })
       .then(device => device.gatt.connect())
       .then(gattServer => {
@@ -28,26 +28,26 @@
       }).catch(err => {
         parent.postMessage('Iframe1AccessGenericAccessServiceFailed', '*');
       });
-  } else if (messageEvent.data === 'Iframe2RequestAndConnect') {
-    navigator.bluetooth.requestDevice({
+    } else if (messageEvent.data === 'Iframe2RequestAndConnect') {
+      navigator.bluetooth.requestDevice({
         filters: [{services: ['generic_access']}]
       })
       .then(device => device.gatt.connect())
       .then(gattServer => {
-        // Since iframe1 can access health_thermometer service, and iframe2 has the
+        // Since iframe1 can access heart_rate service, and iframe2 has the
         // same origin as iframe1, iframe2 should also be able to access
-        // health_thermometer service.
+        // heart_rate service.
         return Promise.all([gattServer.getPrimaryService('generic_access'),
-                            gattServer.getPrimaryService('health_thermometer')]);
+          gattServer.getPrimaryService('heart_rate')]);
       }).then(() => {
         parent.postMessage('Iframe2Connected', '*');
       }).catch(err => {
         console.error(err);
         parent.postMessage('FAIL: ' + err, '*');
       });
-  } else if (messageEvent.data === 'TestIframe1HasGenericAccessService') {
-    navigator.bluetooth.requestDevice({
-        filters: [{services: ['health_thermometer']}]
+    } else if (messageEvent.data === 'TestIframe1HasGenericAccessService') {
+      navigator.bluetooth.requestDevice({
+        filters: [{services: ['heart_rate']}]
       })
       .then(device => device.gatt.connect())
       .then(gattServer => {
@@ -61,7 +61,7 @@
         console.error(err);
         parent.postMessage('FAIL: ' + err, '*');
       });
-  }
-};
-parent.postMessage("Ready", "*");
+    }
+  };
+  parent.postMessage("Ready", "*");
 </script>
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js b/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
index 648ea2b..7b6cd79 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
@@ -150,7 +150,6 @@
   class FakePeripheral {
     constructor(address, fake_central_ptr) {
       this.address = address;
-      this.services_ = [];
       this.fake_central_ptr_ = fake_central_ptr;
     }
 
@@ -167,24 +166,6 @@
       if (success !== true) throw 'setNextGATTConnectionResponse failed.';
     }
 
-    // Adds a fake GATT Service with |uuid| to be discovered when discovering
-    // the peripheral's GATT Attributes. Returns a FakeRemoteGATTService
-    // corresponding to this service. |uuid| should be a BluetoothServiceUUIDs
-    // https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
-    async addFakeService({uuid}) {
-      let {service_id} = await this.fake_central_ptr_.addFakeService(
-        this.address, {uuid: BluetoothUUID.getService(uuid)});
-
-      if (service_id === null) throw 'addFakeService failed';
-
-      let fake_service = new FakeRemoteGATTService(
-        service_id, this.address, this.fake_central_ptr_);
-
-      this.services_.push(fake_service);
-
-      return fake_service;
-    }
-
     // Sets the next GATT Discovery request response for peripheral with
     // |address| to |code|. |code| could be an HCI Error Code from
     // BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a number outside that
@@ -210,13 +191,5 @@
     }
   }
 
-  class FakeRemoteGATTService {
-    constructor(service_id, peripheral_address, fake_central_ptr) {
-      this.service_id_ = service_id;
-      this.peripheral_address_ = peripheral_address;
-      this.fake_central_ptr_ = fake_central_ptr;
-    }
-  }
-
   navigator.bluetooth.test = new FakeBluetooth();
 })();
diff --git a/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png b/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
index ba6d122..cdd5578c 100644
--- a/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
+++ b/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface-expected.txt b/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface-expected.txt
index 66b3e49c..fb5cb01 100644
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface-expected.txt
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface-expected.txt
@@ -23,7 +23,7 @@
 PASS a3[1] is ""
 
 Test bad input to string constructor
-PASS new WebKitCSSMatrix("banana") threw exception SyntaxError: Failed to construct 'DOMMatrix': Failed to parse 'banana'..
+PASS new WebKitCSSMatrix("banana") threw exception SyntaxError: Failed to construct 'WebKitCSSMatrix': Failed to parse 'banana'..
 
 Test attributes on default matrix
 PASS m.a is 1
@@ -58,9 +58,9 @@
 PASS m.f is 20
 
 Test throwing exception from setMatrixValue
-PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'banana'..
-PASS m.setMatrixValue("translate(10em, 20%)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Lengths must be absolute, not relative.
-PASS m.setMatrixValue("translate(10px, 20px) scale()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'translate(10px, 20px) scale()'..
+PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'banana'..
+PASS m.setMatrixValue("translate(10em, 20%)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': The transformation depends on the box size, which is not supported..
+PASS m.setMatrixValue("translate(10px, 20px) scale()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'translate(10px, 20px) scale()'..
 
 Test attributes on translate() and accumulation
 PASS m2.a is 1
@@ -189,12 +189,7 @@
 PASS sx.f is 0
 
 Test multiply with missing argument
-PASS m2.a is 1
-PASS m2.b is 2
-PASS m2.c is 3
-PASS m2.d is 4
-PASS m2.e is 5
-PASS m2.f is 6
+PASS m2 is null
 
 Test inverse
 PASS parseFloat(m2.a) is 0.5
@@ -212,21 +207,8 @@
 PASS parseFloat(m.e) is 10
 PASS parseFloat(m.f) is 20
 
-Test not invertible matrix
-PASS m2.a is NaN
-PASS m2.b is NaN
-PASS m2.c is NaN
-PASS m2.d is NaN
-PASS m2.e is NaN
-PASS m2.f is NaN
-
-Test immutability of inverse
-PASS m.a is 0
-PASS m.b is 0
-PASS m.c is 0
-PASS m.d is 0
-PASS m.e is 0
-PASS m.f is 0
+Test throwing exception from inverse
+PASS m.inverse() threw exception NotSupportedError: Failed to execute 'inverse' on 'WebKitCSSMatrix': The matrix is not invertable..
 
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface.xhtml b/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface.xhtml
index ed7b00c..cc85536 100644
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface.xhtml
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface.xhtml
@@ -255,12 +255,7 @@
 debug("Test multiply with missing argument");
 m = new WebKitCSSMatrix("matrix(1, 2, 3, 4, 5, 6)");
 m2 = m.multiply();
-shouldBe('m2.a', '1');
-shouldBe('m2.b', '2');
-shouldBe('m2.c', '3');
-shouldBe('m2.d', '4');
-shouldBe('m2.e', '5');
-shouldBe('m2.f', '6');
+shouldBe('m2', 'null');
 
 debug("");
 debug("Test inverse");
@@ -284,25 +279,9 @@
 shouldBe('parseFloat(m.f)', '20');
 
 debug("");
-debug("Test not invertible matrix");
+debug("Test throwing exception from inverse");
 m = new WebKitCSSMatrix("matrix(0, 0, 0, 0, 0, 0)"); // not invertible
-m2 = m.inverse();
-
-shouldBeNaN('m2.a');
-shouldBeNaN('m2.b');
-shouldBeNaN('m2.c');
-shouldBeNaN('m2.d');
-shouldBeNaN('m2.e');
-shouldBeNaN('m2.f');
-
-debug("");
-debug("Test immutability of inverse");
-shouldBe('m.a', '0');
-shouldBe('m.b', '0');
-shouldBe('m.c', '0');
-shouldBe('m.d', '0');
-shouldBe('m.e', '0');
-shouldBe('m.f', '0');
+shouldThrow('m.inverse()');
 
 debug("");
 
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface-expected.txt b/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface-expected.txt
index 3563c33..247a66d 100644
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface-expected.txt
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface-expected.txt
@@ -29,7 +29,7 @@
 PASS a3[1] is ""
 
 Test bad input to string constructor
-PASS new WebKitCSSMatrix("banana") threw exception SyntaxError: Failed to construct 'DOMMatrix': Failed to parse 'banana'..
+PASS new WebKitCSSMatrix("banana") threw exception SyntaxError: Failed to construct 'WebKitCSSMatrix': Failed to parse 'banana'..
 
 Test attributes on default matrix
 PASS m.m11 is 1
@@ -104,9 +104,9 @@
 PASS m.m44 is 1
 
 Test throwing exception from setMatrixValue
-PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'banana'..
-PASS m.setMatrixValue("translate3d(10em, 20%, 40)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'translate3d(10em, 20%, 40)'..
-PASS m.setMatrixValue("translate3d(10px, 20px, 30px) scale3d()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'translate3d(10px, 20px, 30px) scale3d()'..
+PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'banana'..
+PASS m.setMatrixValue("translate3d(10em, 20%, 40)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'translate3d(10em, 20%, 40)'..
+PASS m.setMatrixValue("translate3d(10px, 20px, 30px) scale3d()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'translate3d(10px, 20px, 30px) scale3d()'..
 
 Test multiply
 PASS parseFloat(m3.m11) is 538
@@ -216,41 +216,8 @@
 PASS parseFloat(m.m43) is 30
 PASS parseFloat(m.m44) is 1
 
-Test not invertible 3d matrix
-PASS m2.m11 is NaN
-PASS m2.m12 is NaN
-PASS m2.m13 is NaN
-PASS m2.m14 is NaN
-PASS m2.m21 is NaN
-PASS m2.m22 is NaN
-PASS m2.m23 is NaN
-PASS m2.m24 is NaN
-PASS m2.m31 is NaN
-PASS m2.m32 is NaN
-PASS m2.m33 is NaN
-PASS m2.m34 is NaN
-PASS m2.m41 is NaN
-PASS m2.m42 is NaN
-PASS m2.m43 is NaN
-PASS m2.m44 is NaN
-
-Test immutability of inverse
-PASS m.m11 is 0
-PASS m.m12 is 0
-PASS m.m13 is 0
-PASS m.m14 is 0
-PASS m.m21 is 0
-PASS m.m22 is 0
-PASS m.m23 is 0
-PASS m.m24 is 0
-PASS m.m31 is 0
-PASS m.m32 is 0
-PASS m.m33 is 0
-PASS m.m34 is 0
-PASS m.m41 is 0
-PASS m.m42 is 0
-PASS m.m43 is 0
-PASS m.m44 is 0
+Test throwing exception from inverse
+PASS m.inverse() threw exception NotSupportedError: Failed to execute 'inverse' on 'WebKitCSSMatrix': The matrix is not invertable..
 
 Test translate
 PASS m2.m11 is 1
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface.xhtml b/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface.xhtml
index 2ea85b43..392949f 100644
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface.xhtml
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface.xhtml
@@ -277,45 +277,9 @@
 shouldBe('parseFloat(m.m44)', '1');
 
 debug("");
-debug("Test not invertible 3d matrix");
+debug("Test throwing exception from inverse");
 m = new WebKitCSSMatrix("matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)"); // not invertible
-m2 = m.inverse();
-
-shouldBeNaN('m2.m11');
-shouldBeNaN('m2.m12');
-shouldBeNaN('m2.m13');
-shouldBeNaN('m2.m14');
-shouldBeNaN('m2.m21');
-shouldBeNaN('m2.m22');
-shouldBeNaN('m2.m23');
-shouldBeNaN('m2.m24');
-shouldBeNaN('m2.m31');
-shouldBeNaN('m2.m32');
-shouldBeNaN('m2.m33');
-shouldBeNaN('m2.m34');
-shouldBeNaN('m2.m41');
-shouldBeNaN('m2.m42');
-shouldBeNaN('m2.m43');
-shouldBeNaN('m2.m44');
-
-debug("");
-debug("Test immutability of inverse");
-shouldBe('m.m11', '0');
-shouldBe('m.m12', '0');
-shouldBe('m.m13', '0');
-shouldBe('m.m14', '0');
-shouldBe('m.m21', '0');
-shouldBe('m.m22', '0');
-shouldBe('m.m23', '0');
-shouldBe('m.m24', '0');
-shouldBe('m.m31', '0');
-shouldBe('m.m32', '0');
-shouldBe('m.m33', '0');
-shouldBe('m.m34', '0');
-shouldBe('m.m41', '0');
-shouldBe('m.m42', '0');
-shouldBe('m.m43', '0');
-shouldBe('m.m44', '0');
+shouldThrow('m.inverse()');
 
 debug("");
 debug("Test translate");
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash-expected.txt b/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash-expected.txt
new file mode 100644
index 0000000..ddad5eaf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash-expected.txt
@@ -0,0 +1 @@
+PASS if no crash
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash.html b/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash.html
new file mode 100644
index 0000000..ad928d2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+if (window.testRunner)
+    testRunner.dumpAsText();
+
+new WebKitCSSMatrix("translateX(1ex)");
+new WebKitCSSMatrix("translateX(1ch)");
+</script>
+<p>PASS if no crash</p>
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
index c9903255..22f8ae8 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
@@ -8495,7 +8495,7 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix : DOMMatrixReadOnly
+interface WebKitCSSMatrix
     attribute @@toStringTag
     getter a
     getter b
@@ -8520,18 +8520,16 @@
     getter m43
     getter m44
     method constructor
-    method invertSelf
-    method multiplySelf
-    method preMultiplySelf
-    method rotateAxisAngleSelf
-    method rotateFromVectorSelf
-    method rotateSelf
-    method scale3dSelf
-    method scaleSelf
+    method inverse
+    method multiply
+    method rotate
+    method rotateAxisAngle
+    method scale
     method setMatrixValue
-    method skewXSelf
-    method skewYSelf
-    method translateSelf
+    method skewX
+    method skewY
+    method toString
+    method translate
     setter a
     setter b
     setter c
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index eca5b15..a0f6f0de 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -8503,7 +8503,7 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix : DOMMatrixReadOnly
+interface WebKitCSSMatrix
     attribute @@toStringTag
     getter a
     getter b
@@ -8528,18 +8528,16 @@
     getter m43
     getter m44
     method constructor
-    method invertSelf
-    method multiplySelf
-    method preMultiplySelf
-    method rotateAxisAngleSelf
-    method rotateFromVectorSelf
-    method rotateSelf
-    method scale3dSelf
-    method scaleSelf
+    method inverse
+    method multiply
+    method rotate
+    method rotateAxisAngle
+    method scale
     method setMatrixValue
-    method skewXSelf
-    method skewYSelf
-    method translateSelf
+    method skewX
+    method skewY
+    method toString
+    method translate
     setter a
     setter b
     setter c
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp
index 75b38fe..14538dc 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp
@@ -427,7 +427,7 @@
       }
 
       std::unique_ptr<TextResourceDecoder> decoder(TextResourceDecoder::Create(
-          "application/javascript", resource->Encoding()));
+          "application/javascript", WTF::TextEncoding(resource->Encoding())));
       decoder->CheckForBOM(maybe_bom, kMaximumLengthOfBOM);
 
       // The encoding may change when we see the BOM. Check for BOM now
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptStreamerTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptStreamerTest.cpp
index 9499582..dad2cb3 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptStreamerTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptStreamerTest.cpp
@@ -347,7 +347,7 @@
   // It's possible that the encoding of the Resource changes after we start
   // loading it.
   V8TestingScope scope;
-  resource_->SetEncoding("windows-1252");
+  resource_->SetEncodingForTest("windows-1252");
 
   ScriptStreamer::StartStreaming(
       GetPendingScript(), ScriptStreamer::kParsingBlocking, settings_.get(),
@@ -355,7 +355,7 @@
   TestPendingScriptClient* client = new TestPendingScriptClient;
   GetPendingScript()->WatchForLoad(client);
 
-  resource_->SetEncoding("UTF-8");
+  resource_->SetEncodingForTest("UTF-8");
   // \xec\x92\x81 are the raw bytes for \uc481.
   AppendData(
       "function foo() { var foob\xec\x92\x81r = 13; return foob\xec\x92\x81r; "
@@ -384,7 +384,9 @@
   // Byte order marks should be removed before giving the data to V8. They
   // will also affect encoding detection.
   V8TestingScope scope;
-  resource_->SetEncoding("windows-1252");  // This encoding is wrong on purpose.
+
+  // This encoding is wrong on purpose.
+  resource_->SetEncodingForTest("windows-1252");
 
   ScriptStreamer::StartStreaming(
       GetPendingScript(), ScriptStreamer::kParsingBlocking, settings_.get(),
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 9d036af..b2d8eab67 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1476,7 +1476,6 @@
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/WebKit/Source/core/editing:unit_tests",
-    "//third_party/WebKit/Source/core/mojo:unit_tests",
   ]
 }
 
diff --git a/third_party/WebKit/Source/core/core_idl_files.gni b/third_party/WebKit/Source/core/core_idl_files.gni
index 70448a2..992bddb 100644
--- a/third_party/WebKit/Source/core/core_idl_files.gni
+++ b/third_party/WebKit/Source/core/core_idl_files.gni
@@ -62,6 +62,7 @@
                                  "css/StyleMedia.idl",
                                  "css/StyleSheet.idl",
                                  "css/StyleSheetList.idl",
+                                 "css/WebKitCSSMatrix.idl",
                                  "css/cssom/CSSImageValue.idl",
                                  "css/cssom/CSSKeywordValue.idl",
                                  "css/cssom/CSSMatrixComponent.idl",
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index fa4163d..255cc52 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -94,6 +94,8 @@
     "CSSKeyframesRule.h",
     "CSSMarkup.cpp",
     "CSSMarkup.h",
+    "CSSMatrix.cpp",
+    "CSSMatrix.h",
     "CSSMediaRule.cpp",
     "CSSMediaRule.h",
     "CSSNamespaceRule.cpp",
diff --git a/third_party/WebKit/Source/core/css/CSSMatrix.cpp b/third_party/WebKit/Source/core/css/CSSMatrix.cpp
new file mode 100644
index 0000000..92af0333
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/CSSMatrix.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "core/css/CSSMatrix.h"
+
+#include "bindings/core/v8/ExceptionState.h"
+#include "core/CSSPropertyNames.h"
+#include "core/CSSValueKeywords.h"
+#include "core/css/CSSIdentifierValue.h"
+#include "core/css/CSSToLengthConversionData.h"
+#include "core/css/StylePropertySet.h"
+#include "core/css/parser/CSSParser.h"
+#include "core/css/resolver/TransformBuilder.h"
+#include "core/dom/ExceptionCode.h"
+#include "core/frame/UseCounter.h"
+#include "core/layout/api/LayoutViewItem.h"
+#include "core/style/ComputedStyle.h"
+#include "platform/wtf/MathExtras.h"
+
+namespace blink {
+
+CSSMatrix* CSSMatrix::Create(ExecutionContext* execution_context,
+                             const String& s,
+                             ExceptionState& exception_state) {
+  return new CSSMatrix(s, exception_state);
+}
+
+CSSMatrix::CSSMatrix(const TransformationMatrix& m)
+    : matrix_(TransformationMatrix::Create(m)) {}
+
+CSSMatrix::CSSMatrix(const String& s, ExceptionState& exception_state)
+    : matrix_(TransformationMatrix::Create()) {
+  setMatrixValue(s, exception_state);
+}
+
+static inline PassRefPtr<ComputedStyle> CreateInitialStyle() {
+  RefPtr<ComputedStyle> initial_style = ComputedStyle::Create();
+  initial_style->GetFont().Update(nullptr);
+  return initial_style;
+}
+
+void CSSMatrix::setMatrixValue(const String& string,
+                               ExceptionState& exception_state) {
+  if (string.IsEmpty())
+    return;
+
+  if (const CSSValue* value =
+          CSSParser::ParseSingleValue(CSSPropertyTransform, string)) {
+    // Check for a "none" transform. In these cases we can use the default
+    // identity matrix.
+    if (value->IsIdentifierValue() &&
+        (ToCSSIdentifierValue(value))->GetValueID() == CSSValueNone)
+      return;
+
+    DEFINE_STATIC_REF(ComputedStyle, initial_style, CreateInitialStyle());
+    TransformOperations operations =
+        TransformBuilder::CreateTransformOperations(
+            *value, CSSToLengthConversionData(initial_style, initial_style,
+                                              LayoutViewItem(nullptr), 1.0f));
+
+    // Convert transform operations to a TransformationMatrix. This can fail
+    // if a param has a percentage ('%')
+    if (operations.DependsOnBoxSize()) {
+      exception_state.ThrowDOMException(kSyntaxError,
+                                        "The transformation depends on the box "
+                                        "size, which is not supported.");
+    }
+    matrix_ = TransformationMatrix::Create();
+    operations.Apply(FloatSize(0, 0), *matrix_);
+  } else {  // There is something there but parsing failed.
+    exception_state.ThrowDOMException(kSyntaxError,
+                                      "Failed to parse '" + string + "'.");
+  }
+}
+
+// Perform a concatenation of the matrices (this * secondMatrix)
+CSSMatrix* CSSMatrix::multiply(CSSMatrix* second_matrix) const {
+  if (!second_matrix)
+    return nullptr;
+
+  return CSSMatrix::Create(
+      TransformationMatrix(*matrix_).Multiply(*second_matrix->matrix_));
+}
+
+CSSMatrix* CSSMatrix::inverse(ExceptionState& exception_state) const {
+  if (!matrix_->IsInvertible()) {
+    exception_state.ThrowDOMException(kNotSupportedError,
+                                      "The matrix is not invertable.");
+    return nullptr;
+  }
+
+  return CSSMatrix::Create(matrix_->Inverse());
+}
+
+CSSMatrix* CSSMatrix::translate(double x, double y, double z) const {
+  if (std::isnan(x))
+    x = 0;
+  if (std::isnan(y))
+    y = 0;
+  if (std::isnan(z))
+    z = 0;
+  return CSSMatrix::Create(TransformationMatrix(*matrix_).Translate3d(x, y, z));
+}
+
+CSSMatrix* CSSMatrix::scale(double scale_x,
+                            double scale_y,
+                            double scale_z) const {
+  if (std::isnan(scale_x))
+    scale_x = 1;
+  if (std::isnan(scale_y))
+    scale_y = scale_x;
+  if (std::isnan(scale_z))
+    scale_z = 1;
+  return CSSMatrix::Create(
+      TransformationMatrix(*matrix_).Scale3d(scale_x, scale_y, scale_z));
+}
+
+CSSMatrix* CSSMatrix::rotate(double rot_x, double rot_y, double rot_z) const {
+  if (std::isnan(rot_x))
+    rot_x = 0;
+
+  if (std::isnan(rot_y) && std::isnan(rot_z)) {
+    rot_z = rot_x;
+    rot_x = 0;
+    rot_y = 0;
+  }
+
+  if (std::isnan(rot_y))
+    rot_y = 0;
+  if (std::isnan(rot_z))
+    rot_z = 0;
+  return CSSMatrix::Create(
+      TransformationMatrix(*matrix_).Rotate3d(rot_x, rot_y, rot_z));
+}
+
+CSSMatrix* CSSMatrix::rotateAxisAngle(double x,
+                                      double y,
+                                      double z,
+                                      double angle) const {
+  if (std::isnan(x))
+    x = 0;
+  if (std::isnan(y))
+    y = 0;
+  if (std::isnan(z))
+    z = 0;
+  if (std::isnan(angle))
+    angle = 0;
+  if (!x && !y && !z)
+    z = 1;
+  return CSSMatrix::Create(
+      TransformationMatrix(*matrix_).Rotate3d(x, y, z, angle));
+}
+
+CSSMatrix* CSSMatrix::skewX(double angle) const {
+  if (std::isnan(angle))
+    angle = 0;
+  return CSSMatrix::Create(TransformationMatrix(*matrix_).SkewX(angle));
+}
+
+CSSMatrix* CSSMatrix::skewY(double angle) const {
+  if (std::isnan(angle))
+    angle = 0;
+  return CSSMatrix::Create(TransformationMatrix(*matrix_).SkewY(angle));
+}
+
+String CSSMatrix::toString() const {
+  // FIXME - Need to ensure valid CSS floating point values
+  // (https://bugs.webkit.org/show_bug.cgi?id=20674)
+  if (matrix_->IsAffine()) {
+    return String::Format("matrix(%g, %g, %g, %g, %g, %g)", matrix_->A(),
+                          matrix_->B(), matrix_->C(), matrix_->D(),
+                          matrix_->E(), matrix_->F());
+  }
+  return String::Format(
+      "matrix3d(%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, "
+      "%g)",
+      matrix_->M11(), matrix_->M12(), matrix_->M13(), matrix_->M14(),
+      matrix_->M21(), matrix_->M22(), matrix_->M23(), matrix_->M24(),
+      matrix_->M31(), matrix_->M32(), matrix_->M33(), matrix_->M34(),
+      matrix_->M41(), matrix_->M42(), matrix_->M43(), matrix_->M44());
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSMatrix.h b/third_party/WebKit/Source/core/css/CSSMatrix.h
new file mode 100644
index 0000000..1c25efb2
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/CSSMatrix.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CSSMatrix_h
+#define CSSMatrix_h
+
+#include <memory>
+#include "platform/bindings/ScriptWrappable.h"
+#include "platform/transforms/TransformationMatrix.h"
+#include "platform/wtf/text/WTFString.h"
+
+namespace blink {
+
+class ExceptionState;
+class ExecutionContext;
+
+class CSSMatrix final : public GarbageCollectedFinalized<CSSMatrix>,
+                        public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static CSSMatrix* Create(const TransformationMatrix& m) {
+    return new CSSMatrix(m);
+  }
+  static CSSMatrix* Create(ExecutionContext*, const String&, ExceptionState&);
+
+  double a() const { return matrix_->A(); }
+  double b() const { return matrix_->B(); }
+  double c() const { return matrix_->C(); }
+  double d() const { return matrix_->D(); }
+  double e() const { return matrix_->E(); }
+  double f() const { return matrix_->F(); }
+
+  void setA(double f) { matrix_->SetA(f); }
+  void setB(double f) { matrix_->SetB(f); }
+  void setC(double f) { matrix_->SetC(f); }
+  void setD(double f) { matrix_->SetD(f); }
+  void setE(double f) { matrix_->SetE(f); }
+  void setF(double f) { matrix_->SetF(f); }
+
+  double m11() const { return matrix_->M11(); }
+  double m12() const { return matrix_->M12(); }
+  double m13() const { return matrix_->M13(); }
+  double m14() const { return matrix_->M14(); }
+  double m21() const { return matrix_->M21(); }
+  double m22() const { return matrix_->M22(); }
+  double m23() const { return matrix_->M23(); }
+  double m24() const { return matrix_->M24(); }
+  double m31() const { return matrix_->M31(); }
+  double m32() const { return matrix_->M32(); }
+  double m33() const { return matrix_->M33(); }
+  double m34() const { return matrix_->M34(); }
+  double m41() const { return matrix_->M41(); }
+  double m42() const { return matrix_->M42(); }
+  double m43() const { return matrix_->M43(); }
+  double m44() const { return matrix_->M44(); }
+
+  void setM11(double f) { matrix_->SetM11(f); }
+  void setM12(double f) { matrix_->SetM12(f); }
+  void setM13(double f) { matrix_->SetM13(f); }
+  void setM14(double f) { matrix_->SetM14(f); }
+  void setM21(double f) { matrix_->SetM21(f); }
+  void setM22(double f) { matrix_->SetM22(f); }
+  void setM23(double f) { matrix_->SetM23(f); }
+  void setM24(double f) { matrix_->SetM24(f); }
+  void setM31(double f) { matrix_->SetM31(f); }
+  void setM32(double f) { matrix_->SetM32(f); }
+  void setM33(double f) { matrix_->SetM33(f); }
+  void setM34(double f) { matrix_->SetM34(f); }
+  void setM41(double f) { matrix_->SetM41(f); }
+  void setM42(double f) { matrix_->SetM42(f); }
+  void setM43(double f) { matrix_->SetM43(f); }
+  void setM44(double f) { matrix_->SetM44(f); }
+
+  void setMatrixValue(const String&, ExceptionState&);
+
+  // The following math function return a new matrix with the
+  // specified operation applied. The this value is not modified.
+
+  // Multiply this matrix by secondMatrix, on the right
+  // (result = this * secondMatrix)
+  CSSMatrix* multiply(CSSMatrix* second_matrix) const;
+
+  // Return the inverse of this matrix. Throw an exception if the matrix is not
+  // invertible.
+  CSSMatrix* inverse(ExceptionState&) const;
+
+  // Return this matrix translated by the passed values.
+  // Passing a NaN will use a value of 0. This allows the 3D form to used for 2D
+  // operations.
+  // Operation is performed as though the this matrix is multiplied by a matrix
+  // with the translation values on the left
+  // (result = translation(x,y,z) * this)
+  CSSMatrix* translate(double x, double y, double z) const;
+
+  // Returns this matrix scaled by the passed values.
+  // Passing scaleX or scaleZ as NaN uses a value of 1, but passing scaleY of
+  // NaN makes it the same as scaleX. This allows the 3D form to used for 2D
+  // operations Operation is performed as though the this matrix is multiplied
+  // by a matrix with the scale values on the left
+  // (result = scale(x,y,z) * this)
+  CSSMatrix* scale(double scale_x, double scale_y, double scale_z) const;
+
+  // Returns this matrix rotated by the passed values.
+  // If rotY and rotZ are NaN, rotate about Z (rotX=0, rotateY=0, rotateZ=rotX).
+  // Otherwise use a rotation value of 0 for any passed NaN.
+  // Operation is performed as though the this matrix is multiplied by a matrix
+  // with the rotation values on the left (result = rotation(x,y,z) * this)
+  CSSMatrix* rotate(double rot_x, double rot_y, double rot_z) const;
+
+  // Returns this matrix rotated about the passed axis by the passed angle.
+  // Passing a NaN will use a value of 0. If the axis is (0,0,0) use a value
+  // Operation is performed as though the this matrix is multiplied by a matrix
+  // with the rotation values on the left
+  // (result = rotation(x,y,z,angle) * this)
+  CSSMatrix* rotateAxisAngle(double x, double y, double z, double angle) const;
+
+  // Return this matrix skewed along the X axis by the passed values.
+  // Passing a NaN will use a value of 0.
+  // Operation is performed as though the this matrix is multiplied by a matrix
+  // with the skew values on the left (result = skewX(angle) * this)
+  CSSMatrix* skewX(double angle) const;
+
+  // Return this matrix skewed along the Y axis by the passed values.
+  // Passing a NaN will use a value of 0.
+  // Operation is performed as though the this matrix is multiplied by a matrix
+  // with the skew values on the left (result = skewY(angle) * this)
+  CSSMatrix* skewY(double angle) const;
+
+  const TransformationMatrix& Transform() const { return *matrix_; }
+
+  String toString() const;
+
+  DEFINE_INLINE_TRACE() {}
+
+ protected:
+  CSSMatrix(const TransformationMatrix&);
+  CSSMatrix(const String&, ExceptionState&);
+
+  // TransformationMatrix needs to be 16-byte aligned. PartitionAlloc
+  // supports 16-byte alignment but Oilpan doesn't. So we use an std::unique_ptr
+  // to allocate TransformationMatrix on PartitionAlloc.
+  // TODO(oilpan): Oilpan should support 16-byte aligned allocations.
+  std::unique_ptr<TransformationMatrix> matrix_;
+};
+
+}  // namespace blink
+
+#endif  // CSSMatrix_h
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
index 59a4c3d..0aaed58 100644
--- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
+++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -1008,27 +1008,6 @@
 }
 
 template <>
-inline ETextDecorationStyle CSSIdentifierValue::ConvertTo() const {
-  switch (value_id_) {
-    case CSSValueSolid:
-      return ETextDecorationStyle::kSolid;
-    case CSSValueDouble:
-      return ETextDecorationStyle::kDouble;
-    case CSSValueDotted:
-      return ETextDecorationStyle::kDotted;
-    case CSSValueDashed:
-      return ETextDecorationStyle::kDashed;
-    case CSSValueWavy:
-      return ETextDecorationStyle::kWavy;
-    default:
-      break;
-  }
-
-  NOTREACHED();
-  return ETextDecorationStyle::kSolid;
-}
-
-template <>
 inline TextDecorationSkip CSSIdentifierValue::ConvertTo() const {
   switch (value_id_) {
     case CSSValueObjects:
@@ -1137,34 +1116,6 @@
 }
 
 template <>
-inline CSSIdentifierValue::CSSIdentifierValue(ETextOverflow overflow)
-    : CSSValue(kIdentifierClass) {
-  switch (overflow) {
-    case ETextOverflow::kClip:
-      value_id_ = CSSValueClip;
-      break;
-    case ETextOverflow::kEllipsis:
-      value_id_ = CSSValueEllipsis;
-      break;
-  }
-}
-
-template <>
-inline ETextOverflow CSSIdentifierValue::ConvertTo() const {
-  switch (value_id_) {
-    case CSSValueClip:
-      return ETextOverflow::kClip;
-    case CSSValueEllipsis:
-      return ETextOverflow::kEllipsis;
-    default:
-      break;
-  }
-
-  NOTREACHED();
-  return ETextOverflow::kClip;
-}
-
-template <>
 inline CSSIdentifierValue::CSSIdentifierValue(TextEmphasisFill fill)
     : CSSValue(kIdentifierClass) {
   switch (fill) {
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index 53d72324..f6d0b9f 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -2436,8 +2436,9 @@
       custom_all: true,
       interpolable: true,
       runtime_flag: "CSS3TextDecorations",
-      field_template: "storage_only",
+      field_template: "external",
       type_name: "StyleColor",
+      include_paths: ["core/css/StyleColor.h"],
       field_group: "rare-non-inherited",
       default_value: "StyleColor::CurrentColor()",
     },
@@ -2465,10 +2466,10 @@
     {
       name: "text-decoration-style",
       runtime_flag: "CSS3TextDecorations",
-      field_template: "storage_only",
+      field_template: "keyword",
+      keywords: ["solid", "double", "dotted", "dashed", "wavy"],
       field_group: "rare-non-inherited",
-      default_value: "ETextDecorationStyle::kSolid",
-      field_size: 3,
+      default_value: "solid",
     },
     {
       name: "text-indent",
@@ -2497,10 +2498,10 @@
     },
     {
       name: "text-overflow",
-      field_template: "storage_only",
+      field_template: "keyword",
       field_group: "rare-non-inherited",
-      default_value: "ETextOverflow::kClip",
-      field_size: 1,
+      default_value: "clip",
+      keywords: ["clip", "ellipsis"],
     },
     {
       name: "text-shadow",
diff --git a/third_party/WebKit/Source/core/css/CSSStyleSheet.cpp b/third_party/WebKit/Source/core/css/CSSStyleSheet.cpp
index 9e8458a..6ef1896 100644
--- a/third_party/WebKit/Source/core/css/CSSStyleSheet.cpp
+++ b/third_party/WebKit/Source/core/css/CSSStyleSheet.cpp
@@ -109,7 +109,7 @@
 CSSStyleSheet* CSSStyleSheet::CreateInline(Node& owner_node,
                                            const KURL& base_url,
                                            const TextPosition& start_position,
-                                           const String& encoding) {
+                                           const WTF::TextEncoding& encoding) {
   CSSParserContext* parser_context = CSSParserContext::Create(
       owner_node.GetDocument(), owner_node.GetDocument().BaseURL(),
       owner_node.GetDocument().GetReferrerPolicy(), encoding);
diff --git a/third_party/WebKit/Source/core/css/CSSStyleSheet.h b/third_party/WebKit/Source/core/css/CSSStyleSheet.h
index eb00fc5..a0b0438 100644
--- a/third_party/WebKit/Source/core/css/CSSStyleSheet.h
+++ b/third_party/WebKit/Source/core/css/CSSStyleSheet.h
@@ -28,6 +28,7 @@
 #include "core/css/StyleSheet.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Noncopyable.h"
+#include "platform/wtf/text/TextEncoding.h"
 #include "platform/wtf/text/TextPosition.h"
 
 namespace blink {
@@ -56,7 +57,7 @@
       Node&,
       const KURL&,
       const TextPosition& start_position = TextPosition::MinimumPosition(),
-      const String& encoding = String());
+      const WTF::TextEncoding& = WTF::TextEncoding());
   static CSSStyleSheet* CreateInline(
       StyleSheetContents*,
       Node& owner_node,
diff --git a/third_party/WebKit/Source/core/css/CSSTestHelper.cpp b/third_party/WebKit/Source/core/css/CSSTestHelper.cpp
index 4c3f02c..b0f82af 100644
--- a/third_party/WebKit/Source/core/css/CSSTestHelper.cpp
+++ b/third_party/WebKit/Source/core/css/CSSTestHelper.cpp
@@ -34,7 +34,7 @@
 #include "core/css/RuleSet.h"
 #include "core/css/StyleSheetContents.h"
 #include "core/dom/Document.h"
-#include "platform/wtf/text/WTFString.h"
+#include "platform/wtf/text/TextEncoding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
@@ -45,7 +45,7 @@
   document_ = Document::Create();
   TextPosition position;
   style_sheet_ =
-      CSSStyleSheet::CreateInline(*document_, KURL(), position, "UTF-8");
+      CSSStyleSheet::CreateInline(*document_, KURL(), position, UTF8Encoding());
 }
 
 CSSRuleList* CSSTestHelper::CssRules() {
diff --git a/third_party/WebKit/Source/core/css/StyleRuleImport.cpp b/third_party/WebKit/Source/core/css/StyleRuleImport.cpp
index 5856e93..caf0f7b2 100644
--- a/third_party/WebKit/Source/core/css/StyleRuleImport.cpp
+++ b/third_party/WebKit/Source/core/css/StyleRuleImport.cpp
@@ -69,7 +69,7 @@
     const String& href,
     const KURL& base_url,
     ReferrerPolicy referrer_policy,
-    const String& charset,
+    const WTF::TextEncoding& charset,
     const CSSStyleSheetResource* cached_style_sheet) {
   if (style_sheet_)
     style_sheet_->ClearOwnerRule();
@@ -135,7 +135,7 @@
                                 kClientRequestedCredentials);
   options.initiator_info.name = FetchInitiatorTypeNames::css;
   FetchParameters params(ResourceRequest(abs_url), options);
-  params.SetCharset(parent_style_sheet_->Charset());
+  params.SetCharset(String(parent_style_sheet_->Charset().GetName()));
   resource_ = CSSStyleSheetResource::Fetch(params, fetcher);
   if (resource_) {
     // if the import rule is issued dynamically, the sheet may be
diff --git a/third_party/WebKit/Source/core/css/StyleRuleImport.h b/third_party/WebKit/Source/core/css/StyleRuleImport.h
index 0d8134c..d52b793 100644
--- a/third_party/WebKit/Source/core/css/StyleRuleImport.h
+++ b/third_party/WebKit/Source/core/css/StyleRuleImport.h
@@ -74,7 +74,7 @@
     void SetCSSStyleSheet(const String& href,
                           const KURL& base_url,
                           ReferrerPolicy referrer_policy,
-                          const String& charset,
+                          const WTF::TextEncoding& charset,
                           const CSSStyleSheetResource* sheet) override {
       owner_rule_->SetCSSStyleSheet(href, base_url, referrer_policy, charset,
                                     sheet);
@@ -93,7 +93,7 @@
   void SetCSSStyleSheet(const String& href,
                         const KURL& base_url,
                         ReferrerPolicy,
-                        const String& charset,
+                        const WTF::TextEncoding&,
                         const CSSStyleSheetResource*);
 
   StyleRuleImport(const String& href, RefPtr<MediaQuerySet>);
diff --git a/third_party/WebKit/Source/core/css/StyleSheetContents.h b/third_party/WebKit/Source/core/css/StyleSheetContents.h
index cee704a4..3fb1f42 100644
--- a/third_party/WebKit/Source/core/css/StyleSheetContents.h
+++ b/third_party/WebKit/Source/core/css/StyleSheetContents.h
@@ -93,7 +93,9 @@
   // if there are none.
   Document* AnyOwnerDocument() const;
 
-  const String& Charset() const { return parser_context_->Charset(); }
+  const WTF::TextEncoding& Charset() const {
+    return parser_context_->Charset();
+  }
 
   bool LoadCompleted() const;
   bool HasFailedOrCanceledSubresources() const;
diff --git a/third_party/WebKit/Source/core/css/WebKitCSSMatrix.idl b/third_party/WebKit/Source/core/css/WebKitCSSMatrix.idl
new file mode 100644
index 0000000..d9227cf0
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/WebKitCSSMatrix.idl
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008, 2010 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Introduced in DOM Level ?:
+[
+    Constructor(optional DOMString cssValue = null),
+    ConstructorCallWith=ExecutionContext,
+    ImplementedAs=CSSMatrix,
+    RaisesException=Constructor,
+] interface WebKitCSSMatrix {
+
+    // These attributes are simple aliases for certain elements of the 4x4 matrix
+    attribute unrestricted double a; // alias for m11
+    attribute unrestricted double b; // alias for m12
+    attribute unrestricted double c; // alias for m21
+    attribute unrestricted double d; // alias for m22
+    attribute unrestricted double e; // alias for m41
+    attribute unrestricted double f; // alias for m42
+
+    attribute unrestricted double m11;
+    attribute unrestricted double m12;
+    attribute unrestricted double m13;
+    attribute unrestricted double m14;
+    attribute unrestricted double m21;
+    attribute unrestricted double m22;
+    attribute unrestricted double m23;
+    attribute unrestricted double m24;
+    attribute unrestricted double m31;
+    attribute unrestricted double m32;
+    attribute unrestricted double m33;
+    attribute unrestricted double m34;
+    attribute unrestricted double m41;
+    attribute unrestricted double m42;
+    attribute unrestricted double m43;
+    attribute unrestricted double m44;
+
+    [RaisesException, MeasureAs=WebkitCSSMatrixSetMatrixValue] void setMatrixValue([Default=Undefined] optional DOMString string);
+
+    // Multiply this matrix by secondMatrix, on the right (result = this * secondMatrix)
+    [LegacyInterfaceTypeChecking] WebKitCSSMatrix multiply([Default=Undefined] optional WebKitCSSMatrix secondMatrix);
+
+    // Return the inverse of this matrix. Throw an exception if the matrix is not invertible
+    [RaisesException] WebKitCSSMatrix inverse();
+
+    // Return this matrix translated by the passed values.
+    // Passing a NaN will use a value of 0. This allows the 3D form to used for 2D operations
+    WebKitCSSMatrix translate([Default=Undefined] optional unrestricted double x,
+                              [Default=Undefined] optional unrestricted double y,
+                              [Default=Undefined] optional unrestricted double z);
+
+    // Returns this matrix scaled by the passed values.
+    // Passing scaleX or scaleZ as NaN uses a value of 1, but passing scaleY of NaN
+    // makes it the same as scaleX. This allows the 3D form to used for 2D operations
+    WebKitCSSMatrix scale([Default=Undefined] optional unrestricted double scaleX,
+                          [Default=Undefined] optional unrestricted double scaleY,
+                          [Default=Undefined] optional unrestricted double scaleZ);
+
+    // Returns this matrix rotated by the passed values.
+    // If rotY and rotZ are NaN, rotate about Z (rotX=0, rotateY=0, rotateZ=rotX).
+    // Otherwise use a rotation value of 0 for any passed NaN.
+    WebKitCSSMatrix rotate([Default=Undefined] optional unrestricted double rotX,
+                           [Default=Undefined] optional unrestricted double rotY,
+                           [Default=Undefined] optional unrestricted double rotZ);
+
+    // Returns this matrix rotated about the passed axis by the passed angle.
+    // Passing a NaN will use a value of 0. If the axis is (0,0,0) use a value
+    // of (0,0,1).
+    WebKitCSSMatrix rotateAxisAngle([Default=Undefined] optional unrestricted double x,
+                                    [Default=Undefined] optional unrestricted double y,
+                                    [Default=Undefined] optional unrestricted double z,
+                                    [Default=Undefined] optional unrestricted double angle);
+
+    // Returns this matrix skewed along the X axis by the passed values.
+    // Passing a NaN will use a value of 0.
+    WebKitCSSMatrix skewX([Default=Undefined] optional unrestricted double angle);
+
+    // Returns this matrix skewed along the Y axis by the passed values.
+    // Passing a NaN will use a value of 0.
+    WebKitCSSMatrix skewY([Default=Undefined] optional unrestricted double angle);
+
+    [NotEnumerable] stringifier;
+};
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserContext.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserContext.cpp
index fe11bce..723fc31 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserContext.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserContext.cpp
@@ -46,7 +46,7 @@
     const CSSParserContext* other,
     const KURL& base_url,
     ReferrerPolicy referrer_policy,
-    const String& charset,
+    const WTF::TextEncoding& charset,
     const Document* use_counter_document) {
   return new CSSParserContext(
       base_url, charset, other->mode_, other->match_mode_, other->profile_,
@@ -62,15 +62,15 @@
     SelectorProfile profile,
     const Document* use_counter_document) {
   return new CSSParserContext(
-      KURL(), g_empty_string, mode, mode, profile, Referrer(), false, false,
-      kDoNotCheckContentSecurityPolicy, use_counter_document);
+      KURL(), WTF::TextEncoding(), mode, mode, profile, Referrer(), false,
+      false, kDoNotCheckContentSecurityPolicy, use_counter_document);
 }
 
 // static
 CSSParserContext* CSSParserContext::Create(const Document& document) {
   return CSSParserContext::Create(document, document.BaseURL(),
-                                  document.GetReferrerPolicy(), g_empty_string,
-                                  kDynamicProfile);
+                                  document.GetReferrerPolicy(),
+                                  WTF::TextEncoding(), kDynamicProfile);
 }
 
 // static
@@ -78,7 +78,7 @@
     const Document& document,
     const KURL& base_url_override,
     ReferrerPolicy referrer_policy_override,
-    const String& charset,
+    const WTF::TextEncoding& charset,
     SelectorProfile profile) {
   CSSParserMode mode =
       document.InQuirksMode() ? kHTMLQuirksMode : kHTMLStandardMode;
@@ -115,7 +115,7 @@
 
 CSSParserContext::CSSParserContext(
     const KURL& base_url,
-    const String& charset,
+    const WTF::TextEncoding& charset,
     CSSParserMode mode,
     CSSParserMode match_mode,
     SelectorProfile profile,
@@ -160,7 +160,7 @@
 KURL CSSParserContext::CompleteURL(const String& url) const {
   if (url.IsNull())
     return KURL();
-  if (Charset().IsEmpty())
+  if (!Charset().IsValid())
     return KURL(BaseURL(), url);
   return KURL(BaseURL(), url, Charset());
 }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserContext.h b/third_party/WebKit/Source/core/css/parser/CSSParserContext.h
index d096d52..62cc64a 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserContext.h
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserContext.h
@@ -40,7 +40,7 @@
   static CSSParserContext* Create(const CSSParserContext* other,
                                   const KURL& base_url_override,
                                   ReferrerPolicy referrer_policy_override,
-                                  const String& charset_override,
+                                  const WTF::TextEncoding& charset_override,
                                   const Document* use_counter_document);
 
   static CSSParserContext* Create(
@@ -48,11 +48,12 @@
       SelectorProfile = kDynamicProfile,
       const Document* use_counter_document = nullptr);
   static CSSParserContext* Create(const Document&);
-  static CSSParserContext* Create(const Document&,
-                                  const KURL& base_url_override,
-                                  ReferrerPolicy referrer_policy_override,
-                                  const String& charset = g_empty_string,
-                                  SelectorProfile = kDynamicProfile);
+  static CSSParserContext* Create(
+      const Document&,
+      const KURL& base_url_override,
+      ReferrerPolicy referrer_policy_override,
+      const WTF::TextEncoding& charset = WTF::TextEncoding(),
+      SelectorProfile = kDynamicProfile);
 
   bool operator==(const CSSParserContext&) const;
   bool operator!=(const CSSParserContext& other) const {
@@ -62,7 +63,7 @@
   CSSParserMode Mode() const { return mode_; }
   CSSParserMode MatchMode() const { return match_mode_; }
   const KURL& BaseURL() const { return base_url_; }
-  const String& Charset() const { return charset_; }
+  const WTF::TextEncoding& Charset() const { return charset_; }
   const Referrer& GetReferrer() const { return referrer_; }
   bool IsHTMLDocument() const { return is_html_document_; }
   bool IsDynamicProfile() const { return profile_ == kDynamicProfile; }
@@ -96,7 +97,7 @@
 
  private:
   CSSParserContext(const KURL& base_url,
-                   const String& charset,
+                   const WTF::TextEncoding& charset,
                    CSSParserMode,
                    CSSParserMode match_mode,
                    SelectorProfile,
@@ -107,7 +108,7 @@
                    const Document* use_counter_document);
 
   KURL base_url_;
-  String charset_;
+  WTF::TextEncoding charset_;
   CSSParserMode mode_;
   CSSParserMode match_mode_;
   SelectorProfile profile_ = kDynamicProfile;
diff --git a/third_party/WebKit/Source/core/dom/ProcessingInstruction.cpp b/third_party/WebKit/Source/core/dom/ProcessingInstruction.cpp
index 8bf54225..0cfe3fa 100644
--- a/third_party/WebKit/Source/core/dom/ProcessingInstruction.cpp
+++ b/third_party/WebKit/Source/core/dom/ProcessingInstruction.cpp
@@ -194,7 +194,7 @@
     const String& href,
     const KURL& base_url,
     ReferrerPolicy referrer_policy,
-    const String& charset,
+    const WTF::TextEncoding& charset,
     const CSSStyleSheetResource* sheet) {
   if (!isConnected()) {
     DCHECK(!sheet_);
diff --git a/third_party/WebKit/Source/core/dom/ProcessingInstruction.h b/third_party/WebKit/Source/core/dom/ProcessingInstruction.h
index e00477b22..b9fe11a 100644
--- a/third_party/WebKit/Source/core/dom/ProcessingInstruction.h
+++ b/third_party/WebKit/Source/core/dom/ProcessingInstruction.h
@@ -88,7 +88,7 @@
   void SetCSSStyleSheet(const String& href,
                         const KURL& base_url,
                         ReferrerPolicy,
-                        const String& charset,
+                        const WTF::TextEncoding&,
                         const CSSStyleSheetResource*) override;
   void SetXSLStyleSheet(const String& href,
                         const KURL& base_url,
diff --git a/third_party/WebKit/Source/core/dom/SelectorQuery.cpp b/third_party/WebKit/Source/core/dom/SelectorQuery.cpp
index dcf0a16..5720b340 100644
--- a/third_party/WebKit/Source/core/dom/SelectorQuery.cpp
+++ b/third_party/WebKit/Source/core/dom/SelectorQuery.cpp
@@ -550,9 +550,9 @@
     return it->value.get();
 
   CSSSelectorList selector_list = CSSParser::ParseSelector(
-      CSSParserContext::Create(document, document.BaseURL(),
-                               document.GetReferrerPolicy(), g_empty_string,
-                               CSSParserContext::kStaticProfile),
+      CSSParserContext::Create(
+          document, document.BaseURL(), document.GetReferrerPolicy(),
+          WTF::TextEncoding(), CSSParserContext::kStaticProfile),
       nullptr, selectors);
 
   if (!selector_list.First()) {
diff --git a/third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp b/third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp
index 91389fe4..6f4bcda 100644
--- a/third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp
+++ b/third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp
@@ -69,7 +69,7 @@
 
   CSSSelectorList selector_list = CSSParser::ParseSelector(
       CSSParserContext::Create(*document, KURL(), kReferrerPolicyDefault,
-                               g_empty_string,
+                               WTF::TextEncoding(),
                                CSSParserContext::kStaticProfile),
       nullptr, "span::before");
   std::unique_ptr<SelectorQuery> query =
@@ -79,7 +79,7 @@
 
   selector_list = CSSParser::ParseSelector(
       CSSParserContext::Create(*document, KURL(), kReferrerPolicyDefault,
-                               g_empty_string,
+                               WTF::TextEncoding(),
                                CSSParserContext::kStaticProfile),
       nullptr, "span");
   query = SelectorQuery::Adopt(std::move(selector_list));
@@ -98,7 +98,7 @@
 
   CSSSelectorList selector_list = CSSParser::ParseSelector(
       CSSParserContext::Create(*document, KURL(), kReferrerPolicyDefault,
-                               g_empty_string,
+                               WTF::TextEncoding(),
                                CSSParserContext::kStaticProfile),
       nullptr, "p:last-of-type");
   std::unique_ptr<SelectorQuery> query =
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
index eaf9854c..0ed95df6 100644
--- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -602,7 +602,7 @@
                                        TextPosition start_position) {
   CSSStyleSheet* style_sheet = nullptr;
   style_sheet = CSSStyleSheet::CreateInline(element, KURL(), start_position,
-                                            GetDocument().characterSet());
+                                            GetDocument().Encoding());
   style_sheet->Contents()->ParseStringAtPosition(text, start_position);
   return style_sheet;
 }
diff --git a/third_party/WebKit/Source/core/frame/FrameSerializer.cpp b/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
index 272da66..df5c806a 100644
--- a/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
@@ -421,7 +421,8 @@
   if (!is_inline_css) {
     StringBuilder css_text;
     css_text.Append("@charset \"");
-    css_text.Append(style_sheet.Contents()->Charset().DeprecatedLower());
+    css_text.Append(
+        String(style_sheet.Contents()->Charset().GetName()).DeprecatedLower());
     css_text.Append("\";\n\n");
 
     for (unsigned i = 0; i < style_sheet.length(); ++i) {
diff --git a/third_party/WebKit/Source/core/frame/FrameTestHelpers.cpp b/third_party/WebKit/Source/core/frame/FrameTestHelpers.cpp
index f409302..009cdf3 100644
--- a/third_party/WebKit/Source/core/frame/FrameTestHelpers.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameTestHelpers.cpp
@@ -264,10 +264,7 @@
       web_frame_client->GetInterfaceProviderForTesting(), nullptr, opener);
   web_frame_client->Bind(frame, std::move(owned_web_frame_client));
   web_view_->SetMainFrame(frame);
-
-  // TODO(dcheng): The main frame widget currently has a special case.
-  // Eliminate this once WebView is no longer a WebWidget.
-  blink::WebFrameWidget::Create(web_widget_client, web_view_, frame);
+  blink::WebFrameWidget::Create(web_widget_client, frame);
 
   test_web_view_client_ = web_view_client;
 
diff --git a/third_party/WebKit/Source/core/frame/Window.idl b/third_party/WebKit/Source/core/frame/Window.idl
index f610832..3434eb3 100644
--- a/third_party/WebKit/Source/core/frame/Window.idl
+++ b/third_party/WebKit/Source/core/frame/Window.idl
@@ -204,8 +204,6 @@
 
     // https://w3c.github.io/webappsec/specs/powerfulfeatures/#monkey-patching-global-object
     readonly attribute boolean isSecureContext;
-
-    attribute DOMMatrixConstructor WebKitCSSMatrix;
 };
 
 // https://html.spec.whatwg.org/#transferable-objects
diff --git a/third_party/WebKit/Source/core/html/LinkStyle.cpp b/third_party/WebKit/Source/core/html/LinkStyle.cpp
index 3a49da4..98c3125 100644
--- a/third_party/WebKit/Source/core/html/LinkStyle.cpp
+++ b/third_party/WebKit/Source/core/html/LinkStyle.cpp
@@ -61,7 +61,7 @@
     const String& href,
     const KURL& base_url,
     ReferrerPolicy referrer_policy,
-    const String& charset,
+    const WTF::TextEncoding& charset,
     const CSSStyleSheetResource* cached_style_sheet) {
   if (!owner_->isConnected()) {
     // While the stylesheet is asynchronously loading, the owner can be
diff --git a/third_party/WebKit/Source/core/html/LinkStyle.h b/third_party/WebKit/Source/core/html/LinkStyle.h
index 32680b6..f06e525 100644
--- a/third_party/WebKit/Source/core/html/LinkStyle.h
+++ b/third_party/WebKit/Source/core/html/LinkStyle.h
@@ -63,7 +63,7 @@
   void SetCSSStyleSheet(const String& href,
                         const KURL& base_url,
                         ReferrerPolicy,
-                        const String& charset,
+                        const WTF::TextEncoding&,
                         const CSSStyleSheetResource*) override;
   String DebugName() const override { return "LinkStyle"; }
   enum LoadReturnValue { kLoaded, kNotNeeded, kBail };
diff --git a/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp b/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
index e533a3ab..e26f6022 100644
--- a/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
@@ -275,7 +275,7 @@
     const String& href,
     const KURL& base_url,
     ReferrerPolicy referrer_policy,
-    const String& charset,
+    const WTF::TextEncoding&,
     const CSSStyleSheetResource*) {
   ClearResource();
 }
diff --git a/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.h b/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.h
index 1c08f1a..3695d71 100644
--- a/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.h
+++ b/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.h
@@ -110,7 +110,7 @@
   void SetCSSStyleSheet(const String& href,
                         const KURL& base_url,
                         ReferrerPolicy,
-                        const String& charset,
+                        const WTF::TextEncoding&,
                         const CSSStyleSheetResource*) override;
   void DidAppendFirstData(const CSSStyleSheetResource*) override;
   String DebugName() const override { return "CSSPreloaderResourceClient"; }
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.cpp b/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.cpp
index 49a04872..c890617 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.cpp
@@ -6,7 +6,7 @@
 
 namespace blink {
 
-PassRefPtr<HTMLParserReentryPermit> HTMLParserReentryPermit::Create() {
+RefPtr<HTMLParserReentryPermit> HTMLParserReentryPermit::Create() {
   return AdoptRef(new HTMLParserReentryPermit());
 }
 
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.h b/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.h
index 7b5abe5..84b5b85 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.h
+++ b/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.h
@@ -6,8 +6,8 @@
 #define HTMLParserReentryPermit_h
 
 #include "base/macros.h"
-#include "platform/wtf/PassRefPtr.h"
 #include "platform/wtf/RefCounted.h"
+#include "platform/wtf/RefPtr.h"
 
 namespace blink {
 
@@ -38,7 +38,7 @@
 class HTMLParserReentryPermit final
     : public RefCounted<HTMLParserReentryPermit> {
  public:
-  static PassRefPtr<HTMLParserReentryPermit> Create();
+  static RefPtr<HTMLParserReentryPermit> Create();
   ~HTMLParserReentryPermit() = default;
 
   unsigned ScriptNestingLevel() const { return script_nesting_level_; }
diff --git a/third_party/WebKit/Source/core/html/parser/TextResourceDecoder.cpp b/third_party/WebKit/Source/core/html/parser/TextResourceDecoder.cpp
index 3b00250..ed0100e 100644
--- a/third_party/WebKit/Source/core/html/parser/TextResourceDecoder.cpp
+++ b/third_party/WebKit/Source/core/html/parser/TextResourceDecoder.cpp
@@ -114,7 +114,7 @@
   Vector<char, 64> buffer(length + 1);
   memcpy(buffer.data(), encoding_name, length);
   buffer[length] = '\0';
-  return buffer.data();
+  return WTF::TextEncoding(buffer.data());
 }
 
 TextResourceDecoder::ContentType TextResourceDecoder::DetermineContentType(
@@ -190,7 +190,7 @@
   // XHR), treat x-user-defined as windows-1252 (bug 18270)
   if (source == kEncodingFromMetaTag &&
       !strcasecmp(encoding.GetName(), "x-user-defined"))
-    encoding_ = "windows-1252";
+    encoding_ = WTF::TextEncoding("windows-1252");
   else if (source == kEncodingFromMetaTag || source == kEncodingFromXMLHeader ||
            source == kEncodingFromCSSCharset)
     encoding_ = encoding.ClosestByteBasedEquivalent();
diff --git a/third_party/WebKit/Source/core/html/parser/TextResourceDecoderForFuzzing.h b/third_party/WebKit/Source/core/html/parser/TextResourceDecoderForFuzzing.h
index 8b86d970c..3f6d8c0 100644
--- a/third_party/WebKit/Source/core/html/parser/TextResourceDecoderForFuzzing.h
+++ b/third_party/WebKit/Source/core/html/parser/TextResourceDecoderForFuzzing.h
@@ -8,6 +8,7 @@
 #include "core/html/parser/TextResourceDecoder.h"
 
 #include "platform/testing/FuzzedDataProvider.h"
+#include "platform/wtf/text/TextEncoding.h"
 #include "platform/wtf/text/WTFString.h"
 
 namespace blink {
@@ -25,7 +26,8 @@
   TextResourceDecoderForFuzzing(FuzzedDataProvider& fuzzed_data)
       : TextResourceDecoder(
             String::FromUTF8(fuzzed_data.ConsumeBytesInRange(0, 32)),
-            String::FromUTF8(fuzzed_data.ConsumeBytesInRange(0, 32)),
+            WTF::TextEncoding(
+                String::FromUTF8(fuzzed_data.ConsumeBytesInRange(0, 32))),
             FuzzedOption(fuzzed_data),
             KURL()) {}
 
diff --git a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
index 25c823da..6f98e31 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
@@ -142,8 +142,10 @@
 static std::unique_ptr<TextResourceDecoder> CreateResourceTextDecoder(
     const String& mime_type,
     const String& text_encoding_name) {
-  if (!text_encoding_name.IsEmpty())
-    return TextResourceDecoder::Create("text/plain", text_encoding_name);
+  if (!text_encoding_name.IsEmpty()) {
+    return TextResourceDecoder::Create("text/plain",
+                                       WTF::TextEncoding(text_encoding_name));
+  }
   if (DOMImplementation::IsXMLMIMEType(mime_type)) {
     std::unique_ptr<TextResourceDecoder> decoder =
         TextResourceDecoder::Create("application/xml");
@@ -151,12 +153,14 @@
     return decoder;
   }
   if (DeprecatedEqualIgnoringCase(mime_type, "text/html"))
-    return TextResourceDecoder::Create("text/html", "UTF-8");
+    return TextResourceDecoder::Create("text/html", UTF8Encoding());
   if (MIMETypeRegistry::IsSupportedJavaScriptMIMEType(mime_type) ||
       DOMImplementation::IsJSONMIMEType(mime_type))
-    return TextResourceDecoder::Create("text/plain", "UTF-8");
-  if (DOMImplementation::IsTextMIMEType(mime_type))
-    return TextResourceDecoder::Create("text/plain", "ISO-8859-1");
+    return TextResourceDecoder::Create("text/plain", UTF8Encoding());
+  if (DOMImplementation::IsTextMIMEType(mime_type)) {
+    return TextResourceDecoder::Create("text/plain",
+                                       WTF::TextEncoding("ISO-8859-1"));
+  }
   return std::unique_ptr<TextResourceDecoder>();
 }
 
diff --git a/third_party/WebKit/Source/core/inspector/InspectorResourceContentLoader.cpp b/third_party/WebKit/Source/core/inspector/InspectorResourceContentLoader.cpp
index 4ab6f2cf..2842f43 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorResourceContentLoader.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorResourceContentLoader.cpp
@@ -55,7 +55,7 @@
   void SetCSSStyleSheet(const String&,
                         const KURL&,
                         ReferrerPolicy,
-                        const String&,
+                        const WTF::TextEncoding&,
                         const CSSStyleSheetResource*) override;
   void NotifyFinished(Resource*) override;
   String DebugName() const override {
@@ -81,7 +81,7 @@
     const String&,
     const KURL& url,
     ReferrerPolicy,
-    const String&,
+    const WTF::TextEncoding&,
     const CSSStyleSheetResource* resource) {
   ResourceFinished(const_cast<CSSStyleSheetResource*>(resource));
 }
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index 450a9a1..2c398966 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -4606,6 +4606,17 @@
                     { "name": "storageTypes", "type": "string", "description": "Comma separated origin names." }
                 ],
                 "description": "Clears storage for origin."
+            },
+            {
+                "name": "getUsageAndQuota",
+                "parameters": [
+                    { "name": "origin", "type": "string", "description": "Security origin." }
+                ],
+                "returns": [
+                    { "name": "usage", "type": "number", "description": "Storage usage (bytes)." },
+                    { "name": "quota", "type": "number", "description": "Storage quota (bytes)." }
+                ],
+                "description": "Returns usage and quota in bytes."
             }
         ]
     },
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
index a7769672..e00cfde 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
@@ -315,7 +315,7 @@
 
   // Shape each item with the full context of the entire node.
   HarfBuzzShaper shaper(text_content.Characters16(), text_content.length());
-  ShapeResultSpacing<StringView> spacing(text_content);
+  ShapeResultSpacing<String> spacing(text_content);
   for (auto& item : MutableData().items_) {
     if (item.Type() != NGInlineItem::kText)
       continue;
@@ -325,7 +325,7 @@
         &font, item.Direction(), item.StartOffset(), item.EndOffset());
 
     if (UNLIKELY(spacing.SetSpacing(font.GetFontDescription())))
-      shape_result->ApplySpacing(spacing, text_content, item.Direction());
+      shape_result->ApplySpacing(spacing, item.Direction());
 
     item.shape_result_ = std::move(shape_result);
   }
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc
index 3bb0c20..57e63b8c 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc
@@ -66,7 +66,8 @@
       item_index_(0),
       offset_(0),
       break_iterator_(node.Text()),
-      shaper_(node.Text().Characters16(), node.Text().length()) {
+      shaper_(node.Text().Characters16(), node.Text().length()),
+      spacing_(node.Text()) {
   if (break_token) {
     item_index_ = break_token->ItemIndex();
     offset_ = break_token->TextOffset();
@@ -93,7 +94,7 @@
   item_results->clear();
   const Vector<NGInlineItem>& items = node_.Items();
   line_info->SetLineStyle(node_, !item_index_ && !offset_);
-  UpdateBreakIterator(line_info->LineStyle());
+  SetCurrentStyle(line_info->LineStyle());
   position_ = LayoutUnit(0);
   LineBreakState state = LineBreakState::kNotBreakable;
 
@@ -257,7 +258,8 @@
   DCHECK_EQ(item.TextShapeResult()->StartIndexForResult(), item.StartOffset());
   DCHECK_EQ(item.TextShapeResult()->EndIndexForResult(), item.EndOffset());
   ShapingLineBreaker breaker(&shaper_, &item.Style()->GetFont(),
-                             item.TextShapeResult(), &break_iterator_);
+                             item.TextShapeResult(), &break_iterator_,
+                             &spacing_);
   available_width = std::max(LayoutUnit(0), available_width);
   item_result->shape_result = breaker.ShapeLine(
       item_result->start_offset, available_width, &item_result->end_offset);
@@ -429,7 +431,7 @@
       position_ += item_result->inline_size;
     }
   }
-  UpdateBreakIterator(style);
+  SetCurrentStyle(style);
   MoveToNextOf(item);
 }
 
@@ -447,7 +449,7 @@
     position_ += item_result->inline_size;
   }
   DCHECK(item.GetLayoutObject() && item.GetLayoutObject()->Parent());
-  UpdateBreakIterator(item.GetLayoutObject()->Parent()->StyleRef());
+  SetCurrentStyle(item.GetLayoutObject()->Parent()->StyleRef());
   MoveToNextOf(item);
 }
 
@@ -535,7 +537,7 @@
   line_info->SetIsLastLine(false);
 }
 
-void NGLineBreaker::UpdateBreakIterator(const ComputedStyle& style) {
+void NGLineBreaker::SetCurrentStyle(const ComputedStyle& style) {
   auto_wrap_ = style.AutoWrap();
 
   if (auto_wrap_) {
@@ -552,6 +554,8 @@
 
     // TODO(kojii): Implement word-wrap/overflow-wrap property
   }
+
+  spacing_.SetSpacing(style.GetFontDescription());
 }
 
 void NGLineBreaker::MoveToNextOf(const NGInlineItem& item) {
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.h
index 3db5221..16cb269 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.h
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.h
@@ -9,6 +9,7 @@
 #include "core/layout/ng/inline/ng_inline_item_result.h"
 #include "core/layout/ng/inline/ng_inline_node.h"
 #include "platform/fonts/shaping/HarfBuzzShaper.h"
+#include "platform/fonts/shaping/ShapeResultSpacing.h"
 #include "platform/heap/Handle.h"
 #include "platform/text/TextBreakIterator.h"
 #include "platform/wtf/Allocator.h"
@@ -78,7 +79,7 @@
   void HandleOverflow(LayoutUnit available_width, NGLineInfo*);
   void Rewind(NGLineInfo*, unsigned new_end);
 
-  void UpdateBreakIterator(const ComputedStyle&);
+  void SetCurrentStyle(const ComputedStyle&);
 
   void MoveToNextOf(const NGInlineItem&);
   void MoveToNextOf(const NGInlineItemResult&);
@@ -93,6 +94,7 @@
   LayoutUnit position_;
   LazyLineBreakIterator break_iterator_;
   HarfBuzzShaper shaper_;
+  ShapeResultSpacing<String> spacing_;
 
   unsigned auto_wrap_ : 1;
 };
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
index 95aa083..9e5d76a 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
@@ -426,4 +426,21 @@
   return length;
 }
 
+NGBoxStrut GetScrollbarSizes(const LayoutObject* layout_object) {
+  NGPhysicalBoxStrut sizes;
+  const ComputedStyle* style = layout_object->Style();
+  if (!style->IsOverflowVisible()) {
+    const LayoutBox* box = ToLayoutBox(layout_object);
+    LayoutUnit vertical = LayoutUnit(box->VerticalScrollbarWidth());
+    LayoutUnit horizontal = LayoutUnit(box->HorizontalScrollbarHeight());
+    sizes.bottom = horizontal;
+    if (style->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
+      sizes.left = vertical;
+    else
+      sizes.right = vertical;
+  }
+  return sizes.ConvertToLogical(
+      FromPlatformWritingMode(style->GetWritingMode()), style->Direction());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
index 1b456e5..597e563 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
@@ -14,6 +14,7 @@
 
 namespace blink {
 class ComputedStyle;
+class LayoutObject;
 class LayoutUnit;
 class Length;
 struct MinMaxContentSize;
@@ -124,6 +125,9 @@
                                          Optional<LayoutUnit> min,
                                          Optional<LayoutUnit> max);
 
+// Returns scrollbar sizes or this layout object.
+NGBoxStrut GetScrollbarSizes(const LayoutObject*);
+
 }  // namespace blink
 
 #endif  // NGLengthUtils_h
diff --git a/third_party/WebKit/Source/core/loader/TextResourceDecoderBuilder.cpp b/third_party/WebKit/Source/core/loader/TextResourceDecoderBuilder.cpp
index 9c5bc49..a71f775 100644
--- a/third_party/WebKit/Source/core/loader/TextResourceDecoderBuilder.cpp
+++ b/third_party/WebKit/Source/core/loader/TextResourceDecoderBuilder.cpp
@@ -104,7 +104,7 @@
       const WTF::TextEncoding hint_encoding =
           encoding_from_domain.IsValid()
               ? encoding_from_domain
-              : settings->GetDefaultTextEncodingName();
+              : WTF::TextEncoding(settings->GetDefaultTextEncodingName());
       // Disable autodetection for XML to honor the default encoding (UTF-8) for
       // unlabelled documents.
       if (DOMImplementation::IsXMLMIMEType(mime_type_))
@@ -126,7 +126,7 @@
     parent_frame = ToLocalFrame(frame->Tree().Parent());
 
   if (!encoding_.IsEmpty()) {
-    decoder->SetEncoding(encoding_.GetString(),
+    decoder->SetEncoding(WTF::TextEncoding(encoding_.GetString()),
                          TextResourceDecoder::kEncodingFromHTTPHeader);
   }
 
diff --git a/third_party/WebKit/Source/core/loader/resource/StyleSheetResourceClient.h b/third_party/WebKit/Source/core/loader/resource/StyleSheetResourceClient.h
index eae489f67..518f5df 100644
--- a/third_party/WebKit/Source/core/loader/resource/StyleSheetResourceClient.h
+++ b/third_party/WebKit/Source/core/loader/resource/StyleSheetResourceClient.h
@@ -46,7 +46,7 @@
   virtual void SetCSSStyleSheet(const String& /* href */,
                                 const KURL& /* baseURL */,
                                 ReferrerPolicy,
-                                const String& /* charset */,
+                                const WTF::TextEncoding&,
                                 const CSSStyleSheetResource*) {}
   virtual void SetXSLStyleSheet(const String& /* href */,
                                 const KURL& /* baseURL */,
diff --git a/third_party/WebKit/Source/core/loader/resource/TextResource.cpp b/third_party/WebKit/Source/core/loader/resource/TextResource.cpp
index 8eafa09d..04f9652 100644
--- a/third_party/WebKit/Source/core/loader/resource/TextResource.cpp
+++ b/third_party/WebKit/Source/core/loader/resource/TextResource.cpp
@@ -16,16 +16,18 @@
                            const String& mime_type,
                            const String& charset)
     : Resource(resource_request, type, options),
-      decoder_(TextResourceDecoder::Create(mime_type, charset)) {}
+      decoder_(
+          TextResourceDecoder::Create(mime_type, WTF::TextEncoding(charset))) {}
 
 TextResource::~TextResource() {}
 
 void TextResource::SetEncoding(const String& chs) {
-  decoder_->SetEncoding(chs, TextResourceDecoder::kEncodingFromHTTPHeader);
+  decoder_->SetEncoding(WTF::TextEncoding(chs),
+                        TextResourceDecoder::kEncodingFromHTTPHeader);
 }
 
-String TextResource::Encoding() const {
-  return decoder_->Encoding().GetName();
+WTF::TextEncoding TextResource::Encoding() const {
+  return decoder_->Encoding();
 }
 
 String TextResource::DecodedText() const {
diff --git a/third_party/WebKit/Source/core/loader/resource/TextResource.h b/third_party/WebKit/Source/core/loader/resource/TextResource.h
index 9d86945..dceb5c95c 100644
--- a/third_party/WebKit/Source/core/loader/resource/TextResource.h
+++ b/third_party/WebKit/Source/core/loader/resource/TextResource.h
@@ -19,8 +19,9 @@
   // call time.
   String DecodedText() const;
 
-  void SetEncoding(const String&) override;
-  String Encoding() const override;
+  WTF::TextEncoding Encoding() const override;
+
+  void SetEncodingForTest(const String& encoding) { SetEncoding(encoding); }
 
  protected:
   TextResource(const ResourceRequest&,
@@ -30,6 +31,8 @@
                const String& charset);
   ~TextResource() override;
 
+  void SetEncoding(const String&) override;
+
  private:
   std::unique_ptr<TextResourceDecoder> decoder_;
 };
diff --git a/third_party/WebKit/Source/core/mojo/BUILD.gn b/third_party/WebKit/Source/core/mojo/BUILD.gn
index aaa52ac..202d78e 100644
--- a/third_party/WebKit/Source/core/mojo/BUILD.gn
+++ b/third_party/WebKit/Source/core/mojo/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//mojo/public/tools/bindings/mojom.gni")
 import("//third_party/WebKit/Source/core/core.gni")
 
 blink_core_sources("mojo") {
@@ -24,39 +23,3 @@
     "//services/service_manager/public/cpp",
   ]
 }
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "tests/JsToCppTest.cpp",
-  ]
-
-  data = [
-    "tests/JsToCppTest.js",
-  ]
-
-  configs += [
-    "//third_party/WebKit/Source/core:blink_core_pch",
-    "//third_party/WebKit/Source:config",
-    "//third_party/WebKit/Source:inside_blink",
-  ]
-
-  deps = [
-    ":test_bindings_blink",
-    "//mojo/public/cpp/bindings",
-    "//testing/gtest",
-    "//third_party/WebKit/Source/core:core",
-    "//third_party/WebKit/Source/core:testing",
-  ]
-
-  data_deps = [
-    ":test_bindings",  # For JS bindings: crbug.com/729649.
-    "//mojo/public/js:new_bindings",
-  ]
-}
-
-mojom("test_bindings") {
-  sources = [
-    "tests/JsToCpp.mojom",
-  ]
-}
diff --git a/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.cpp b/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.cpp
deleted file mode 100644
index 08411ee3..0000000
--- a/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.cpp
+++ /dev/null
@@ -1,436 +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.
-
-#include "bindings/core/v8/ScriptController.h"
-#include "bindings/core/v8/ScriptSourceCode.h"
-#include "bindings/core/v8/V8BindingForCore.h"
-#include "bindings/core/v8/V8BindingForTesting.h"
-#include "bindings/core/v8/V8ScriptRunner.h"
-#include "core/frame/Settings.h"
-#include "core/mojo/MojoHandle.h"
-#include "core/page/Page.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/system/wait.h"
-#include "platform/testing/UnitTestHelpers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/Source/core/mojo/tests/JsToCpp.mojom-blink.h"
-
-namespace blink {
-namespace {
-
-// Global value updated by some checks to prevent compilers from optimizing
-// reads out of existence.
-uint32_t g_waste_accumulator = 0;
-
-// Negative numbers with different values in each byte, the last of
-// which can survive promotion to double and back.
-const int8_t kExpectedInt8Value = -65;
-const int16_t kExpectedInt16Value = -16961;
-const int32_t kExpectedInt32Value = -1145258561;
-const int64_t kExpectedInt64Value = -77263311946305LL;
-
-// Positive numbers with different values in each byte, the last of
-// which can survive promotion to double and back.
-const uint8_t kExpectedUInt8Value = 65;
-const uint16_t kExpectedUInt16Value = 16961;
-const uint32_t kExpectedUInt32Value = 1145258561;
-const uint64_t kExpectedUInt64Value = 77263311946305LL;
-
-// Double/float values, including special case constants.
-const double kExpectedDoubleVal = 3.14159265358979323846;
-const double kExpectedDoubleInf = std::numeric_limits<double>::infinity();
-const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN();
-const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal);
-const float kExpectedFloatInf = std::numeric_limits<float>::infinity();
-const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN();
-
-// NaN has the property that it is not equal to itself.
-#define EXPECT_NAN(x) EXPECT_NE(x, x)
-
-String MojoBindingsScriptPath() {
-  String filepath = testing::ExecutableDir();
-  filepath.append("/gen/mojo/public/js/mojo_bindings.js");
-  return filepath;
-}
-
-String TestBindingsScriptPath() {
-  String filepath = testing::ExecutableDir();
-  filepath.append(
-      "/gen/third_party/WebKit/Source/core/mojo/tests/JsToCpp.mojom.js");
-  return filepath;
-}
-
-String TestScriptPath() {
-  String filepath = testing::BlinkRootDir();
-  filepath.append("/Source/core/mojo/tests/JsToCppTest.js");
-  return filepath;
-}
-
-v8::Local<v8::Value> ExecuteScript(const String& script_path,
-                                   LocalFrame& frame) {
-  RefPtr<SharedBuffer> script_src = testing::ReadFromFile(script_path);
-  return frame.GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
-      ScriptSourceCode(String(script_src->Data(), script_src->size())));
-}
-
-void CheckDataPipe(mojo::DataPipeConsumerHandle data_pipe_handle) {
-  MojoResult result = Wait(data_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE);
-  EXPECT_EQ(MOJO_RESULT_OK, result);
-
-  const void* buffer = nullptr;
-  unsigned num_bytes = 0;
-  result = BeginReadDataRaw(data_pipe_handle, &buffer, &num_bytes,
-                            MOJO_READ_DATA_FLAG_NONE);
-  EXPECT_EQ(MOJO_RESULT_OK, result);
-  EXPECT_EQ(64u, num_bytes);
-  for (unsigned i = 0; i < num_bytes; ++i) {
-    EXPECT_EQ(i, static_cast<unsigned>(static_cast<const char*>(buffer)[i]));
-  }
-  EndReadDataRaw(data_pipe_handle, num_bytes);
-}
-
-void CheckMessagePipe(mojo::MessagePipeHandle message_pipe_handle) {
-  MojoResult result = Wait(message_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE);
-  EXPECT_EQ(MOJO_RESULT_OK, result);
-
-  std::vector<uint8_t> bytes;
-  std::vector<mojo::ScopedHandle> handles;
-  result = ReadMessageRaw(message_pipe_handle, &bytes, &handles, 0);
-  EXPECT_EQ(MOJO_RESULT_OK, result);
-  EXPECT_EQ(64u, bytes.size());
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_EQ(255 - i, bytes[i]);
-  }
-}
-
-js_to_cpp::blink::EchoArgsPtr BuildSampleEchoArgs() {
-  auto args = js_to_cpp::blink::EchoArgs::New();
-  args->si64 = kExpectedInt64Value;
-  args->si32 = kExpectedInt32Value;
-  args->si16 = kExpectedInt16Value;
-  args->si8 = kExpectedInt8Value;
-  args->ui64 = kExpectedUInt64Value;
-  args->ui32 = kExpectedUInt32Value;
-  args->ui16 = kExpectedUInt16Value;
-  args->ui8 = kExpectedUInt8Value;
-  args->float_val = kExpectedFloatVal;
-  args->float_inf = kExpectedFloatInf;
-  args->float_nan = kExpectedFloatNan;
-  args->double_val = kExpectedDoubleVal;
-  args->double_inf = kExpectedDoubleInf;
-  args->double_nan = kExpectedDoubleNan;
-  args->name = "coming";
-  args->string_array.emplace(3);
-  (*args->string_array)[0] = "one";
-  (*args->string_array)[1] = "two";
-  (*args->string_array)[2] = "three";
-  return args;
-}
-
-void CheckSampleEchoArgs(const js_to_cpp::blink::EchoArgsPtr& arg) {
-  EXPECT_EQ(kExpectedInt64Value, arg->si64);
-  EXPECT_EQ(kExpectedInt32Value, arg->si32);
-  EXPECT_EQ(kExpectedInt16Value, arg->si16);
-  EXPECT_EQ(kExpectedInt8Value, arg->si8);
-  EXPECT_EQ(kExpectedUInt64Value, arg->ui64);
-  EXPECT_EQ(kExpectedUInt32Value, arg->ui32);
-  EXPECT_EQ(kExpectedUInt16Value, arg->ui16);
-  EXPECT_EQ(kExpectedUInt8Value, arg->ui8);
-  EXPECT_EQ(kExpectedFloatVal, arg->float_val);
-  EXPECT_EQ(kExpectedFloatInf, arg->float_inf);
-  EXPECT_NAN(arg->float_nan);
-  EXPECT_EQ(kExpectedDoubleVal, arg->double_val);
-  EXPECT_EQ(kExpectedDoubleInf, arg->double_inf);
-  EXPECT_NAN(arg->double_nan);
-  EXPECT_EQ(String("coming"), arg->name);
-  EXPECT_EQ(String("one"), (*arg->string_array)[0]);
-  EXPECT_EQ(String("two"), (*arg->string_array)[1]);
-  EXPECT_EQ(String("three"), (*arg->string_array)[2]);
-  CheckDataPipe(arg->data_handle.get());
-  CheckMessagePipe(arg->message_handle.get());
-}
-
-void CheckSampleEchoArgsList(const js_to_cpp::blink::EchoArgsListPtr& list) {
-  if (list.is_null())
-    return;
-  CheckSampleEchoArgs(list->item);
-  CheckSampleEchoArgsList(list->next);
-}
-
-// More forgiving checks are needed in the face of potentially corrupt
-// messages. The values don't matter so long as all accesses are within
-// bounds.
-void CheckCorruptedString(const String& arg) {
-  for (size_t i = 0; i < arg.length(); ++i)
-    g_waste_accumulator += arg[i];
-}
-
-void CheckCorruptedStringArray(const Optional<Vector<String>>& string_array) {
-  if (!string_array)
-    return;
-  for (size_t i = 0; i < string_array->size(); ++i)
-    CheckCorruptedString((*string_array)[i]);
-}
-
-void CheckCorruptedDataPipe(mojo::DataPipeConsumerHandle data_pipe_handle) {
-  unsigned char buffer[100];
-  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
-  MojoResult result = ReadDataRaw(data_pipe_handle, buffer, &buffer_size,
-                                  MOJO_READ_DATA_FLAG_NONE);
-  if (result != MOJO_RESULT_OK)
-    return;
-  for (uint32_t i = 0; i < buffer_size; ++i)
-    g_waste_accumulator += buffer[i];
-}
-
-void CheckCorruptedMessagePipe(mojo::MessagePipeHandle message_pipe_handle) {
-  std::vector<uint8_t> bytes;
-  std::vector<mojo::ScopedHandle> handles;
-  MojoResult result = ReadMessageRaw(message_pipe_handle, &bytes, &handles, 0);
-  if (result != MOJO_RESULT_OK)
-    return;
-  for (uint32_t i = 0; i < bytes.size(); ++i)
-    g_waste_accumulator += bytes[i];
-}
-
-void CheckCorruptedEchoArgs(const js_to_cpp::blink::EchoArgsPtr& arg) {
-  if (arg.is_null())
-    return;
-  CheckCorruptedString(arg->name);
-  CheckCorruptedStringArray(arg->string_array);
-  if (arg->data_handle.is_valid())
-    CheckCorruptedDataPipe(arg->data_handle.get());
-  if (arg->message_handle.is_valid())
-    CheckCorruptedMessagePipe(arg->message_handle.get());
-}
-
-void CheckCorruptedEchoArgsList(const js_to_cpp::blink::EchoArgsListPtr& list) {
-  if (list.is_null())
-    return;
-  CheckCorruptedEchoArgs(list->item);
-  CheckCorruptedEchoArgsList(list->next);
-}
-
-// Base Provider implementation class. It's expected that tests subclass and
-// override the appropriate Provider functions. When test is done quit the
-// run_loop().
-class CppSideConnection : public js_to_cpp::blink::CppSide {
- public:
-  CppSideConnection() : mishandled_messages_(0), binding_(this) {}
-  ~CppSideConnection() override {}
-
-  void set_js_side(js_to_cpp::blink::JsSidePtr js_side) {
-    js_side_ = std::move(js_side);
-  }
-  js_to_cpp::blink::JsSide* js_side() { return js_side_.get(); }
-
-  void Bind(mojo::InterfaceRequest<js_to_cpp::blink::CppSide> request) {
-    binding_.Bind(std::move(request));
-    // Keep the pipe open even after validation errors.
-    binding_.EnableTestingMode();
-  }
-
-  // js_to_cpp::CppSide:
-  void StartTest() override { NOTREACHED(); }
-
-  void TestFinished() override { NOTREACHED(); }
-
-  void PingResponse() override { mishandled_messages_ += 1; }
-
-  void EchoResponse(js_to_cpp::blink::EchoArgsListPtr list) override {
-    mishandled_messages_ += 1;
-  }
-
-  void BitFlipResponse(
-      js_to_cpp::blink::EchoArgsListPtr list,
-      js_to_cpp::blink::ForTestingAssociatedPtrInfo not_used) override {
-    mishandled_messages_ += 1;
-  }
-
-  void BackPointerResponse(js_to_cpp::blink::EchoArgsListPtr list) override {
-    mishandled_messages_ += 1;
-  }
-
- protected:
-  js_to_cpp::blink::JsSidePtr js_side_;
-  int mishandled_messages_;
-  mojo::Binding<js_to_cpp::blink::CppSide> binding_;
-};
-
-// Trivial test to verify a message sent from JS is received.
-class PingCppSideConnection : public CppSideConnection {
- public:
-  PingCppSideConnection() : got_message_(false) {}
-  ~PingCppSideConnection() override {}
-
-  // js_to_cpp::CppSide:
-  void StartTest() override { js_side_->Ping(); }
-
-  void PingResponse() override {
-    got_message_ = true;
-    testing::ExitRunLoop();
-  }
-
-  bool DidSucceed() { return got_message_ && !mishandled_messages_; }
-
- private:
-  bool got_message_;
-};
-
-// Test that parameters are passed with correct values.
-class EchoCppSideConnection : public CppSideConnection {
- public:
-  EchoCppSideConnection() : message_count_(0), termination_seen_(false) {}
-  ~EchoCppSideConnection() override {}
-
-  // js_to_cpp::CppSide:
-  void StartTest() override {
-    js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
-  }
-
-  void EchoResponse(js_to_cpp::blink::EchoArgsListPtr list) override {
-    message_count_ += 1;
-
-    const js_to_cpp::blink::EchoArgsPtr& special_arg = list->item;
-    EXPECT_EQ(-1, special_arg->si64);
-    EXPECT_EQ(-1, special_arg->si32);
-    EXPECT_EQ(-1, special_arg->si16);
-    EXPECT_EQ(-1, special_arg->si8);
-    EXPECT_EQ(String("going"), special_arg->name);
-    CheckDataPipe(special_arg->data_handle.get());
-    CheckMessagePipe(special_arg->message_handle.get());
-
-    CheckSampleEchoArgsList(list->next);
-  }
-
-  void TestFinished() override {
-    termination_seen_ = true;
-    testing::ExitRunLoop();
-  }
-
-  bool DidSucceed() {
-    return termination_seen_ && !mishandled_messages_ &&
-           message_count_ == kExpectedMessageCount;
-  }
-
- private:
-  static const int kExpectedMessageCount = 10;
-  int message_count_;
-  bool termination_seen_;
-};
-
-// Test that corrupted messages don't wreak havoc.
-class BitFlipCppSideConnection : public CppSideConnection {
- public:
-  BitFlipCppSideConnection() : termination_seen_(false) {}
-  ~BitFlipCppSideConnection() override {}
-
-  // js_to_cpp::CppSide:
-  void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); }
-
-  void BitFlipResponse(
-      js_to_cpp::blink::EchoArgsListPtr list,
-      js_to_cpp::blink::ForTestingAssociatedPtrInfo not_used) override {
-    CheckCorruptedEchoArgsList(list);
-  }
-
-  void TestFinished() override {
-    termination_seen_ = true;
-    testing::ExitRunLoop();
-  }
-
-  bool DidSucceed() { return termination_seen_; }
-
- private:
-  bool termination_seen_;
-};
-
-// Test that severely random messages don't wreak havoc.
-class BackPointerCppSideConnection : public CppSideConnection {
- public:
-  BackPointerCppSideConnection() : termination_seen_(false) {}
-  ~BackPointerCppSideConnection() override {}
-
-  // js_to_cpp::CppSide:
-  void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); }
-
-  void BackPointerResponse(js_to_cpp::blink::EchoArgsListPtr list) override {
-    CheckCorruptedEchoArgsList(list);
-  }
-
-  void TestFinished() override {
-    termination_seen_ = true;
-    testing::ExitRunLoop();
-  }
-
-  bool DidSucceed() { return termination_seen_; }
-
- private:
-  bool termination_seen_;
-};
-
-class JsToCppTest : public ::testing::Test {
- public:
-  void RunTest(CppSideConnection* cpp_side) {
-    js_to_cpp::blink::CppSidePtr cpp_side_ptr;
-    cpp_side->Bind(MakeRequest(&cpp_side_ptr));
-
-    js_to_cpp::blink::JsSidePtr js_side_ptr;
-    auto js_side_request = MakeRequest(&js_side_ptr);
-    js_side_ptr->SetCppSide(std::move(cpp_side_ptr));
-    cpp_side->set_js_side(std::move(js_side_ptr));
-
-    V8TestingScope scope;
-    scope.GetPage().GetSettings().SetScriptEnabled(true);
-    ExecuteScript(MojoBindingsScriptPath(), scope.GetFrame());
-    ExecuteScript(TestBindingsScriptPath(), scope.GetFrame());
-
-    v8::Local<v8::Value> start_fn =
-        ExecuteScript(TestScriptPath(), scope.GetFrame());
-    ASSERT_FALSE(start_fn.IsEmpty());
-    ASSERT_TRUE(start_fn->IsFunction());
-    v8::Local<v8::Object> global_proxy = scope.GetContext()->Global();
-    v8::Local<v8::Value> args[1] = {
-        ToV8(MojoHandle::Create(
-                 mojo::ScopedHandle::From(js_side_request.PassMessagePipe())),
-             global_proxy, scope.GetIsolate())};
-    V8ScriptRunner::CallFunction(
-        start_fn.As<v8::Function>(), scope.GetExecutionContext(), global_proxy,
-        WTF_ARRAY_LENGTH(args), args, scope.GetIsolate());
-    testing::EnterRunLoop();
-  }
-};
-
-TEST_F(JsToCppTest, Ping) {
-  PingCppSideConnection cpp_side_connection;
-  RunTest(&cpp_side_connection);
-  EXPECT_TRUE(cpp_side_connection.DidSucceed());
-}
-
-TEST_F(JsToCppTest, Echo) {
-  EchoCppSideConnection cpp_side_connection;
-  RunTest(&cpp_side_connection);
-  EXPECT_TRUE(cpp_side_connection.DidSucceed());
-}
-
-TEST_F(JsToCppTest, BitFlip) {
-  // These tests generate a lot of expected validation errors. Suppress logging.
-  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
-
-  BitFlipCppSideConnection cpp_side_connection;
-  RunTest(&cpp_side_connection);
-  EXPECT_TRUE(cpp_side_connection.DidSucceed());
-}
-
-TEST_F(JsToCppTest, BackPointer) {
-  // These tests generate a lot of expected validation errors. Suppress logging.
-  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
-
-  BackPointerCppSideConnection cpp_side_connection;
-  RunTest(&cpp_side_connection);
-  EXPECT_TRUE(cpp_side_connection.DidSucceed());
-}
-
-}  // namespace
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/mojo/tests/OWNERS b/third_party/WebKit/Source/core/mojo/tests/OWNERS
deleted file mode 100644
index 08850f4..0000000
--- a/third_party/WebKit/Source/core/mojo/tests/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/WebKit/Source/core/paint/BoxPainterBase.cpp b/third_party/WebKit/Source/core/paint/BoxPainterBase.cpp
index 9b69143..b059b5a7 100644
--- a/third_party/WebKit/Source/core/paint/BoxPainterBase.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxPainterBase.cpp
@@ -26,10 +26,6 @@
     return;
   GraphicsContext& context = info.context;
 
-  // https://bugs.chromium.org/p/skia/issues/detail?id=237
-  if (context.Printing())
-    return;
-
   FloatRoundedRect border = style.GetRoundedBorderFor(
       paint_rect, include_logical_left_edge, include_logical_right_edge);
 
@@ -153,10 +149,6 @@
   DCHECK(style.BoxShadow());
   GraphicsContext& context = info.context;
 
-  // https://bugs.chromium.org/p/skia/issues/detail?id=237
-  if (context.Printing())
-    return;
-
   bool is_horizontal = style.IsHorizontalWritingMode();
   GraphicsContextStateSaver state_saver(context, false);
 
diff --git a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
index 3cfe7ca..91c57c6 100644
--- a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
@@ -4,7 +4,6 @@
 
 #include "core/paint/InlineTextBoxPainter.h"
 
-#include "core/editing/CompositionUnderline.h"
 #include "core/editing/Editor.h"
 #include "core/editing/markers/CompositionMarker.h"
 #include "core/editing/markers/DocumentMarkerController.h"
@@ -539,15 +538,14 @@
   return true;
 }
 
-unsigned InlineTextBoxPainter::UnderlinePaintStart(
-    const CompositionUnderline& underline) {
+unsigned InlineTextBoxPainter::MarkerPaintStart(const DocumentMarker& marker) {
   DCHECK(inline_text_box_.Truncation() != kCFullTruncation);
   DCHECK(inline_text_box_.Len());
 
   // Start painting at the beginning of the text or the specified underline
   // start offset, whichever is higher.
   unsigned paint_start =
-      std::max(inline_text_box_.Start(), underline.StartOffset());
+      std::max(inline_text_box_.Start(), marker.StartOffset());
   // Cap the maximum paint start to (if no truncation) the last character,
   // else the last character before the truncation ellipsis.
   return std::min(paint_start, (inline_text_box_.Truncation() == kCNoTruncation)
@@ -556,8 +554,7 @@
                                          inline_text_box_.Truncation() - 1);
 }
 
-unsigned InlineTextBoxPainter::UnderlinePaintEnd(
-    const CompositionUnderline& underline) {
+unsigned InlineTextBoxPainter::MarkerPaintEnd(const DocumentMarker& marker) {
   DCHECK(inline_text_box_.Truncation() != kCFullTruncation);
   DCHECK(inline_text_box_.Len());
 
@@ -565,7 +562,7 @@
   // offset, whichever is lower.
   unsigned paint_end = std::min(
       inline_text_box_.end() + 1,
-      underline.EndOffset());  // end() points at the last char, not past it.
+      marker.EndOffset());  // end() points at the last char, not past it.
   // Cap the maximum paint end to (if no truncation) one past the last
   // character, else one past the last character before the truncation
   // ellipsis.
@@ -575,7 +572,7 @@
                                        inline_text_box_.Truncation());
 }
 
-void InlineTextBoxPainter::PaintSingleCompositionBackgroundRun(
+void InlineTextBoxPainter::PaintSingleMarkerBackgroundRun(
     GraphicsContext& context,
     const LayoutPoint& box_origin,
     const ComputedStyle& style,
@@ -679,17 +676,16 @@
       case DocumentMarker::kComposition:
       case DocumentMarker::kActiveSuggestion: {
         const StyleableMarker& styleable_marker = ToStyleableMarker(marker);
-        CompositionUnderline underline(
-            styleable_marker.StartOffset(), styleable_marker.EndOffset(),
-            styleable_marker.UnderlineColor(), styleable_marker.IsThick(),
-            styleable_marker.BackgroundColor());
-        if (marker_paint_phase == DocumentMarkerPaintPhase::kBackground)
-          PaintSingleCompositionBackgroundRun(
-              paint_info.context, box_origin, style, font,
-              underline.BackgroundColor(), UnderlinePaintStart(underline),
-              UnderlinePaintEnd(underline));
-        else
-          PaintCompositionUnderline(paint_info.context, box_origin, underline);
+        if (marker_paint_phase == DocumentMarkerPaintPhase::kBackground) {
+          PaintSingleMarkerBackgroundRun(paint_info.context, box_origin, style,
+                                         font,
+                                         styleable_marker.BackgroundColor(),
+                                         MarkerPaintStart(styleable_marker),
+                                         MarkerPaintEnd(styleable_marker));
+        } else {
+          PaintStyleableMarkerUnderline(paint_info.context, box_origin,
+                                        styleable_marker);
+        }
       } break;
       default:
         NOTREACHED();
@@ -1034,18 +1030,18 @@
   rect.Expand(outsets);
 }
 
-void InlineTextBoxPainter::PaintCompositionUnderline(
+void InlineTextBoxPainter::PaintStyleableMarkerUnderline(
     GraphicsContext& context,
     const LayoutPoint& box_origin,
-    const CompositionUnderline& underline) {
-  if (underline.GetColor() == Color::kTransparent)
+    const StyleableMarker& marker) {
+  if (marker.UnderlineColor() == Color::kTransparent)
     return;
 
   if (inline_text_box_.Truncation() == kCFullTruncation)
     return;
 
-  unsigned paint_start = UnderlinePaintStart(underline);
-  unsigned paint_end = UnderlinePaintEnd(underline);
+  unsigned paint_start = MarkerPaintStart(marker);
+  unsigned paint_end = MarkerPaintEnd(marker);
   DCHECK_LT(paint_start, paint_end);
 
   // start of line to draw
@@ -1098,7 +1094,7 @@
           .PrimaryFont();
   DCHECK(font_data);
   int baseline = font_data ? font_data->GetFontMetrics().Ascent() : 0;
-  if (underline.Thick() && inline_text_box_.LogicalHeight() - baseline >= 2)
+  if (marker.IsThick() && inline_text_box_.LogicalHeight() - baseline >= 2)
     line_thickness = 2;
 
   // We need to have some space between underlines of subsequent clauses,
@@ -1108,7 +1104,7 @@
   start += 1;
   width -= 2;
 
-  context.SetStrokeColor(underline.GetColor());
+  context.SetStrokeColor(marker.UnderlineColor());
   context.SetStrokeThickness(line_thickness);
   context.DrawLineForText(
       FloatPoint(
diff --git a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.h b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.h
index 9b80871..814f35f4 100644
--- a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.h
+++ b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.h
@@ -14,7 +14,6 @@
 struct PaintInfo;
 
 class Color;
-class CompositionUnderline;
 class ComputedStyle;
 class DocumentMarker;
 class Font;
@@ -23,6 +22,7 @@
 class LayoutObject;
 class LayoutPoint;
 class LayoutTextCombine;
+class StyleableMarker;
 class TextMatchMarker;
 
 enum class DocumentMarkerPaintPhase { kForeground, kBackground };
@@ -62,13 +62,13 @@
  private:
   enum class PaintOptions { kNormal, kCombinedText };
 
-  void PaintSingleCompositionBackgroundRun(GraphicsContext&,
-                                           const LayoutPoint& box_origin,
-                                           const ComputedStyle&,
-                                           const Font&,
-                                           Color background_color,
-                                           int start_pos,
-                                           int end_pos);
+  void PaintSingleMarkerBackgroundRun(GraphicsContext&,
+                                      const LayoutPoint& box_origin,
+                                      const ComputedStyle&,
+                                      const Font&,
+                                      Color background_color,
+                                      int start_pos,
+                                      int end_pos);
   template <PaintOptions>
   void PaintSelection(GraphicsContext&,
                       const LayoutRect& box_rect,
@@ -77,11 +77,11 @@
                       Color text_color,
                       LayoutTextCombine* = nullptr);
 
-  void PaintCompositionUnderline(GraphicsContext&,
-                                 const LayoutPoint& box_origin,
-                                 const CompositionUnderline&);
-  unsigned UnderlinePaintStart(const CompositionUnderline&);
-  unsigned UnderlinePaintEnd(const CompositionUnderline&);
+  void PaintStyleableMarkerUnderline(GraphicsContext&,
+                                     const LayoutPoint& box_origin,
+                                     const StyleableMarker&);
+  unsigned MarkerPaintStart(const DocumentMarker&);
+  unsigned MarkerPaintEnd(const DocumentMarker&);
   bool ShouldPaintTextBox(const PaintInfo&);
   void ExpandToIncludeNewlineForSelection(LayoutRect&);
   LayoutObject& InlineLayoutObject() const;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index 34436a65..321a433 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -1317,6 +1317,8 @@
 }
 
 PaintLayer* PaintLayer::RemoveChild(PaintLayer* old_child) {
+  old_child->MarkCompositingContainerChainForNeedsRepaint();
+
   if (old_child->PreviousSibling())
     old_child->PreviousSibling()->SetNextSibling(old_child->NextSibling());
   if (old_child->NextSibling())
@@ -1361,8 +1363,6 @@
   if (old_child->EnclosingPaginationLayer())
     old_child->ClearPaginationRecursive();
 
-  SetNeedsRepaint();
-
   return old_child;
 }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
index b34af85..c9be82e 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
@@ -1090,4 +1090,33 @@
   GetDocument().View()->UpdateAllLifecyclePhases();
 }
 
+TEST_P(PaintLayerTest, NeedsRepaintOnRemovingStackedLayer) {
+  EnableCompositing();
+  SetBodyInnerHTML(
+      "<style>body {margin-top: 200px; backface-visibility: hidden}</style>"
+      "<div id='target' style='position: absolute; top: 0'>Text</div>");
+
+  auto* body = GetDocument().body();
+  auto* body_layer = body->GetLayoutBox()->Layer();
+  auto* target_element = GetDocument().getElementById("target");
+  auto* target_object = target_element->GetLayoutObject();
+  auto* target_layer = ToLayoutBoxModelObject(target_object)->Layer();
+
+  // |container| is not the CompositingContainer of |target| because |target|
+  // is stacked but |container| is not a stacking context.
+  EXPECT_TRUE(target_layer->StackingNode()->IsStacked());
+  EXPECT_NE(body_layer, target_layer->CompositingContainer());
+  auto* old_compositing_container = target_layer->CompositingContainer();
+
+  body->setAttribute(HTMLNames::styleAttr, "margin-top: 0");
+  target_element->setAttribute(HTMLNames::styleAttr, "top: 0");
+  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
+
+  EXPECT_FALSE(target_object->HasLayer());
+  EXPECT_TRUE(body_layer->NeedsRepaint());
+  EXPECT_TRUE(old_compositing_container->NeedsRepaint());
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index 82bac06..e45f1b5 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -1512,24 +1512,6 @@
   }
   void SetTextDecoration(TextDecoration v) { SetTextDecorationInternal(v); }
 
-  // text-decoration-color
-  void SetTextDecorationColor(const StyleColor& c) {
-    SET_VAR(rare_non_inherited_data_, text_decoration_color_, c);
-  }
-
-  // text-decoration-style
-  static ETextDecorationStyle InitialTextDecorationStyle() {
-    return ETextDecorationStyle::kSolid;
-  }
-  ETextDecorationStyle TextDecorationStyle() const {
-    return static_cast<ETextDecorationStyle>(
-        rare_non_inherited_data_->text_decoration_style_);
-  }
-  void SetTextDecorationStyle(ETextDecorationStyle v) {
-    SET_VAR(rare_non_inherited_data_, text_decoration_style_,
-            static_cast<unsigned>(v));
-  }
-
   // text-decoration-skip
   static TextDecorationSkip InitialTextDecorationSkip() {
     return TextDecorationSkip::kObjects;
@@ -1541,16 +1523,6 @@
     SetTextDecorationSkipInternal(v);
   }
 
-  // text-overflow
-  static ETextOverflow InitialTextOverflow() { return ETextOverflow::kClip; }
-  ETextOverflow TextOverflow() const {
-    return static_cast<ETextOverflow>(rare_non_inherited_data_->text_overflow_);
-  }
-  void SetTextOverflow(ETextOverflow overflow) {
-    SET_VAR(rare_non_inherited_data_, text_overflow_,
-            static_cast<unsigned>(overflow));
-  }
-
   // touch-action
   static TouchAction InitialTouchAction() {
     return TouchAction::kTouchActionAuto;
@@ -3455,9 +3427,6 @@
     return rare_non_inherited_data_->multi_col_data_
         ->visited_link_column_rule_color_;
   }
-  StyleColor TextDecorationColor() const {
-    return rare_non_inherited_data_->text_decoration_color_;
-  }
   StyleColor VisitedLinkTextDecorationColor() const {
     return rare_non_inherited_data_->visited_link_text_decoration_color_;
   }
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
index c3447f4..f90fa53f 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
@@ -193,8 +193,6 @@
                                          static_cast<unsigned>(b));
 }
 
-enum class ETextDecorationStyle { kSolid, kDouble, kDotted, kDashed, kWavy };
-
 static const size_t kTextDecorationSkipBits = 3;
 enum class TextDecorationSkip { kNone = 0x0, kObjects = 0x1, kInk = 0x2 };
 inline TextDecorationSkip operator&(TextDecorationSkip a,
@@ -232,8 +230,6 @@
   kCustom
 };
 
-enum class ETextOverflow : unsigned { kClip, kEllipsis };
-
 static const size_t kGridAutoFlowBits = 4;
 enum InternalGridAutoFlowAlgorithm {
   kInternalAutoFlowAlgorithmSparse = 0x1,
diff --git a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
index ad26ca3..d6bee49e 100644
--- a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
@@ -179,11 +179,12 @@
     return;
 
   if (!decoder_) {
-    if (!response_encoding_.IsEmpty())
-      decoder_ =
-          TextResourceDecoder::Create("text/javascript", response_encoding_);
-    else
-      decoder_ = TextResourceDecoder::Create("text/javascript", "UTF-8");
+    if (!response_encoding_.IsEmpty()) {
+      decoder_ = TextResourceDecoder::Create(
+          "text/javascript", WTF::TextEncoding(response_encoding_));
+    } else {
+      decoder_ = TextResourceDecoder::Create("text/javascript", UTF8Encoding());
+    }
   }
 
   if (!len)
diff --git a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
index 624a05b..ad9014f1 100644
--- a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
+++ b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
@@ -1710,10 +1710,12 @@
 
 std::unique_ptr<TextResourceDecoder> XMLHttpRequest::CreateDecoder() const {
   if (response_type_code_ == kResponseTypeJSON)
-    return TextResourceDecoder::Create("application/json", "UTF-8");
+    return TextResourceDecoder::Create("application/json", UTF8Encoding());
 
-  if (!final_response_charset_.IsEmpty())
-    return TextResourceDecoder::Create("text/plain", final_response_charset_);
+  if (!final_response_charset_.IsEmpty()) {
+    return TextResourceDecoder::Create(
+        "text/plain", WTF::TextEncoding(final_response_charset_));
+  }
 
   // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
   if (ResponseIsXML()) {
@@ -1728,9 +1730,9 @@
   }
 
   if (ResponseIsHTML())
-    return TextResourceDecoder::Create("text/html", "UTF-8");
+    return TextResourceDecoder::Create("text/html", UTF8Encoding());
 
-  return TextResourceDecoder::Create("text/plain", "UTF-8");
+  return TextResourceDecoder::Create("text/plain", UTF8Encoding());
 }
 
 void XMLHttpRequest::DidReceiveData(const char* data, unsigned len) {
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index 16e167a20..ed73390 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -371,6 +371,7 @@
   "front_end/object_ui/ObjectPropertiesSection.js",
   "front_end/object_ui/objectValue.css",
   "front_end/object_ui/RemoteObjectPreviewFormatter.js",
+  "front_end/perf_ui/chartViewport.css",
   "front_end/perf_ui/ChartViewport.js",
   "front_end/perf_ui/filmStripDialog.css",
   "front_end/perf_ui/filmStripView.css",
diff --git a/third_party/WebKit/Source/devtools/front_end/help/Help.js b/third_party/WebKit/Source/devtools/front_end/help/Help.js
index fa39639..67316d5 100644
--- a/third_party/WebKit/Source/devtools/front_end/help/Help.js
+++ b/third_party/WebKit/Source/devtools/front_end/help/Help.js
@@ -33,6 +33,10 @@
  * @param {number} latestVersion
  */
 Help._showReleaseNoteIfNeeded = function(lastSeenVersion, latestVersion) {
+  if (!lastSeenVersion) {
+    Help.releaseNoteVersionSetting().set(latestVersion);
+    return;
+  }
   if (lastSeenVersion >= latestVersion)
     return;
   Help.releaseNoteVersionSetting().set(latestVersion);
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/ChartViewport.js b/third_party/WebKit/Source/devtools/front_end/perf_ui/ChartViewport.js
index 22f3335..21633d5 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/ChartViewport.js
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/ChartViewport.js
@@ -3,15 +3,48 @@
 // found in the LICENSE file.
 
 /**
+ * @interface
+ */
+PerfUI.ChartViewportDelegate = function() {};
+
+PerfUI.ChartViewportDelegate.prototype = {
+  /**
+   * @param {number} startTime
+   * @param {number} endTime
+   */
+  requestWindowTimes(startTime, endTime) {},
+
+  /**
+   * @param {number} startTime
+   * @param {number} endTime
+   */
+  updateRangeSelection(startTime, endTime) {},
+
+  /**
+   * @param {number} width
+   * @param {number} height
+   */
+  setSize(width, height) {},
+
+  update() {}
+};
+
+/**
  * @unrestricted
  */
 PerfUI.ChartViewport = class extends UI.VBox {
-  constructor() {
-    super(true);
+  /**
+   * @param {!PerfUI.ChartViewportDelegate} delegate
+   */
+  constructor(delegate) {
+    super();
+    this.registerRequiredCSS('perf_ui/chartViewport.css');
+
+    this._delegate = delegate;
 
     this.viewportElement = this.contentElement.createChild('div', 'fill');
     this.viewportElement.addEventListener('mousemove', this._updateCursorPosition.bind(this), false);
-    this.viewportElement.addEventListener('mouseout', this._showCursor.bind(this, false), false);
+    this.viewportElement.addEventListener('mouseout', this._onMouseOut.bind(this), false);
     this.viewportElement.addEventListener('mousewheel', this._onMouseWheel.bind(this), false);
     this.viewportElement.addEventListener('keydown', this._onChartKeyDown.bind(this), false);
     this.viewportElement.addEventListener('keyup', this._onChartKeyUp.bind(this), false);
@@ -24,11 +57,11 @@
         this._endRangeSelection.bind(this), 'text', null);
 
     this._alwaysShowVerticalScroll = false;
-    this._vScrollElement = this.contentElement.createChild('div', 'flame-chart-v-scroll');
+    this._vScrollElement = this.contentElement.createChild('div', 'chart-viewport-v-scroll');
     this._vScrollContent = this._vScrollElement.createChild('div');
     this._vScrollElement.addEventListener('scroll', this._onScroll.bind(this), false);
 
-    this._selectionOverlay = this.contentElement.createChild('div', 'flame-chart-selection-overlay hidden');
+    this._selectionOverlay = this.contentElement.createChild('div', 'chart-viewport-selection-overlay hidden');
     this._selectedTimeSpanLabel = this._selectionOverlay.createChild('div', 'time-span');
 
     this._cursorElement = this.contentElement.createChild('div', 'chart-cursor-element hidden');
@@ -56,9 +89,6 @@
     return [this._vScrollElement];
   }
 
-  /**
-   * @private
-   */
   _updateScrollBar() {
     const showScroll = this._alwaysShowVerticalScroll || this._totalHeight > this._offsetHeight;
     if (this._vScrollElement.classList.contains('hidden') !== showScroll)
@@ -95,17 +125,18 @@
     this._updateContentElementSize();
   }
 
-  /**
-   * @private
-   */
   _updateContentElementSize() {
     var offsetWidth = this._vScrollElement.offsetLeft;
     if (!offsetWidth)
       offsetWidth = this.contentElement.offsetWidth;
     this._offsetWidth = offsetWidth;
     this._offsetHeight = this.contentElement.offsetHeight;
+    this._delegate.setSize(this._offsetWidth, this._offsetHeight);
   }
 
+  /**
+   * @param {number} totalHeight
+   */
   setContentHeight(totalHeight) {
     this._totalHeight = totalHeight;
     this._vScrollContent.style.height = totalHeight + 'px';
@@ -131,26 +162,31 @@
   /**
    * @return {number}
    */
-  getScrollOffset() {
+  scrollOffset() {
     return this._vScrollElement.scrollTop;
   }
 
   /**
+   * @param {number} zeroTime
+   * @param {number} totalTime
+   */
+  setBoundaries(zeroTime, totalTime) {
+    this._minimumBoundary = zeroTime;
+    this._totalTime = totalTime;
+  }
+
+  /**
    * @param {!Event} e
-   * @private
    */
   _onMouseWheel(e) {
-    if (!this._enabled())
-      return;
     var doZoomInstead = e.shiftKey ^ (Common.moduleSetting('flamechartMouseWheelAction').get() === 'zoom');
     var panVertically = !doZoomInstead && (e.wheelDeltaY || Math.abs(e.wheelDeltaX) === 120);
     var panHorizontally = doZoomInstead && Math.abs(e.wheelDeltaX) > Math.abs(e.wheelDeltaY);
     if (panVertically) {
       this._vScrollElement.scrollTop -= (e.wheelDeltaY || e.wheelDeltaX) / 120 * this._offsetHeight / 8;
     } else if (panHorizontally) {
-      var shift = -e.wheelDeltaX * this._pixelToTime;
       this._muteAnimation = true;
-      this._handlePanGesture(shift);
+      this._handlePanGesture(-e.wheelDeltaX);
       this._muteAnimation = false;
     } else {  // Zoom.
       const mouseWheelZoomSpeed = 1 / 120;
@@ -168,15 +204,11 @@
   _startDragging(event) {
     if (event.shiftKey)
       return false;
-    if (this._windowRight === Infinity)
-      return false;
     this._isDragging = true;
-    this._initMaxDragOffset(event);
     this._dragStartPointX = event.pageX;
     this._dragStartPointY = event.pageY;
     this._dragStartScrollTop = this._vScrollElement.scrollTop;
     this.viewportElement.style.cursor = '';
-    this.hideHighlight();
     return true;
   }
 
@@ -187,60 +219,24 @@
     var pixelShift = this._dragStartPointX - event.pageX;
     this._dragStartPointX = event.pageX;
     this._muteAnimation = true;
-    this._handlePanGesture(pixelShift * this._pixelToTime);
+    this._handlePanGesture(pixelShift);
     this._muteAnimation = false;
     var pixelScroll = this._dragStartPointY - event.pageY;
     this._vScrollElement.scrollTop = this._dragStartScrollTop + pixelScroll;
-    this._updateMaxDragOffset(event.pageX, event.pageY);
   }
 
-  /**
-   * @private
-   */
   _endDragging() {
     this._isDragging = false;
-    this._updateHighlight();
   }
 
   /**
    * @param {!MouseEvent} event
-   * @private
-   */
-  _initMaxDragOffset(event) {
-    this._maxDragOffsetSquared = 0;
-    this._dragStartX = event.pageX;
-    this._dragStartY = event.pageY;
-  }
-
-  /**
-   * @param {number} x
-   * @param {number} y
-   * @private
-   */
-  _updateMaxDragOffset(x, y) {
-    var dx = x - this._dragStartX;
-    var dy = y - this._dragStartY;
-    var dragOffsetSquared = dx * dx + dy * dy;
-    this._maxDragOffsetSquared = Math.max(this._maxDragOffsetSquared, dragOffsetSquared);
-  }
-
-  /**
-   * @return {number}
-   */
-  maxDragOffset() {
-    return Math.sqrt(this._maxDragOffsetSquared);
-  }
-
-  /**
-   * @param {!MouseEvent} event
-   * @private
    * @return {boolean}
    */
   _startRangeSelection(event) {
     if (!event.shiftKey)
       return false;
     this._isDragging = true;
-    this._initMaxDragOffset(event);
     this._selectionOffsetShiftX = event.offsetX - event.pageX;
     this._selectionOffsetShiftY = event.offsetY - event.pageY;
     this._selectionStartX = event.offsetX;
@@ -249,16 +245,11 @@
     style.width = '1px';
     this._selectedTimeSpanLabel.textContent = '';
     this._selectionOverlay.classList.remove('hidden');
-    this.hideHighlight();
     return true;
   }
 
-  /**
-   * @private
-   */
   _endRangeSelection() {
     this._isDragging = false;
-    this._updateHighlight();
   }
 
   hideRangeSelection() {
@@ -267,26 +258,21 @@
 
   /**
    * @param {!MouseEvent} event
-   * @private
    */
   _rangeSelectionDragging(event) {
-    this._updateMaxDragOffset(event.pageX, event.pageY);
     var x = Number.constrain(event.pageX + this._selectionOffsetShiftX, 0, this._offsetWidth);
-    var start = this._cursorTime(this._selectionStartX);
-    var end = this._cursorTime(x);
+    var start = this.pixelToTime(this._selectionStartX);
+    var end = this.pixelToTime(x);
     this._rangeSelectionStart = Math.min(start, end);
     this._rangeSelectionEnd = Math.max(start, end);
     this._updateRangeSelectionOverlay();
-    this._flameChartDelegate.updateRangeSelection(this._rangeSelectionStart, this._rangeSelectionEnd);
+    this._delegate.updateRangeSelection(this._rangeSelectionStart, this._rangeSelectionEnd);
   }
 
-  /**
-   * @private
-   */
   _updateRangeSelectionOverlay() {
     var /** @const */ margin = 100;
-    var left = Number.constrain(this._timeToPosition(this._rangeSelectionStart), -margin, this._offsetWidth + margin);
-    var right = Number.constrain(this._timeToPosition(this._rangeSelectionEnd), -margin, this._offsetWidth + margin);
+    var left = Number.constrain(this.timeToPosition(this._rangeSelectionStart), -margin, this._offsetWidth + margin);
+    var right = Number.constrain(this.timeToPosition(this._rangeSelectionEnd), -margin, this._offsetWidth + margin);
     var style = this._selectionOverlay.style;
     style.left = left + 'px';
     style.width = (right - left) + 'px';
@@ -294,20 +280,48 @@
     this._selectedTimeSpanLabel.textContent = Number.preciseMillisToString(timeSpan, 2);
   }
 
-  /**
-   * @private
-   */
   _onScroll() {
     this._scrollTop = this._vScrollElement.scrollTop;
     this.scheduleUpdate();
   }
 
+  _onMouseOut() {
+    this._lastMouseOffsetX = -1;
+    this._showCursor(false);
+  }
+
   /**
    * @param {!Event} e
    */
   _updateCursorPosition(e) {
     this._showCursor(e.shiftKey);
     this._cursorElement.style.left = e.offsetX + 'px';
+    this._lastMouseOffsetX = e.offsetX;
+  }
+
+  /**
+   * @param {number} x
+   * @return {number}
+   */
+  pixelToTime(x) {
+    return this.pixelToTimeOffset(x) + this._timeWindowLeft;
+  }
+
+  /**
+   * @param {number} x
+   * @return {number}
+   */
+  pixelToTimeOffset(x) {
+    return x * (this._timeWindowRight - this._timeWindowLeft) / this._offsetWidth;
+  }
+
+  /**
+   * @param {number} time
+   * @return {number}
+   */
+  timeToPosition(time) {
+    return Math.floor(
+        (time - this._timeWindowLeft) / (this._timeWindowRight - this._timeWindowLeft) * this._offsetWidth);
   }
 
   /**
@@ -334,102 +348,74 @@
 
   /**
    * @param {!Event} e
-   * @private
    */
   _handleZoomPanKeys(e) {
     if (!UI.KeyboardShortcut.hasNoModifiers(e))
       return;
-    var zoomMultiplier = e.shiftKey ? 0.8 : 0.3;
-    var panMultiplier = e.shiftKey ? 320 : 80;
-    if (e.code === 'KeyA') {
-      this._handlePanGesture(-panMultiplier * this._pixelToTime);
-      e.consume(true);
-    } else if (e.code === 'KeyD') {
-      this._handlePanGesture(panMultiplier * this._pixelToTime);
-      e.consume(true);
-    } else if (e.code === 'KeyW') {
-      this._handleZoomGesture(-zoomMultiplier);
-      e.consume(true);
-    } else if (e.code === 'KeyS') {
-      this._handleZoomGesture(zoomMultiplier);
-      e.consume(true);
+    var zoomFactor = e.shiftKey ? 0.8 : 0.3;
+    var panOffset = e.shiftKey ? 320 : 80;
+    switch (e.code) {
+      case 'KeyA':
+        this._handlePanGesture(-panOffset);
+        break;
+      case 'KeyD':
+        this._handlePanGesture(panOffset);
+        break;
+      case 'KeyW':
+        this._handleZoomGesture(-zoomFactor);
+        break;
+      case 'KeyS':
+        this._handleZoomGesture(zoomFactor);
+        break;
+      default:
+        return;
     }
+    e.consume(true);
   }
 
   /**
    * @param {number} zoom
-   * @private
    */
   _handleZoomGesture(zoom) {
     this._cancelAnimation();
-    var bounds = this._windowForGesture();
-    var cursorTime = this._cursorTime(this._lastMouseOffsetX);
+    var bounds = {left: this._timeWindowLeft, right: this._timeWindowRight};
+    var cursorTime = this.pixelToTime(this._lastMouseOffsetX);
     bounds.left += (bounds.left - cursorTime) * zoom;
     bounds.right += (bounds.right - cursorTime) * zoom;
     this._requestWindowTimes(bounds);
   }
 
   /**
-   * @param {number} shift
-   * @private
+   * @param {number} offset
    */
-  _handlePanGesture(shift) {
+  _handlePanGesture(offset) {
     this._cancelAnimation();
-    var bounds = this._windowForGesture();
-    shift = Number.constrain(
-        shift, this._minimumBoundary - bounds.left, this._totalTime + this._minimumBoundary - bounds.right);
-    bounds.left += shift;
-    bounds.right += shift;
+    var bounds = {left: this._timeWindowLeft, right: this._timeWindowRight};
+    var timeOffset = Number.constrain(
+        this.pixelToTimeOffset(offset), this._minimumBoundary - bounds.left,
+        this._totalTime + this._minimumBoundary - bounds.right);
+    bounds.left += timeOffset;
+    bounds.right += timeOffset;
     this._requestWindowTimes(bounds);
   }
 
   /**
-   * @private
-   * @return {{left: number, right: number}}
-   */
-  _windowForGesture() {
-    var windowLeft = this._timeWindowLeft ? this._timeWindowLeft : this._dataProvider.minimumBoundary();
-    var windowRight = this._timeWindowRight !== Infinity ?
-        this._timeWindowRight :
-        this._dataProvider.minimumBoundary() + this._dataProvider.totalTime();
-    return {left: windowLeft, right: windowRight};
-  }
-
-  /**
-   * @param {{left: number, right: number}} bounds
-   * @private
+   * @param {!{left: number, right: number}} bounds
    */
   _requestWindowTimes(bounds) {
-    bounds.left = Number.constrain(bounds.left, this._minimumBoundary, this._totalTime + this._minimumBoundary);
-    bounds.right = Number.constrain(bounds.right, this._minimumBoundary, this._totalTime + this._minimumBoundary);
+    var maxBound = this._minimumBoundary + this._totalTime;
+    if (bounds.left < this._minimumBoundary) {
+      bounds.right = Math.min(bounds.right + this._minimumBoundary - bounds.left, maxBound);
+      bounds.left = this._minimumBoundary;
+    } else if (bounds.right > maxBound) {
+      bounds.left = Math.max(bounds.left - bounds.right + maxBound, this._minimumBoundary);
+      bounds.right = maxBound;
+    }
     if (bounds.right - bounds.left < PerfUI.FlameChart.MinimalTimeWindowMs)
       return;
-    this._flameChartDelegate.requestWindowTimes(bounds.left, bounds.right);
+    this._delegate.requestWindowTimes(bounds.left, bounds.right);
   }
 
-  /**
-   * @param {number} startTime
-   * @param {number} endTime
-   * @private
-   */
-  _animateWindowTimes(startTime, endTime) {
-    this._timeWindowLeft = startTime;
-    this._timeWindowRight = endTime;
-    this._updateHighlight();
-    this.update();
-  }
-
-  /**
-   * @private
-   */
-  _animationCompleted() {
-    delete this._cancelWindowTimesAnimation;
-    this._updateHighlight();
-  }
-
-  /**
-   * @private
-   */
   _cancelAnimation() {
     if (!this._cancelWindowTimesAnimation)
       return;
@@ -444,11 +430,12 @@
       return;
     this._updateTimerId = this.element.window().requestAnimationFrame(() => {
       this._updateTimerId = 0;
-      this.update();
+      this._update();
     });
   }
 
-  update() {
+  _update() {
+    this._delegate.update();
   }
 
   /**
@@ -467,10 +454,21 @@
     }
     this._cancelAnimation();
     this._cancelWindowTimesAnimation = UI.animateFunction(
-        this.element.window(), this._animateWindowTimes.bind(this),
+        this.element.window(), animateWindowTimes.bind(this),
         [{from: this._timeWindowLeft, to: startTime}, {from: this._timeWindowRight, to: endTime}], 5,
-        this._animationCompleted.bind(this));
+        () => delete this._cancelWindowTimesAnimation);
     this._pendingAnimationTimeLeft = startTime;
     this._pendingAnimationTimeRight = endTime;
+
+    /**
+     * @param {number} startTime
+     * @param {number} endTime
+     * @this {PerfUI.ChartViewport}
+     */
+    function animateWindowTimes(startTime, endTime) {
+      this._timeWindowLeft = startTime;
+      this._timeWindowRight = endTime;
+      this._update();
+    }
   }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js b/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
index 21a236d..d83dd14 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
@@ -49,25 +49,31 @@
 
 /**
  * @unrestricted
+ * @implements {PerfUI.ChartViewportDelegate}
  */
-PerfUI.FlameChart = class extends PerfUI.ChartViewport {
+PerfUI.FlameChart = class extends UI.VBox {
   /**
    * @param {!PerfUI.FlameChartDataProvider} dataProvider
    * @param {!PerfUI.FlameChartDelegate} flameChartDelegate
    * @param {!Common.Setting=} groupExpansionSetting
    */
   constructor(dataProvider, flameChartDelegate, groupExpansionSetting) {
-    super();
+    super(true);
     this.registerRequiredCSS('perf_ui/flameChart.css');
     this.contentElement.classList.add('flame-chart-main-pane');
-    this._flameChartDelegate = flameChartDelegate;
     this._groupExpansionSetting = groupExpansionSetting;
     this._groupExpansionState = groupExpansionSetting && groupExpansionSetting.get() || {};
+    this._flameChartDelegate = flameChartDelegate;
+
+    this._chartViewport = new PerfUI.ChartViewport(this);
+    this._chartViewport.show(this.contentElement);
 
     this._dataProvider = dataProvider;
     this._calculator = new PerfUI.FlameChart.Calculator(dataProvider);
 
-    this._canvas = /** @type {!HTMLCanvasElement} */ (this.viewportElement.createChild('canvas'));
+    this._viewportElement = this._chartViewport.viewportElement;
+    this._canvas = /** @type {!HTMLCanvasElement} */ (this._viewportElement.createChild('canvas'));
+
     this._canvas.tabIndex = 1;
     this.setDefaultFocusedElement(this._canvas);
     this._canvas.addEventListener('mousemove', this._onMouseMove.bind(this), false);
@@ -75,10 +81,14 @@
     this._canvas.addEventListener('click', this._onClick.bind(this), false);
     this._canvas.addEventListener('keydown', this._onKeyDown.bind(this), false);
 
-    this._entryInfo = this.viewportElement.createChild('div', 'flame-chart-entry-info');
-    this._markerHighlighElement = this.viewportElement.createChild('div', 'flame-chart-marker-highlight-element');
-    this._highlightElement = this.viewportElement.createChild('div', 'flame-chart-highlight-element');
-    this._selectedElement = this.viewportElement.createChild('div', 'flame-chart-selected-element');
+    this._entryInfo = this._viewportElement.createChild('div', 'flame-chart-entry-info');
+    this._markerHighlighElement = this._viewportElement.createChild('div', 'flame-chart-marker-highlight-element');
+    this._highlightElement = this._viewportElement.createChild('div', 'flame-chart-highlight-element');
+    this._selectedElement = this._viewportElement.createChild('div', 'flame-chart-selected-element');
+
+    UI.installDragHandle(
+        this._viewportElement, this._startDragging.bind(this), this._dragging.bind(this), this._endDragging.bind(this),
+        null);
 
     this._rulerEnabled = true;
     this._windowLeft = 0.0;
@@ -90,7 +100,6 @@
     this._barHeight = 17;
     this._textBaseline = 5;
     this._textPadding = 5;
-    this._paddingLeft = 0;
     this._markerRadius = 6;
 
     /** @const */
@@ -143,19 +152,16 @@
   }
 
   /**
-   * @param {number} value
-   */
-  setPaddingLeft(value) {
-    this._paddingLeft = value;
-  }
-
-  /**
    * @param {boolean} enable
    */
   enableRuler(enable) {
     this._rulerEnabled = enable;
   }
 
+  alwaysShowVerticalScroll() {
+    this._chartViewport.alwaysShowVerticalScroll();
+  }
+
   /**
    * @param {number} entryIndex
    */
@@ -183,6 +189,61 @@
   }
 
   /**
+   * @override
+   * @param {number} startTime
+   * @param {number} endTime
+   */
+  requestWindowTimes(startTime, endTime) {
+    this._flameChartDelegate.requestWindowTimes(startTime, endTime);
+  }
+
+  /**
+   * @override
+   * @param {number} startTime
+   * @param {number} endTime
+   */
+  updateRangeSelection(startTime, endTime) {
+    this._flameChartDelegate.updateRangeSelection(startTime, endTime);
+  }
+
+  /**
+   * @override
+   * @param {number} width
+   * @param {number} height
+   */
+  setSize(width, height) {
+    this._offsetWidth = width;
+    this._offsetHeight = height;
+  }
+
+  /**
+   * @param {!MouseEvent} event
+   */
+  _startDragging(event) {
+    this.hideHighlight();
+    this._maxDragOffset = 0;
+    this._dragStartX = event.pageX;
+    this._dragStartY = event.pageY;
+    return true;
+  }
+
+  /**
+   * @param {!MouseEvent} event
+   */
+  _dragging(event) {
+    var dx = event.pageX - this._dragStartX;
+    var dy = event.pageY - this._dragStartY;
+    this._maxDragOffset = Math.max(this._maxDragOffset, Math.sqrt(dx * dx + dy * dy));
+  }
+
+  /**
+   * @param {!MouseEvent} event
+   */
+  _endDragging(event) {
+    this._updateHighlight();
+  }
+
+  /**
    * @return {?PerfUI.FlameChart.TimelineData}
    */
   _timelineData() {
@@ -201,36 +262,36 @@
     var timelineData = this._timelineData();
     if (!timelineData)
       return;
-    // Think in terms of not where we are, but where we'll be after animation (if present)
-    var timeLeft = this._cancelWindowTimesAnimation ? this._pendingAnimationTimeLeft : this._timeWindowLeft;
-    var timeRight = this._cancelWindowTimesAnimation ? this._pendingAnimationTimeRight : this._timeWindowRight;
+    var timeLeft = this._timeWindowLeft;
+    var timeRight = this._timeWindowRight;
     var entryStartTime = timelineData.entryStartTimes[entryIndex];
     var entryTotalTime = timelineData.entryTotalTimes[entryIndex];
     var entryEndTime = entryStartTime + entryTotalTime;
     var minEntryTimeWindow = Math.min(entryTotalTime, timeRight - timeLeft);
 
     var level = timelineData.entryLevels[entryIndex];
-    this.setScrollOffset(this._levelToOffset(level), this._levelHeight(level));
+    this._chartViewport.setScrollOffset(this._levelToOffset(level), this._levelHeight(level));
 
     var minVisibleWidthPx = 30;
     var futurePixelToTime = (timeRight - timeLeft) / this._offsetWidth;
     minEntryTimeWindow = Math.max(minEntryTimeWindow, futurePixelToTime * minVisibleWidthPx);
     if (timeLeft > entryEndTime) {
       var delta = timeLeft - entryEndTime + minEntryTimeWindow;
-      this._flameChartDelegate.requestWindowTimes(timeLeft - delta, timeRight - delta);
+      this.requestWindowTimes(timeLeft - delta, timeRight - delta);
     } else if (timeRight < entryStartTime) {
       var delta = entryStartTime - timeRight + minEntryTimeWindow;
-      this._flameChartDelegate.requestWindowTimes(timeLeft + delta, timeRight + delta);
+      this.requestWindowTimes(timeLeft + delta, timeRight + delta);
     }
   }
 
   /**
-   * @override
    * @param {number} startTime
    * @param {number} endTime
    */
   setWindowTimes(startTime, endTime) {
-    super.setWindowTimes(startTime, endTime);
+    this._chartViewport.setWindowTimes(startTime, endTime);
+    this._timeWindowLeft = startTime;
+    this._timeWindowRight = endTime;
     this._updateHighlight();
   }
 
@@ -242,11 +303,11 @@
     this._lastMouseOffsetY = event.offsetY;
     if (!this._enabled())
       return;
-    if (this.isDragging())
+    if (this._chartViewport.isDragging())
       return;
     if (this._coordinatesToGroupIndex(event.offsetX, event.offsetY) >= 0) {
       this.hideHighlight();
-      this.viewportElement.style.cursor = 'pointer';
+      this._viewportElement.style.cursor = 'pointer';
       return;
     }
     this._updateHighlight();
@@ -264,10 +325,10 @@
       this.hideHighlight();
       return;
     }
-    if (this.isDragging())
+    if (this._chartViewport.isDragging())
       return;
     this._updatePopover(entryIndex);
-    this.viewportElement.style.cursor = this._dataProvider.canJumpToEntry(entryIndex) ? 'pointer' : 'default';
+    this._viewportElement.style.cursor = this._dataProvider.canJumpToEntry(entryIndex) ? 'pointer' : 'default';
     this.highlightEntry(entryIndex);
   }
 
@@ -325,14 +386,14 @@
     // So if there was drag (mouse move) in the middle of that events
     // we skip the click. Otherwise we jump to the sources.
     var /** @const */ clickThreshold = 5;
-    if (this.maxDragOffset() > clickThreshold)
+    if (this._maxDragOffset > clickThreshold)
       return;
     var groupIndex = this._coordinatesToGroupIndex(event.offsetX, event.offsetY);
     if (groupIndex >= 0) {
       this._toggleGroupVisibility(groupIndex);
       return;
     }
-    this.hideRangeSelection();
+    this._chartViewport.hideRangeSelection();
     this.dispatchEventToListeners(PerfUI.FlameChart.Events.EntrySelected, this._highlightedEntryIndex);
   }
 
@@ -438,25 +499,16 @@
 
   /**
    * @param {number} x
-   * @return {number}
-   */
-  _cursorTime(x) {
-    return (x + this._pixelWindowLeft - this._paddingLeft) * this._pixelToTime + this._minimumBoundary;
-  }
-
-  /**
-   * @param {number} x
    * @param {number} y
    * @return {number}
    */
   _coordinatesToEntryIndex(x, y) {
     if (x < 0 || y < 0)
       return -1;
-    y += this.getScrollOffset();
     var timelineData = this._timelineData();
     if (!timelineData)
       return -1;
-    var cursorTime = this._cursorTime(x);
+    y += this._chartViewport.scrollOffset();
     var cursorLevel = this._visibleLevelOffsets.upperBound(y) - 1;
     if (cursorLevel < 0 || !this._visibleLevels[cursorLevel])
       return -1;
@@ -477,6 +529,7 @@
     function comparator(time, entryIndex) {
       return time - entryStartTimes[entryIndex];
     }
+    var cursorTime = this._chartViewport.pixelToTime(x);
     var indexOnLevel = Math.max(entryIndexes.upperBound(cursorTime, comparator) - 1, 0);
 
     /**
@@ -488,15 +541,16 @@
       if (entryIndex === undefined)
         return false;
       var startTime = entryStartTimes[entryIndex];
+      var startX = this._chartViewport.timeToPosition(startTime);
       var duration = entryTotalTimes[entryIndex];
       if (isNaN(duration)) {
-        var dx = (startTime - cursorTime) / this._pixelToTime;
+        var dx = startX - x;
         var dy = this._levelHeight(cursorLevel) / 2 - offsetFromLevel;
         return dx * dx + dy * dy < this._markerRadius * this._markerRadius;
       }
-      var endTime = startTime + duration;
-      var barThreshold = 3 * this._pixelToTime;
-      return startTime - barThreshold < cursorTime && cursorTime < endTime + barThreshold;
+      var endX = this._chartViewport.timeToPosition(startTime + duration);
+      var /** @const */ barThresholdPx = 3;
+      return startX - barThresholdPx < x && x < endX + barThresholdPx;
     }
 
     var entryIndex = entryIndexes[indexOnLevel];
@@ -516,7 +570,7 @@
   _coordinatesToGroupIndex(x, y) {
     if (x < 0 || y < 0)
       return -1;
-    y += this.getScrollOffset();
+    y += this._chartViewport.scrollOffset();
     var groups = this._rawTimelineData.groups || [];
     var group = this._groupOffsets.upperBound(y) - 1;
 
@@ -542,9 +596,9 @@
     if (!markers)
       return -1;
     var /** @const */ accurracyOffsetPx = 4;
-    var time = this._cursorTime(x);
-    var leftTime = this._cursorTime(x - accurracyOffsetPx);
-    var rightTime = this._cursorTime(x + accurracyOffsetPx);
+    var time = this._chartViewport.pixelToTime(x);
+    var leftTime = this._chartViewport.pixelToTime(x - accurracyOffsetPx);
+    var rightTime = this._chartViewport.pixelToTime(x + accurracyOffsetPx);
     var left = this._markerIndexBeforeTime(leftTime);
     var markerIndex = -1;
     var distance = Infinity;
@@ -579,14 +633,12 @@
     var context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
     context.save();
     var ratio = window.devicePixelRatio;
-    var top = this.getScrollOffset();
+    var top = this._chartViewport.scrollOffset();
     context.scale(ratio, ratio);
     context.translate(0, -top);
     var defaultFont = '11px ' + Host.fontFamily();
     context.font = defaultFont;
 
-    var timeWindowRight = this._timeWindowRight;
-    var timeWindowLeft = this._timeWindowLeft - this._paddingLeft / this._timeToPixel;
     var entryTotalTimes = timelineData.entryTotalTimes;
     var entryStartTimes = timelineData.entryStartTimes;
     var entryLevels = timelineData.entryLevels;
@@ -608,13 +660,13 @@
       // Entries are ordered by start time within a level, so find the last visible entry.
       var levelIndexes = this._timelineLevels[level];
       var rightIndexOnLevel =
-          levelIndexes.lowerBound(timeWindowRight, (time, entryIndex) => time - entryStartTimes[entryIndex]) - 1;
+          levelIndexes.lowerBound(this._timeWindowRight, (time, entryIndex) => time - entryStartTimes[entryIndex]) - 1;
       var lastDrawOffset = Infinity;
       for (var entryIndexOnLevel = rightIndexOnLevel; entryIndexOnLevel >= 0; --entryIndexOnLevel) {
         var entryIndex = levelIndexes[entryIndexOnLevel];
         var entryStartTime = entryStartTimes[entryIndex];
         var entryOffsetRight = entryStartTime + (entryTotalTimes[entryIndex] || 0);
-        if (entryOffsetRight <= timeWindowLeft)
+        if (entryOffsetRight <= this._timeWindowLeft)
           break;
 
         var barX = this._timeToPositionClipped(entryStartTime);
@@ -690,7 +742,7 @@
         context.font = this._dataProvider.entryFont(entryIndex) || defaultFont;
         text = UI.trimTextMiddle(context, text, barWidth - 2 * textPadding);
       }
-      var unclippedBarX = this._timeToPosition(entryStartTime);
+      var unclippedBarX = this._chartViewport.timeToPosition(entryStartTime);
       var barHeight = this._levelHeight(barLevel);
       if (this._dataProvider.decorateEntry(
               entryIndex, context, text, barX, barY, barWidth, barHeight, unclippedBarX, this._timeToPixel))
@@ -720,7 +772,7 @@
    */
   _drawGroupHeaders(width, height) {
     var context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
-    var top = this.getScrollOffset();
+    var top = this._chartViewport.scrollOffset();
     var ratio = window.devicePixelRatio;
     var groups = this._rawTimelineData.groups || [];
     if (!groups.length)
@@ -878,7 +930,7 @@
   _drawCollapsedOverviewForGroup(group, y, endLevel) {
     var range = new Common.SegmentedRange(mergeCallback);
     var timeWindowRight = this._timeWindowRight;
-    var timeWindowLeft = this._timeWindowLeft - this._paddingLeft / this._timeToPixel;
+    var timeWindowLeft = this._timeWindowLeft;
     var context = /** @type {!CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
     var barHeight = group.style.height;
     var entryStartTimes = this._rawTimelineData.entryStartTimes;
@@ -903,7 +955,7 @@
         var color = this._dataProvider.entryColor(entryIndex);
         var endBarX = this._timeToPositionClipped(entryEndTime);
         if (group.style.useDecoratorsForOverview && this._dataProvider.forceDecoration(entryIndex)) {
-          var unclippedBarX = this._timeToPosition(entryStartTime);
+          var unclippedBarX = this._chartViewport.timeToPosition(entryStartTime);
           var barWidth = endBarX - barX;
           context.beginPath();
           context.fillStyle = color;
@@ -949,7 +1001,7 @@
   _drawFlowEvents(context, width, height) {
     context.save();
     var ratio = window.devicePixelRatio;
-    var top = this.getScrollOffset();
+    var top = this._chartViewport.scrollOffset();
     var arrowWidth = 6;
     context.scale(ratio, ratio);
     context.translate(0, -top);
@@ -963,8 +1015,8 @@
     for (var i = 0; i < endIndex; ++i) {
       if (!td.flowEndTimes[i] || td.flowEndTimes[i] < this._timeWindowLeft)
         continue;
-      var startX = this._timeToPosition(td.flowStartTimes[i]);
-      var endX = this._timeToPosition(td.flowEndTimes[i]);
+      var startX = this._chartViewport.timeToPosition(td.flowStartTimes[i]);
+      var endX = this._chartViewport.timeToPosition(td.flowEndTimes[i]);
       var startLevel = td.flowStartLevels[i];
       var endLevel = td.flowEndLevels[i];
       var startY = this._levelToOffset(startLevel) + this._levelHeight(startLevel) / 2;
@@ -1042,7 +1094,7 @@
     var style = element.style;
     style.left = barX + 'px';
     style.backgroundColor = marker.color();
-    this.viewportElement.appendChild(element);
+    this._viewportElement.appendChild(element);
   }
 
   /**
@@ -1168,8 +1220,8 @@
    * @param {number} entryIndex
    */
   setSelectedEntry(entryIndex) {
-    if (entryIndex === -1 && !this.isDragging())
-      this.hideRangeSelection();
+    if (entryIndex === -1 && !this._chartViewport.isDragging())
+      this._chartViewport.hideRangeSelection();
     if (this._selectedEntryIndex === entryIndex)
       return;
     this._selectedEntryIndex = entryIndex;
@@ -1199,14 +1251,14 @@
     barWidth = Math.max(barWidth, elementMinWidthPx);
     barX = barCenter - barWidth / 2;
     var entryLevel = timelineData.entryLevels[entryIndex];
-    var barY = this._levelToOffset(entryLevel) - this.getScrollOffset();
+    var barY = this._levelToOffset(entryLevel) - this._chartViewport.scrollOffset();
     var barHeight = this._levelHeight(entryLevel);
     var style = element.style;
     style.left = barX + 'px';
     style.top = barY + 'px';
     style.width = barWidth + 'px';
     style.height = barHeight - 1 + 'px';
-    this.viewportElement.appendChild(element);
+    this._viewportElement.appendChild(element);
   }
 
   /**
@@ -1214,15 +1266,7 @@
    * @return {number}
    */
   _timeToPositionClipped(time) {
-    return Number.constrain(this._timeToPosition(time), 0, this._offsetWidth);
-  }
-
-  /**
-   * @param {number} time
-   * @return {number}
-   */
-  _timeToPosition(time) {
-    return Math.floor((time - this._minimumBoundary) * this._timeToPixel) - this._pixelWindowLeft + this._paddingLeft;
+    return Number.constrain(this._chartViewport.timeToPosition(time), 0, this._offsetWidth);
   }
 
   /**
@@ -1258,23 +1302,24 @@
       this._windowRight = 1;
     }
 
-    var totalPixels = Math.floor((this._offsetWidth - this._paddingLeft) / windowWidth);
+    var totalPixels = Math.floor(this._offsetWidth / windowWidth);
     this._pixelWindowLeft = Math.floor(totalPixels * this._windowLeft);
 
     this._timeToPixel = totalPixels / this._totalTime;
     this._pixelToTime = this._totalTime / totalPixels;
+
+    this._chartViewport.setBoundaries(this._minimumBoundary, this._totalTime);
   }
 
   _updateHeight() {
     var height = this._levelToOffset(this._dataProvider.maxStackDepth());
-    this.setContentHeight(height);
+    this._chartViewport.setContentHeight(height);
   }
 
   /**
    * @override
    */
   onResize() {
-    super.onResize();
     this.scheduleUpdate();
   }
 
@@ -1289,15 +1334,12 @@
     this._updateBoundaries();
     this._calculator._updateBoundaries(this);
     this._draw(this._offsetWidth, this._offsetHeight);
-    if (!this.isDragging())
+    if (!this._chartViewport.isDragging())
       this._updateHighlight();
   }
 
-  /**
-   * @override
-   */
   reset() {
-    super.reset();
+    this._chartViewport.reset();
     this._rawTimelineData = null;
     this._rawTimelineDataLength = 0;
     this._highlightedMarkerIndex = -1;
@@ -1305,7 +1347,11 @@
     this._selectedEntryIndex = -1;
     /** @type {!Map<string,!Map<string,number>>} */
     this._textWidth = new Map();
-    this.scheduleUpdate();
+    this._chartViewport.scheduleUpdate();
+  }
+
+  scheduleUpdate() {
+    this._chartViewport.scheduleUpdate();
   }
 
   _enabled() {
@@ -1514,8 +1560,7 @@
     this._zeroTime = mainPane._dataProvider.minimumBoundary();
     this._minimumBoundaries = this._zeroTime + mainPane._windowLeft * this._totalTime;
     this._maximumBoundaries = this._zeroTime + mainPane._windowRight * this._totalTime;
-    this._paddingLeft = mainPane._paddingLeft;
-    this._width = mainPane._offsetWidth - this._paddingLeft;
+    this._width = mainPane._offsetWidth;
     this._timeToPixel = this._width / this.boundarySpan();
   }
 
@@ -1525,7 +1570,7 @@
    * @return {number}
    */
   computePosition(time) {
-    return Math.round((time - this._minimumBoundaries) * this._timeToPixel + this._paddingLeft);
+    return Math.round((time - this._minimumBoundaries) * this._timeToPixel);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/chartViewport.css b/third_party/WebKit/Source/devtools/front_end/perf_ui/chartViewport.css
new file mode 100644
index 0000000..22e1d6d
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/chartViewport.css
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+.chart-viewport-v-scroll {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    overflow-x: hidden;
+    z-index: 200;
+    padding-left: 1px;
+}
+
+.chart-viewport-v-scroll.always-show-scrollbar {
+    overflow-y: scroll;
+}
+
+/* force non overlay scrollbars for Mac */
+:host-context(.platform-mac) .chart-viewport-v-scroll {
+    right: 2px;
+    top: 3px;
+    bottom: 3px;
+}
+
+:host-context(.platform-mac) ::-webkit-scrollbar {
+    width: 8px;
+}
+
+:host-context(.platform-mac) ::-webkit-scrollbar-thumb {
+    background-color: hsla(0, 0%, 56%, 0.6);
+    border-radius: 50px;
+}
+
+:host-context(.platform-mac) .chart-viewport-v-scroll:hover::-webkit-scrollbar-thumb {
+    background-color: hsla(0, 0%, 25%, 0.6);
+}
+
+/* force non overlay scrollbars for Aura Overlay Scrollbar enabled */
+:host-context(.overlay-scrollbar-enabled) ::-webkit-scrollbar {
+    width: 10px;
+}
+
+:host-context(.overlay-scrollbar-enabled) ::-webkit-scrollbar-thumb {
+    background-color: hsla(0, 0%, 0%, 0.5);
+}
+
+:host-context(.overlay-scrollbar-enabled) .chart-viewport-v-scroll:hover::-webkit-scrollbar-thumb {
+    background-color: hsla(0, 0%, 0%, 0.7);
+}
+
+.chart-viewport-selection-overlay {
+    position: absolute;
+    z-index: 100;
+    background-color: rgba(56, 121, 217, 0.3);
+    border-color: rgb(16, 81, 177);
+    border-width: 0 1px;
+    border-style: solid;
+    pointer-events: none;
+    top: 0;
+    bottom: 0;
+    text-align: center;
+}
+
+.chart-viewport-selection-overlay .time-span {
+    white-space: nowrap;
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/flameChart.css b/third_party/WebKit/Source/devtools/front_end/perf_ui/flameChart.css
index bad7324..8e3c69e 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/flameChart.css
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/flameChart.css
@@ -31,74 +31,6 @@
     background-color: rgba(56, 121, 217, 0.1);
 }
 
-.flame-chart-v-scroll {
-    position: absolute;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    overflow-x: hidden;
-    z-index: 200;
-    padding-left: 1px;
-}
-
-.flame-chart-v-scroll.always-show-scrollbar {
-    overflow-y: scroll;
-}
-
-/* force non overlay scrollbars for Mac */
-:host-context(.platform-mac) .flame-chart-v-scroll {
-    right: 2px;
-    top: 3px;
-    bottom: 3px;
-}
-
-:host-context(.platform-mac) ::-webkit-scrollbar {
-    width: 8px;
-}
-
-:host-context(.platform-mac) ::-webkit-scrollbar-thumb {
-    background-color: hsla(0, 0%, 56%, 0.6);
-    border-radius: 50px;
-}
-
-:host-context(.platform-mac) .flame-chart-v-scroll:hover::-webkit-scrollbar-thumb {
-    background-color: hsla(0, 0%, 25%, 0.6);
-}
-
-/* force non overlay scrollbars for Aura Overlay Scrollbar enabled */
-:host-context(.overlay-scrollbar-enabled) ::-webkit-scrollbar {
-    width: 10px;
-}
-
-:host-context(.overlay-scrollbar-enabled) ::-webkit-scrollbar-thumb {
-    background-color: hsla(0, 0%, 0%, 0.5);
-}
-
-:host-context(.overlay-scrollbar-enabled) .flame-chart-v-scroll:hover::-webkit-scrollbar-thumb {
-    background-color: hsla(0, 0%, 0%, 0.7);
-}
-
-.flame-chart-selection-overlay {
-    position: absolute;
-    z-index: 100;
-    background-color: rgba(56, 121, 217, 0.3);
-    border-color: rgb(16, 81, 177);
-    border-width: 0 1px;
-    border-style: solid;
-    pointer-events: none;
-    top: 0;
-    bottom: 0;
-    text-align: center;
-}
-
-.flame-chart-selection-overlay .time-span {
-    white-space: nowrap;
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: 0;
-}
-
 .chart-cursor-element {
     position: absolute;
     top: 0;
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/module.json b/third_party/WebKit/Source/devtools/front_end/perf_ui/module.json
index 5d966ec..a0c8a70 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/module.json
@@ -44,6 +44,7 @@
         "TimelineOverviewPane.js"
     ],
     "resources": [
+        "chartViewport.css",
         "filmStripView.css",
         "filmStripDialog.css",
         "flameChart.css",
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js b/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
index fa24238..ec93484 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
@@ -49,8 +49,8 @@
 
     this.contentElement.appendChild(this._sidebarTree.element);
     this._applicationTreeElement = this._addSidebarSection(Common.UIString('Application'));
-    this._manifestTreeElement = new Resources.AppManifestTreeElement(panel);
-    this._applicationTreeElement.appendChild(this._manifestTreeElement);
+    var manifestTreeElement = new Resources.AppManifestTreeElement(panel);
+    this._applicationTreeElement.appendChild(manifestTreeElement);
     this.serviceWorkersTreeElement = new Resources.ServiceWorkersTreeElement(panel);
     this._applicationTreeElement.appendChild(this.serviceWorkersTreeElement);
     var clearStorageTreeElement = new Resources.ClearStorageTreeElement(panel);
@@ -116,7 +116,7 @@
 
     var selection = this._panel.lastSelectedItemPath();
     if (!selection.length)
-      this._manifestTreeElement.select();
+      manifestTreeElement.select();
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js b/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
index 32cc5ff5..4c07cfc1 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
@@ -3,16 +3,19 @@
 // found in the LICENSE file.
 /**
  * @implements {SDK.TargetManager.Observer}
- * @unrestricted
  */
-Resources.ClearStorageView = class extends UI.VBox {
+Resources.ClearStorageView = class extends UI.ThrottledWidget {
   constructor() {
-    super(true);
+    super(true, 1000);
 
     this._reportView = new UI.ReportView(Common.UIString('Clear storage'));
     this._reportView.registerRequiredCSS('resources/clearStorageView.css');
     this._reportView.element.classList.add('clear-storage-header');
     this._reportView.show(this.contentElement);
+    /** @type {?SDK.Target} */
+    this._target = null;
+    /** @type {?string} */
+    this._securityOrigin = null;
 
     this._settings = new Map();
     for (var type
@@ -22,6 +25,8 @@
                  Protocol.Storage.StorageType.Websql])
       this._settings.set(type, Common.settings.createSetting('clear-storage-' + type, true));
 
+    var quota = this._reportView.appendSection(Common.UIString('Usage'));
+    this._quotaRow = quota.appendRow();
 
     var application = this._reportView.appendSection(Common.UIString('Application'));
     this._appendItem(application, Common.UIString('Unregister service workers'), 'service_workers');
@@ -93,9 +98,12 @@
   _updateOrigin(url) {
     this._securityOrigin = new Common.ParsedURL(url).securityOrigin();
     this._reportView.setSubtitle(this._securityOrigin);
+    this.doUpdate();
   }
 
   _clear() {
+    if (!this._securityOrigin)
+      return;
     var storageTypes = [];
     for (var type of this._settings.keys()) {
       if (this._settings.get(type).get())
@@ -155,4 +163,32 @@
       this._clearButton.textContent = label;
     }, 500);
   }
+
+  /**
+   * @override
+   * @return {!Promise<?>}
+   */
+  async doUpdate() {
+    if (!this._securityOrigin)
+      return;
+
+    var securityOrigin = /** @type {string} */ (this._securityOrigin);
+    var response = await this._target.storageAgent().invoke_getUsageAndQuota({origin: securityOrigin});
+    if (response[Protocol.Error]) {
+      this._quotaRow.textContent = '';
+      return;
+    }
+    this._quotaRow.textContent = Common.UIString(
+        '%s storage quota used out of %s', Number.bytesToString(response.usage), Number.bytesToString(response.quota));
+
+    this._usageUpdatedForTest(response.usage, response.quota);
+    this.update();
+  }
+
+  /**
+   * @param {number} usage
+   * @param {number} quota
+   */
+  _usageUpdatedForTest(usage, quota) {
+  }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css b/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
index 729a8fc..eb43ba64 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
@@ -431,7 +431,7 @@
 }
 
 .timeline-flamechart-resizer {
-    height: 8px;
+    flex: 8px 0 0;
     background-color: #f3f3f3;
     border: 1px #a3a3a3;
     border-style: solid none;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ThrottledWidget.js b/third_party/WebKit/Source/devtools/front_end/ui/ThrottledWidget.js
index 6caf67cc..bbb4f37 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/ThrottledWidget.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/ThrottledWidget.js
@@ -7,10 +7,11 @@
 UI.ThrottledWidget = class extends UI.VBox {
   /**
    * @param {boolean=} isWebComponent
+   * @param {number=} timeout
    */
-  constructor(isWebComponent) {
+  constructor(isWebComponent, timeout) {
     super(isWebComponent);
-    this._updateThrottler = new Common.Throttler(100);
+    this._updateThrottler = new Common.Throttler(timeout === undefined ? 100 : timeout);
     this._updateWhenVisible = false;
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
index 6f458e91..f927d21 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
@@ -36,7 +36,7 @@
  * @param {?function(!MouseEvent): boolean} elementDragStart
  * @param {function(!MouseEvent)} elementDrag
  * @param {?function(!MouseEvent)} elementDragEnd
- * @param {string} cursor
+ * @param {?string} cursor
  * @param {?string=} hoverCursor
  * @param {number=} startDelay
  */
@@ -66,7 +66,7 @@
   if (startDelay)
     element.addEventListener('mouseup', onMouseUp, false);
   if (hoverCursor !== null)
-    element.style.cursor = hoverCursor || cursor;
+    element.style.cursor = hoverCursor || cursor || '';
 };
 
 /**
@@ -74,7 +74,7 @@
  * @param {?function(!MouseEvent):boolean} elementDragStart
  * @param {function(!MouseEvent)} elementDrag
  * @param {?function(!MouseEvent)} elementDragEnd
- * @param {string} cursor
+ * @param {?string} cursor
  * @param {!Event} event
  */
 UI.elementDragStart = function(targetElement, elementDragStart, elementDrag, elementDragEnd, cursor, event) {
@@ -117,7 +117,7 @@
    * @param {?function(!MouseEvent):boolean} elementDragStart
    * @param {function(!MouseEvent)} elementDrag
    * @param {?function(!MouseEvent)} elementDragEnd
-   * @param {string} cursor
+   * @param {?string} cursor
    * @param {!Event} event
    */
   elementDragStart(targetElement, elementDragStart, elementDrag, elementDragEnd, cursor, event) {
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBRequest.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBRequest.cpp
index 0f25f71..f34d20f4 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBRequest.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBRequest.cpp
@@ -260,8 +260,21 @@
 }
 
 bool IDBRequest::ShouldEnqueueEvent() const {
-  if (!GetExecutionContext())
+  const ExecutionContext* execution_context = GetExecutionContext();
+
+  // https://crbug.com/733642 - Document::Shutdown() calls
+  // LocalDOMWindow::ClearEventQueue(), which nulls out the context's event
+  // queue, before calling ExecutionContext::NotifyContextDestroyed(). The
+  // latter eventually calls IDBRequest::ContextDestroyed(), which aborts the
+  // request. As an aborted IDBRequest is removed from its' IDBTransaction
+  // result queue, it may unblock another request whose result is already
+  // available. If the unblocked request hasn't received a
+  // NotifyContextDestroyed() call yet, it will hang onto an ExecutionContext
+  // whose event queue has been nulled out. The event queue null check covers
+  // these specific circumstances.
+  if (!execution_context || !execution_context->GetEventQueue())
     return false;
+
   DCHECK(ready_state_ == PENDING || ready_state_ == DONE);
   if (request_aborted_)
     return false;
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBRequestLoader.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBRequestLoader.cpp
index 0c18c8b..6756714 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBRequestLoader.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBRequestLoader.cpp
@@ -26,6 +26,11 @@
 }
 
 void IDBRequestLoader::Start() {
+#if DCHECK_IS_ON()
+  DCHECK(!started_) << "Start() was already called";
+  started_ = true;
+#endif  // DCHECK_IS_ON()
+
   // TODO(pwnall): Start() / StartNextValue() unwrap large values sequentially.
   //               Consider parallelizing. The main issue is that the Blob reads
   //               will have to be throttled somewhere, and the extra complexity
@@ -35,6 +40,14 @@
 }
 
 void IDBRequestLoader::Cancel() {
+#if DCHECK_IS_ON()
+  DCHECK(started_) << "Cancel() called on a loader that hasn't been Start()ed";
+  DCHECK(!canceled_) << "Cancel() was already called";
+  canceled_ = true;
+
+  DCHECK(file_reader_loading_);
+  file_reader_loading_ = false;
+#endif  // DCHECK_IS_ON()
   loader_->Cancel();
 }
 
@@ -60,6 +73,10 @@
   }
 
   wrapped_data_.ReserveCapacity(unwrapper.WrapperBlobSize());
+#if DCHECK_IS_ON()
+  DCHECK(!file_reader_loading_);
+  file_reader_loading_ = true;
+#endif  // DCHECK_IS_ON()
   loader_->Start(context, unwrapper.WrapperBlobHandle());
 }
 
@@ -74,6 +91,16 @@
 }
 
 void IDBRequestLoader::DidFinishLoading() {
+#if DCHECK_IS_ON()
+  DCHECK(started_)
+      << "FileReaderLoader called DidFinishLoading() before it was Start()ed";
+  DCHECK(!canceled_)
+      << "FileReaderLoader called DidFinishLoading() after it was Cancel()ed";
+
+  DCHECK(file_reader_loading_);
+  file_reader_loading_ = false;
+#endif  // DCHECK_IS_ON()
+
   *current_value_ = IDBValueUnwrapper::Unwrap(
       current_value_->Get(), SharedBuffer::AdoptVector(wrapped_data_));
   ++current_value_;
@@ -82,14 +109,32 @@
 }
 
 void IDBRequestLoader::DidFail(FileError::ErrorCode) {
+#if DCHECK_IS_ON()
+  DCHECK(started_)
+      << "FileReaderLoader called DidFail() before it was Start()ed";
+  DCHECK(!canceled_)
+      << "FileReaderLoader called DidFail() after it was Cancel()ed";
+
+  DCHECK(file_reader_loading_);
+  file_reader_loading_ = false;
+#endif  // DCHECK_IS_ON()
+
   ReportError();
 }
 
 void IDBRequestLoader::ReportSuccess() {
+#if DCHECK_IS_ON()
+  DCHECK(started_);
+  DCHECK(!canceled_);
+#endif  // DCHECK_IS_ON()
   queue_item_->OnResultLoadComplete();
 }
 
 void IDBRequestLoader::ReportError() {
+#if DCHECK_IS_ON()
+  DCHECK(started_);
+  DCHECK(!canceled_);
+#endif  // DCHECK_IS_ON()
   queue_item_->OnResultLoadComplete(
       DOMException::Create(kDataError, "Failed to read large IndexedDB value"));
 }
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBRequestLoader.h b/third_party/WebKit/Source/modules/indexeddb/IDBRequestLoader.h
index f002222d..d447dcb 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBRequestLoader.h
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBRequestLoader.h
@@ -84,6 +84,18 @@
 
   // The value being currently unwrapped.
   Vector<RefPtr<IDBValue>>::iterator current_value_;
+
+#if DCHECK_IS_ON()
+  // True after Start() is called.
+  bool started_ = false;
+
+  // True after Cancel() is called.
+  bool canceled_ = false;
+
+  // True between a call to FileReaderLoader::Start() and the FileReaderLoader's
+  // call to DidFinishLoading() or to DidFail().
+  bool file_reader_loading_ = false;
+#endif  // DCHECK_IS_ON()
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBRequestQueueItem.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBRequestQueueItem.cpp
index 48afd5c4..ade7e98 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBRequestQueueItem.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBRequestQueueItem.cpp
@@ -179,6 +179,13 @@
     // The backing store can get the result back to the request after it's been
     // aborted due to a transaction abort. In this case, we can't rely on
     // IDBRequest::Abort() to call CancelLoading().
+
+    // Setting loader_ to null here makes sure we don't call Cancel() on a
+    // IDBRequestLoader that hasn't been Start()ed. The current implementation
+    // behaves well even if Cancel() is called without Start() being called, but
+    // this reset makes the IDBRequestLoader lifecycle easier to reason about.
+    loader_.reset();
+
     CancelLoading();
     return;
   }
@@ -195,7 +202,11 @@
 
   if (loader_) {
     loader_->Cancel();
-    loader_ = nullptr;
+    loader_.reset();
+
+    // IDBRequestLoader::Cancel() should not call any of the EnqueueResponse
+    // variants.
+    DCHECK(!ready_);
   }
 
   // Mark this item as ready so the transaction's result queue can be drained.
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBTransactionTest.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBTransactionTest.cpp
index 99c276caf..f06ee92 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBTransactionTest.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBTransactionTest.cpp
@@ -282,6 +282,48 @@
   EXPECT_EQ(0U, live_transactions.size());
 }
 
+TEST_F(IDBTransactionTest, DocumentShutdownWithQueuedAndBlockedResults) {
+  // This test covers the conditions of https://crbug.com/733642
+
+  V8TestingScope scope;
+  std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
+  EXPECT_CALL(*backend, Close()).Times(1);
+  BuildTransaction(scope, std::move(backend));
+
+  PersistentHeapHashSet<WeakMember<IDBTransaction>> live_transactions;
+  live_transactions.insert(transaction_);
+
+  ThreadState::Current()->CollectAllGarbage();
+  EXPECT_EQ(1U, live_transactions.size());
+
+  Persistent<IDBRequest> request1 =
+      IDBRequest::Create(scope.GetScriptState(), IDBAny::CreateUndefined(),
+                         transaction_.Get(), IDBRequest::AsyncTraceState());
+  Persistent<IDBRequest> request2 =
+      IDBRequest::Create(scope.GetScriptState(), IDBAny::CreateUndefined(),
+                         transaction_.Get(), IDBRequest::AsyncTraceState());
+  DeactivateNewTransactions(scope.GetIsolate());
+
+  request1->HandleResponse(CreateIDBValue(scope.GetIsolate(), true));
+  request2->HandleResponse(CreateIDBValue(scope.GetIsolate(), false));
+
+  request1.Clear();  // The transaction is holding onto the requests.
+  request2.Clear();
+  ThreadState::Current()->CollectAllGarbage();
+  EXPECT_EQ(1U, live_transactions.size());
+
+  // This will generate an Abort() call to the back end which is dropped by the
+  // fake proxy, so an explicit OnAbort call is made.
+  scope.GetDocument().Shutdown();
+  transaction_->OnAbort(DOMException::Create(kAbortError, "Aborted"));
+  transaction_.Clear();
+
+  url_loader_mock_factory_->ServeAsynchronousRequests();
+
+  ThreadState::Current()->CollectAllGarbage();
+  EXPECT_EQ(0U, live_transactions.size());
+}
+
 TEST_F(IDBTransactionTest, TransactionFinish) {
   V8TestingScope scope;
   std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
diff --git a/third_party/WebKit/Source/modules/indexeddb/OWNERS b/third_party/WebKit/Source/modules/indexeddb/OWNERS
index bbe466e53..b48e060 100644
--- a/third_party/WebKit/Source/modules/indexeddb/OWNERS
+++ b/third_party/WebKit/Source/modules/indexeddb/OWNERS
@@ -1 +1,3 @@
 file://content/browser/indexed_db/OWNERS
+
+pwnall@chromium.org
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
index 55d99ad..ffafadd 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
@@ -433,6 +433,9 @@
 }
 
 void RemotePlayback::StopListeningForAvailability() {
+  if (!RuntimeEnabledFeatures::RemotePlaybackBackendEnabled())
+    return;
+
   if (!is_listening_)
     return;
 
@@ -447,6 +450,9 @@
 }
 
 void RemotePlayback::MaybeStartListeningForAvailability() {
+  if (!RuntimeEnabledFeatures::RemotePlaybackBackendEnabled())
+    return;
+
   if (is_listening_)
     return;
 
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 46622713..354bea4 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -857,11 +857,11 @@
       status: "stable",
     },
     // Whether or not the Remote Playback API backend is implemented.
-    // Not web-exposed, set per platform. For LayoutTests, always enabled.
+    // Not web-exposed, set per platform. For LayoutTests, disabled by
+    // default.
     {
       name: "RemotePlaybackBackend",
       settable_from_internals: true,
-      status: "test"
     },
     {
       name: "RenderingPipelineThrottling",
diff --git a/third_party/WebKit/Source/platform/fonts/CustomFontData.h b/third_party/WebKit/Source/platform/fonts/CustomFontData.h
index f940ed7a..44a3bd3 100644
--- a/third_party/WebKit/Source/platform/fonts/CustomFontData.h
+++ b/third_party/WebKit/Source/platform/fonts/CustomFontData.h
@@ -22,14 +22,14 @@
 #define CustomFontData_h
 
 #include "platform/PlatformExport.h"
-#include "platform/wtf/PassRefPtr.h"
 #include "platform/wtf/RefCounted.h"
+#include "platform/wtf/RefPtr.h"
 
 namespace blink {
 
 class PLATFORM_EXPORT CustomFontData : public RefCounted<CustomFontData> {
  public:
-  static PassRefPtr<CustomFontData> Create() {
+  static RefPtr<CustomFontData> Create() {
     return AdoptRef(new CustomFontData());
   }
 
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
index c5734e0..103445d 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
@@ -356,10 +356,9 @@
     glyph_bounding_box_.SetWidth(glyph_bounding_box_.Width() + total_space);
 }
 
-void ShapeResult::ApplySpacing(ShapeResultSpacing<StringView>& spacing,
-                               const StringView& text,
+void ShapeResult::ApplySpacing(ShapeResultSpacing<String>& spacing,
                                TextDirection direction) {
-  ApplySpacing(spacing, text, direction == TextDirection::kRtl);
+  ApplySpacing(spacing, spacing.Text(), IsRtl(direction));
 }
 
 PassRefPtr<ShapeResult> ShapeResult::ApplySpacingToCopy(
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h
index 1139558e..cfc0560f 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h
@@ -91,9 +91,7 @@
     return LayoutUnit::FromFloatCeil(PositionForOffset(offset));
   }
 
-  void ApplySpacing(ShapeResultSpacing<StringView>&,
-                    const StringView&,
-                    TextDirection);
+  void ApplySpacing(ShapeResultSpacing<String>&, TextDirection);
   PassRefPtr<ShapeResult> ApplySpacingToCopy(ShapeResultSpacing<TextRun>&,
                                              const TextRun&) const;
 
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.cpp
index d475587..3a95cb5 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.cpp
@@ -65,7 +65,8 @@
 
   if (expansion_) {
     ComputeExpansion(text_.AllowsLeadingExpansion(),
-                     text_.AllowsTrailingExpansion(), text_.GetTextJustify());
+                     text_.AllowsTrailingExpansion(), text_.Direction(),
+                     text_.GetTextJustify());
   }
 }
 
@@ -73,6 +74,7 @@
 void ShapeResultSpacing<TextContainerType>::ComputeExpansion(
     bool allows_leading_expansion,
     bool allows_trailing_expansion,
+    TextDirection direction,
     TextJustify text_justify) {
   DCHECK_GT(expansion_, 0);
 
@@ -80,8 +82,15 @@
   is_after_expansion_ = !allows_leading_expansion;
 
   bool is_after_expansion = is_after_expansion_;
-  expansion_opportunity_count_ =
-      Character::ExpansionOpportunityCount(text_, is_after_expansion);
+  if (text_.Is8Bit()) {
+    expansion_opportunity_count_ = Character::ExpansionOpportunityCount(
+        text_.Characters8(), text_.length(), direction, is_after_expansion,
+        text_justify_);
+  } else {
+    expansion_opportunity_count_ = Character::ExpansionOpportunityCount(
+        text_.Characters16(), text_.length(), direction, is_after_expansion,
+        text_justify_);
+  }
   if (is_after_expansion && !allows_trailing_expansion) {
     DCHECK_GT(expansion_opportunity_count_, 0u);
     --expansion_opportunity_count_;
@@ -110,12 +119,20 @@
   return expansion_per_opportunity_;
 }
 
+// Test if the |run| is the first sub-run of the original text container, for
+// containers that can create sub-runs such as TextRun or StringView.
 template <typename TextContainerType>
-bool ShapeResultSpacing<TextContainerType>::IsFirstRun(
+inline bool ShapeResultSpacing<TextContainerType>::IsFirstRun(
     const TextContainerType& run) const {
   return &run == &text_ || run.Bytes() == text_.Bytes();
 }
 
+template <>
+inline bool ShapeResultSpacing<String>::IsFirstRun(const String& run) const {
+  // String::Substring() should not be used because it copies to a new buffer.
+  return &run == &text_ || run.Impl() == text_.Impl();
+}
+
 template <typename TextContainerType>
 float ShapeResultSpacing<TextContainerType>::ComputeSpacing(
     const TextContainerType& run,
@@ -175,6 +192,6 @@
 
 // Instantiate the template class.
 template class ShapeResultSpacing<TextRun>;
-template class ShapeResultSpacing<StringView>;
+template class ShapeResultSpacing<String>;
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
index 9e31f41..da429606 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
@@ -22,8 +22,10 @@
  public:
   ShapeResultSpacing(const TextContainerType&);
 
+  const TextContainerType& Text() const { return text_; }
   float LetterSpacing() const { return letter_spacing_; }
   bool HasSpacing() const { return has_spacing_; }
+  bool HasExpansion() const { return expansion_opportunity_count_; }
   bool IsVerticalOffset() const { return is_vertical_offset_; }
 
   // Set letter-spacing and word-spacing.
@@ -39,12 +41,12 @@
   float ComputeSpacing(const TextContainerType&, size_t, float& offset);
 
  private:
-  bool HasExpansion() const { return expansion_opportunity_count_; }
   bool IsAfterExpansion() const { return is_after_expansion_; }
   bool IsFirstRun(const TextContainerType&) const;
 
   void ComputeExpansion(bool allows_leading_expansion,
                         bool allows_trailing_expansion,
+                        TextDirection,
                         TextJustify);
 
   float NextExpansion();
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp
index d306d49..351cb23 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp
@@ -8,6 +8,7 @@
 #include "platform/fonts/shaping/HarfBuzzShaper.h"
 #include "platform/fonts/shaping/ShapeResult.h"
 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h"
+#include "platform/fonts/shaping/ShapeResultSpacing.h"
 #include "platform/text/TextBreakIterator.h"
 
 namespace blink {
@@ -16,12 +17,18 @@
     const HarfBuzzShaper* shaper,
     const Font* font,
     const ShapeResult* result,
-    const LazyLineBreakIterator* break_iterator)
+    const LazyLineBreakIterator* break_iterator,
+    ShapeResultSpacing<String>* spacing)
     : shaper_(shaper),
       font_(font),
       result_(result),
-      break_iterator_(break_iterator) {
+      break_iterator_(break_iterator),
+      spacing_(spacing) {
   text_ = String(shaper->GetText(), shaper->TextLength());
+
+  // ShapeResultSpacing is stateful when it has expansions. We may use it in
+  // arbitrary order that it cannot have expansions.
+  DCHECK(!spacing_ || !spacing_->HasExpansion());
 }
 
 namespace {
@@ -53,23 +60,35 @@
 // ShapingLineBreaker computes using visual positions. This function flips
 // logical advance to visual, or vice versa.
 LayoutUnit FlipRtl(LayoutUnit value, TextDirection direction) {
-  return direction != TextDirection::kRtl ? value : -value;
+  return IsLtr(direction) ? value : -value;
 }
 
 // Snaps a visual position to the line start direction.
 LayoutUnit SnapStart(float value, TextDirection direction) {
-  return direction != TextDirection::kRtl ? LayoutUnit::FromFloatFloor(value)
-                                          : LayoutUnit::FromFloatCeil(value);
+  return IsLtr(direction) ? LayoutUnit::FromFloatFloor(value)
+                          : LayoutUnit::FromFloatCeil(value);
 }
 
 // Snaps a visual position to the line end direction.
 LayoutUnit SnapEnd(float value, TextDirection direction) {
-  return direction != TextDirection::kRtl ? LayoutUnit::FromFloatCeil(value)
-                                          : LayoutUnit::FromFloatFloor(value);
+  return IsLtr(direction) ? LayoutUnit::FromFloatCeil(value)
+                          : LayoutUnit::FromFloatFloor(value);
 }
 
 }  // namespace
 
+inline PassRefPtr<ShapeResult> ShapingLineBreaker::Shape(
+    TextDirection direction,
+    unsigned start,
+    unsigned end) {
+  if (!spacing_ || !spacing_->HasSpacing())
+    return shaper_->Shape(font_, direction, start, end);
+
+  RefPtr<ShapeResult> result = shaper_->Shape(font_, direction, start, end);
+  result->ApplySpacing(*spacing_, direction);
+  return result.Release();
+}
+
 // Shapes a line of text by finding a valid and appropriate break opportunity
 // based on the shaping results for the entire paragraph. Re-shapes the start
 // and end of the line as needed.
@@ -160,7 +179,7 @@
                         direction) -
                     start_position,
                 direction);
-    line_start_result = shaper_->Shape(font_, direction, start, first_safe);
+    line_start_result = Shape(direction, start, first_safe);
     available_space += line_start_result->SnappedWidth() - original_width;
   }
 
@@ -180,8 +199,7 @@
           result_->PositionForOffset(previous_safe - range_start), direction);
       while (break_opportunity > previous_safe && previous_safe >= start) {
         DCHECK_LE(break_opportunity, range_end);
-        line_end_result =
-            shaper_->Shape(font_, direction, previous_safe, break_opportunity);
+        line_end_result = Shape(direction, previous_safe, break_opportunity);
         if (line_end_result->SnappedWidth() <=
             FlipRtl(end_position - safe_position, direction))
           break;
@@ -247,11 +265,11 @@
     result_->CopyRange(start, range_end, line_result.Get());
   } else if (first_safe < range_end) {
     // Otherwise reshape to the first safe, then copy the rest.
-    line_result = shaper_->Shape(font_, direction, start, first_safe);
+    line_result = Shape(direction, start, first_safe);
     result_->CopyRange(first_safe, range_end, line_result.Get());
   } else {
     // If no safe-to-break in the ragne, reshape the whole range.
-    line_result = shaper_->Shape(font_, direction, start, range_end);
+    line_result = Shape(direction, start, range_end);
   }
   return line_result.Release();
 }
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.h
index 75fecc1..f58f070 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.h
@@ -7,6 +7,7 @@
 
 #include "platform/LayoutUnit.h"
 #include "platform/PlatformExport.h"
+#include "platform/text/TextDirection.h"
 #include "platform/wtf/text/AtomicString.h"
 
 namespace blink {
@@ -16,6 +17,8 @@
 class HarfBuzzShaper;
 class LazyLineBreakIterator;
 enum class LineBreakType;
+template <typename TextContainerType>
+class ShapeResultSpacing;
 
 // Shapes a line of text by finding the ideal break position as indicated by the
 // available space and the shape results for the entire paragraph. Once an ideal
@@ -34,7 +37,8 @@
   ShapingLineBreaker(const HarfBuzzShaper*,
                      const Font*,
                      const ShapeResult*,
-                     const LazyLineBreakIterator*);
+                     const LazyLineBreakIterator*,
+                     ShapeResultSpacing<String>* = nullptr);
   ~ShapingLineBreaker() {}
 
   // Shapes a line of text by finding a valid and appropriate break opportunity
@@ -45,6 +49,7 @@
                                     unsigned* break_offset);
 
  private:
+  PassRefPtr<ShapeResult> Shape(TextDirection, unsigned start, unsigned end);
   PassRefPtr<ShapeResult> ShapeToEnd(unsigned start,
                                      LayoutUnit start_position,
                                      unsigned range_end);
@@ -54,6 +59,9 @@
   const ShapeResult* result_;
   const LazyLineBreakIterator* break_iterator_;
   String text_;
+  // TODO(kojii): ShapeResultSpacing is not const because it's stateful when it
+  // has expansions. Split spacing and expansions to make this const.
+  ShapeResultSpacing<String>* spacing_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
index dd03847..97c52ce 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -768,7 +768,6 @@
   // Only one content layer, and the first child layer is the dummy layer for
   // the transform node.
   const cc::Layer* transform_node_layer = RootLayer()->children()[0].get();
-  EXPECT_EQ(transform_node_layer->id(), transform_node.owning_layer_id);
   auto transform_node_index = transform_node_layer->transform_tree_index();
   EXPECT_EQ(transform_node_index, transform_node.id);
 
@@ -1700,7 +1699,6 @@
   const cc::TransformNode* cc_transform_node =
       GetPropertyTrees().transform_tree.Node(
           transform_node_layer->transform_tree_index());
-  EXPECT_EQ(transform_node_layer->id(), cc_transform_node->owning_layer_id);
   auto transform_node_index = transform_node_layer->transform_tree_index();
   EXPECT_EQ(transform_node_index, cc_transform_node->id);
 }
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PropertyTreeManager.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PropertyTreeManager.cpp
index 467cd6e..5ef8abd 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PropertyTreeManager.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PropertyTreeManager.cpp
@@ -74,10 +74,6 @@
       transform_tree.Insert(cc::TransformNode(), kRealRootNodeId));
   DCHECK_EQ(transform_node.id, kSecondaryRootNodeId);
   transform_node.source_node_id = transform_node.parent_id;
-  // Setting owning layer id on cc property tree transform nodes is temporary
-  // until we can remove animation subsystem dependency on layer
-  // references. http://crbug.com/709137
-  transform_node.owning_layer_id = root_layer_->id();
 
   // TODO(jaydasika): We shouldn't set ToScreen and FromScreen of root
   // transform node here. They should be set while updating transform tree in
@@ -106,7 +102,6 @@
       *clip_tree.Node(clip_tree.Insert(cc::ClipNode(), kRealRootNodeId));
   DCHECK_EQ(clip_node.id, kSecondaryRootNodeId);
 
-  clip_node.owning_layer_id = root_layer_->id();
   clip_node.clip_type = cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP;
   clip_node.clip = gfx::RectF(
       gfx::SizeF(root_layer_->layer_tree_host()->device_viewport_size()));
@@ -166,10 +161,6 @@
 
   cc::TransformNode& compositor_node = *GetTransformTree().Node(id);
   compositor_node.source_node_id = parent_id;
-  // Setting owning layer id on cc property tree transform nodes is temporary
-  // until we can remove animation subsystem dependency on layer
-  // references. http://crbug.com/709137
-  compositor_node.owning_layer_id = dummy_layer->id();
 
   FloatPoint3D origin = transform_node->Origin();
   compositor_node.pre_local.matrix().setTranslate(-origin.X(), -origin.Y(),
@@ -222,7 +213,6 @@
   int id = GetClipTree().Insert(cc::ClipNode(), parent_id);
 
   cc::ClipNode& compositor_node = *GetClipTree().Node(id);
-  compositor_node.owning_layer_id = dummy_layer->id();
 
   // TODO(jbroman): Don't discard rounded corners.
   compositor_node.clip = clip_node->ClipRect().Rect();
diff --git a/third_party/WebKit/Source/platform/image-decoders/FastSharedBufferReaderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/FastSharedBufferReaderTest.cpp
index 2485265..5eef483 100644
--- a/third_party/WebKit/Source/platform/image-decoders/FastSharedBufferReaderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/FastSharedBufferReaderTest.cpp
@@ -30,6 +30,7 @@
 
 #include "platform/image-decoders/FastSharedBufferReader.h"
 #include "platform/image-decoders/SegmentReader.h"
+#include "third_party/skia/include/core/SkRWBuffer.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
index 2b00aa0..ff36b94 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
@@ -28,8 +28,6 @@
 #define ImageDecoder_h
 
 #include <memory>
-#include "SkColorPriv.h"
-#include "SkColorSpaceXform.h"
 #include "platform/PlatformExport.h"
 #include "platform/SharedBuffer.h"
 #include "platform/graphics/ColorBehavior.h"
@@ -39,10 +37,10 @@
 #include "platform/image-decoders/SegmentReader.h"
 #include "platform/wtf/Assertions.h"
 #include "platform/wtf/RefPtr.h"
-#include "platform/wtf/Threading.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/WTFString.h"
 #include "public/platform/Platform.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
 
 namespace blink {
 
@@ -100,10 +98,10 @@
   static std::unique_ptr<ImageDecoder> Create(
       PassRefPtr<SharedBuffer> data,
       bool data_complete,
-      AlphaOption alphaoption,
+      AlphaOption alpha_option,
       const ColorBehavior& color_behavior) {
     return Create(SegmentReader::CreateFromSharedBuffer(std::move(data)),
-                  data_complete, alphaoption, color_behavior);
+                  data_complete, alpha_option, color_behavior);
   }
 
   virtual String FilenameExtension() const = 0;
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageFrame.h b/third_party/WebKit/Source/platform/image-decoders/ImageFrame.h
index 7cd665a2..7c0bbf9 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageFrame.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageFrame.h
@@ -31,11 +31,11 @@
 #include "platform/geometry/IntRect.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Assertions.h"
-#include "platform/wtf/PassRefPtr.h"
 #include "public/platform/WebVector.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColorPriv.h"
-#include "third_party/skia/include/core/SkImage.h"
+
+class SkImage;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/SegmentReader.cpp b/third_party/WebKit/Source/platform/image-decoders/SegmentReader.cpp
index f40f5e7..fc4ffe9 100644
--- a/third_party/WebKit/Source/platform/image-decoders/SegmentReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/SegmentReader.cpp
@@ -11,6 +11,7 @@
 #include "platform/wtf/RefPtr.h"
 #include "platform/wtf/ThreadingPrimitives.h"
 #include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRWBuffer.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/SegmentReader.h b/third_party/WebKit/Source/platform/image-decoders/SegmentReader.h
index 8fc14e7..0c99cf2 100644
--- a/third_party/WebKit/Source/platform/image-decoders/SegmentReader.h
+++ b/third_party/WebKit/Source/platform/image-decoders/SegmentReader.h
@@ -9,9 +9,9 @@
 #include "platform/wtf/Noncopyable.h"
 #include "platform/wtf/PassRefPtr.h"
 #include "platform/wtf/ThreadSafeRefCounted.h"
-#include "third_party/skia/include/core/SkData.h"
-#include "third_party/skia/include/core/SkRWBuffer.h"
-#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkData;
+class SkROBuffer;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
index 5666ef6..b225a10 100644
--- a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
@@ -31,6 +31,7 @@
 #include "platform/image-decoders/bmp/BMPImageDecoder.h"
 
 #include "platform/image-decoders/FastSharedBufferReader.h"
+#include "platform/image-decoders/bmp/BMPImageReader.h"
 #include "platform/wtf/PtrUtil.h"
 
 namespace blink {
@@ -46,6 +47,8 @@
     : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
       decoded_offset_(0) {}
 
+BMPImageDecoder::~BMPImageDecoder() = default;
+
 void BMPImageDecoder::OnSetData(SegmentReader* data) {
   if (reader_)
     reader_->SetData(data);
diff --git a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.h
index e79a93c..0da6e091 100644
--- a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.h
@@ -31,16 +31,20 @@
 #ifndef BMPImageDecoder_h
 #define BMPImageDecoder_h
 
-#include "platform/image-decoders/bmp/BMPImageReader.h"
 #include <memory>
+#include "platform/image-decoders/ImageDecoder.h"
 
 namespace blink {
 
+class BMPImageReader;
+
 // This class decodes the BMP image format.
 class PLATFORM_EXPORT BMPImageDecoder final : public ImageDecoder {
  public:
   BMPImageDecoder(AlphaOption, const ColorBehavior&, size_t max_decoded_bytes);
 
+  ~BMPImageDecoder() override;
+
   // ImageDecoder:
   String FilenameExtension() const override { return "bmp"; }
   void OnSetData(SegmentReader*) override;
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
index 4759d53..8beecc83 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
@@ -75,6 +75,7 @@
 #include "platform/image-decoders/gif/GIFImageReader.h"
 
 #include <string.h>
+#include "platform/image-decoders/FastSharedBufferReader.h"
 #include "platform/wtf/PtrUtil.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.h b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.h
index 60d07e4..86a2296 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.h
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.h
@@ -41,7 +41,6 @@
 // Define ourselves as the clientPtr.  Mozilla just hacked their C++ callback
 // class into this old C decoder, so we will too.
 #include <memory>
-#include "platform/image-decoders/FastSharedBufferReader.h"
 #include "platform/image-decoders/gif/GIFImageDecoder.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Noncopyable.h"
@@ -49,6 +48,8 @@
 
 namespace blink {
 
+class FastSharedBufferReader;
+
 const int kCLoopCountNotSeen = -2;
 
 // List of possible parsing states.
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
index 2fdf29e..57007c3 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
@@ -28,6 +28,8 @@
 
 #include "platform/image-decoders/webp/WEBPImageDecoder.h"
 
+#include "third_party/skia/include/core/SkData.h"
+
 #if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN)
 #error Blink assumes a little-endian target.
 #endif
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
index 483f213..ba2bd3d1 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
@@ -30,10 +30,11 @@
 #define WEBPImageDecoder_h
 
 #include "platform/image-decoders/ImageDecoder.h"
-#include "third_party/skia/include/core/SkData.h"
 #include "webp/decode.h"
 #include "webp/demux.h"
 
+class SkData;
+
 namespace blink {
 
 class PLATFORM_EXPORT WEBPImageDecoder final : public ImageDecoder {
diff --git a/third_party/WebKit/Source/platform/loader/fetch/Resource.cpp b/third_party/WebKit/Source/platform/loader/fetch/Resource.cpp
index 6804885..1d1f861b7 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/Resource.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/Resource.cpp
@@ -178,7 +178,7 @@
 }
 
 String Resource::CachedMetadataHandlerImpl::Encoding() const {
-  return resource_->Encoding();
+  return String(resource_->Encoding().GetName());
 }
 
 void Resource::CachedMetadataHandlerImpl::SetSerializedCachedMetadata(
diff --git a/third_party/WebKit/Source/platform/loader/fetch/Resource.h b/third_party/WebKit/Source/platform/loader/fetch/Resource.h
index 1b300e9..e1c25fea 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/Resource.h
+++ b/third_party/WebKit/Source/platform/loader/fetch/Resource.h
@@ -44,6 +44,7 @@
 #include "platform/wtf/HashCountedSet.h"
 #include "platform/wtf/HashSet.h"
 #include "platform/wtf/text/AtomicString.h"
+#include "platform/wtf/text/TextEncoding.h"
 #include "platform/wtf/text/WTFString.h"
 #include "public/platform/WebDataConsumerHandle.h"
 
@@ -105,8 +106,7 @@
 
   DECLARE_VIRTUAL_TRACE();
 
-  virtual void SetEncoding(const String&) {}
-  virtual String Encoding() const { return String(); }
+  virtual WTF::TextEncoding Encoding() const { return WTF::TextEncoding(); }
   virtual void AppendData(const char*, size_t);
   virtual void FinishAsError(const ResourceError&);
   virtual void SetCORSFailed() {}
@@ -420,6 +420,8 @@
 
   void TriggerNotificationForFinishObservers();
 
+  virtual void SetEncoding(const String&) {}
+
  private:
   class CachedMetadataHandlerImpl;
   class ServiceWorkerResponseCachedMetadataHandler;
diff --git a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
index 2f6d1541..d0e10b6 100644
--- a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
+++ b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
@@ -89,12 +89,6 @@
   return FilePathToWebString(BlinkRootFilePath());
 }
 
-String ExecutableDir() {
-  base::FilePath path;
-  base::PathService::Get(base::DIR_EXE, &path);
-  return FilePathToWebString(base::MakeAbsoluteFilePath(path));
-}
-
 String WebTestDataPath(const String& relative_path) {
   return FilePathToWebString(
       BlinkRootFilePath()
diff --git a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.h b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.h
index 6f18cad..3b94f961 100644
--- a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.h
+++ b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.h
@@ -52,9 +52,6 @@
 // /src/third_party/WebKit.
 String BlinkRootDir();
 
-// Returns directory containing the current executable as absolute path.
-String ExecutableDir();
-
 // Returns test data absolute path for webkit_unit_tests, i.e.
 // <blinkRootDir>/Source/web/tests/data/<relativePath>.
 // It returns the top web test directory if |relativePath| was not specified.
diff --git a/third_party/WebKit/Source/platform/wtf/PassRefPtr.h b/third_party/WebKit/Source/platform/wtf/PassRefPtr.h
index cde758f..b2d9031 100644
--- a/third_party/WebKit/Source/platform/wtf/PassRefPtr.h
+++ b/third_party/WebKit/Source/platform/wtf/PassRefPtr.h
@@ -37,10 +37,6 @@
 class RefPtr;
 template <typename T>
 class PassRefPtr;
-template <typename T>
-PassRefPtr<T> AdoptRef(T*);
-
-inline void Adopted(const void*) {}
 
 // requireAdoption() is not overloaded for WTF::RefCounted, which has a built-in
 // assumption that adoption is required. requireAdoption() is for bootstrapping
@@ -94,12 +90,7 @@
   bool operator!() const { return !ptr_; }
   explicit operator bool() const { return ptr_ != nullptr; }
 
-  friend PassRefPtr AdoptRef<T>(T*);
-
  private:
-  enum AdoptRefTag { kAdoptRef };
-  PassRefPtr(T* ptr, AdoptRefTag) : ptr_(ptr) {}
-
   PassRefPtr& operator=(const PassRefPtr&) {
     static_assert(!sizeof(T*), "PassRefPtr should never be assigned to");
     return *this;
@@ -206,12 +197,6 @@
 }
 
 template <typename T>
-PassRefPtr<T> AdoptRef(T* p) {
-  Adopted(p);
-  return PassRefPtr<T>(p, PassRefPtr<T>::kAdoptRef);
-}
-
-template <typename T>
 inline T* GetPtr(const PassRefPtr<T>& p) {
   return p.Get();
 }
@@ -219,6 +204,5 @@
 }  // namespace WTF
 
 using WTF::PassRefPtr;
-using WTF::AdoptRef;
 
 #endif  // WTF_PassRefPtr_h
diff --git a/third_party/WebKit/Source/platform/wtf/PassRefPtrTest.cpp b/third_party/WebKit/Source/platform/wtf/PassRefPtrTest.cpp
index 2386156..f479330 100644
--- a/third_party/WebKit/Source/platform/wtf/PassRefPtrTest.cpp
+++ b/third_party/WebKit/Source/platform/wtf/PassRefPtrTest.cpp
@@ -5,6 +5,7 @@
 #include "platform/wtf/PassRefPtr.h"
 
 #include "platform/wtf/RefCounted.h"
+#include "platform/wtf/RefPtr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace WTF {
diff --git a/third_party/WebKit/Source/platform/wtf/RefPtr.h b/third_party/WebKit/Source/platform/wtf/RefPtr.h
index b4b8113..01856fc 100644
--- a/third_party/WebKit/Source/platform/wtf/RefPtr.h
+++ b/third_party/WebKit/Source/platform/wtf/RefPtr.h
@@ -35,6 +35,13 @@
 class PassRefPtr;
 template <typename T>
 class RefPtrValuePeeker;
+template <typename T>
+class RefPtr;
+
+template <typename T>
+RefPtr<T> AdoptRef(T*);
+
+inline void Adopted(const void*) {}
 
 template <typename T>
 class RefPtr {
@@ -52,6 +59,9 @@
     RefIfNotNull(ptr_);
   }
   RefPtr(RefPtr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; }
+  template <typename U>
+  RefPtr(RefPtr<U>&& o, EnsurePtrConvertibleArgDecl(U, T))
+      : ptr_(o.LeakRef()) {}
 
   // See comments in PassRefPtr.h for an explanation of why this takes a const
   // reference.
@@ -70,11 +80,7 @@
   ALWAYS_INLINE T* Get() const { return ptr_; }
   T* LeakRef() WARN_UNUSED_RESULT;
   void Clear();
-  PassRefPtr<T> Release() WARN_UNUSED_RESULT {
-    PassRefPtr<T> tmp = AdoptRef(ptr_);
-    ptr_ = nullptr;
-    return tmp;
-  }
+  PassRefPtr<T> Release() WARN_UNUSED_RESULT { return std::move(*this); }
 
   T& operator*() const { return *ptr_; }
   ALWAYS_INLINE T* operator->() const { return ptr_; }
@@ -99,6 +105,11 @@
   static T* HashTableDeletedValue() { return reinterpret_cast<T*>(-1); }
 
  private:
+  friend RefPtr AdoptRef<T>(T*);
+
+  enum AdoptRefTag { kAdoptRef };
+  RefPtr(T* ptr, AdoptRefTag) : ptr_(ptr) {}
+
   T* ptr_;
 };
 
@@ -212,8 +223,15 @@
   T* ptr_;
 };
 
+template <typename T>
+RefPtr<T> AdoptRef(T* p) {
+  Adopted(p);
+  return RefPtr<T>(p, RefPtr<T>::kAdoptRef);
+}
+
 }  // namespace WTF
 
 using WTF::RefPtr;
+using WTF::AdoptRef;
 
 #endif  // WTF_RefPtr_h
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextCodecICU.cpp b/third_party/WebKit/Source/platform/wtf/text/TextCodecICU.cpp
index cb399fe..c940be1 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextCodecICU.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/TextCodecICU.cpp
@@ -278,7 +278,7 @@
   if (cached_converter) {
     err = U_ZERO_ERROR;
     const char* cached_name = ucnv_getName(cached_converter, &err);
-    if (U_SUCCESS(err) && encoding_ == cached_name) {
+    if (U_SUCCESS(err) && encoding_ == TextEncoding(cached_name)) {
       converter_icu_ = cached_converter;
       cached_converter = 0;
       return;
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextEncoding.h b/third_party/WebKit/Source/platform/wtf/text/TextEncoding.h
index 1d2aa86..77db194 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextEncoding.h
+++ b/third_party/WebKit/Source/platform/wtf/text/TextEncoding.h
@@ -39,8 +39,8 @@
 
  public:
   TextEncoding() : name_(0) {}
-  TextEncoding(const char* name);
-  TextEncoding(const String& name);
+  explicit TextEncoding(const char* name);
+  explicit TextEncoding(const String& name);
 
   bool IsValid() const { return name_; }
   const char* GetName() const { return name_; }
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index 6c62292..71d710b 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -83,16 +83,22 @@
 WebFrameWidget* WebFrameWidget::Create(WebWidgetClient* client,
                                        WebLocalFrame* local_root) {
   DCHECK(client) << "A valid WebWidgetClient must be supplied.";
-  // Pass the WebFrameWidget's self-reference to the caller.
-  return WebFrameWidgetImpl::Create(client, local_root);
-}
+  if (!local_root->Parent()) {
+    // Note: this isn't a leak, as the object has a self-reference that the
+    // caller needs to release by calling Close().
+    WebLocalFrameBase& main_frame = ToWebLocalFrameBase(*local_root);
+    DCHECK(main_frame.ViewImpl());
+    // Note: this can't DCHECK that the view's main frame points to
+    // |main_frame|, as provisional frames violate this precondition.
+    // TODO(dcheng): Remove the special bridge class for main frame widgets.
+    return new WebViewFrameWidget(*client, *main_frame.ViewImpl(), main_frame);
+  }
 
-WebFrameWidget* WebFrameWidget::Create(WebWidgetClient* client,
-                                       WebView* web_view,
-                                       WebLocalFrame* main_frame) {
-  DCHECK(client) << "A valid WebWidgetClient must be supplied.";
-  return new WebViewFrameWidget(*client, static_cast<WebViewBase&>(*web_view),
-                                ToWebLocalFrameBase(*main_frame));
+  DCHECK(local_root->Parent()->IsWebRemoteFrame())
+      << "Only local roots can have web frame widgets.";
+  // Note: this isn't a leak, as the object has a self-reference that the
+  // caller needs to release by calling Close().
+  return WebFrameWidgetImpl::Create(client, local_root);
 }
 
 WebFrameWidgetImpl* WebFrameWidgetImpl::Create(WebWidgetClient* client,
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index accdbfd..df90500 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -2064,10 +2064,7 @@
       WebTreeScopeType::kDocument, &web_frame_client, nullptr, nullptr);
   web_frame_client.Bind(local_frame);
   web_view->SetMainFrame(local_frame);
-
-  // TODO(dcheng): The main frame widget currently has a special case.
-  // Eliminate this once WebView is no longer a WebWidget.
-  blink::WebFrameWidget::Create(&web_widget_client, web_view, local_frame);
+  blink::WebFrameWidget::Create(&web_widget_client, local_frame);
 
   WebGestureEvent event(WebInputEvent::kGestureTap, WebInputEvent::kNoModifiers,
                         WebInputEvent::kTimeStampForTesting);
diff --git a/third_party/WebKit/public/web/WebFrameWidget.h b/third_party/WebKit/public/web/WebFrameWidget.h
index da2f088..ef2a0bb 100644
--- a/third_party/WebKit/public/web/WebFrameWidget.h
+++ b/third_party/WebKit/public/web/WebFrameWidget.h
@@ -41,18 +41,11 @@
 class WebDragData;
 class WebLocalFrame;
 class WebInputMethodController;
-class WebView;
 class WebWidgetClient;
 
 class WebFrameWidget : public WebWidget {
  public:
   BLINK_EXPORT static WebFrameWidget* Create(WebWidgetClient*, WebLocalFrame*);
-  // Creates a frame widget for a WebView. Temporary helper to help transition
-  // away from WebView inheriting WebWidget.
-  // TODO(dcheng): Remove once transition is complete.
-  BLINK_EXPORT static WebFrameWidget* Create(WebWidgetClient*,
-                                             WebView*,
-                                             WebLocalFrame* main_frame);
 
   // Sets the visibility of the WebFrameWidget.
   // We still track page-level visibility, but additionally we need to notify a
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index c9c4472..5721b9d 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -119,6 +119,7 @@
     'message_center_unittests',
     'midi_unittests',
     'mojo_common_unittests',
+    'mojo_js_integration_tests',
     'mojo_js_unittests',
     'mojo_public_bindings_unittests',
     'mojo_public_system_unittests',
@@ -275,6 +276,7 @@
     'midi_unittests.exe',
     'mini_installer.exe',
     'mksnapshot.exe',
+    'mojo_js_integration_tests.exe',
     'mojo_js_unittests.exe',
     'mojo_message_pipe_perftests.exe',
     'mojo_public_bindings_perftests.exe',
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
index b2ced8f..a665d2c 100644
--- a/tools/gn/command_gen.cc
+++ b/tools/gn/command_gen.cc
@@ -37,6 +37,7 @@
 const char kSwitchIdeValueVs2013[] = "vs2013";
 const char kSwitchIdeValueVs2015[] = "vs2015";
 const char kSwitchIdeValueVs2017[] = "vs2017";
+const char kSwitchIdeValueWinSdk[] = "winsdk";
 const char kSwitchIdeValueXcode[] = "xcode";
 const char kSwitchIdeValueJson[] = "json";
 const char kSwitchNinjaExtraArgs[] = "ninja-extra-args";
@@ -207,9 +208,13 @@
     std::string filters;
     if (command_line->HasSwitch(kSwitchFilters))
       filters = command_line->GetSwitchValueASCII(kSwitchFilters);
+    std::string win_kit;
+    if (command_line->HasSwitch(kSwitchIdeValueWinSdk))
+      win_kit = command_line->GetSwitchValueASCII(kSwitchIdeValueWinSdk);
     bool no_deps = command_line->HasSwitch(kSwitchNoDeps);
-    bool res = VisualStudioWriter::RunAndWriteFiles(
-        build_settings, builder, version, sln_name, filters, no_deps, err);
+    bool res = VisualStudioWriter::RunAndWriteFiles(build_settings, builder,
+                                                    version, sln_name, filters,
+                                                    win_kit, no_deps, err);
     if (res && !quiet) {
       OutputString("Generating Visual Studio projects took " +
                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
@@ -321,6 +326,11 @@
       Don't include targets dependencies to the solution. Changes the way how
       --filters option works. Only directly matching targets are included.
 
+  --winsdk=<sdk_version>
+      Use the specified Windows 10 SDK version to generate project files.
+      As an example, "10.0.15063.0" can be specified to use Creators Update SDK
+      instead of the default one.
+
 Xcode Flags
 
   --workspace=<file_name>
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
index a498fb9..eede7f4 100644
--- a/tools/gn/visual_studio_writer.cc
+++ b/tools/gn/visual_studio_writer.cc
@@ -74,12 +74,12 @@
 const char kToolsetVersionVs2017[] = "v141";               // Visual Studio 2017
 const char kProjectVersionVs2013[] = "12.0";               // Visual Studio 2013
 const char kProjectVersionVs2015[] = "14.0";               // Visual Studio 2015
-const char kProjectVersionVs2017[] = "15.0";               // Visual Studio 2015
+const char kProjectVersionVs2017[] = "15.0";               // Visual Studio 2017
 const char kVersionStringVs2013[] = "Visual Studio 2013";  // Visual Studio 2013
 const char kVersionStringVs2015[] = "Visual Studio 2015";  // Visual Studio 2015
 const char kVersionStringVs2017[] = "Visual Studio 2017";  // Visual Studio 2017
 const char kWindowsKitsVersion[] = "10";                   // Windows 10 SDK
-const char kWindowsKitsIncludeVersion[] = "10.0.14393.0";  // Windows 10 SDK
+const char kWindowsKitsDefaultVersion[] = "10.0.14393.0";  // Windows 10 SDK
 
 const char kGuidTypeProject[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
 const char kGuidTypeFolder[] = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
@@ -89,7 +89,7 @@
 
 const char kConfigurationName[] = "GN";
 
-std::string GetWindowsKitsIncludeDirs() {
+std::string GetWindowsKitsIncludeDirs(const std::string& win_kit) {
   std::string kits_path;
 
 #if defined(OS_WIN)
@@ -115,9 +115,8 @@
                 kWindowsKitsVersion + "\\";
   }
 
-  return kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\shared;" +
-         kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\um;" +
-         kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\winrt;";
+  const std::string kit_prefix = kits_path + "Include\\" + win_kit + "\\";
+  return kit_prefix + "shared;" + kit_prefix + "um;" + kit_prefix + "winrt;";
 }
 
 std::string GetConfigurationType(const Target* target, Err* err) {
@@ -259,12 +258,16 @@
 
 VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings,
                                        const char* config_platform,
-                                       Version version)
+                                       Version version,
+                                       const std::string& win_kit)
     : build_settings_(build_settings),
       config_platform_(config_platform),
       ninja_path_output_(build_settings->build_dir(),
                          build_settings->root_path_utf8(),
-                         EscapingMode::ESCAPE_NINJA_COMMAND) {
+                         EscapingMode::ESCAPE_NINJA_COMMAND),
+      windows_sdk_version_(win_kit) {
+  DCHECK(!win_kit.empty());
+
   switch (version) {
     case Version::Vs2013:
       project_version_ = kProjectVersionVs2013;
@@ -285,7 +288,7 @@
       NOTREACHED() << "Not a valid Visual Studio Version: " << version;
   }
 
-  windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs();
+  windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs(win_kit);
 }
 
 VisualStudioWriter::~VisualStudioWriter() {
@@ -297,12 +300,17 @@
                                           Version version,
                                           const std::string& sln_name,
                                           const std::string& filters,
+                                          const std::string& win_sdk,
                                           bool no_deps,
                                           Err* err) {
   std::vector<const Target*> targets;
   if (!FilterTargets(build_settings, builder, filters, no_deps, &targets, err))
     return false;
 
+  std::string win_kit = kWindowsKitsDefaultVersion;
+  if (!win_sdk.empty())
+    win_kit = win_sdk;
+
   const char* config_platform = "Win32";
 
   // Assume the "target_cpu" variable does not change between different
@@ -315,7 +323,7 @@
       config_platform = "x64";
   }
 
-  VisualStudioWriter writer(build_settings, config_platform, version);
+  VisualStudioWriter writer(build_settings, config_platform, version, win_kit);
   writer.projects_.reserve(targets.size());
   writer.folders_.reserve(targets.size());
 
@@ -436,7 +444,7 @@
     globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true");
     globals->SubElement("PreferredToolArchitecture")->Text("x64");
     globals->SubElement("WindowsTargetPlatformVersion")
-        ->Text(kWindowsKitsIncludeVersion);
+        ->Text(windows_sdk_version_);
   }
 
   project.SubElement(
diff --git a/tools/gn/visual_studio_writer.h b/tools/gn/visual_studio_writer.h
index 17935731..db2f3ea 100644
--- a/tools/gn/visual_studio_writer.h
+++ b/tools/gn/visual_studio_writer.h
@@ -37,12 +37,14 @@
   // semicolon-separated list of label patterns used to limit the set of
   // generated projects. Only matching targets and their dependencies (unless
   // |no_deps| is true) will be included to the solution. On failure will
-  // populate |err| and will return false.
+  // populate |err| and will return false. |win_sdk| is the Windows SDK version
+  // which will be used by Visual Studio IntelliSense.
   static bool RunAndWriteFiles(const BuildSettings* build_settings,
                                const Builder& builder,
                                Version version,
                                const std::string& sln_name,
                                const std::string& filters,
+                               const std::string& win_sdk,
                                bool no_deps,
                                Err* err);
 
@@ -98,7 +100,8 @@
 
   VisualStudioWriter(const BuildSettings* build_settings,
                      const char* config_platform,
-                     Version version);
+                     Version version,
+                     const std::string& win_kit);
   ~VisualStudioWriter();
 
   bool WriteProjectFiles(const Target* target, Err* err);
@@ -150,6 +153,9 @@
   // Path formatter for ninja targets.
   PathOutput ninja_path_output_;
 
+  // Windows 10 SDK version string (e.g. 10.0.14393.0)
+  std::string windows_sdk_version_;
+
   DISALLOW_COPY_AND_ASSIGN(VisualStudioWriter);
 };
 
diff --git a/tools/gn/visual_studio_writer_unittest.cc b/tools/gn/visual_studio_writer_unittest.cc
index f89c1c1..1ad7c61c 100644
--- a/tools/gn/visual_studio_writer_unittest.cc
+++ b/tools/gn/visual_studio_writer_unittest.cc
@@ -28,7 +28,8 @@
 
 TEST_F(VisualStudioWriterTest, ResolveSolutionFolders) {
   VisualStudioWriter writer(setup_.build_settings(), "Win32",
-                            VisualStudioWriter::Version::Vs2015);
+                            VisualStudioWriter::Version::Vs2015,
+                            "10.0.14393.0");
 
   std::string path =
       MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");
@@ -82,7 +83,8 @@
 
 TEST_F(VisualStudioWriterTest, ResolveSolutionFolders_AbsPath) {
   VisualStudioWriter writer(setup_.build_settings(), "Win32",
-                            VisualStudioWriter::Version::Vs2015);
+                            VisualStudioWriter::Version::Vs2015,
+                            "10.0.14393.0");
 
   std::string path =
       MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 00b77cd..89839114 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3894,6 +3894,14 @@
   <int value="20" label="FutureCat (&gt;10.10), 8-bit (?)"/>
 </enum>
 
+<enum name="CdmHostVerificationStatus">
+  <int value="0" label="Not Called"/>
+  <int value="1" label="Success"/>
+  <int value="2" label="CDM Load Failed"/>
+  <int value="3" label="Get Function Failed"/>
+  <int value="4" label="Init Verification Failed"/>
+</enum>
+
 <enum name="CdmPromiseResult">
   <int value="0" label="Success"/>
   <int value="1" label="NotSupportedError"/>
@@ -22163,7 +22171,6 @@
   <int value="-1460462432" label="disable-media-source"/>
   <int value="-1456004000" label="VrShell:disabled"/>
   <int value="-1450576851" label="OmniboxUIExperimentVerticalLayout:enabled"/>
-  <int value="-1444051091" label="ash-disable-night-light"/>
   <int value="-1443796945" label="OfflinePagesSharing:disabled"/>
   <int value="-1440440375" label="WebVrAutopresent:enabled"/>
   <int value="-1440152291" label="disable-gesture-typing"/>
@@ -23023,6 +23030,7 @@
   <int value="1657713458" label="disable-virtual-keyboard-overscroll"/>
   <int value="1658644418" label="disable-app-list-voice-search"/>
   <int value="1661925474" label="silent-debugger-extension-api"/>
+  <int value="1664401033" label="ColorCorrectRendering:enabled"/>
   <int value="1668611601" label="enable-encrypted-media"/>
   <int value="1673427566" label="ChromeHomeExpandButton:disabled"/>
   <int value="1689123607" label="enable-app-link"/>
@@ -23035,6 +23043,7 @@
   <int value="1701972870" label="NTPSnippetsIncreasedVisibility:enabled"/>
   <int value="1702821235" label="WebAssembly:enabled"/>
   <int value="1705724232" label="use-android-midi-api"/>
+  <int value="1713230497" label="ColorCorrectRendering:disabled"/>
   <int value="1723601083" label="enable-app-window-controls"/>
   <int value="1724800383" label="AsmJsToWebAssembly:disabled"/>
   <int value="1730094138" label="enable-md-storage-manager"/>
@@ -37847,6 +37856,7 @@
   <int value="0" label="No WebContents"/>
   <int value="1" label="Prerendered WebContents"/>
   <int value="2" label="Spare WebContents"/>
+  <int value="3" label="Transferred WebContents"/>
 </enum>
 
 <enum name="WebFontCacheHit">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c053a7a..8a6d4ca 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -28618,6 +28618,14 @@
   </summary>
 </histogram>
 
+<histogram name="Media.EME.CdmHostVerificationStatus"
+    enum="CdmHostVerificationStatus">
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    The status of CDM host verification. This is reported per CDM load.
+  </summary>
+</histogram>
+
 <histogram name="Media.EME.CdmInterfaceVersion">
   <owner>xhwang@chromium.org</owner>
   <summary>
@@ -74131,6 +74139,33 @@
   </summary>
 </histogram>
 
+<histogram name="SubresourceFilter.PageLoad.Activation.CPUDuration"
+    units="microseconds">
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    Records how much thread CPU time it takes to decide whether subresource
+    filtering should be activated for a main frame.
+  </summary>
+</histogram>
+
+<histogram name="SubresourceFilter.PageLoad.Activation.WallDuration"
+    units="microseconds">
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    Records how long it takes to decide whether subresource filtering should be
+    activated for a main frame.
+  </summary>
+</histogram>
+
+<histogram name="SubresourceFilter.PageLoad.ActivationState"
+    enum="SubresourceFilterActivationState">
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    Whenever a document load is committed in a main frame, records whether
+    subresource filtering should be activated for that load.
+  </summary>
+</histogram>
+
 <histogram name="SubresourceFilter.PageLoad.FinalURLMatch" units="matches">
   <owner>melandory@chromium.org</owner>
   <summary>
diff --git a/tools/perf/docs/OWNERS b/tools/perf/docs/OWNERS
deleted file mode 100644
index a85fa5e6..0000000
--- a/tools/perf/docs/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file apk_size_regressions.md=agrieve@chromium.org
diff --git a/ui/accessibility/ax_tree_combiner.cc b/ui/accessibility/ax_tree_combiner.cc
index ae73652..68a9c8b 100644
--- a/ui/accessibility/ax_tree_combiner.cc
+++ b/ui/accessibility/ax_tree_combiner.cc
@@ -40,7 +40,7 @@
   ProcessTree(root);
 
   // Set the root id.
-  combined_.root_id = combined_.nodes[0].id;
+  combined_.root_id = combined_.nodes.size() > 0 ? combined_.nodes[0].id : 0;
 
   // Finally, handle the tree ID, taking into account which subtree might
   // have focus and mapping IDs from the tree data appropriately.
diff --git a/ui/accessibility/ax_tree_combiner_unittest.cc b/ui/accessibility/ax_tree_combiner_unittest.cc
index b80771d..70f861c0 100644
--- a/ui/accessibility/ax_tree_combiner_unittest.cc
+++ b/ui/accessibility/ax_tree_combiner_unittest.cc
@@ -192,4 +192,15 @@
   EXPECT_EQ(6, combined.tree_data.focus_id);
 }
 
+TEST(CombineAXTreesTest, EmptyTree) {
+  AXTreeUpdate tree;
+
+  AXTreeCombiner combiner;
+  combiner.AddTree(tree, true);
+  combiner.Combine();
+
+  const AXTreeUpdate& combined = combiner.combined();
+  ASSERT_EQ(0U, combined.nodes.size());
+}
+
 }  // namespace ui
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index e76925d..d8a0150 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -134,7 +134,7 @@
       command_line->HasSwitch(cc::switches::kUIEnableLayerLists);
 
   settings.enable_color_correct_rasterization =
-      command_line->HasSwitch(switches::kEnableColorCorrectRendering);
+      base::FeatureList::IsEnabled(features::kColorCorrectRendering);
 
   // UI compositor always uses partial raster if not using zero-copy. Zero copy
   // doesn't currently support partial raster.
diff --git a/ui/compositor/compositor_util.cc b/ui/compositor/compositor_util.cc
index 20231eb..0ced0a7e 100644
--- a/ui/compositor/compositor_util.cc
+++ b/ui/compositor/compositor_util.cc
@@ -29,7 +29,7 @@
   renderer_settings.show_overdraw_feedback =
       command_line->HasSwitch(cc::switches::kShowOverdrawFeedback);
   renderer_settings.enable_color_correct_rendering =
-      command_line->HasSwitch(switches::kEnableColorCorrectRendering) ||
+      base::FeatureList::IsEnabled(features::kColorCorrectRendering) ||
       command_line->HasSwitch(switches::kEnableHDR);
   // Populate buffer_to_texture_target_map for all buffer usage/formats.
   for (int usage_idx = 0; usage_idx <= static_cast<int>(gfx::BufferUsage::LAST);
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
index d74ab25c..d9d07345 100644
--- a/ui/display/mac/screen_mac.mm
+++ b/ui/display/mac/screen_mac.mm
@@ -84,8 +84,7 @@
   // https://crbug.com/654488
   CGColorSpaceRef color_space = [[screen colorSpace] CGColorSpace];
   static bool color_correct_rendering_enabled =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableColorCorrectRendering);
+      base::FeatureList::IsEnabled(features::kColorCorrectRendering);
   if (base::mac::IsAtLeastOS10_12() && !color_correct_rendering_enabled)
     color_space = base::mac::GetSystemColorSpace();
 
diff --git a/ui/gfx/color_space_switches.cc b/ui/gfx/color_space_switches.cc
index 33295ee..31e7ca8 100644
--- a/ui/gfx/color_space_switches.cc
+++ b/ui/gfx/color_space_switches.cc
@@ -5,11 +5,14 @@
 #include "ui/gfx/color_space_switches.h"
 #include "build/build_config.h"
 
-namespace switches {
+namespace features {
 
-// Convert rasterization and compositing inputs to the output color space
-// before operating on them.
-const char kEnableColorCorrectRendering[] = "enable-color-correct-rendering";
+const base::Feature kColorCorrectRendering{"ColorCorrectRendering",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace features
+
+namespace switches {
 
 // Force all monitors to be treated as though they have the specified color
 // profile. Accepted values are "srgb" and "generic-rgb" (currently used by Mac
diff --git a/ui/gfx/color_space_switches.h b/ui/gfx/color_space_switches.h
index 904eca3..0af262b 100644
--- a/ui/gfx/color_space_switches.h
+++ b/ui/gfx/color_space_switches.h
@@ -5,12 +5,17 @@
 #ifndef UI_GFX_COLOR_SPACE_SWITCHES_H_
 #define UI_GFX_COLOR_SPACE_SWITCHES_H_
 
+#include "base/feature_list.h"
 #include "build/build_config.h"
 #include "ui/gfx/switches_export.h"
 
-namespace switches {
+namespace features {
 
-GFX_SWITCHES_EXPORT extern const char kEnableColorCorrectRendering[];
+GFX_SWITCHES_EXPORT extern const base::Feature kColorCorrectRendering;
+
+}  // namespace features
+
+namespace switches {
 
 GFX_SWITCHES_EXPORT extern const char kForceColorProfile[];
 
diff --git a/ui/gfx/mac/io_surface.cc b/ui/gfx/mac/io_surface.cc
index 8fb16032..ee10bf3 100644
--- a/ui/gfx/mac/io_surface.cc
+++ b/ui/gfx/mac/io_surface.cc
@@ -210,8 +210,7 @@
   // Ensure that all IOSurfaces start as sRGB when color correct rendering
   // is enabled.
   static bool color_correct_rendering_enabled =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableColorCorrectRendering);
+      base::FeatureList::IsEnabled(features::kColorCorrectRendering);
   if (color_correct_rendering_enabled)
     force_color_space = true;
 
diff --git a/ui/gl/gl_context.cc b/ui/gl/gl_context.cc
index 08a0c78b..0f3b3561 100644
--- a/ui/gl/gl_context.cc
+++ b/ui/gl/gl_context.cc
@@ -136,6 +136,10 @@
   InitializeDynamicBindings();
 }
 
+void GLContext::ForceReleaseVirtuallyCurrent() {
+  NOTREACHED();
+}
+
 bool GLContext::HasExtension(const char* name) {
   std::string extensions = GetExtensions();
   extensions += " ";
diff --git a/ui/gl/gl_context.h b/ui/gl/gl_context.h
index 69f4c38..0d1aa6d 100644
--- a/ui/gl/gl_context.h
+++ b/ui/gl/gl_context.h
@@ -176,6 +176,11 @@
   // extension entry points.
   void ReinitializeDynamicBindings();
 
+  // Forces this context, which must be a virtual context, to be no
+  // longer considered virtually current. The real context remains
+  // current.
+  virtual void ForceReleaseVirtuallyCurrent();
+
  protected:
   virtual ~GLContext();
 
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
index 9681153..36a73ff366 100644
--- a/ui/ozone/BUILD.gn
+++ b/ui/ozone/BUILD.gn
@@ -138,6 +138,7 @@
     ":ozone_base",
     "//base",
     "//ipc",
+    "//services/service_manager/public/cpp",
     "//skia",
     "//ui/display/types",
     "//ui/events",
diff --git a/ui/ozone/DEPS b/ui/ozone/DEPS
index e76be7b..1ac3a7a 100644
--- a/ui/ozone/DEPS
+++ b/ui/ozone/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+services/service_manager/public/cpp",
   "+skia/ext",
   "+third_party/khronos",
   "+third_party/skia",
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
index 4b4913b..7c3cba7 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -103,6 +103,9 @@
     return;
   }
 
+  // TODO(dcastagna): remove glFlush since eglImageFlushExternalEXT called on
+  // the image should be enough (crbug.com/720045).
+  glFlush();
   unsubmitted_frames_.back()->Flush();
 
   SwapCompletionCallback surface_swap_callback = base::Bind(
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index 8dc9fe3..a6f87c7 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/ozone/ozone_export.h"
 
 namespace display {
@@ -24,7 +25,6 @@
 }
 
 namespace service_manager {
-class BinderRegistry;
 class Connector;
 }
 
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index b02a804..0acad06b 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -194,17 +194,15 @@
 
 NonClientFrameView* DialogDelegate::CreateNonClientFrameView(Widget* widget) {
   if (ShouldUseCustomFrame())
-    return CreateDialogFrameView(widget, gfx::Insets());
+    return CreateDialogFrameView(widget);
   return WidgetDelegate::CreateNonClientFrameView(widget);
 }
 
 // static
-NonClientFrameView* DialogDelegate::CreateDialogFrameView(
-    Widget* widget,
-    const gfx::Insets& content_margins) {
+NonClientFrameView* DialogDelegate::CreateDialogFrameView(Widget* widget) {
   BubbleFrameView* frame = new BubbleFrameView(
       LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_TITLE),
-      content_margins);
+      gfx::Insets());
   const BubbleBorder::Shadow kShadow = BubbleBorder::SMALL_SHADOW;
   std::unique_ptr<BubbleBorder> border(
       new BubbleBorder(BubbleBorder::FLOAT, kShadow, gfx::kPlaceholderColor));
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index becd0692..054edfe 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -104,12 +104,7 @@
   ClientView* CreateClientView(Widget* widget) override;
   NonClientFrameView* CreateNonClientFrameView(Widget* widget) override;
 
-  // Create a frame view using the new dialog style.
-  // |content_margins|: margins between the content and the inside of the
-  // border, in pixels.
-  static NonClientFrameView* CreateDialogFrameView(
-      Widget* widget,
-      const gfx::Insets& content_margins);
+  static NonClientFrameView* CreateDialogFrameView(Widget* widget);
 
   // Returns true if this particular dialog should use a Chrome-styled frame
   // like the one used for bubbles. The alternative is a more platform-native