diff --git a/DEPS b/DEPS
index ed89047..655b1c8 100644
--- a/DEPS
+++ b/DEPS
@@ -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': '22eb7ae54e4351f70a5a01b0130c8b0dc713586c',
+  'pdfium_revision': '0646275cd9c19e9fb97eaa023494bb771e47e095',
   # 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': 'bdff8501d71be9e9e710f3effa09a5c3a7acaa6a',
+  'catapult_revision': 'dc62dbb86964e5fe9b9175588ed10a4b4c229a69',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/browser/aw_safe_browsing_resource_throttle.cc b/android_webview/browser/aw_safe_browsing_resource_throttle.cc
index 8f7815280..a43fe85 100644
--- a/android_webview/browser/aw_safe_browsing_resource_throttle.cc
+++ b/android_webview/browser/aw_safe_browsing_resource_throttle.cc
@@ -43,8 +43,7 @@
           resource_type,
           safe_browsing::CreateSBThreatTypeSet(
               {safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
-               safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
-               safe_browsing::SB_THREAT_TYPE_URL_UNWANTED}),
+               safe_browsing::SB_THREAT_TYPE_URL_PHISHING}),
           database_manager,
           ui_manager),
       request_(request) {}
@@ -57,17 +56,4 @@
   Cancel();
 }
 
-void AwSafeBrowsingResourceThrottle::OnCheckBrowseUrlResult(
-    const GURL& url,
-    SBThreatType threat_type,
-    const ThreatMetadata& metadata) {
-  if (threat_type != safe_browsing::SB_THREAT_TYPE_URL_PHISHING &&
-      threat_type != safe_browsing::SB_THREAT_TYPE_URL_MALWARE) {
-    // If we don't recognize the threat type, just mark it as safe
-    threat_type = safe_browsing::SB_THREAT_TYPE_SAFE;
-  }
-
-  BaseResourceThrottle::OnCheckBrowseUrlResult(url, threat_type, metadata);
-}
-
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_safe_browsing_resource_throttle.h b/android_webview/browser/aw_safe_browsing_resource_throttle.h
index 5991c84bb..65a17e6 100644
--- a/android_webview/browser/aw_safe_browsing_resource_throttle.h
+++ b/android_webview/browser/aw_safe_browsing_resource_throttle.h
@@ -35,10 +35,6 @@
 
   static const void* kUserDataKey;
 
-  void OnCheckBrowseUrlResult(const GURL& url,
-                              SBThreatType threat_type,
-                              const ThreatMetadata& metadata) override;
-
  private:
   AwSafeBrowsingResourceThrottle(
       net::URLRequest* request,
diff --git a/android_webview/browser/net/aw_cookie_store_wrapper.cc b/android_webview/browser/net/aw_cookie_store_wrapper.cc
index 5a92c05b..88b8a60 100644
--- a/android_webview/browser/net/aw_cookie_store_wrapper.cc
+++ b/android_webview/browser/net/aw_cookie_store_wrapper.cc
@@ -246,7 +246,7 @@
     bool secure_source,
     bool modify_http_only,
     SetCookiesCallback callback) {
-  DCHECK(client_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(client_task_runner_->RunsTasksInCurrentSequence());
   PostTaskToCookieStoreTaskRunner(base::BindOnce(
       &SetCanonicalCookieAsyncOnCookieThread, std::move(cookie), secure_source,
       modify_http_only, CreateWrappedCallback<bool>(std::move(callback))));
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index b4a26bf..a1ddd7a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -35,6 +35,8 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.net.test.EmbeddedTestServer;
 
+import java.util.Arrays;
+
 /**
  * Test suite for SafeBrowsing.
  *
@@ -79,10 +81,9 @@
     public static class MockSafeBrowsingApiHandler implements SafeBrowsingApiHandler {
         private Observer mObserver;
         private static final String SAFE_METADATA = "{}";
-        private static final String PHISHING_METADATA = "{\"matches\":[{\"threat_type\":\"5\"}]}";
-        private static final String MALWARE_METADATA = "{\"matches\":[{\"threat_type\":\"4\"}]}";
-        private static final String UNWANTED_SOFTWARE_METADATA =
-                "{\"matches\":[{\"threat_type\":\"3\"}]}";
+        private static final int PHISHING_CODE = 5;
+        private static final int MALWARE_CODE = 4;
+        private static final int UNWANTED_SOFTWARE_CODE = 3;
 
         @Override
         public boolean init(Context context, Observer result) {
@@ -90,15 +91,27 @@
             return true;
         }
 
+        private String buildMetadataFromCode(int code) {
+            return "{\"matches\":[{\"threat_type\":\"" + code + "\"}]}";
+        }
+
         @Override
         public void startUriLookup(final long callbackId, String uri, int[] threatsOfInterest) {
             final String metadata;
-            if (uri.endsWith(PHISHING_HTML_PATH)) {
-                metadata = PHISHING_METADATA;
-            } else if (uri.endsWith(MALWARE_HTML_PATH)) {
-                metadata = MALWARE_METADATA;
-            } else if (uri.endsWith(UNWANTED_SOFTWARE_HTML_PATH)) {
-                metadata = UNWANTED_SOFTWARE_METADATA;
+            Arrays.sort(threatsOfInterest);
+
+            // TODO(ntfschr): remove this assert once we support UwS warnings (crbug/729272)
+            assertEquals(Arrays.binarySearch(threatsOfInterest, UNWANTED_SOFTWARE_CODE), -1);
+
+            if (uri.endsWith(PHISHING_HTML_PATH)
+                    && Arrays.binarySearch(threatsOfInterest, PHISHING_CODE) >= 0) {
+                metadata = buildMetadataFromCode(PHISHING_CODE);
+            } else if (uri.endsWith(MALWARE_HTML_PATH)
+                    && Arrays.binarySearch(threatsOfInterest, MALWARE_CODE) >= 0) {
+                metadata = buildMetadataFromCode(MALWARE_CODE);
+            } else if (uri.endsWith(UNWANTED_SOFTWARE_HTML_PATH)
+                    && Arrays.binarySearch(threatsOfInterest, UNWANTED_SOFTWARE_CODE) >= 0) {
+                metadata = buildMetadataFromCode(UNWANTED_SOFTWARE_CODE);
             } else {
                 metadata = SAFE_METADATA;
             }
@@ -280,8 +293,8 @@
     @Feature({"AndroidWebView"})
     @CommandLineFlags.Add(AwSwitches.WEBVIEW_ENABLE_SAFEBROWSING_SUPPORT)
     public void testSafeBrowsingDoesNotBlockUnwantedSoftwarePages() throws Throwable {
-        // TODO(ntfschr): this is a temporary check until we add support for Unwanted Software
-        // warnings
+        // TODO(ntfschr): this is a temporary check until we add support for UwS warnings
+        // (crbug/729272)
         loadGreenPage();
         final String responseUrl = mTestServer.getURL(UNWANTED_SOFTWARE_HTML_PATH);
         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
diff --git a/ash/ime/ime_controller_unittest.cc b/ash/ime/ime_controller_unittest.cc
index 46a93cf..d454d86 100644
--- a/ash/ime/ime_controller_unittest.cc
+++ b/ash/ime/ime_controller_unittest.cc
@@ -19,10 +19,9 @@
 namespace {
 
 // Refreshes the IME list with fake IMEs and fake menu items.
-void RefreshImes(const std::string& current_ime_id,
-                 const std::vector<std::string>& ime_ids,
-                 const std::vector<std::string>& menu_item_keys =
-                     std::vector<std::string>()) {
+void RefreshImesWithMenuItems(const std::string& current_ime_id,
+                              const std::vector<std::string>& ime_ids,
+                              const std::vector<std::string>& menu_item_keys) {
   std::vector<mojom::ImeInfoPtr> available_imes;
   for (const std::string& ime_id : ime_ids) {
     mojom::ImeInfoPtr ime = mojom::ImeInfo::New();
@@ -39,6 +38,13 @@
       current_ime_id, std::move(available_imes), std::move(menu_items));
 }
 
+// Refreshes the IME list without adding any menu items.
+void RefreshImes(const std::string& current_ime_id,
+                 const std::vector<std::string>& ime_ids) {
+  const std::vector<std::string> empty_menu_items;
+  RefreshImesWithMenuItems(current_ime_id, ime_ids, empty_menu_items);
+}
+
 class TestImeObserver : public IMEObserver {
  public:
   TestImeObserver() = default;
@@ -92,7 +98,7 @@
   TestImeObserver observer;
   Shell::Get()->system_tray_notifier()->AddIMEObserver(&observer);
 
-  RefreshImes("ime1", {"ime1", "ime2"}, {"menu1"});
+  RefreshImesWithMenuItems("ime1", {"ime1", "ime2"}, {"menu1"});
 
   // Cached data was updated.
   EXPECT_EQ("ime1", controller->current_ime().id);
diff --git a/base/task_scheduler/task_scheduler_impl.h b/base/task_scheduler/task_scheduler_impl.h
index e1dfd4f..f9af1b5 100644
--- a/base/task_scheduler/task_scheduler_impl.h
+++ b/base/task_scheduler/task_scheduler_impl.h
@@ -103,14 +103,10 @@
   AtomicFlag join_for_testing_returned_;
 #endif
 
-#if defined(OS_WIN)
-// The check below cannot be &&'ed with defined(OS_WIN) since the preprocessor
-// will find that the macro is undefined even if defined(OS_WIN) is not true.
-#if COM_INIT_CHECK_HOOK_ENABLED()
+#if defined(OS_WIN) && defined(COM_INIT_CHECK_HOOK_ENABLED)
   // Provides COM initialization verification for supported builds.
   base::win::ComInitCheckHook com_init_check_hook_;
 #endif
-#endif
 
   DISALLOW_COPY_AND_ASSIGN(TaskSchedulerImpl);
 };
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index d6392d1..ac12aed 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1724,14 +1724,6 @@
         # dump_syms, so this is still required (https://crbug.com/622406).
         cflags += [ "-fno-standalone-debug" ]
       }
-    } else if (is_android) {
-      # Breakpad can't handle DWARF 4 symbols properly yet, so use DWARF 3
-      # explicitly on android where we are hitting https://crbug.com/638485.
-      # The arguments MUST be in this order because of a gcc arg parsing bug.
-      cflags = [
-        "-gdwarf-3",
-        "-g2",
-      ]
     } else {
       cflags = [ "-g2" ]
     }
@@ -1765,16 +1757,16 @@
     cflags = []
     ldflags = [ "/DEBUG" ]
   } else {
+    cflags = [ "-g1" ]
     if (is_android) {
-      # Breakpad can't handle DWARF 4 symbols properly yet, so use DWARF 3
-      # explicitly on android where we are hitting https://crbug.com/638485.
-      # The arguments MUST be in this order because of a gcc arg parsing bug.
-      cflags = [
-        "-gdwarf-3",
-        "-g1",
-      ]
-    } else {
-      cflags = [ "-g1" ]
+      # Android defaults to symbol_level=1 builds in production builds
+      # (https://crbug.com/648948), but clang, unlike gcc, doesn't emit
+      # DW_AT_linkage_name in -g1 builds. -fdebug-info-for-profiling enables
+      # that (and a bunch of other things we don't need), so that we get
+      # qualified names in stacks.
+      # TODO(thakis): Consider making clang emit DW_AT_linkage_name in -g1 mode;
+      #               failing that consider doing this on non-Android too.
+      cflags += [ "-fdebug-info-for-profiling" ]
     }
 
     # Note: -gsplit-dwarf implicitly turns on -g2 with clang, so don't pass it.
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni
index 5429938..fc6c681 100644
--- a/build/config/ios/rules.gni
+++ b/build/config/ios/rules.gni
@@ -1614,3 +1614,75 @@
 set_defaults("ios_xctest_test") {
   configs = default_executable_configs
 }
+
+# Template to build a XCUITest that consists of two parts: the test runner
+# application bundle and the xctest dynamic library.
+#
+# Arguments
+#
+#   deps:
+#       list of labels to depends on, these values are used to create the
+#       xctest dynamic library.
+#
+# This template defines two targets, one named "${target_name}" is the xctest
+# dynamic library, and the other named "${target_name}_runner" is the test
+# runner application bundle.
+#
+template("ios_xcuitest_test") {
+  assert(false,
+         "ios_xcuitest_test is incomplete and should not be used, see " +
+             "crbug.com/709289 for tracking")
+
+  _xcuitest_target = target_name
+  _xcuitest_runner_target = _xcuitest_target + "_runner"
+  _xctrunner_path =
+      "$ios_sdk_platform_path/Developer/Library/Xcode/Agents/XCTRunner.app"
+
+  _xcuitest_runner_info_plist_merge_plist =
+      _xcuitest_runner_target + "_info_plist_merge_plist"
+  _xcuitest_runner_info_plist_target = _xcuitest_runner_target + "_info_plist"
+  _xcuitest_runner_info_plist_bundle =
+      _xcuitest_runner_target + "_info_plist_bundle"
+
+  action(_xcuitest_runner_info_plist_merge_plist) {
+    testonly = true
+    script = "//build/config/mac/plist_util.py"
+
+    sources = [
+      "$_xctrunner_path/Info.plist",
+
+      # NOTE: The XCTRunnerAddition+Info.plist must come after the Info.plist
+      # because it overrides the values under "CFBundleIdentifier" and
+      # "CFBundleName".
+      "//ios/chrome/app/resources/XCTRunnerAddition+Info.plist",
+    ]
+
+    _output_name = "$target_gen_dir/${_xcuitest_runner_target}_merged.plist"
+    outputs = [
+      _output_name,
+    ]
+    args = [
+             "merge",
+             "-f=xml1",
+             "-o=" + rebase_path(_output_name, root_build_dir),
+           ] + rebase_path(sources, root_build_dir)
+  }
+
+  ios_info_plist(_xcuitest_runner_info_plist_target) {
+    testonly = true
+    visibility = [ ":$_xcuitest_runner_info_plist_bundle" ]
+
+    executable_name = _xcuitest_target
+    info_plist_target = ":$_xcuitest_runner_info_plist_merge_plist"
+    if (ios_automatically_manage_certs) {
+      # Use the same bundle identifier for XCUITest tests as for unit tests
+      # when managing certificates as the number of free certs is limited.
+      extra_substitutions =
+          [ "PRODUCT_NAME=${ios_generic_test_bundle_id_suffix}" ]
+    }
+  }
+}
+
+set_defaults("ios_xcuitest_test") {
+  configs = default_executable_configs
+}
diff --git a/build/mac/should_use_hermetic_xcode.py b/build/mac/should_use_hermetic_xcode.py
index 68e288e..dc1b930 100755
--- a/build/mac/should_use_hermetic_xcode.py
+++ b/build/mac/should_use_hermetic_xcode.py
@@ -3,8 +3,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Prints "1" if Chrome targets should be built with hermetic xcode. Otherwise
-prints "0".
+"""
+Prints "1" if Chrome targets should be built with hermetic Xcode.
+Prints "2" if Chrome targets should be built with hermetic Xcode, but the OS
+version does not meet the minimum requirements of the hermetic version of Xcode.
+Otherwise prints "0".
 
 Usage:
   python should_use_hermetic_xcode.py <target_os>
@@ -13,6 +16,12 @@
 import os
 import sys
 
+_THIS_DIR_PATH = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+_BUILD_PATH = os.path.join(_THIS_DIR_PATH, os.pardir)
+sys.path.insert(0, _BUILD_PATH)
+
+import mac_toolchain
+
 
 def _IsCorpMachine():
   return os.path.isdir('/Library/GoogleCorpSupport/')
@@ -21,6 +30,8 @@
 def main():
   allow_corp = sys.argv[1] == 'mac' and _IsCorpMachine()
   if os.environ.get('FORCE_MAC_TOOLCHAIN') or allow_corp:
+    if not mac_toolchain.PlatformMeetsHermeticXcodeRequirements(sys.argv[1]):
+      return "2"
     return "1"
   else:
     return "0"
diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py
index 123e5e0d3..534f9d198 100755
--- a/build/mac_toolchain.py
+++ b/build/mac_toolchain.py
@@ -14,6 +14,7 @@
 """
 
 import os
+import platform
 import plistlib
 import shutil
 import subprocess
@@ -24,10 +25,14 @@
 import urllib2
 
 # This can be changed after running /build/package_mac_toolchain.py.
-MAC_TOOLCHAIN_VERSION = '5B1008'
+MAC_TOOLCHAIN_VERSION = '8E2002'
 MAC_TOOLCHAIN_SUB_REVISION = 3
 MAC_TOOLCHAIN_VERSION = '%s-%s' % (MAC_TOOLCHAIN_VERSION,
                                    MAC_TOOLCHAIN_SUB_REVISION)
+# The toolchain will not be downloaded if the minimum OS version is not met.
+# 16 is the major version number for macOS 10.12.
+MAC_MINIMUM_OS_VERSION = 16
+
 IOS_TOOLCHAIN_VERSION = '8C1002'
 IOS_TOOLCHAIN_SUB_REVISION = 1
 IOS_TOOLCHAIN_VERSION = '%s-%s' % (IOS_TOOLCHAIN_VERSION,
@@ -44,6 +49,13 @@
 STAMP_FILE = os.path.join(BASE_DIR, '%s_files', 'toolchain_build_revision')
 TOOLCHAIN_URL = 'gs://chrome-mac-sdk/'
 
+
+def PlatformMeetsHermeticXcodeRequirements(target_os):
+  if target_os == 'ios':
+    return True
+  return int(platform.release().split('.')[0]) >= MAC_MINIMUM_OS_VERSION
+
+
 def GetPlatforms():
   default_target_os = ["mac"]
   try:
@@ -232,6 +244,10 @@
     return 0
 
   for target_os in GetPlatforms():
+    if not PlatformMeetsHermeticXcodeRequirements(target_os):
+      print 'OS version does not support toolchain.'
+      continue
+
     if target_os == 'ios':
       default_version = IOS_TOOLCHAIN_VERSION
       toolchain_filename = 'ios-toolchain-%s.tgz'
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index 7b632bd..066298f 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -58,5 +58,8 @@
   _result = exec_script("//build/mac/should_use_hermetic_xcode.py",
                         [ target_os ],
                         "value")
+  assert(_result != 2,
+         "Do not allow building targets with the default" +
+             "hermetic toolchain if the minimum OS version is not met.")
   use_system_xcode = _result == 0
 }
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc
index d2c51d18..c89a657 100644
--- a/cc/blink/web_layer_impl.cc
+++ b/cc/blink/web_layer_impl.cc
@@ -226,6 +226,10 @@
   return layer_->HasTickingAnimationForTesting();
 }
 
+void WebLayerImpl::SetScrollable(const blink::WebSize& size) {
+  layer_->SetScrollable(size);
+}
+
 void WebLayerImpl::SetScrollPosition(blink::WebFloatPoint position) {
   layer_->SetScrollOffset(gfx::ScrollOffset(position.x, position.y));
 }
@@ -235,14 +239,6 @@
                               layer_->scroll_offset().y());
 }
 
-void WebLayerImpl::SetScrollClipLayer(WebLayer* clip_layer) {
-  if (!clip_layer) {
-    layer_->SetScrollClipLayerId(Layer::INVALID_ID);
-    return;
-  }
-  layer_->SetScrollClipLayerId(clip_layer->Id());
-}
-
 bool WebLayerImpl::Scrollable() const {
   return layer_->scrollable();
 }
diff --git a/cc/blink/web_layer_impl.h b/cc/blink/web_layer_impl.h
index f85b1d9..8f831166 100644
--- a/cc/blink/web_layer_impl.h
+++ b/cc/blink/web_layer_impl.h
@@ -91,9 +91,9 @@
   void SetFiltersOrigin(const blink::WebFloatPoint& origin) override;
   void SetBackgroundFilters(const cc::FilterOperations& filters) override;
   bool HasTickingAnimationForTesting() override;
+  void SetScrollable(const blink::WebSize&) override;
   void SetScrollPosition(blink::WebFloatPoint position) override;
   blink::WebFloatPoint ScrollPosition() const override;
-  void SetScrollClipLayer(blink::WebLayer* clip_layer) override;
   bool Scrollable() const override;
   void SetUserScrollable(bool horizontal, bool vertical) override;
   bool UserScrollableHorizontal() const override;
diff --git a/cc/input/scrollbar_animation_controller_unittest.cc b/cc/input/scrollbar_animation_controller_unittest.cc
index 6a891258..19fea25 100644
--- a/cc/input/scrollbar_animation_controller_unittest.cc
+++ b/cc/input/scrollbar_animation_controller_unittest.cc
@@ -82,7 +82,6 @@
     clip_layer_ = clip.get();
     scroll_layer->SetElementId(
         LayerIdToElementIdForTesting(scroll_layer->id()));
-    scroll_layer->SetScrollClipLayer(clip_layer_->id());
     LayerImpl* scroll_layer_ptr = scroll_layer.get();
 
     const int kTrackStart = 0;
@@ -119,6 +118,7 @@
     h_scrollbar_layer_->test_properties()->opacity_can_animate = true;
 
     clip_layer_->SetBounds(gfx::Size(100, 100));
+    scroll_layer_ptr->SetScrollable(gfx::Size(100, 100));
     scroll_layer_ptr->SetBounds(gfx::Size(200, 200));
     host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
     host_impl_.active_tree()->UpdateScrollbarGeometries();
@@ -185,12 +185,15 @@
 
   // Make the Layer non-scrollable, scrollbar disappears.
   clip_layer_->SetBounds(gfx::Size(200, 200));
+  LayerImpl* scroll_layer = host_impl_.active_tree()->LayerById(1);
+  scroll_layer->SetScrollable(gfx::Size(200, 200));
   host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
   scrollbar_controller_->DidScrollUpdate();
   ExpectScrollbarsOpacity(0);
 
   // Make the layer scrollable, scrollbar appears again.
   clip_layer_->SetBounds(gfx::Size(100, 100));
+  scroll_layer->SetScrollable(gfx::Size(100, 100));
   host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
   scrollbar_controller_->DidScrollUpdate();
   ExpectScrollbarsOpacity(1);
@@ -208,6 +211,7 @@
   // Shrink along X axis, horizontal scrollbar should appear.
   clip_layer_->SetBounds(gfx::Size(100, 200));
   EXPECT_EQ(gfx::Size(100, 200), clip_layer_->bounds());
+  scroll_layer->SetScrollable(gfx::Size(100, 200));
   host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   scrollbar_controller_->DidScrollBegin();
@@ -221,6 +225,7 @@
   // should disappear.
   clip_layer_->SetBounds(gfx::Size(200, 100));
   EXPECT_EQ(gfx::Size(200, 100), clip_layer_->bounds());
+  scroll_layer->SetScrollable(gfx::Size(200, 100));
   host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   scrollbar_controller_->DidScrollBegin();
@@ -1353,20 +1358,18 @@
     scrollbar->test_properties()->opacity = 0.0f;
     scrollbar_layer_ = scrollbar.get();
     scrollbar_layer_->test_properties()->opacity_can_animate = true;
-    std::unique_ptr<LayerImpl> clip =
+    std::unique_ptr<LayerImpl> root =
         LayerImpl::Create(host_impl_.active_tree(), 3);
-    clip_layer_ = clip.get();
+
+    scroll_layer->SetScrollable(gfx::Size(100, 100));
     scroll_layer->SetElementId(
         LayerIdToElementIdForTesting(scroll_layer->id()));
-
-    scroll_layer->SetScrollClipLayer(clip_layer_->id());
     LayerImpl* scroll_layer_ptr = scroll_layer.get();
     scroll_layer->test_properties()->AddChild(std::move(scrollbar));
-    clip->test_properties()->AddChild(std::move(scroll_layer));
-    host_impl_.active_tree()->SetRootLayerForTesting(std::move(clip));
+    root->test_properties()->AddChild(std::move(scroll_layer));
+    host_impl_.active_tree()->SetRootLayerForTesting(std::move(root));
 
     scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id());
-    clip_layer_->SetBounds(gfx::Size(100, 100));
     scroll_layer_ptr->SetBounds(gfx::Size(200, 200));
     host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
     DCHECK(host_impl_.active_tree()->ScrollbarGeometriesNeedUpdate());
@@ -1385,7 +1388,6 @@
   TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   std::unique_ptr<ScrollbarAnimationController> scrollbar_controller_;
-  LayerImpl* clip_layer_;
   SolidColorScrollbarLayerImpl* scrollbar_layer_;
 
   base::Closure start_fade_;
@@ -1451,21 +1453,17 @@
   EXPECT_EQ(HORIZONTAL, scrollbar_layer_->orientation());
 
   // Shrink along X axis, horizontal scrollbar should appear.
-  clip_layer_->SetBounds(gfx::Size(100, 200));
-  EXPECT_EQ(gfx::Size(100, 200), clip_layer_->bounds());
+  scroll_layer->SetScrollable(gfx::Size(100, 200));
   host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
-
   scrollbar_controller_->DidScrollBegin();
 
   scrollbar_controller_->DidScrollUpdate();
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
-
   scrollbar_controller_->DidScrollEnd();
 
   // Shrink along Y axis and expand along X, horizontal scrollbar
   // should disappear.
-  clip_layer_->SetBounds(gfx::Size(200, 100));
-  EXPECT_EQ(gfx::Size(200, 100), clip_layer_->bounds());
+  scroll_layer->SetScrollable(gfx::Size(200, 100));
   host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   scrollbar_controller_->DidScrollBegin();
@@ -1484,20 +1482,16 @@
   EXPECT_EQ(VERTICAL, scrollbar_layer_->orientation());
 
   // Shrink along X axis, vertical scrollbar should remain invisible.
-  clip_layer_->SetBounds(gfx::Size(100, 200));
-  EXPECT_EQ(gfx::Size(100, 200), clip_layer_->bounds());
+  scroll_layer->SetScrollable(gfx::Size(100, 200));
   host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
-
   scrollbar_controller_->DidScrollBegin();
 
   scrollbar_controller_->DidScrollUpdate();
   EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
-
   scrollbar_controller_->DidScrollEnd();
 
   // Shrink along Y axis and expand along X, vertical scrollbar should appear.
-  clip_layer_->SetBounds(gfx::Size(200, 100));
-  EXPECT_EQ(gfx::Size(200, 100), clip_layer_->bounds());
+  scroll_layer->SetScrollable(gfx::Size(200, 100));
   host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   scrollbar_controller_->DidScrollBegin();
diff --git a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
index 8df33897..970afe6 100644
--- a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
+++ b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
@@ -70,7 +70,6 @@
     scroll_layer->SetElementId(
         LayerIdToElementIdForTesting(scroll_layer->id()));
     clip_layer_ = clip.get();
-    scroll_layer->SetScrollClipLayer(clip_layer_->id());
     LayerImpl* scroll_layer_ptr = scroll_layer.get();
 
     const int kId = 2;
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 4740816bb..21f7f0d 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -54,7 +54,6 @@
       sorting_context_id(0),
       use_parent_backface_visibility(false),
       background_color(0),
-      scroll_clip_layer_id(INVALID_ID),
       scrollable(false),
       user_scrollable_horizontal(true),
       user_scrollable_vertical(true),
@@ -92,10 +91,8 @@
       force_render_surface_for_testing_(false),
       subtree_property_changed_(false),
       may_contain_video_(false),
-      is_scroll_clip_layer_(false),
       needs_show_scrollbars_(false),
       has_transform_node_(false),
-      has_scroll_node_(false),
       subtree_has_copy_request_(false),
       safe_opaque_background_color_(0),
       num_unclipped_descendants_(0) {}
@@ -298,16 +295,14 @@
     SetPropertyTreesNeedRebuild();
   }
 
-  if (scrollable() && has_scroll_node_) {
-    if (ScrollNode* node = layer_tree_host_->property_trees()->scroll_tree.Node(
-            scroll_tree_index())) {
-      node->bounds = inputs_.bounds;
-    }
+  if (scrollable()) {
+    auto& scroll_tree = layer_tree_host_->property_trees()->scroll_tree;
+    if (auto* scroll_node = scroll_tree.Node(scroll_tree_index_))
+      scroll_node->bounds = inputs_.bounds;
+    else
+      SetPropertyTreesNeedRebuild();
   }
 
-  if (is_scroll_clip_layer_)
-    layer_tree_host_->property_trees()->scroll_tree.set_needs_update(true);
-
   SetNeedsCommit();
 }
 
@@ -817,30 +812,24 @@
   property_trees.transform_tree.set_needs_update(true);
 }
 
-void Layer::SetScrollClipLayerId(int clip_layer_id) {
+void Layer::SetScrollable(const gfx::Size& bounds) {
   DCHECK(IsPropertyChangeAllowed());
-  if (inputs_.scroll_clip_layer_id == clip_layer_id)
+  if (inputs_.scrollable && inputs_.scroll_container_bounds == bounds)
     return;
-  inputs_.scroll_clip_layer_id = clip_layer_id;
+  bool was_scrollable = inputs_.scrollable;
+  inputs_.scrollable = true;
+  inputs_.scroll_container_bounds = bounds;
 
-  SetPropertyTreesNeedRebuild();
-
-  bool scrollable = clip_layer_id != Layer::INVALID_ID;
-  SetScrollable(scrollable);
-
-  SetNeedsCommit();
-}
-
-Layer* Layer::scroll_clip_layer() const {
-  DCHECK(layer_tree_host_);
-  return layer_tree_host_->LayerById(inputs_.scroll_clip_layer_id);
-}
-
-void Layer::SetScrollable(bool scrollable) {
-  DCHECK(IsPropertyChangeAllowed());
-  if (inputs_.scrollable == scrollable)
+  if (!layer_tree_host_)
     return;
-  inputs_.scrollable = scrollable;
+
+  auto& scroll_tree = layer_tree_host_->property_trees()->scroll_tree;
+  auto* scroll_node = scroll_tree.Node(scroll_tree_index_);
+  if (was_scrollable && scroll_node)
+    scroll_node->scroll_clip_layer_bounds = inputs_.scroll_container_bounds;
+  else
+    SetPropertyTreesNeedRebuild();
+
   SetNeedsCommit();
 }
 
@@ -854,13 +843,16 @@
   if (!layer_tree_host_)
     return;
 
-  if (has_scroll_node_) {
-    if (ScrollNode* node = layer_tree_host_->property_trees()->scroll_tree.Node(
-            scroll_tree_index())) {
-      node->user_scrollable_horizontal = horizontal;
-      node->user_scrollable_vertical = vertical;
+  if (scrollable()) {
+    auto& scroll_tree = layer_tree_host_->property_trees()->scroll_tree;
+    if (auto* scroll_node = scroll_tree.Node(scroll_tree_index_)) {
+      scroll_node->user_scrollable_horizontal = horizontal;
+      scroll_node->user_scrollable_vertical = vertical;
+    } else {
+      SetPropertyTreesNeedRebuild();
     }
   }
+
   SetNeedsCommit();
 }
 
@@ -944,6 +936,8 @@
   DCHECK(IsPropertyChangeAllowed());
   if (transform_tree_index_ == index)
     return;
+  if (index == TransformTree::kInvalidNodeId)
+    has_transform_node_ = false;
   transform_tree_index_ = index;
   SetNeedsPushProperties();
 }
@@ -1182,8 +1176,8 @@
   layer->SetUseParentBackfaceVisibility(inputs_.use_parent_backface_visibility);
   layer->SetShouldCheckBackfaceVisibility(should_check_backface_visibility_);
 
-  layer->SetScrollClipLayer(inputs_.scroll_clip_layer_id);
-  layer->SetScrollable(inputs_.scrollable);
+  if (scrollable())
+    layer->SetScrollable(inputs_.scroll_container_bounds);
   layer->SetMutableProperties(inputs_.mutable_properties);
 
   // The property trees must be safe to access because they will be used below
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index bbf9c0bbb..1dc0735e 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -217,14 +217,13 @@
   gfx::ScrollOffset scroll_offset() const { return inputs_.scroll_offset; }
   void SetScrollOffsetFromImplSide(const gfx::ScrollOffset& scroll_offset);
 
-  // TODO(pdr): Remove scroll_clip_layer_id and store the scroll clip bounds
-  // directly instead of using scroll_clip_layer's bounds.
-  void SetScrollClipLayerId(int clip_layer_id);
-  Layer* scroll_clip_layer() const;
-
-  // Marks this layer as being scrollable and needing an associated scroll node
-  // with bounds synced to this layer's bounds.
-  void SetScrollable(bool scrollable = true);
+  // Marks this layer as being scrollable and needing an associated scroll node.
+  // The scroll node's bounds and scroll_clip_layer_bounds will be kept in sync
+  // with this layer. Once scrollable, a Layer cannot become un-scrollable.
+  void SetScrollable(const gfx::Size& scroll_container_bounds);
+  gfx::Size scroll_container_bounds() const {
+    return inputs_.scroll_container_bounds;
+  }
   bool scrollable() const { return inputs_.scrollable; }
 
   void SetUserScrollable(bool horizontal, bool vertical);
@@ -256,8 +255,8 @@
   }
 
   void set_did_scroll_callback(
-      const base::Callback<void(const gfx::ScrollOffset&)>& callback) {
-    inputs_.did_scroll_callback = callback;
+      base::Callback<void(const gfx::ScrollOffset&)> callback) {
+    inputs_.did_scroll_callback = std::move(callback);
   }
 
   void SetForceRenderSurfaceForTesting(bool force_render_surface);
@@ -418,8 +417,6 @@
 
   void SetScrollbarsHiddenFromImplSide(bool hidden);
 
-  void set_is_scroll_clip_layer() { is_scroll_clip_layer_ = true; }
-
   const gfx::Rect& update_rect() const { return inputs_.update_rect; }
 
   LayerTreeHost* layer_tree_host() const { return layer_tree_host_; }
@@ -429,7 +426,6 @@
 
   bool has_transform_node() { return has_transform_node_; }
   void SetHasTransformNode(bool val) { has_transform_node_ = val; }
-  void SetHasScrollNode(bool val) { has_scroll_node_ = val; }
 
  protected:
   friend class LayerImpl;
@@ -573,14 +569,14 @@
 
     gfx::ScrollOffset scroll_offset;
 
-    // This variable indicates which ancestor layer (if any) whose size,
-    // transformed relative to this layer, defines the maximum scroll offset
-    // for this layer.
-    int scroll_clip_layer_id;
+    // Size of the scroll container that this layer scrolls in.
+    gfx::Size scroll_container_bounds;
 
     // Indicates that this layer will need a scroll property node and that this
-    // layer's bounds correspond to the scroll node's bounds.
+    // layer's bounds correspond to the scroll node's bounds (both |bounds| and
+    // |scroll_container_bounds|).
     bool scrollable : 1;
+
     bool user_scrollable_horizontal : 1;
     bool user_scrollable_vertical : 1;
 
@@ -633,12 +629,10 @@
   bool force_render_surface_for_testing_ : 1;
   bool subtree_property_changed_ : 1;
   bool may_contain_video_ : 1;
-  bool is_scroll_clip_layer_ : 1;
   bool needs_show_scrollbars_ : 1;
   // Whether the nodes referred to by *_tree_index_
   // "belong" to this layer. Only applicable if use_layer_lists is false.
   bool has_transform_node_ : 1;
-  bool has_scroll_node_ : 1;
   // This value is valid only when LayerTreeHost::has_copy_request() is true
   bool subtree_has_copy_request_ : 1;
   SkColor safe_opaque_background_color_;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index e30b44d..6e45fd3 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -52,7 +52,6 @@
     : layer_id_(id),
       layer_tree_impl_(tree_impl),
       test_properties_(nullptr),
-      scroll_clip_layer_id_(Layer::INVALID_ID),
       main_thread_scrolling_reasons_(
           MainThreadScrollingReason::kNotScrollingOnMain),
       scrollable_(false),
@@ -271,30 +270,21 @@
   return scroll_tree.ScrollBy(scroll_node, scroll, layer_tree_impl());
 }
 
-void LayerImpl::SetScrollClipLayer(int scroll_clip_layer_id) {
-  if (scroll_clip_layer_id_ == scroll_clip_layer_id)
+void LayerImpl::SetScrollable(const gfx::Size& bounds) {
+  if (scrollable_ && scroll_container_bounds_ == bounds)
     return;
-  scroll_clip_layer_id_ = scroll_clip_layer_id;
+  scrollable_ = true;
+  scroll_container_bounds_ = bounds;
 
-  // The scrolling bounds are determined from the scroll clip layer's bounds.
+  // Scrollbar positions depend on the bounds.
   layer_tree_impl()->SetScrollbarGeometriesNeedUpdate();
 
-  bool scrollable = scroll_clip_layer_id_ != Layer::INVALID_ID;
-  SetScrollable(scrollable);
-}
-
-LayerImpl* LayerImpl::scroll_clip_layer() const {
-  return layer_tree_impl()->LayerById(scroll_clip_layer_id_);
-}
-
-void LayerImpl::SetScrollable(bool scrollable) {
-  if (scrollable_ == scrollable)
-    return;
-  scrollable_ = scrollable;
-  if (scrollable && layer_tree_impl()->settings().scrollbar_animator ==
-                        LayerTreeSettings::AURA_OVERLAY) {
+  if (layer_tree_impl()->settings().scrollbar_animator ==
+      LayerTreeSettings::AURA_OVERLAY) {
     set_needs_show_scrollbars(true);
   }
+
+  NoteLayerPropertyChanged();
 }
 
 std::unique_ptr<LayerImpl> LayerImpl::CreateLayerImpl(
@@ -344,8 +334,8 @@
   }
 
   layer->SetBounds(bounds_);
-  layer->SetScrollClipLayer(scroll_clip_layer_id_);
-  layer->SetScrollable(scrollable_);
+  if (scrollable_)
+    layer->SetScrollable(scroll_container_bounds_);
   layer->SetMutableProperties(mutable_properties_);
 
   // If the main thread commits multiple times before the impl thread actually
@@ -511,8 +501,9 @@
 
   bounds_ = bounds;
 
-  // Scrollbar positions depend on scrolling bounds and scroll clip bounds.
-  layer_tree_impl()->SetScrollbarGeometriesNeedUpdate();
+  // Scrollbar positions depend on the scrolling layer bounds.
+  if (scrollable())
+    layer_tree_impl()->SetScrollbarGeometriesNeedUpdate();
 
   NoteLayerPropertyChanged();
 }
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index c8e2610..65c190f2 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -295,14 +295,11 @@
   // initial scroll
   gfx::Vector2dF ScrollBy(const gfx::Vector2dF& scroll);
 
-  // TODO(pdr): Remove scroll_clip_layer_id and use the scroll node's scroll
-  // clip bounds instead of the scroll_clip_layer bounds.
-  void SetScrollClipLayer(int scroll_clip_layer_id);
-  LayerImpl* scroll_clip_layer() const;
-
-  // Marks this layer as being scrollable and having an associated scroll node
-  // with bounds synced to this layer's bounds.
-  void SetScrollable(bool scrollable = true);
+  // Marks this layer as being scrollable and needing an associated scroll node.
+  // The scroll node's bounds and scroll_clip_layer_bounds will be kept in sync
+  // with this layer.
+  void SetScrollable(const gfx::Size& bounds);
+  gfx::Size scroll_container_bounds() const { return scroll_container_bounds_; }
   bool scrollable() const { return scrollable_; }
 
   void set_main_thread_scrolling_reasons(
@@ -476,13 +473,16 @@
 
   // Properties synchronized from the associated Layer.
   gfx::Size bounds_;
-  int scroll_clip_layer_id_;
 
   gfx::Vector2dF offset_to_transform_parent_;
   uint32_t main_thread_scrolling_reasons_;
 
-  // Indicates that this layer is scrollable and has an associated scroll node
-  // with bounds synced to this layer's bounds.
+  // Size of the scroll container that this layer scrolls in.
+  gfx::Size scroll_container_bounds_;
+
+  // Indicates that this layer will have a scroll property node and that this
+  // layer's bounds correspond to the scroll node's bounds (both |bounds| and
+  // |scroll_container_bounds|).
   bool scrollable_ : 1;
 
   bool should_flatten_transform_from_property_tree_ : 1;
diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc
index 326ef45..9a5f4db7 100644
--- a/cc/layers/layer_impl_unittest.cc
+++ b/cc/layers/layer_impl_unittest.cc
@@ -130,18 +130,13 @@
   host_impl.SetVisible(true);
   EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get()));
   host_impl.CreatePendingTree();
-  std::unique_ptr<LayerImpl> root_clip_ptr =
-      LayerImpl::Create(host_impl.pending_tree(), 1);
-  LayerImpl* root_clip = root_clip_ptr.get();
   std::unique_ptr<LayerImpl> root_ptr =
       LayerImpl::Create(host_impl.pending_tree(), 2);
   LayerImpl* root = root_ptr.get();
-  root_clip_ptr->test_properties()->AddChild(std::move(root_ptr));
-  host_impl.pending_tree()->SetRootLayerForTesting(std::move(root_clip_ptr));
+  host_impl.pending_tree()->SetRootLayerForTesting(std::move(root_ptr));
 
   root->test_properties()->force_render_surface = true;
   root->SetMasksToBounds(true);
-  root->SetScrollClipLayer(root_clip->id());
   root->layer_tree_impl()->ResetAllChangeTracking();
 
   root->test_properties()->AddChild(
@@ -150,7 +145,6 @@
   child->test_properties()->AddChild(
       LayerImpl::Create(host_impl.pending_tree(), 8));
   LayerImpl* grand_child = child->test_properties()->children[0];
-  root->SetScrollClipLayer(root_clip->id());
   host_impl.pending_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   // Adding children is an internal operation and should not mark layers as
@@ -224,19 +218,15 @@
   FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
   host_impl.SetVisible(true);
   EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get()));
-  std::unique_ptr<LayerImpl> root_clip_ptr =
-      LayerImpl::Create(host_impl.active_tree(), 1);
-  LayerImpl* root_clip = root_clip_ptr.get();
   std::unique_ptr<LayerImpl> root_ptr =
       LayerImpl::Create(host_impl.active_tree(), 2);
   LayerImpl* root = root_ptr.get();
-  root_clip_ptr->test_properties()->AddChild(std::move(root_ptr));
-  host_impl.active_tree()->SetRootLayerForTesting(std::move(root_clip_ptr));
+  host_impl.active_tree()->SetRootLayerForTesting(std::move(root_ptr));
 
   root->test_properties()->AddChild(
       LayerImpl::Create(host_impl.active_tree(), 7));
   LayerImpl* child = root->test_properties()->children[0];
-  root->SetScrollClipLayer(root_clip->id());
+  root->SetScrollable(gfx::Size(100, 100));
   host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   // Make root the inner viewport container layer. This ensures the later call
@@ -306,7 +296,7 @@
       LayerImpl::Create(host_impl.active_tree(), 2);
   LayerImpl* layer = layer_ptr.get();
   root->test_properties()->AddChild(std::move(layer_ptr));
-  layer->SetScrollClipLayer(root->id());
+  layer->SetScrollable(gfx::Size(1, 1));
   std::unique_ptr<LayerImpl> layer2_ptr =
       LayerImpl::Create(host_impl.active_tree(), 3);
   LayerImpl* layer2 = layer2_ptr.get();
@@ -493,12 +483,12 @@
         ->root_layer_for_testing()
         ->test_properties()
         ->AddChild(LayerImpl::Create(host_impl_.active_tree(), root_id_ + 1));
-    layer()->SetScrollClipLayer(root_id_);
     // Set the max scroll offset by noting that the root layer has bounds (1,1),
     // thus whatever bounds are set for the layer will be the max scroll
     // offset plus 1 in each direction.
     host_impl_.active_tree()->root_layer_for_testing()->SetBounds(
         gfx::Size(1, 1));
+    layer()->SetScrollable(gfx::Size(1, 1));
     gfx::Vector2d max_scroll_offset(51, 81);
     layer()->SetBounds(gfx::Size(max_scroll_offset.x(), max_scroll_offset.y()));
     host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
diff --git a/cc/layers/layer_perftest.cc b/cc/layers/layer_perftest.cc
index 61086a0d..a0bee52 100644
--- a/cc/layers/layer_perftest.cc
+++ b/cc/layers/layer_perftest.cc
@@ -79,8 +79,6 @@
     test_layer->SetDoubleSided(double_sided);
     test_layer->SetHideLayerAndSubtree(hide_layer_and_subtree);
     test_layer->SetMasksToBounds(masks_to_bounds);
-    test_layer->SetScrollClipLayerId(scrollable ? test_layer->id()
-                                                : Layer::INVALID_ID);
     test_layer->PushPropertiesTo(impl_layer.get());
 
     transform_origin_z += 0.01f;
diff --git a/cc/layers/layer_position_constraint_unittest.cc b/cc/layers/layer_position_constraint_unittest.cc
index 89fc3d0..ad31d96 100644
--- a/cc/layers/layer_position_constraint_unittest.cc
+++ b/cc/layers/layer_position_constraint_unittest.cc
@@ -124,15 +124,15 @@
     inner_viewport_container_layer_->SetMasksToBounds(true);
     scroll_layer_->SetElementId(
         LayerIdToElementIdForTesting(scroll_layer_->id()));
-    scroll_layer_->SetScrollClipLayerId(inner_viewport_container_layer_->id());
+    scroll_layer_->SetScrollable(clip_bounds);
     scroll_layer_->SetIsContainerForFixedPositionLayers(true);
 
     outer_viewport_container_layer_->SetMasksToBounds(true);
     child_->SetElementId(LayerIdToElementIdForTesting(child_->id()));
-    child_->SetScrollClipLayerId(outer_viewport_container_layer_->id());
+    child_->SetScrollable(clip_bounds);
     grand_child_->SetElementId(
         LayerIdToElementIdForTesting(grand_child_->id()));
-    grand_child_->SetScrollClipLayerId(outer_viewport_container_layer_->id());
+    grand_child_->SetScrollable(clip_bounds);
 
     grand_child_->AddChild(great_grand_child_);
     child_->AddChild(grand_child_);
@@ -1071,7 +1071,7 @@
   great_grand_child_->SetIsContainerForFixedPositionLayers(true);
   great_grand_child_->SetElementId(
       LayerIdToElementIdForTesting(great_grand_child_->id()));
-  great_grand_child_->SetScrollClipLayerId(root_->id());
+  great_grand_child_->SetScrollable(gfx::Size(100, 100));
   great_great_grand_child->SetPositionConstraint(fixed_to_top_left_);
 
   CommitAndUpdateImplPointers();
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index b1808b9..6355412b 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -913,8 +913,7 @@
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetContentsOpaque(true));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetPosition(gfx::PointF(4.f, 9.f)));
   // We can use any layer pointer here since we aren't syncing for real.
-  EXPECT_SET_NEEDS_COMMIT(2,
-                          test_layer->SetScrollClipLayerId(test_layer->id()));
+  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetScrollable(gfx::Size(1, 1)));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetUserScrollable(true, false));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetScrollOffset(
       gfx::ScrollOffset(10, 10)));
diff --git a/cc/layers/painted_scrollbar_layer_impl_unittest.cc b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
index 3932b0e..2dd6d0b7 100644
--- a/cc/layers/painted_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
@@ -57,9 +57,6 @@
   scrollbar_layer_impl->set_thumb_ui_resource_id(thumb_uid);
   scrollbar_layer_impl->set_thumb_opacity(thumb_opacity);
 
-  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
-  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
-
   impl.CalcDrawProps(viewport_size);
 
   gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index fcd74017..32c0f39 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -297,9 +297,9 @@
 
   // Choose bounds to give max_scroll_offset = (30, 50).
   layer_tree_root->SetBounds(gfx::Size(70, 150));
-  scroll_layer->SetScrollClipLayerId(layer_tree_root->id());
   scroll_layer->SetScrollOffset(gfx::ScrollOffset(10, 20));
   scroll_layer->SetBounds(gfx::Size(100, 200));
+  scroll_layer->SetScrollable(gfx::Size(70, 150));
   content_layer->SetBounds(gfx::Size(100, 200));
 
   layer_tree_host_->SetRootLayer(layer_tree_root);
@@ -322,6 +322,7 @@
                     cc_scrollbar_layer->clip_layer_length());
 
   layer_tree_root->SetBounds(gfx::Size(700, 1500));
+  scroll_layer->SetScrollable(gfx::Size(700, 1500));
   scroll_layer->SetBounds(gfx::Size(1000, 2000));
   scroll_layer->SetScrollOffset(gfx::ScrollOffset(100, 200));
   content_layer->SetBounds(gfx::Size(1000, 2000));
@@ -344,32 +345,28 @@
                      cc_scrollbar_layer->clip_layer_length());
 }
 
-#define UPDATE_AND_EXTRACT_LAYER_POINTERS()                                  \
-  do {                                                                       \
-    scrollbar_layer->UpdateInternalContentScale();                           \
-    scrollbar_layer->UpdateThumbAndTrackGeometry();                          \
-    root_clip_layer_impl = layer_tree_host_->CommitAndCreateLayerImplTree(); \
-    root_clip_layer_impl->layer_tree_impl()->UpdateScrollbarGeometries();    \
-    scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(          \
-        root_clip_layer_impl->layer_tree_impl()->LayerById(                  \
-            scrollbar_layer->id()));                                         \
+#define UPDATE_AND_EXTRACT_LAYER_POINTERS()                                    \
+  do {                                                                         \
+    scrollbar_layer->UpdateInternalContentScale();                             \
+    scrollbar_layer->UpdateThumbAndTrackGeometry();                            \
+    root_layer_impl = layer_tree_host_->CommitAndCreateLayerImplTree();        \
+    root_layer_impl->layer_tree_impl()->UpdateScrollbarGeometries();           \
+    scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(            \
+        root_layer_impl->layer_tree_impl()->LayerById(scrollbar_layer->id())); \
   } while (false)
 
 TEST_F(ScrollbarLayerTest, UpdatePropertiesOfScrollBarWhenThumbRemoved) {
-  scoped_refptr<Layer> root_clip_layer = Layer::Create();
   scoped_refptr<Layer> root_layer = Layer::Create();
   scoped_refptr<Layer> content_layer = Layer::Create();
   scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
       FakePaintedScrollbarLayer::Create(false, true, root_layer->element_id());
 
-  root_layer->SetScrollClipLayerId(root_clip_layer->id());
-  // Give the root-clip a size that will result in MaxScrollOffset = (80, 0).
-  root_clip_layer->SetBounds(gfx::Size(20, 50));
+  // Give the root layer a size that will result in MaxScrollOffset = (80, 0).
+  root_layer->SetScrollable(gfx::Size(20, 50));
   root_layer->SetBounds(gfx::Size(100, 50));
   content_layer->SetBounds(gfx::Size(100, 50));
 
-  layer_tree_host_->SetRootLayer(root_clip_layer);
-  root_clip_layer->AddChild(root_layer);
+  layer_tree_host_->SetRootLayer(root_layer);
   root_layer->AddChild(content_layer);
   root_layer->AddChild(scrollbar_layer);
 
@@ -380,7 +377,7 @@
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10);
   scrollbar_layer->fake_scrollbar()->set_thumb_length(4);
-  LayerImpl* root_clip_layer_impl = nullptr;
+  LayerImpl* root_layer_impl = nullptr;
   PaintedScrollbarLayerImpl* scrollbar_layer_impl = nullptr;
 
   layer_tree_host_->BuildPropertyTreesForTesting();
@@ -396,21 +393,18 @@
 }
 
 TEST_F(ScrollbarLayerTest, ThumbRect) {
-  scoped_refptr<Layer> root_clip_layer = Layer::Create();
   scoped_refptr<Layer> root_layer = Layer::Create();
   scoped_refptr<Layer> content_layer = Layer::Create();
   scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
       FakePaintedScrollbarLayer::Create(false, true, root_layer->element_id());
 
   root_layer->SetElementId(LayerIdToElementIdForTesting(root_layer->id()));
-  root_layer->SetScrollClipLayerId(root_clip_layer->id());
-  // Give the root-clip a size that will result in MaxScrollOffset = (80, 0).
-  root_clip_layer->SetBounds(gfx::Size(20, 50));
+  // Give the root layer a size that will result in MaxScrollOffset = (80, 0).
+  root_layer->SetScrollable(gfx::Size(20, 50));
   root_layer->SetBounds(gfx::Size(100, 50));
   content_layer->SetBounds(gfx::Size(100, 50));
 
-  layer_tree_host_->SetRootLayer(root_clip_layer);
-  root_clip_layer->AddChild(root_layer);
+  layer_tree_host_->SetRootLayer(root_layer);
   root_layer->AddChild(content_layer);
   root_layer->AddChild(scrollbar_layer);
 
@@ -422,7 +416,7 @@
   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10);
   scrollbar_layer->fake_scrollbar()->set_thumb_length(4);
   layer_tree_host_->UpdateLayers();
-  LayerImpl* root_clip_layer_impl = nullptr;
+  LayerImpl* root_layer_impl = nullptr;
   PaintedScrollbarLayerImpl* scrollbar_layer_impl = nullptr;
 
   // Thumb is at the edge of the scrollbar (should be inset to
@@ -475,18 +469,15 @@
 }
 
 TEST_F(ScrollbarLayerTest, ThumbRectForOverlayLeftSideVerticalScrollbar) {
-  scoped_refptr<Layer> root_clip_layer = Layer::Create();
   scoped_refptr<Layer> root_layer = Layer::Create();
   // Create an overlay left side vertical scrollbar.
   scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
       FakePaintedScrollbarLayer::Create(false, true, VERTICAL, true, true,
                                         root_layer->element_id());
-  root_layer->SetScrollClipLayerId(root_clip_layer->id());
-  root_clip_layer->SetBounds(gfx::Size(50, 20));
+  root_layer->SetScrollable(gfx::Size(20, 50));
   root_layer->SetBounds(gfx::Size(50, 100));
 
-  layer_tree_host_->SetRootLayer(root_clip_layer);
-  root_clip_layer->AddChild(root_layer);
+  layer_tree_host_->SetRootLayer(root_layer);
   root_layer->AddChild(scrollbar_layer);
 
   root_layer->SetScrollOffset(gfx::ScrollOffset(0, 0));
@@ -496,7 +487,7 @@
   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10);
   scrollbar_layer->fake_scrollbar()->set_thumb_length(4);
   layer_tree_host_->UpdateLayers();
-  LayerImpl* root_clip_layer_impl = nullptr;
+  LayerImpl* root_layer_impl = nullptr;
   PaintedScrollbarLayerImpl* scrollbar_layer_impl = nullptr;
 
   // Thumb is at the edge of the scrollbar (should be inset to
@@ -542,9 +533,6 @@
   scrollbar_layer_impl->SetClipLayerLength(200 / 3.f);
   scrollbar_layer_impl->SetScrollLayerLength(100 + 200 / 3.f);
 
-  DCHECK(layer_tree_host_->active_tree()->ScrollbarGeometriesNeedUpdate());
-  layer_tree_host_->active_tree()->UpdateScrollbarGeometries();
-
   // Thickness should be overridden to 3.
   {
     std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
@@ -598,7 +586,6 @@
   scoped_refptr<Layer> layer_tree_root = Layer::Create();
   scoped_refptr<Layer> scroll_layer = Layer::Create();
   scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id()));
-  scroll_layer->SetScrollClipLayerId(layer_tree_root->id());
   scoped_refptr<Layer> child1 = Layer::Create();
   scoped_refptr<Layer> child2;
   const bool kIsLeftSideVerticalScrollbar = false;
@@ -612,6 +599,7 @@
 
   // Choose layer bounds to give max_scroll_offset = (8, 8).
   layer_tree_root->SetBounds(gfx::Size(2, 2));
+  scroll_layer->SetScrollable(gfx::Size(2, 2));
   scroll_layer->SetBounds(gfx::Size(10, 10));
 
   layer_tree_host_->UpdateLayers();
@@ -653,7 +641,6 @@
 
   scoped_refptr<Layer> layer_tree_root = Layer::Create();
   scoped_refptr<Layer> scroll_layer = Layer::Create();
-  scroll_layer->SetScrollClipLayerId(layer_tree_root->id());
   scroll_layer->SetElementId(ElementId(200));
   scoped_refptr<Layer> child1 = Layer::Create();
   scoped_refptr<SolidColorScrollbarLayer> scrollbar_layer;
@@ -732,7 +719,6 @@
   scoped_refptr<Layer> layer_tree_root = Layer::Create();
   scoped_refptr<Layer> scroll_layer = Layer::Create();
   scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id()));
-  scroll_layer->SetScrollClipLayerId(layer_tree_root->id());
   scoped_refptr<Layer> child1 = Layer::Create();
   scoped_refptr<Layer> scrollbar_layer;
   const bool kIsLeftSideVerticalScrollbar = false;
@@ -746,6 +732,7 @@
 
   layer_tree_root->SetBounds(gfx::Size(2, 2));
   scroll_layer->SetBounds(gfx::Size(10, 10));
+  scroll_layer->SetScrollable(layer_tree_root->bounds());
   layer_tree_host_->UpdateLayers();
   LayerTreeHostImpl* host_impl = layer_tree_host_->host_impl();
   host_impl->CreatePendingTree();
@@ -772,10 +759,7 @@
 
   LayerTestCommon::LayerImplTest impl;
 
-  LayerImpl* clip_layer = impl.AddChildToRoot<LayerImpl>();
-  LayerImpl* scroll_layer = impl.AddChild<LayerImpl>(clip_layer);
-
-  scroll_layer->SetScrollClipLayer(clip_layer->id());
+  LayerImpl* scroll_layer = impl.AddChildToRoot<LayerImpl>();
   scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id()));
 
   const int kTrackStart = 0;
@@ -789,7 +773,7 @@
           kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar);
 
   scrollbar_layer->SetScrollElementId(scroll_layer->element_id());
-  clip_layer->SetBounds(gfx::Size(980, 980));
+  scroll_layer->SetScrollable(gfx::Size(980, 980));
   scroll_layer->SetBounds(gfx::Size(980, 980));
 
   impl.host_impl()->active_tree()->BuildPropertyTreesForTesting();
@@ -835,32 +819,41 @@
           scroll_layer, HORIZONTAL, kThumbThickness, kTrackStart,
           kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar);
   scrollbar_layer->SetScrollElementId(scroll_layer->element_id());
-  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  EXPECT_TRUE(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
   impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
 
-  scroll_layer->SetScrollClipLayer(clip_layer->id());
-  scroll_layer->SetScrollable();
-  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  scroll_layer->SetBounds(gfx::Size(900, 900));
+  // If the scroll layer is not scrollable, the bounds do not affect scrollbar
+  // geometries.
+  EXPECT_FALSE(
+      impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+
+  scroll_layer->SetScrollable(gfx::Size(900, 900));
+  EXPECT_TRUE(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
   impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
 
   clip_layer->SetBounds(gfx::Size(900, 900));
-  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
-  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
+  // The clip layer for scrolling is managed independently of the scroll
+  // container bounds so changing the clip does not require an update.
+  EXPECT_FALSE(
+      impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
 
   scroll_layer->SetBounds(gfx::Size(980, 980));
-  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  // Changes to the bounds should also require an update.
+  EXPECT_TRUE(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
   impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
 
   clip_layer->SetViewportBoundsDelta(gfx::Vector2dF(1, 2));
-  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  EXPECT_TRUE(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
   impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
 
   // Not changing the current value should not require an update.
-  scroll_layer->SetScrollClipLayer(clip_layer->id());
+  scroll_layer->SetScrollable(gfx::Size(900, 900));
   clip_layer->SetBounds(gfx::Size(900, 900));
   scroll_layer->SetBounds(gfx::Size(980, 980));
   clip_layer->SetViewportBoundsDelta(gfx::Vector2dF(1, 2));
-  DCHECK(!impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
+  EXPECT_TRUE(
+      !impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
 }
 
 class ScrollbarLayerSolidColorThumbTest : public testing::Test {
@@ -909,8 +902,6 @@
   horizontal_scrollbar_layer_->SetClipLayerLength(5.f);
   horizontal_scrollbar_layer_->SetScrollLayerLength(15.f);
   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
-  DCHECK(host_impl_->active_tree()->ScrollbarGeometriesNeedUpdate());
-  host_impl_->active_tree()->UpdateScrollbarGeometries();
   EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
 
   // The thumb's length should never be less than its thickness.
@@ -922,9 +913,6 @@
 
 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) {
   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
-  DCHECK(host_impl_->active_tree()->ScrollbarGeometriesNeedUpdate());
-  host_impl_->active_tree()->UpdateScrollbarGeometries();
-
   horizontal_scrollbar_layer_->SetCurrentPos(0.f);
   horizontal_scrollbar_layer_->SetClipLayerLength(12.f);
   horizontal_scrollbar_layer_->SetScrollLayerLength(112.f);
@@ -953,9 +941,6 @@
   layers[0]->SetBounds(gfx::Size(100, 3));
   layers[1]->SetBounds(gfx::Size(3, 100));
 
-  DCHECK(host_impl_->active_tree()->ScrollbarGeometriesNeedUpdate());
-  host_impl_->active_tree()->UpdateScrollbarGeometries();
-
   EXPECT_EQ(gfx::Rect(20, 0, 20, 3),
             horizontal_scrollbar_layer_->ComputeThumbQuadRect());
   EXPECT_EQ(gfx::Rect(0, 20, 3, 20),
@@ -982,7 +967,6 @@
                           int expected_deleted,
                           bool use_solid_color_scrollbar) {
     std::unique_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false));
-    scoped_refptr<Layer> root_clip_layer = Layer::Create();
     scoped_refptr<Layer> layer_tree_root = Layer::Create();
     scoped_refptr<Layer> content_layer = Layer::Create();
     scoped_refptr<Layer> scrollbar_layer;
@@ -1004,7 +988,7 @@
 
     scrollbar_layer->SetIsDrawable(true);
     scrollbar_layer->SetBounds(gfx::Size(100, 100));
-    layer_tree_root->SetScrollClipLayerId(root_clip_layer->id());
+    layer_tree_root->SetScrollable(gfx::Size(100, 200));
     layer_tree_root->SetScrollOffset(gfx::ScrollOffset(10, 20));
     layer_tree_root->SetBounds(gfx::Size(100, 200));
     content_layer->SetBounds(gfx::Size(100, 200));
diff --git a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
index 4cff576..a2b24303 100644
--- a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
@@ -39,9 +39,6 @@
   // SolidColorScrollbarLayers construct with opacity = 0.f, so override.
   scrollbar_layer_impl->test_properties()->opacity = 1.f;
 
-  DCHECK(impl.host_impl()->active_tree()->ScrollbarGeometriesNeedUpdate());
-  impl.host_impl()->active_tree()->UpdateScrollbarGeometries();
-
   impl.CalcDrawProps(viewport_size);
 
   gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
diff --git a/cc/test/layer_tree_json_parser.cc b/cc/test/layer_tree_json_parser.cc
index 426bc6fe..8987791 100644
--- a/cc/test/layer_tree_json_parser.cc
+++ b/cc/test/layer_tree_json_parser.cc
@@ -98,29 +98,6 @@
   if (dict->GetBoolean("ContentsOpaque", &contents_opaque))
     new_layer->SetContentsOpaque(contents_opaque);
 
-  bool scrollable;
-  // TODO(wjmaclean) At some time in the future we may wish to test that a
-  // reconstructed layer tree contains the correct linkage for the scroll
-  // clip layer. This is complicated by the fact that the json output doesn't
-  // (currently) re-construct the tree with the same layer IDs as the original.
-  // But, since a clip layer is always an ancestor of the scrollable layer, we
-  // can just count the number of upwards hops to the clip layer and write that
-  // into the json file (with 0 hops implying no clip layer, i.e. not
-  // scrollable). Reconstructing the tree can then be accomplished by passing
-  // the parent pointer to this function and traversing the same number of
-  // ancestors to determine the pointer to the clip layer. The LayerTreesMatch()
-  // function should then check that both original and reconstructed layers
-  // have the same positioning with respect to their clip layers.
-  //
-  // For now, we can safely indicate a layer is scrollable by giving it a
-  // pointer to itself, something not normally allowed in a working tree.
-  //
-  // https://code.google.com/p/chromium/issues/detail?id=330622
-  //
-  if (dict->GetBoolean("Scrollable", &scrollable))
-    new_layer->SetScrollClipLayerId(scrollable ? new_layer->id()
-                                               : Layer::INVALID_ID);
-
   bool is_3d_sorted;
   if (dict->GetBoolean("Is3DSorted", &is_3d_sorted)) {
     // A non-zero context ID will put the layer into a 3D sorting context
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 14666f6..c3b179f 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -57,18 +57,16 @@
   inner_viewport_scroll_layer->AddChild(outer_viewport_container_layer);
   outer_viewport_container_layer->AddChild(outer_scroll_layer);
 
-  inner_viewport_scroll_layer->SetScrollClipLayerId(
-      inner_viewport_container_layer->id());
   inner_viewport_scroll_layer->SetElementId(
       LayerIdToElementIdForTesting(inner_viewport_scroll_layer->id()));
-  outer_scroll_layer->SetScrollClipLayerId(
-      outer_viewport_container_layer->id());
   outer_scroll_layer->SetElementId(
       LayerIdToElementIdForTesting(outer_scroll_layer->id()));
 
   inner_viewport_container_layer->SetBounds(inner_bounds);
+  inner_viewport_scroll_layer->SetScrollable(inner_bounds);
   inner_viewport_scroll_layer->SetBounds(outer_bounds);
   outer_viewport_container_layer->SetBounds(outer_bounds);
+  outer_scroll_layer->SetScrollable(outer_bounds);
 
   inner_viewport_scroll_layer->SetIsContainerForFixedPositionLayers(true);
   outer_scroll_layer->SetIsContainerForFixedPositionLayers(true);
diff --git a/cc/test/test_layer_tree_host_base.cc b/cc/test/test_layer_tree_host_base.cc
index a252a59..76aa612 100644
--- a/cc/test/test_layer_tree_host_base.cc
+++ b/cc/test/test_layer_tree_host_base.cc
@@ -115,7 +115,7 @@
     if (!tile_size.IsEmpty())
       pending_layer->set_fixed_tile_size(tile_size);
     pending_layer->SetDrawsContent(true);
-    pending_layer->SetScrollClipLayer(new_pending_root->id());
+    pending_layer->SetScrollable(gfx::Size(1, 1));
     pending_root = new_pending_root.get();
     pending_tree->SetRootLayerForTesting(std::move(new_pending_root));
   } else {
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index 7578582..d9de4290f 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -735,23 +735,6 @@
   }
 }
 
-static void UpdateScrollTree(ScrollTree* scroll_tree,
-                             const LayerTreeHost* layer_tree_host) {
-  if (!scroll_tree->needs_update())
-    return;
-
-  for (int i = ScrollTree::kRootNodeId;
-       i < static_cast<int>(scroll_tree->size()); ++i) {
-    ScrollNode* scroll_node = scroll_tree->Node(i);
-    if (Layer* scroll_layer =
-            layer_tree_host->LayerById(scroll_node->owning_layer_id)) {
-      if (Layer* scroll_clip_layer = scroll_layer->scroll_clip_layer()) {
-        scroll_node->scroll_clip_layer_bounds = scroll_clip_layer->bounds();
-      }
-    }
-  }
-}
-
 static void ComputeClips(PropertyTrees* property_trees) {
   DCHECK(!property_trees->transform_tree.needs_update());
   ClipTree* clip_tree = &property_trees->clip_tree;
@@ -886,7 +869,6 @@
     property_trees->clip_tree.set_needs_update(true);
     property_trees->effect_tree.set_needs_update(true);
   }
-  UpdateScrollTree(&property_trees->scroll_tree, layer_tree_host);
   ComputeTransforms(&property_trees->transform_tree);
   ComputeEffects(&property_trees->effect_tree);
   // Computation of clips uses ToScreen which is updated while computing
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 1fe36ba..f7210f02 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -537,30 +537,23 @@
       LayerImpl::Create(host_impl.active_tree(), 2));
   LayerImpl* scroll_layer = scroll_layer_scoped_ptr.get();
   scroll_layer->SetBounds(gfx::Size(10, 20));
-  std::unique_ptr<LayerImpl> clip_layer_scoped_ptr(
-      LayerImpl::Create(host_impl.active_tree(), 4));
-  LayerImpl* clip_layer = clip_layer_scoped_ptr.get();
 
   scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id()));
-  scroll_layer->SetScrollClipLayer(clip_layer->id());
-  clip_layer->SetBounds(
+  scroll_layer->SetScrollable(
       gfx::Size(scroll_layer->bounds().width() + kMaxScrollOffset.x(),
                 scroll_layer->bounds().height() + kMaxScrollOffset.y()));
-  scroll_layer->SetScrollClipLayer(clip_layer->id());
   SetScrollOffsetDelta(scroll_layer, kScrollDelta);
   gfx::Transform impl_transform;
   scroll_layer->test_properties()->AddChild(std::move(sublayer_scoped_ptr));
-  LayerImpl* scroll_layer_raw_ptr = scroll_layer_scoped_ptr.get();
-  clip_layer->test_properties()->AddChild(std::move(scroll_layer_scoped_ptr));
-  scroll_layer_raw_ptr->layer_tree_impl()
+  scroll_layer_scoped_ptr->layer_tree_impl()
       ->property_trees()
       ->scroll_tree.UpdateScrollOffsetBaseForTesting(
-          scroll_layer_raw_ptr->element_id(), kScrollOffset);
+          scroll_layer_scoped_ptr->element_id(), kScrollOffset);
 
   std::unique_ptr<LayerImpl> root(
       LayerImpl::Create(host_impl.active_tree(), 3));
   root->SetBounds(gfx::Size(3, 4));
-  root->test_properties()->AddChild(std::move(clip_layer_scoped_ptr));
+  root->test_properties()->AddChild(std::move(scroll_layer_scoped_ptr));
   LayerImpl* root_layer = root.get();
   host_impl.active_tree()->SetRootLayerForTesting(std::move(root));
 
@@ -1169,7 +1162,6 @@
   root->SetDrawsContent(true);
   root->SetBounds(gfx::Size(100, 100));
   child->SetDrawsContent(true);
-  child->SetScrollClipLayer(root->id());
   child->SetBounds(gfx::Size(100, 100));
   child->SetMasksToBounds(true);
 
@@ -5322,7 +5314,7 @@
 
   intervening->SetMasksToBounds(true);
   clip_parent->SetMasksToBounds(true);
-  intervening->SetScrollClipLayer(clip_parent->id());
+  intervening->SetScrollable(gfx::Size(1, 1));
   intervening->SetElementId(LayerIdToElementIdForTesting(intervening->id()));
   intervening->SetCurrentScrollOffset(gfx::ScrollOffset(3, 3));
 
@@ -6108,7 +6100,6 @@
   fixed->test_properties()->position_constraint = constraint;
 
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayer(container->id());
 
   gfx::Transform container_transform;
   container_transform.Translate3d(10.0, 20.0, 0.0);
@@ -6119,6 +6110,7 @@
   container->SetBounds(gfx::Size(40, 40));
   container->SetDrawsContent(true);
   scroller->SetBounds(gfx::Size(30, 30));
+  scroller->SetScrollable(container->bounds());
   scroller->SetDrawsContent(true);
   fixed->SetBounds(gfx::Size(50, 50));
   fixed->SetDrawsContent(true);
@@ -6232,7 +6224,7 @@
   container->SetBounds(gfx::Size(50, 50));
   scroller->SetBounds(gfx::Size(100, 100));
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayer(container->id());
+  scroller->SetScrollable(container->bounds());
   scroller->SetDrawsContent(true);
 
   gfx::Transform end_scale;
@@ -6275,7 +6267,6 @@
   host()->SetRootLayer(root);
 
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
   scroll_child->SetScrollParent(scroller.get());
 
   gfx::Transform rotate;
@@ -6283,6 +6274,7 @@
   root->SetBounds(gfx::Size(50, 50));
   container->SetBounds(gfx::Size(50, 50));
   scroller->SetBounds(gfx::Size(100, 100));
+  scroller->SetScrollable(container->bounds());
   scroller->SetPosition(gfx::PointF(10.3f, 10.3f));
   scroll_child->SetBounds(gfx::Size(10, 10));
   scroll_child->SetTransform(rotate);
@@ -6323,7 +6315,6 @@
   scroller->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
 
   LayerStickyPositionConstraint sticky_position;
   sticky_position.is_sticky = true;
@@ -6338,6 +6329,7 @@
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(1000, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(10, 20));
 
@@ -6396,7 +6388,6 @@
   sticky_pos->SetScrollParent(scroller.get());
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
 
   // The sticky layer has already been scrolled on the main thread side, and has
   // stuck. This test then checks that further changes from cc-only scrolling
@@ -6415,6 +6406,7 @@
   container->SetBounds(gfx::Size(100, 100));
   container->SetPosition(gfx::PointF(50, 50));
   scroller->SetBounds(gfx::Size(1000, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(60, 70));
 
@@ -6472,7 +6464,6 @@
   scroller->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
 
   LayerStickyPositionConstraint sticky_position;
   sticky_position.is_sticky = true;
@@ -6487,6 +6478,7 @@
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(100, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(0, 200));
 
@@ -6518,7 +6510,6 @@
   scroller->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
 
   LayerStickyPositionConstraint sticky_position;
   sticky_position.is_sticky = true;
@@ -6533,6 +6524,7 @@
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(1000, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(0, 150));
 
@@ -6586,7 +6578,6 @@
   scroller->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(root->id());
   LayerTreeHost::ViewportLayers viewport_layers;
   viewport_layers.page_scale = root;
   viewport_layers.inner_viewport_container = root;
@@ -6604,6 +6595,7 @@
   sticky_pos->SetStickyPositionConstraint(sticky_position);
 
   root->SetBounds(gfx::Size(100, 100));
+  scroller->SetScrollable(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(100, 1000));
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(0, 70));
@@ -6662,8 +6654,6 @@
   outer_viewport->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(root->id());
-  outer_viewport->SetScrollClipLayerId(outer_clip->id());
   LayerTreeHost::ViewportLayers viewport_layers;
   viewport_layers.page_scale = root;
   viewport_layers.inner_viewport_container = root;
@@ -6683,8 +6673,10 @@
   sticky_pos->SetStickyPositionConstraint(sticky_position);
 
   root->SetBounds(gfx::Size(100, 100));
+  scroller->SetScrollable(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(100, 1000));
   outer_clip->SetBounds(gfx::Size(100, 100));
+  outer_viewport->SetScrollable(gfx::Size(100, 100));
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(0, 70));
 
@@ -6745,7 +6737,6 @@
   scroller->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
 
   LayerStickyPositionConstraint sticky_position;
   sticky_position.is_sticky = true;
@@ -6762,6 +6753,7 @@
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(1000, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(145, 0));
 
@@ -6850,7 +6842,6 @@
   scroller->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
 
   LayerStickyPositionConstraint sticky_position;
   sticky_position.is_sticky = true;
@@ -6865,6 +6856,7 @@
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(1000, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(10, 20));
 
@@ -6943,7 +6935,6 @@
   sticky_container->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
 
   LayerStickyPositionConstraint sticky_position;
   sticky_position.is_sticky = true;
@@ -6958,6 +6949,7 @@
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(1000, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_container->SetPosition(gfx::PointF(20, 20));
   sticky_container->SetBounds(gfx::Size(30, 30));
   sticky_pos->SetBounds(gfx::Size(10, 10));
@@ -7041,7 +7033,6 @@
   scroller->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
   gfx::Transform t;
   t.Scale(2, 2);
   sticky_pos->SetTransform(t);
@@ -7059,6 +7050,7 @@
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(1000, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(0, 20));
 
@@ -7120,7 +7112,6 @@
   sticky_container->AddChild(sticky_pos);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
   gfx::Transform t;
   t.Scale(2, 2);
   sticky_container->SetTransform(t);
@@ -7138,6 +7129,7 @@
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(1000, 1000));
+  scroller->SetScrollable(container->bounds());
   sticky_container->SetBounds(gfx::Size(50, 50));
   sticky_pos->SetBounds(gfx::Size(10, 10));
   sticky_pos->SetPosition(gfx::PointF(0, 20));
@@ -7199,11 +7191,11 @@
   outer_sticky->AddChild(inner_sticky);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
 
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
   scroller->SetBounds(gfx::Size(100, 1000));
+  scroller->SetScrollable(container->bounds());
   outer_sticky->SetBounds(gfx::Size(10, 50));
   outer_sticky->SetPosition(gfx::PointF(0, 50));
   inner_sticky->SetBounds(gfx::Size(10, 10));
@@ -7300,12 +7292,12 @@
   LayerPositionConstraint fixed_position;
   fixed_position.set_is_fixed_position(true);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
   fixed_pos->SetPositionConstraint(fixed_position);
 
   root->SetBounds(gfx::Size(50, 50));
   container->SetBounds(gfx::Size(50, 50));
   scroller->SetBounds(gfx::Size(50, 50));
+  scroller->SetScrollable(container->bounds());
   fixed_pos->SetBounds(gfx::Size(50, 50));
 
   gfx::Transform rotate;
@@ -7345,12 +7337,12 @@
   LayerPositionConstraint fixed_position;
   fixed_position.set_is_fixed_position(true);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayerId(container->id());
   fixed_pos->SetPositionConstraint(fixed_position);
 
   root->SetBounds(gfx::Size(50, 50));
   container->SetBounds(gfx::Size(50, 50));
   scroller->SetBounds(gfx::Size(100, 100));
+  scroller->SetScrollable(container->bounds());
   scroller->SetPosition(gfx::PointF(10.3f, 10.3f));
   fixed_pos->SetBounds(gfx::Size(10, 10));
 
@@ -8212,11 +8204,6 @@
   inner_viewport_scroll_layer->AddChild(outer_viewport_container_layer);
   outer_viewport_container_layer->AddChild(outer_viewport_scroll_layer);
 
-  inner_viewport_scroll_layer->SetScrollClipLayerId(
-      inner_viewport_container_layer->id());
-  outer_viewport_scroll_layer->SetScrollClipLayerId(
-      outer_viewport_container_layer->id());
-
   inner_viewport_scroll_layer->SetIsContainerForFixedPositionLayers(true);
   outer_viewport_scroll_layer->SetIsContainerForFixedPositionLayers(true);
 
@@ -8592,7 +8579,7 @@
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
   scroller->SetCurrentScrollOffset(gfx::ScrollOffset(100, 100));
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
-  scroller->SetScrollClipLayer(frame_clip->id());
+  scroller->SetScrollable(frame_clip->bounds());
   scroller->SetDrawsContent(true);
   fixed->SetPosition(gfx::PointF(100, 100));
   fixed->SetBounds(gfx::Size(50, 50));
@@ -8943,7 +8930,6 @@
                                                        player.get());
   player->AddAnimation(std::move(transform_animation));
   grandchild_ptr->set_visible_layer_rect(gfx::Rect());
-  child_ptr->SetScrollClipLayer(root_ptr->id());
   root_ptr->test_properties()->transform = singular;
   child_ptr->test_properties()->transform = singular;
   root_ptr->layer_tree_impl()->property_trees()->needs_rebuild = true;
@@ -9749,7 +9735,6 @@
   scroll_parent->test_properties()->scroll_children =
       base::MakeUnique<std::set<LayerImpl*>>();
   scroll_parent->test_properties()->scroll_children->insert(scroll_child);
-  scroll_parent->SetScrollClipLayer(scroll_clip->id());
 
   scroll_parent->SetDrawsContent(true);
   scroll_child->SetDrawsContent(true);
@@ -10037,22 +10022,25 @@
   child9->AddChild(grand_child12);
   host()->SetRootLayer(root1);
 
+  root1->SetBounds(gfx::Size(1, 1));
   parent2->AddMainThreadScrollingReasons(
       MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
   parent2->AddMainThreadScrollingReasons(
       MainThreadScrollingReason::kScrollbarScrolling);
   parent2->SetElementId(LayerIdToElementIdForTesting(parent2->id()));
-  parent2->SetScrollClipLayerId(root1->id());
+  parent2->SetScrollable(root1->bounds());
   child6->AddMainThreadScrollingReasons(
       MainThreadScrollingReason::kScrollbarScrolling);
   grand_child10->AddMainThreadScrollingReasons(
       MainThreadScrollingReason::kScrollbarScrolling);
 
-  child7->SetScrollClipLayerId(parent3->id());
+  parent3->SetBounds(gfx::Size(2, 2));
+  child7->SetScrollable(parent3->bounds());
   child7->SetElementId(LayerIdToElementIdForTesting(child7->id()));
 
   child8->SetScrollParent(child7.get());
-  grand_child11->SetScrollClipLayerId(parent3->id());
+  child8->SetBounds(gfx::Size(3, 3));
+  grand_child11->SetScrollable(child8->bounds());
   grand_child11->SetElementId(
       LayerIdToElementIdForTesting(grand_child11->id()));
 
@@ -10087,6 +10075,7 @@
   // The node owned by root1
   ScrollNode scroll_root1;
   scroll_root1.id = 1;
+  scroll_root1.bounds = root1->bounds();
   scroll_root1.owning_layer_id = root1->id();
   scroll_root1.user_scrollable_horizontal = true;
   scroll_root1.user_scrollable_vertical = true;
@@ -10141,6 +10130,7 @@
   scroll_grand_child11.owning_layer_id = grand_child11->id();
   scroll_grand_child11.element_id = grand_child11->element_id();
   scroll_grand_child11.scrollable = true;
+  scroll_grand_child11.scroll_clip_layer_bounds = child8->bounds();
   scroll_grand_child11.user_scrollable_horizontal = true;
   scroll_grand_child11.user_scrollable_vertical = true;
   scroll_grand_child11.transform_id = grand_child11->transform_tree_index();
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 1aa17af9..3e781b3 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2017,6 +2017,10 @@
   return !!InnerViewportScrollLayer();
 }
 
+LayerImpl* LayerTreeHostImpl::InnerViewportContainerLayer() const {
+  return active_tree_->InnerViewportContainerLayer();
+}
+
 LayerImpl* LayerTreeHostImpl::InnerViewportScrollLayer() const {
   return active_tree_->InnerViewportScrollLayer();
 }
@@ -2029,6 +2033,10 @@
   return scroll_tree.Node(inner_viewport_scroll_layer->scroll_tree_index());
 }
 
+LayerImpl* LayerTreeHostImpl::OuterViewportContainerLayer() const {
+  return active_tree_->OuterViewportContainerLayer();
+}
+
 LayerImpl* LayerTreeHostImpl::OuterViewportScrollLayer() const {
   return active_tree_->OuterViewportScrollLayer();
 }
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 8c765ef..b5510a6 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -439,8 +439,10 @@
   virtual void ActivateSyncTree();
 
   // Shortcuts to layers/nodes on the active tree.
+  LayerImpl* InnerViewportContainerLayer() const;
   LayerImpl* InnerViewportScrollLayer() const;
   ScrollNode* InnerViewportScrollNode() const;
+  LayerImpl* OuterViewportContainerLayer() const;
   LayerImpl* OuterViewportScrollLayer() const;
   ScrollNode* OuterViewportScrollNode() const;
   ScrollNode* CurrentlyScrollingNode();
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index f37061a..9f362a34 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -339,13 +339,14 @@
 
     std::unique_ptr<LayerImpl> inner_clip =
         LayerImpl::Create(layer_tree_impl, kInnerViewportClipLayerId);
-    inner_clip->SetBounds(
-        gfx::Size(content_size.width() / 2, content_size.height() / 2));
+    gfx::Size viewport_scroll_bounds =
+        gfx::Size(content_size.width() / 2, content_size.height() / 2);
+    inner_clip->SetBounds(viewport_scroll_bounds);
 
     std::unique_ptr<LayerImpl> page_scale =
         LayerImpl::Create(layer_tree_impl, kPageScaleLayerId);
 
-    inner_scroll->SetScrollClipLayer(inner_clip->id());
+    inner_scroll->SetScrollable(viewport_scroll_bounds);
     inner_scroll->SetElementId(
         LayerIdToElementIdForTesting(inner_scroll->id()));
     inner_scroll->SetBounds(content_size);
@@ -359,7 +360,7 @@
 
     std::unique_ptr<LayerImpl> outer_scroll =
         LayerImpl::Create(layer_tree_impl, kOuterViewportScrollLayerId);
-    outer_scroll->SetScrollClipLayer(outer_clip->id());
+    outer_scroll->SetScrollable(content_size);
     outer_scroll->SetElementId(
         LayerIdToElementIdForTesting(outer_scroll->id()));
     outer_scroll->layer_tree_impl()
@@ -414,13 +415,9 @@
     root->SetBounds(content_size);
     root->SetPosition(gfx::PointF());
 
-    std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 2);
-    clip->SetBounds(content_size);
-    clip->SetPosition(gfx::PointF());
-
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3);
     scroll->SetBounds(scroll_content_size);
-    scroll->SetScrollClipLayer(clip->id());
+    scroll->SetScrollable(content_size);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
@@ -451,10 +448,9 @@
     squash2->SetDrawsContent(true);
 
     scroll->test_properties()->AddChild(std::move(squash2));
-    clip->test_properties()->AddChild(std::move(scroll));
-    clip->test_properties()->AddChild(std::move(scrollbar));
-    clip->test_properties()->AddChild(std::move(squash1));
-    root->test_properties()->AddChild(std::move(clip));
+    root->test_properties()->AddChild(std::move(scroll));
+    root->test_properties()->AddChild(std::move(scrollbar));
+    root->test_properties()->AddChild(std::move(squash1));
 
     layer_tree_impl->SetRootLayerForTesting(std::move(root));
     layer_tree_impl->BuildPropertyTreesForTesting();
@@ -496,6 +492,7 @@
                                    ->children.back();
     content_layer->SetBounds(content_size);
     host_impl_->OuterViewportScrollLayer()->SetBounds(content_size);
+    host_impl_->OuterViewportScrollLayer()->SetScrollable(viewport_size);
 
     LayerImpl* outer_clip =
         host_impl_->OuterViewportScrollLayer()->test_properties()->parent;
@@ -507,6 +504,7 @@
                                       ->parent;
     inner_clip_layer->SetBounds(viewport_size);
     host_impl_->InnerViewportScrollLayer()->SetBounds(viewport_size);
+    host_impl_->InnerViewportScrollLayer()->SetScrollable(viewport_size);
 
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
@@ -517,17 +515,15 @@
   }
 
   std::unique_ptr<LayerImpl> CreateScrollableLayer(int id,
-                                                   const gfx::Size& size,
-                                                   LayerImpl* clip_layer) {
-    DCHECK(clip_layer);
-    DCHECK(id != clip_layer->id());
+                                                   const gfx::Size& size) {
     std::unique_ptr<LayerImpl> layer =
         LayerImpl::Create(host_impl_->active_tree(), id);
-    layer->SetScrollClipLayer(clip_layer->id());
     layer->SetElementId(LayerIdToElementIdForTesting(layer->id()));
     layer->SetDrawsContent(true);
     layer->SetBounds(size);
-    clip_layer->SetBounds(gfx::Size(size.width() / 2, size.height() / 2));
+    gfx::Size scroll_container_bounds =
+        gfx::Size(size.width() / 2, size.height() / 2);
+    layer->SetScrollable(scroll_container_bounds);
     return layer;
   }
 
@@ -809,21 +805,17 @@
   gfx::ScrollOffset scroll_offset(20, 30);
   gfx::Vector2d scroll_delta(11, -15);
 
-  auto root_clip_owned = LayerImpl::Create(host_impl_->active_tree(), 2);
-  auto* root_clip = root_clip_owned.get();
   auto root_owned = LayerImpl::Create(host_impl_->active_tree(), 1);
   auto* root = root_owned.get();
 
-  root_clip->SetBounds(gfx::Size(10, 10));
-  root_clip->test_properties()->AddChild(std::move(root_owned));
   root->SetBounds(gfx::Size(110, 110));
-  root->SetScrollClipLayer(root_clip->id());
+  root->SetScrollable(gfx::Size(10, 10));
   root->SetElementId(LayerIdToElementIdForTesting(root->id()));
   root->layer_tree_impl()
       ->property_trees()
       ->scroll_tree.UpdateScrollOffsetBaseForTesting(root->element_id(),
                                                      scroll_offset);
-  host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_clip_owned));
+  host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_owned));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   std::unique_ptr<ScrollAndScaleSet> scroll_info;
@@ -857,20 +849,14 @@
   int id = outer_viewport_scroll_layer->id();
   std::unique_ptr<LayerImpl> child =
       LayerImpl::Create(host_impl_->active_tree(), id + 2);
-  std::unique_ptr<LayerImpl> child_clip =
-      LayerImpl::Create(host_impl_->active_tree(), id + 3);
 
-  child_clip->SetBounds(gfx::Size(100, 100));
-
-  child->SetScrollClipLayer(child_clip->id());
+  child->SetScrollable(gfx::Size(100, 100));
   child->SetElementId(LayerIdToElementIdForTesting(child->id()));
   child->SetBounds(gfx::Size(100, 400));
   child->SetPosition(gfx::PointF());
   child->SetDrawsContent(true);
 
-  child_clip->test_properties()->AddChild(std::move(child));
-  outer_viewport_scroll_layer->test_properties()->AddChild(
-      std::move(child_clip));
+  outer_viewport_scroll_layer->test_properties()->AddChild(std::move(child));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   base::HistogramTester histogram_tester;
@@ -1196,13 +1182,9 @@
   root->SetBounds(content_size);
   root->SetPosition(gfx::PointF());
 
-  std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 2);
-  clip->SetBounds(content_size);
-  clip->SetPosition(gfx::PointF());
-
   std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3);
   scroll->SetBounds(scroll_content_size);
-  scroll->SetScrollClipLayer(clip->id());
+  scroll->SetScrollable(content_size);
   scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
   scroll->SetDrawsContent(true);
 
@@ -1222,8 +1204,7 @@
 
   scroll->test_properties()->AddChild(std::move(drawn_scrollbar));
   scroll->test_properties()->AddChild(std::move(squash));
-  clip->test_properties()->AddChild(std::move(scroll));
-  root->test_properties()->AddChild(std::move(clip));
+  root->test_properties()->AddChild(std::move(scroll));
 
   layer_tree_impl->SetRootLayerForTesting(std::move(root));
   layer_tree_impl->BuildPropertyTreesForTesting();
@@ -1446,8 +1427,7 @@
   ASSERT_EQ(1u, scroll_layer->test_properties()->children.size());
   LayerImpl* overflow = scroll_layer->test_properties()->children[0];
   overflow->SetBounds(overflow_size);
-  overflow->SetScrollClipLayer(
-      scroll_layer->test_properties()->parent->test_properties()->parent->id());
+  overflow->SetScrollable(gfx::Size(100, 100));
   overflow->SetElementId(LayerIdToElementIdForTesting(overflow->id()));
   overflow->layer_tree_impl()
       ->property_trees()
@@ -1763,7 +1743,7 @@
   DrawFrame();
 
   EXPECT_EQ(scroll_layer, host_impl_->InnerViewportScrollLayer());
-  LayerImpl* container_layer = scroll_layer->scroll_clip_layer();
+  LayerImpl* container_layer = host_impl_->InnerViewportContainerLayer();
   EXPECT_EQ(gfx::Size(50, 50), container_layer->bounds());
 
   float min_page_scale = 1.f, max_page_scale = 4.f;
@@ -2165,19 +2145,15 @@
   LayerImpl* child;
   LayerImpl* child_clip;
 
-  std::unique_ptr<LayerImpl> scroll_parent_clip =
-      LayerImpl::Create(host_impl_->active_tree(), 6);
   std::unique_ptr<LayerImpl> scroll_parent =
-      CreateScrollableLayer(7, gfx::Size(10, 10), scroll_parent_clip.get());
+      CreateScrollableLayer(7, gfx::Size(10, 10));
   parent = scroll_parent.get();
-  scroll_parent_clip->test_properties()->AddChild(std::move(scroll_parent));
-
-  viewport_scroll->test_properties()->AddChild(std::move(scroll_parent_clip));
+  viewport_scroll->test_properties()->AddChild(std::move(scroll_parent));
 
   std::unique_ptr<LayerImpl> scroll_child_clip =
       LayerImpl::Create(host_impl_->active_tree(), 8);
   std::unique_ptr<LayerImpl> scroll_child =
-      CreateScrollableLayer(9, gfx::Size(10, 10), scroll_child_clip.get());
+      CreateScrollableLayer(9, gfx::Size(10, 10));
   child = scroll_child.get();
   scroll_child->SetPosition(gfx::PointF(20.f, 20.f));
   scroll_child_clip->test_properties()->AddChild(std::move(scroll_child));
@@ -2791,7 +2767,7 @@
   DrawFrame();
 
   LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer();
-  LayerImpl* inner_container = inner_scroll->scroll_clip_layer();
+  LayerImpl* inner_container = host_impl_->InnerViewportContainerLayer();
   DCHECK(inner_scroll);
   DCHECK(inner_container);
   EXPECT_EQ(gfx::ScrollOffset(50, 50), inner_scroll->MaxScrollOffset());
@@ -3232,14 +3208,17 @@
   gfx::Size content_size(1000, 1000);
 
   const int horiz_id = 11;
-  const int child_clip_id = 14;
   const int child_scroll_id = 15;
 
   CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size);
   host_impl_->active_tree()->InnerViewportContainerLayer()->SetBounds(
       inner_viewport_size);
+  host_impl_->active_tree()->InnerViewportScrollLayer()->SetScrollable(
+      inner_viewport_size);
   host_impl_->active_tree()->OuterViewportContainerLayer()->SetBounds(
       outer_viewport_size);
+  host_impl_->active_tree()->OuterViewportScrollLayer()->SetScrollable(
+      outer_viewport_size);
   LayerImpl* root_scroll =
       host_impl_->active_tree()->OuterViewportScrollLayer();
   std::unique_ptr<SolidColorScrollbarLayerImpl> horiz_scrollbar =
@@ -3248,8 +3227,6 @@
   std::unique_ptr<LayerImpl> child =
       LayerImpl::Create(host_impl_->active_tree(), child_scroll_id);
   child->SetBounds(content_size);
-  std::unique_ptr<LayerImpl> child_clip =
-      LayerImpl::Create(host_impl_->active_tree(), child_clip_id);
   child->SetBounds(inner_viewport_size);
 
   horiz_scrollbar->SetScrollElementId(root_scroll->element_id());
@@ -3274,7 +3251,6 @@
   const int horiz_1_id = 11;
   const int vert_2_id = 12;
   const int horiz_2_id = 13;
-  const int child_clip_id = 14;
   const int child_scroll_id = 15;
 
   CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size);
@@ -3306,12 +3282,8 @@
 
   std::unique_ptr<LayerImpl> child =
       LayerImpl::Create(host_impl_->active_tree(), child_scroll_id);
-  child->SetBounds(content_size);
-  std::unique_ptr<LayerImpl> child_clip =
-      LayerImpl::Create(host_impl_->active_tree(), child_clip_id);
   child->SetBounds(viewport_size);
   LayerImpl* child_ptr = child.get();
-  LayerImpl* child_clip_ptr = child_clip.get();
 
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
@@ -3337,11 +3309,10 @@
   animation_task_ = base::Closure();
 
   // Check scrollbar registration on a sublayer.
-  child->SetScrollClipLayer(child_clip->id());
+  child->SetScrollable(viewport_size);
   child->SetElementId(LayerIdToElementIdForTesting(child->id()));
   ElementId child_scroll_element_id = child->element_id();
-  child_clip->test_properties()->AddChild(std::move(child));
-  root_scroll->test_properties()->AddChild(std::move(child_clip));
+  root_scroll->test_properties()->AddChild(std::move(child));
   EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size());
   EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId(
                          child_scroll_element_id));
@@ -3357,7 +3328,7 @@
   // Changing one of the child layers should result in a scrollbar animation
   // update.
   animation_task_ = base::Closure();
-  child_clip_ptr->SetBounds(gfx::Size(200, 200));
+  child_ptr->SetBounds(gfx::Size(200, 200));
   child_ptr->set_needs_show_scrollbars(true);
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
   host_impl_->active_tree()->HandleScrollbarShowRequestsFromMain();
@@ -4229,7 +4200,6 @@
 TEST_F(LayerTreeHostImplTest, ScrollRootIgnored) {
   std::unique_ptr<LayerImpl> root =
       LayerImpl::Create(host_impl_->active_tree(), 1);
-  root->SetScrollClipLayer(Layer::INVALID_ID);
   root->test_properties()->force_render_surface = true;
   host_impl_->active_tree()->SetRootLayerForTesting(std::move(root));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
@@ -4325,7 +4295,7 @@
     std::unique_ptr<LayerImpl> outer_clip = LayerImpl::Create(tree_impl, 5);
 
     root_clip->SetBounds(inner_viewport_size);
-    root->SetScrollClipLayer(root_clip->id());
+    root->SetScrollable(inner_viewport_size);
     root->SetElementId(LayerIdToElementIdForTesting(root->id()));
     root->SetBounds(outer_viewport_size);
     root->SetPosition(gfx::PointF());
@@ -4333,7 +4303,7 @@
     root_clip->test_properties()->force_render_surface = true;
     root->test_properties()->is_container_for_fixed_position_layers = true;
     outer_clip->SetBounds(outer_viewport_size);
-    outer_scroll->SetScrollClipLayer(outer_clip->id());
+    outer_scroll->SetScrollable(outer_viewport_size);
     outer_scroll->SetElementId(
         LayerIdToElementIdForTesting(outer_scroll->id()));
     outer_scroll->SetBounds(scroll_layer_size);
@@ -4778,14 +4748,10 @@
   LayerImpl* outer_viewport_scroll_layer =
       host_impl_->active_tree()->OuterViewportScrollLayer();
   int id = outer_viewport_scroll_layer->id();
-
   std::unique_ptr<LayerImpl> child =
       LayerImpl::Create(host_impl_->active_tree(), id + 2);
-  std::unique_ptr<LayerImpl> child_clip =
-      LayerImpl::Create(host_impl_->active_tree(), id + 3);
 
-  child_clip->SetBounds(sub_content_layer_size);
-  child->SetScrollClipLayer(child_clip->id());
+  child->SetScrollable(sub_content_layer_size);
   child->SetElementId(LayerIdToElementIdForTesting(child->id()));
   child->SetBounds(sub_content_size);
   child->SetPosition(gfx::PointF());
@@ -4794,9 +4760,7 @@
 
   // scroll child to limit
   SetScrollOffsetDelta(child.get(), gfx::Vector2dF(0, 100.f));
-  child_clip->test_properties()->AddChild(std::move(child));
-  outer_viewport_scroll_layer->test_properties()->AddChild(
-      std::move(child_clip));
+  outer_viewport_scroll_layer->test_properties()->AddChild(std::move(child));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   // Scroll 25px to hide browser controls
@@ -5329,19 +5293,19 @@
   content_layer->SetPosition(gfx::PointF());
   content_layer->SetBounds(contents_size);
 
-  LayerImpl* scroll_clip_layer =
+  LayerImpl* scroll_container_layer =
       CreateBasicVirtualViewportLayers(surface_size, surface_size);
 
   std::unique_ptr<LayerImpl> scroll_layer =
       LayerImpl::Create(host_impl_->active_tree(), 12);
-  scroll_layer->SetScrollClipLayer(scroll_clip_layer->id());
+  scroll_layer->SetScrollable(surface_size);
   scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id()));
   scroll_layer->SetBounds(contents_size);
   scroll_layer->SetPosition(gfx::PointF());
   scroll_layer->test_properties()->AddChild(std::move(content_layer));
-  scroll_clip_layer->test_properties()->AddChild(std::move(scroll_layer));
+  scroll_container_layer->test_properties()->AddChild(std::move(scroll_layer));
 
-  scroll_clip_layer->test_properties()->force_render_surface = true;
+  scroll_container_layer->test_properties()->force_render_surface = true;
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   host_impl_->SetViewportSize(surface_size);
@@ -5365,8 +5329,7 @@
   LayerImpl* root =
       CreateBasicVirtualViewportLayers(surface_size, surface_size);
 
-  root->test_properties()->AddChild(
-      CreateScrollableLayer(12, contents_size, root));
+  root->test_properties()->AddChild(CreateScrollableLayer(12, contents_size));
   root->test_properties()->force_render_surface = true;
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
@@ -5388,8 +5351,7 @@
   gfx::Size surface_size(10, 10);
   std::unique_ptr<LayerImpl> root =
       LayerImpl::Create(host_impl_->active_tree(), 1);
-  root->test_properties()->AddChild(
-      CreateScrollableLayer(2, surface_size, root.get()));
+  root->test_properties()->AddChild(CreateScrollableLayer(2, surface_size));
   root->test_properties()->force_render_surface = true;
   host_impl_->active_tree()->SetRootLayerForTesting(std::move(root));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
@@ -5414,8 +5376,7 @@
   std::unique_ptr<LayerImpl> root =
       LayerImpl::Create(host_impl_->active_tree(), 1);
   root->test_properties()->force_render_surface = true;
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(2, surface_size, root.get());
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
 
   gfx::Transform matrix;
   matrix.RotateAboutXAxis(180.0);
@@ -5443,23 +5404,17 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) {
   gfx::Size surface_size(10, 10);
-  std::unique_ptr<LayerImpl> clip_layer =
-      LayerImpl::Create(host_impl_->active_tree(), 3);
   std::unique_ptr<LayerImpl> content_layer =
-      CreateScrollableLayer(1, surface_size, clip_layer.get());
+      CreateScrollableLayer(1, surface_size);
   content_layer->set_main_thread_scrolling_reasons(
       MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
-  content_layer->SetScrollClipLayer(Layer::INVALID_ID);
 
   // Note: we can use the same clip layer for both since both calls to
   // CreateScrollableLayer() use the same surface size.
   std::unique_ptr<LayerImpl> scroll_layer =
-      CreateScrollableLayer(2, surface_size, clip_layer.get());
+      CreateScrollableLayer(2, surface_size);
   scroll_layer->test_properties()->AddChild(std::move(content_layer));
-  clip_layer->test_properties()->AddChild(std::move(scroll_layer));
-  clip_layer->test_properties()->force_render_surface = true;
-
-  host_impl_->active_tree()->SetRootLayerForTesting(std::move(clip_layer));
+  host_impl_->active_tree()->SetRootLayerForTesting(std::move(scroll_layer));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   host_impl_->SetViewportSize(surface_size);
@@ -5481,23 +5436,18 @@
   SetupScrollAndContentsLayers(viewport_size);
 
   // Setup the layers so that the outer viewport is scrollable.
-  host_impl_->active_tree()
-      ->InnerViewportScrollLayer()
-      ->test_properties()
-      ->parent->SetBounds(viewport_size);
-  host_impl_->active_tree()->OuterViewportScrollLayer()->SetBounds(
-      gfx::Size(40, 40));
+  host_impl_->InnerViewportScrollLayer()->test_properties()->parent->SetBounds(
+      viewport_size);
+  host_impl_->OuterViewportScrollLayer()->SetBounds(gfx::Size(40, 40));
   host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f);
   DrawFrame();
 
-  LayerImpl* root_scroll =
-      host_impl_->active_tree()->OuterViewportScrollLayer();
-  LayerImpl* inner_scroll =
-      host_impl_->active_tree()->InnerViewportScrollLayer();
-  EXPECT_EQ(viewport_size, root_scroll->scroll_clip_layer()->bounds());
+  LayerImpl* root_container = host_impl_->OuterViewportContainerLayer();
+  EXPECT_EQ(viewport_size, root_container->bounds());
 
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
+  LayerImpl* root_scroll = host_impl_->OuterViewportScrollLayer();
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
@@ -5512,6 +5462,7 @@
 
   std::unique_ptr<ScrollAndScaleSet> scroll_info =
       host_impl_->ProcessScrollDeltas();
+  LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer();
   EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), inner_scroll->element_id(),
                                  expected_scroll_delta));
 
@@ -5530,25 +5481,20 @@
   SetupScrollAndContentsLayers(viewport_size);
 
   // Setup the layers so that the outer viewport is scrollable.
-  host_impl_->active_tree()
-      ->InnerViewportScrollLayer()
-      ->test_properties()
-      ->parent->SetBounds(viewport_size);
-  host_impl_->active_tree()->OuterViewportScrollLayer()->SetBounds(
-      gfx::Size(40, 40));
+  host_impl_->InnerViewportScrollLayer()->test_properties()->parent->SetBounds(
+      viewport_size);
+  host_impl_->OuterViewportScrollLayer()->SetBounds(gfx::Size(40, 40));
   host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f);
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   DrawFrame();
 
-  LayerImpl* root_scroll =
-      host_impl_->active_tree()->OuterViewportScrollLayer();
-  LayerImpl* inner_scroll =
-      host_impl_->active_tree()->InnerViewportScrollLayer();
-  EXPECT_EQ(viewport_size, root_scroll->scroll_clip_layer()->bounds());
+  LayerImpl* root_container = host_impl_->OuterViewportContainerLayer();
+  EXPECT_EQ(viewport_size, root_container->bounds());
 
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
+  LayerImpl* root_scroll = host_impl_->OuterViewportScrollLayer();
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
@@ -5571,6 +5517,7 @@
   // The scroll delta is not scaled because the main thread did not scale.
   std::unique_ptr<ScrollAndScaleSet> scroll_info =
       host_impl_->ProcessScrollDeltas();
+  LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer();
   EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), inner_scroll->element_id(),
                                  expected_scroll_delta));
 
@@ -5602,7 +5549,7 @@
   std::unique_ptr<LayerImpl> scrollable_child_clip =
       LayerImpl::Create(host_impl_->active_tree(), 6);
   std::unique_ptr<LayerImpl> scrollable_child =
-      CreateScrollableLayer(7, surface_size, scrollable_child_clip.get());
+      CreateScrollableLayer(7, surface_size);
   scrollable_child_clip->test_properties()->AddChild(
       std::move(scrollable_child));
   child->test_properties()->AddChild(std::move(scrollable_child_clip));
@@ -5692,10 +5639,9 @@
 
   root->test_properties()->force_render_surface = true;
   std::unique_ptr<LayerImpl> grand_child =
-      CreateScrollableLayer(13, content_size, root);
+      CreateScrollableLayer(13, content_size);
 
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(12, content_size, root);
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(12, content_size);
   LayerImpl* grand_child_layer = grand_child.get();
   child->test_properties()->AddChild(std::move(grand_child));
 
@@ -5758,10 +5704,9 @@
 
   root->test_properties()->force_render_surface = true;
   std::unique_ptr<LayerImpl> grand_child =
-      CreateScrollableLayer(13, content_size, root);
+      CreateScrollableLayer(13, content_size);
 
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(12, content_size, root);
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(12, content_size);
   LayerImpl* grand_child_layer = grand_child.get();
   child->test_properties()->AddChild(std::move(grand_child));
 
@@ -5856,23 +5801,21 @@
   std::unique_ptr<LayerImpl> root_clip =
       LayerImpl::Create(host_impl_->active_tree(), kViewportClipLayerId);
   root_clip->test_properties()->force_render_surface = true;
-  std::unique_ptr<LayerImpl> root_scrolling = CreateScrollableLayer(
-      kViewportScrollLayerId, surface_size, root_clip.get());
+  std::unique_ptr<LayerImpl> root_scrolling =
+      CreateScrollableLayer(kViewportScrollLayerId, surface_size);
   root_scrolling->test_properties()->is_container_for_fixed_position_layers =
       true;
 
   std::unique_ptr<LayerImpl> grand_child =
-      CreateScrollableLayer(5, surface_size, root_clip.get());
+      CreateScrollableLayer(5, surface_size);
 
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(4, surface_size, root_clip.get());
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(4, surface_size);
   LayerImpl* grand_child_layer = grand_child.get();
   child->test_properties()->AddChild(std::move(grand_child));
 
   LayerImpl* child_layer = child.get();
   root_scrolling->test_properties()->AddChild(std::move(child));
   root_clip->test_properties()->AddChild(std::move(root_scrolling));
-  EXPECT_EQ(viewport_size, root_clip->bounds());
   root_ptr->test_properties()->AddChild(std::move(root_clip));
   host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_ptr));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
@@ -6007,14 +5950,15 @@
   std::unique_ptr<LayerImpl> root_clip =
       LayerImpl::Create(host_impl_->active_tree(), 3);
   root_clip->test_properties()->force_render_surface = true;
-  std::unique_ptr<LayerImpl> root_scroll = CreateScrollableLayer(
-      kViewportClipLayerId, content_size, root_clip.get());
+  std::unique_ptr<LayerImpl> root_scroll =
+      CreateScrollableLayer(kViewportClipLayerId, content_size);
   // Make 'root' the clip layer for child: since they have the same sizes the
   // child will have zero max_scroll_offset and scrolls will bubble.
-  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(
-      kViewportScrollLayerId, content_size, root_scroll.get());
+  std::unique_ptr<LayerImpl> child =
+      CreateScrollableLayer(kViewportScrollLayerId, content_size);
   child->test_properties()->is_container_for_fixed_position_layers = true;
   root_scroll->SetBounds(content_size);
+  child->SetScrollable(content_size);
 
   ElementId root_scroll_id = root_scroll->element_id();
   root_scroll->test_properties()->AddChild(std::move(child));
@@ -6063,12 +6007,12 @@
       LayerImpl::Create(host_impl_->active_tree(), kPageScaleLayerId);
   std::unique_ptr<LayerImpl> inner_clip =
       LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId);
-  std::unique_ptr<LayerImpl> inner_scroll = CreateScrollableLayer(
-      kInnerViewportScrollLayerId, surface_size, inner_clip.get());
+  std::unique_ptr<LayerImpl> inner_scroll =
+      CreateScrollableLayer(kInnerViewportScrollLayerId, surface_size);
   std::unique_ptr<LayerImpl> outer_clip =
       LayerImpl::Create(host_impl_->active_tree(), kOuterViewportClipLayerId);
-  std::unique_ptr<LayerImpl> outer_scroll = CreateScrollableLayer(
-      kOuterViewportScrollLayerId, surface_size, outer_clip.get());
+  std::unique_ptr<LayerImpl> outer_scroll =
+      CreateScrollableLayer(kOuterViewportScrollLayerId, surface_size);
   inner_clip->test_properties()->force_render_surface = true;
   inner_scroll->test_properties()->is_container_for_fixed_position_layers =
       true;
@@ -6105,12 +6049,12 @@
       LayerImpl::Create(host_impl_->active_tree(), 4);
   std::unique_ptr<LayerImpl> inner_clip2 =
       LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId2);
-  std::unique_ptr<LayerImpl> inner_scroll2 = CreateScrollableLayer(
-      kInnerViewportScrollLayerId2, surface_size, inner_clip2.get());
+  std::unique_ptr<LayerImpl> inner_scroll2 =
+      CreateScrollableLayer(kInnerViewportScrollLayerId2, surface_size);
   std::unique_ptr<LayerImpl> outer_clip2 =
       LayerImpl::Create(host_impl_->active_tree(), kOuterViewportClipLayerId2);
-  std::unique_ptr<LayerImpl> outer_scroll2 = CreateScrollableLayer(
-      kOuterViewportScrollLayerId2, surface_size, outer_clip2.get());
+  std::unique_ptr<LayerImpl> outer_scroll2 =
+      CreateScrollableLayer(kOuterViewportScrollLayerId2, surface_size);
   inner_scroll2->test_properties()->is_container_for_fixed_position_layers =
       true;
   outer_scroll2->test_properties()->is_container_for_fixed_position_layers =
@@ -6198,8 +6142,8 @@
   // Create a child layer that is rotated to a non-axis-aligned angle.
   std::unique_ptr<LayerImpl> clip_layer =
       LayerImpl::Create(host_impl_->active_tree(), child_clip_layer_id);
-  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(
-      child_layer_id, scroll_layer->bounds(), clip_layer.get());
+  std::unique_ptr<LayerImpl> child =
+      CreateScrollableLayer(child_layer_id, scroll_layer->bounds());
   gfx::Transform rotate_transform;
   rotate_transform.Translate(-50.0, -50.0);
   rotate_transform.Rotate(child_layer_angle);
@@ -6207,8 +6151,10 @@
   clip_layer->test_properties()->transform = rotate_transform;
 
   // Only allow vertical scrolling.
-  clip_layer->SetBounds(
-      gfx::Size(child->bounds().width(), child->bounds().height() / 2));
+  gfx::Size scroll_container_bounds =
+      gfx::Size(child->bounds().width(), child->bounds().height() / 2);
+  clip_layer->SetBounds(scroll_container_bounds);
+  child->SetScrollable(scroll_container_bounds);
   // The rotation depends on the layer's transform origin, and the child layer
   // is a different size than the clip, so make sure the clip layer's origin
   // lines up over the child.
@@ -6216,6 +6162,7 @@
       clip_layer->bounds().width() * 0.5f, clip_layer->bounds().height(), 0.f);
   LayerImpl* child_ptr = child.get();
   clip_layer->test_properties()->AddChild(std::move(child));
+  // TODO(pdr): Shouldn't clip_layer be scroll_layer's parent?
   scroll_layer->test_properties()->AddChild(std::move(clip_layer));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
@@ -6286,8 +6233,8 @@
   // Create a child layer that is rotated on its x axis, with perspective.
   std::unique_ptr<LayerImpl> clip_layer =
       LayerImpl::Create(host_impl_->active_tree(), child_clip_layer_id);
-  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(
-      child_layer_id, scroll_layer->bounds(), clip_layer.get());
+  std::unique_ptr<LayerImpl> child =
+      CreateScrollableLayer(child_layer_id, scroll_layer->bounds());
   LayerImpl* child_ptr = child.get();
   gfx::Transform perspective_transform;
   perspective_transform.Translate(-50.0, -50.0);
@@ -6411,8 +6358,11 @@
   int height = 20;
   int scale = 3;
   SetupScrollAndContentsLayers(gfx::Size(width, height));
+  gfx::Size container_bounds = gfx::Size(width * scale - 1, height * scale);
   host_impl_->active_tree()->InnerViewportContainerLayer()->SetBounds(
-      gfx::Size(width * scale - 1, height * scale));
+      container_bounds);
+  host_impl_->active_tree()->InnerViewportScrollLayer()->SetScrollable(
+      container_bounds);
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   host_impl_->active_tree()->SetDeviceScaleFactor(scale);
@@ -6431,6 +6381,7 @@
   LayerImpl* clip_layer =
       scroll_layer->test_properties()->parent->test_properties()->parent;
   clip_layer->SetBounds(gfx::Size(10, 20));
+  scroll_layer->SetScrollable(gfx::Size(10, 20));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   host_impl_->BindToClient(&scroll_watcher, false);
@@ -6543,6 +6494,7 @@
   LayerImpl* clip_layer =
       scroll_layer->test_properties()->parent->test_properties()->parent;
   clip_layer->SetBounds(gfx::Size(10, 20));
+  scroll_layer->SetScrollable(gfx::Size(10, 20));
   scroll_layer->SetDrawsContent(true);
 
   // Draw first frame to clear any pending draws and check scroll.
@@ -6705,14 +6657,13 @@
       LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId);
   root_clip->test_properties()->force_render_surface = true;
 
-  std::unique_ptr<LayerImpl> root = CreateScrollableLayer(
-      kInnerViewportScrollLayerId, surface_size, root_clip.get());
+  std::unique_ptr<LayerImpl> root =
+      CreateScrollableLayer(kInnerViewportScrollLayerId, surface_size);
 
   std::unique_ptr<LayerImpl> grand_child =
-      CreateScrollableLayer(3, surface_size, root_clip.get());
+      CreateScrollableLayer(3, surface_size);
 
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(2, surface_size, root_clip.get());
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
   LayerImpl* grand_child_layer = grand_child.get();
   child->test_properties()->AddChild(std::move(grand_child));
 
@@ -6845,6 +6796,7 @@
       scroll_layer->test_properties()->parent->test_properties()->parent;
 
   clip_layer->SetBounds(gfx::Size(50, 50));
+  scroll_layer->SetScrollable(gfx::Size(50, 50));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   host_impl_->SetViewportSize(gfx::Size(50, 50));
@@ -7002,20 +6954,15 @@
   // passing through the outer viewport still scroll correctly and affect
   // browser controls.
   {
-    std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 10);
-    clip->SetBounds(viewport_size);
-    clip->SetPosition(gfx::PointF());
-
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(gfx::Size(400, 400));
-    scroll->SetScrollClipLayer(clip->id());
+    scroll->SetScrollable(viewport_size);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
     scroll_layer = scroll.get();
 
-    clip->test_properties()->AddChild(std::move(scroll));
-    inner_scroll_layer->test_properties()->AddChild(std::move(clip));
+    inner_scroll_layer->test_properties()->AddChild(std::move(scroll));
 
     // Move the outer viewport layer away so that scrolls won't target it.
     host_impl_->active_tree()->OuterViewportContainerLayer()->SetPosition(
@@ -7093,35 +7040,23 @@
   // with another scrolling div inside it. Set the outer "div" to be the outer
   // viewport.
   {
-    std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 10);
-    clip->SetBounds(content_size);
-    clip->SetPosition(gfx::PointF());
-
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(gfx::Size(400, 400));
-    scroll->SetScrollClipLayer(clip->id());
+    scroll->SetScrollable(content_size);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
-    std::unique_ptr<LayerImpl> clip2 = LayerImpl::Create(layer_tree_impl, 12);
-    clip2->SetBounds(gfx::Size(300, 300));
-    clip2->SetPosition(gfx::PointF());
-    clip2->SetDrawsContent(true);
-
     std::unique_ptr<LayerImpl> scroll2 = LayerImpl::Create(layer_tree_impl, 13);
     scroll2->SetBounds(gfx::Size(500, 500));
-    scroll2->SetScrollClipLayer(clip2->id());
+    scroll2->SetScrollable(gfx::Size(300, 300));
     scroll2->SetElementId(LayerIdToElementIdForTesting(scroll2->id()));
     scroll2->SetDrawsContent(true);
 
     scroll_layer = scroll.get();
     child_scroll_layer = scroll2.get();
 
-    clip2->test_properties()->AddChild(std::move(scroll2));
-    scroll->test_properties()->AddChild(std::move(clip2));
-
-    clip->test_properties()->AddChild(std::move(scroll));
-    content_layer->test_properties()->AddChild(std::move(clip));
+    scroll->test_properties()->AddChild(std::move(scroll2));
+    content_layer->test_properties()->AddChild(std::move(scroll));
     LayerTreeImpl::ViewportLayerIds viewport_ids;
     viewport_ids.page_scale = layer_tree_impl->PageScaleLayer()->id();
     viewport_ids.inner_viewport_scroll = inner_scroll_layer->id();
@@ -7228,36 +7163,26 @@
   // set as the outer viewport. Add a sibling scrolling layer that isn't a child
   // of the outer viewport scroll layer.
   {
-    std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 10);
-    clip->SetBounds(content_size);
-    clip->SetPosition(gfx::PointF(100, 100));
-
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(gfx::Size(1200, 1200));
-    scroll->SetScrollClipLayer(clip->id());
+    scroll->SetScrollable(content_size);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
     outer_scroll_layer = scroll.get();
 
-    clip->test_properties()->AddChild(std::move(scroll));
-    content_layer->test_properties()->AddChild(std::move(clip));
+    content_layer->test_properties()->AddChild(std::move(scroll));
 
     // Create the non-descendant.
-    std::unique_ptr<LayerImpl> clip2 = LayerImpl::Create(layer_tree_impl, 14);
-    clip2->SetBounds(gfx::Size(600, 600));
-    clip2->SetPosition(gfx::PointF());
-
     std::unique_ptr<LayerImpl> scroll2 = LayerImpl::Create(layer_tree_impl, 15);
     scroll2->SetBounds(gfx::Size(1200, 1200));
-    scroll2->SetScrollClipLayer(clip2->id());
+    scroll2->SetScrollable(gfx::Size(600, 600));
     scroll2->SetElementId(LayerIdToElementIdForTesting(scroll2->id()));
     scroll2->SetDrawsContent(true);
 
     sibling_scroll_layer = scroll2.get();
 
-    clip2->test_properties()->AddChild(std::move(scroll2));
-    content_layer->test_properties()->AddChild(std::move(clip2));
+    content_layer->test_properties()->AddChild(std::move(scroll2));
 
     LayerImpl* inner_container =
         host_impl_->active_tree()->InnerViewportContainerLayer();
@@ -8468,7 +8393,7 @@
   root->SetBounds(root_size);
 
   gfx::ScrollOffset scroll_offset(100000, 0);
-  scrolling_layer->SetScrollClipLayer(root->id());
+  scrolling_layer->SetScrollable(content_layer_bounds);
   scrolling_layer->SetElementId(
       LayerIdToElementIdForTesting(scrolling_layer->id()));
   host_impl_->pending_tree()->BuildPropertyTreesForTesting();
@@ -8927,11 +8852,10 @@
       LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId);
   root_clip->test_properties()->force_render_surface = true;
 
-  std::unique_ptr<LayerImpl> root_scroll = CreateScrollableLayer(
-      kInnerViewportScrollLayerId, content_size, root_clip.get());
+  std::unique_ptr<LayerImpl> root_scroll =
+      CreateScrollableLayer(kInnerViewportScrollLayerId, content_size);
   root_scroll->test_properties()->is_container_for_fixed_position_layers = true;
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(2, content_size, root_clip.get());
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size);
 
   root_scroll->test_properties()->AddChild(std::move(child));
   ElementId root_id = root_scroll->element_id();
@@ -8982,17 +8906,17 @@
   root->test_properties()->force_render_surface = true;
 
   std::unique_ptr<LayerImpl> root_scrolling_owned =
-      CreateScrollableLayer(12, surface_size, root);
+      CreateScrollableLayer(12, surface_size);
   auto* root_scrolling = root_scrolling_owned.get();
   root->test_properties()->AddChild(std::move(root_scrolling_owned));
 
   std::unique_ptr<LayerImpl> child_owned =
-      CreateScrollableLayer(13, surface_size, root);
+      CreateScrollableLayer(13, surface_size);
   auto* child = child_owned.get();
   root_scrolling->test_properties()->AddChild(std::move(child_owned));
 
   std::unique_ptr<LayerImpl> grand_child_owned =
-      CreateScrollableLayer(14, surface_size, root);
+      CreateScrollableLayer(14, surface_size);
   auto* grand_child = grand_child_owned.get();
   child->test_properties()->AddChild(std::move(grand_child_owned));
 
@@ -9078,10 +9002,9 @@
       CreateBasicVirtualViewportLayers(surface_size, surface_size);
   root_clip->test_properties()->force_render_surface = true;
   std::unique_ptr<LayerImpl> root_scroll =
-      CreateScrollableLayer(11, content_size, root_clip);
+      CreateScrollableLayer(11, content_size);
   ElementId root_scroll_id = root_scroll->element_id();
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(12, content_size, root_clip);
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(12, content_size);
 
   root_scroll->test_properties()->AddChild(std::move(child));
   root_clip->test_properties()->AddChild(std::move(root_scroll));
@@ -9175,8 +9098,8 @@
       LayerImpl::Create(host_impl_->active_tree(), child_scroll_clip_layer_id);
 
   int child_scroll_layer_id = 8;
-  std::unique_ptr<LayerImpl> child_scroll = CreateScrollableLayer(
-      child_scroll_layer_id, content_size, child_scroll_clip.get());
+  std::unique_ptr<LayerImpl> child_scroll =
+      CreateScrollableLayer(child_scroll_layer_id, content_size);
 
   child_scroll->SetPosition(gfx::PointF(10.f, 10.f));
 
@@ -9198,15 +9121,13 @@
   gfx::Size content_size(100, 100);
   SetupScrollAndContentsLayers(content_size);
 
-  LayerImpl* root = host_impl_->active_tree()->LayerById(1);
-
   int scroll_layer_id = 2;
   LayerImpl* scroll_layer =
       host_impl_->active_tree()->LayerById(scroll_layer_id);
 
   int child_scroll_layer_id = 7;
   std::unique_ptr<LayerImpl> child_scroll =
-      CreateScrollableLayer(child_scroll_layer_id, content_size, root);
+      CreateScrollableLayer(child_scroll_layer_id, content_size);
   child_scroll->SetDrawsContent(false);
 
   scroll_layer->test_properties()->AddChild(std::move(child_scroll));
@@ -9966,7 +9887,7 @@
 
     std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11);
     scroll->SetBounds(scroll_content_size);
-    scroll->SetScrollClipLayer(clip->id());
+    scroll->SetScrollable(root_layer_size);
     scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id()));
     scroll->SetDrawsContent(true);
 
@@ -10034,7 +9955,7 @@
     std::unique_ptr<LayerImpl> page_scale =
         LayerImpl::Create(layer_tree_impl, kPageScaleLayerId);
 
-    inner_scroll->SetScrollClipLayer(inner_clip->id());
+    inner_scroll->SetScrollable(inner_viewport);
     inner_scroll->SetElementId(
         LayerIdToElementIdForTesting(inner_scroll->id()));
     inner_scroll->SetBounds(outer_viewport);
@@ -10048,7 +9969,7 @@
 
     std::unique_ptr<LayerImpl> outer_scroll =
         LayerImpl::Create(layer_tree_impl, kOuterViewportScrollLayerId);
-    outer_scroll->SetScrollClipLayer(outer_clip->id());
+    outer_scroll->SetScrollable(outer_viewport);
     outer_scroll->SetElementId(
         LayerIdToElementIdForTesting(outer_scroll->id()));
     outer_scroll->layer_tree_impl()
@@ -10260,8 +10181,7 @@
   LayerImpl* outer_scroll = host_impl_->OuterViewportScrollLayer();
   LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer();
 
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(10, outer_viewport, outer_scroll);
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(10, outer_viewport);
   LayerImpl* child_scroll = child.get();
   outer_scroll->test_properties()->children[0]->test_properties()->AddChild(
       std::move(child));
@@ -10333,8 +10253,7 @@
 
   LayerImpl* outer_scroll = host_impl_->OuterViewportScrollLayer();
 
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(10, outer_viewport, outer_scroll);
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(10, outer_viewport);
   LayerImpl* child_scroll = child.get();
   outer_scroll->test_properties()->children[0]->test_properties()->AddChild(
       std::move(child));
@@ -10510,14 +10429,13 @@
   root->test_properties()->force_render_surface = true;
 
   // A div layer which has an event handler.
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(26, surface_size, root);
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(26, surface_size);
   LayerImpl* child_layer = child.get();
 
   // The layer tree should create a layer for the div layer, which is the
   // actual scrolling layer.
   std::unique_ptr<LayerImpl> grand_child =
-      CreateScrollableLayer(27, content_size, root);
+      CreateScrollableLayer(27, content_size);
   LayerImpl* grand_child_layer = grand_child.get();
 
   child->test_properties()->AddChild(std::move(grand_child));
@@ -10571,19 +10489,18 @@
   root->test_properties()->force_render_surface = true;
 
   // A div layer which has an event handler.
-  std::unique_ptr<LayerImpl> child =
-      CreateScrollableLayer(26, surface_size, root);
+  std::unique_ptr<LayerImpl> child = CreateScrollableLayer(26, surface_size);
   LayerImpl* child_layer = child.get();
 
   // The layer tree should create a layer for the div layer, which is the
   // actual scrolling layer.
   std::unique_ptr<LayerImpl> grand_child =
-      CreateScrollableLayer(27, content_size, root);
+      CreateScrollableLayer(27, content_size);
   LayerImpl* grand_child_layer = grand_child.get();
 
   // A child scrollable layer inside grand_child_layer.
   std::unique_ptr<LayerImpl> great_grand_child =
-      CreateScrollableLayer(28, inner_size, root);
+      CreateScrollableLayer(28, inner_size);
   LayerImpl* great_grand_child_layer = great_grand_child.get();
 
   grand_child->test_properties()->AddChild(std::move(great_grand_child));
@@ -12223,7 +12140,6 @@
 
   const int scrollbar_1_id = 10;
   const int scrollbar_2_id = 11;
-  const int child_clip_id = 12;
   const int child_scroll_id = 13;
 
   CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12341,14 +12257,12 @@
       SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(),
                                            scrollbar_2_id, VERTICAL, 15, 0,
                                            true, true);
-  std::unique_ptr<LayerImpl> child_clip =
-      LayerImpl::Create(host_impl_->active_tree(), child_clip_id);
   std::unique_ptr<LayerImpl> child =
       LayerImpl::Create(host_impl_->active_tree(), child_scroll_id);
   child->SetPosition(gfx::PointF(50, 50));
   child->SetBounds(child_layer_size);
   child->SetDrawsContent(true);
-  child->SetScrollClipLayer(child_clip_id);
+  child->SetScrollable(gfx::Size(100, 100));
   child->SetElementId(LayerIdToElementIdForTesting(child->id()));
   ElementId child_element_id = child->element_id();
 
@@ -12364,8 +12278,7 @@
   scrollbar_2->SetPosition(gfx::PointF(0, 0));
 
   child->test_properties()->AddChild(std::move(scrollbar_2));
-  child_clip->test_properties()->AddChild(std::move(child));
-  root_scroll->test_properties()->AddChild(std::move(child_clip));
+  root_scroll->test_properties()->AddChild(std::move(child));
 
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
   host_impl_->active_tree()->UpdateScrollbarGeometries();
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index cd297d7f..46bf072 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -813,17 +813,18 @@
         break;
       case 2:
         // child_ should create a scroll node.
-        child_->SetScrollClipLayerId(root_->id());
+        child_->SetScrollable(gfx::Size(1, 1));
         break;
       case 3:
         // child_ should create a clip node.
         child_->SetMasksToBounds(true);
         break;
       case 4:
-        // child_ should not create any property tree node.
+        // child_ should only create the scroll-related nodes.
         child_->SetMasksToBounds(false);
         child_->SetForceRenderSurfaceForTesting(false);
-        child_->SetScrollClipLayerId(Layer::INVALID_ID);
+        // Should have no effect because empty bounds do not prevent scrolling.
+        child_->SetScrollable(gfx::Size(0, 0));
     }
   }
 
@@ -877,11 +878,11 @@
         EXPECT_NE(root_clip_node, child_clip_node);
         break;
       case 4:
-        // child_ should not create any property tree nodes.
-        EXPECT_EQ(child_transform_node, root_transform_node);
+        // child_ should only create the scroll-related nodes.
+        EXPECT_EQ(child_transform_node->id, child_scroll_node->transform_id);
         EXPECT_EQ(child_effect_node, root_effect_node);
         EXPECT_EQ(root_clip_node, child_clip_node);
-        EXPECT_EQ(root_scroll_node, child_scroll_node);
+        EXPECT_NE(root_scroll_node, child_scroll_node);
         EndTest();
         break;
     }
@@ -2742,7 +2743,7 @@
 
     scoped_refptr<Layer> pinch = Layer::Create();
     pinch->SetBounds(gfx::Size(500, 500));
-    pinch->SetScrollClipLayerId(root_clip->id());
+    pinch->SetScrollable(gfx::Size(200, 200));
     pinch->SetIsContainerForFixedPositionLayers(true);
     page_scale_layer->AddChild(pinch);
     root_clip->AddChild(page_scale_layer);
@@ -4766,8 +4767,8 @@
     scoped_refptr<Layer> overscroll_elasticity_layer = Layer::Create();
     scoped_refptr<Layer> page_scale_layer = Layer::Create();
     scoped_refptr<Layer> inner_viewport_scroll_layer = Layer::Create();
-    inner_viewport_scroll_layer->SetScrollClipLayerId(
-        inner_viewport_container_layer->id());
+    inner_viewport_scroll_layer->SetScrollable(
+        inner_viewport_container_layer->bounds());
     inner_viewport_scroll_layer->SetIsContainerForFixedPositionLayers(true);
 
     root_layer_->AddChild(inner_viewport_container_layer);
@@ -6191,7 +6192,7 @@
 
     scoped_refptr<Layer> pinch = Layer::Create();
     pinch->SetBounds(gfx::Size(500, 500));
-    pinch->SetScrollClipLayerId(root_clip->id());
+    pinch->SetScrollable(gfx::Size(500, 500));
     pinch->SetIsContainerForFixedPositionLayers(true);
     page_scale_layer->AddChild(pinch);
     root_clip->AddChild(page_scale_layer);
@@ -6498,7 +6499,7 @@
 
     scoped_refptr<Layer> pinch = Layer::Create();
     pinch->SetBounds(gfx::Size(500, 500));
-    pinch->SetScrollClipLayerId(root_clip->id());
+    pinch->SetScrollable(gfx::Size(500, 500));
     pinch->SetIsContainerForFixedPositionLayers(true);
     page_scale_layer->AddChild(pinch);
     root_clip->AddChild(page_scale_layer);
@@ -6904,7 +6905,7 @@
     CreateVirtualViewportLayers(root.get(), outer_viewport_scroll_layer,
                                 gfx::Size(50, 50), gfx::Size(50, 50),
                                 layer_tree_host());
-    outer_viewport_scroll_layer->scroll_clip_layer()->SetMasksToBounds(true);
+    layer_tree_host()->outer_viewport_container_layer()->SetMasksToBounds(true);
     outer_viewport_scroll_layer->AddChild(content_layer);
 
     client_.set_bounds(root->bounds());
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index b9fa010f..1574fb2 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -724,7 +724,7 @@
     LayerTreeHostAnimationTest::SetupTree();
 
     scroll_layer_ = FakePictureLayer::Create(&client_);
-    scroll_layer_->SetScrollClipLayerId(layer_tree_host()->root_layer()->id());
+    scroll_layer_->SetScrollable(gfx::Size(100, 100));
     scroll_layer_->SetBounds(gfx::Size(1000, 1000));
     client_.set_bounds(scroll_layer_->bounds());
     scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
@@ -785,7 +785,7 @@
     scroll_layer_->SetBounds(gfx::Size(10000, 10000));
     client_.set_bounds(scroll_layer_->bounds());
     scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
-    scroll_layer_->SetScrollClipLayerId(layer_tree_host()->root_layer()->id());
+    scroll_layer_->SetScrollable(gfx::Size(10, 10));
     layer_tree_host()->root_layer()->AddChild(scroll_layer_);
 
     AttachPlayersToTimeline();
@@ -845,7 +845,7 @@
     scroll_layer_->SetBounds(gfx::Size(10000, 10000));
     client_.set_bounds(scroll_layer_->bounds());
     scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
-    scroll_layer_->SetScrollClipLayerId(layer_tree_host()->root_layer()->id());
+    scroll_layer_->SetScrollable(gfx::Size(10, 10));
     layer_tree_host()->root_layer()->AddChild(scroll_layer_);
 
     AttachPlayersToTimeline();
@@ -951,7 +951,7 @@
     LayerTreeHostAnimationTest::SetupTree();
 
     scroll_layer_ = FakePictureLayer::Create(&client_);
-    scroll_layer_->SetScrollClipLayerId(layer_tree_host()->root_layer()->id());
+    scroll_layer_->SetScrollable(gfx::Size(100, 100));
     scroll_layer_->SetBounds(gfx::Size(10000, 10000));
     client_.set_bounds(scroll_layer_->bounds());
     scroll_layer_->SetScrollOffset(gfx::ScrollOffset(100.0, 200.0));
diff --git a/cc/trees/layer_tree_host_unittest_damage.cc b/cc/trees/layer_tree_host_unittest_damage.cc
index 578324f..34950c6e 100644
--- a/cc/trees/layer_tree_host_unittest_damage.cc
+++ b/cc/trees/layer_tree_host_unittest_damage.cc
@@ -339,19 +339,14 @@
     root_layer->SetMasksToBounds(true);
     layer_tree_host()->SetRootLayer(root_layer);
 
-    scoped_refptr<Layer> scroll_clip_layer = Layer::Create();
     content_layer_ = FakePictureLayer::Create(&client_);
     content_layer_->SetElementId(
         LayerIdToElementIdForTesting(content_layer_->id()));
-    content_layer_->SetScrollClipLayerId(scroll_clip_layer->id());
+    content_layer_->SetScrollable(root_layer->bounds());
     content_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
     content_layer_->SetBounds(gfx::Size(100, 200));
     content_layer_->SetIsDrawable(true);
-    scroll_clip_layer->SetBounds(
-        gfx::Size(content_layer_->bounds().width() - 30,
-                  content_layer_->bounds().height() - 50));
-    scroll_clip_layer->AddChild(content_layer_);
-    root_layer->AddChild(scroll_clip_layer);
+    root_layer->AddChild(content_layer_);
 
     scoped_refptr<Layer> scrollbar_layer = FakePaintedScrollbarLayer::Create(
         false, true, content_layer_->element_id());
diff --git a/cc/trees/layer_tree_host_unittest_picture.cc b/cc/trees/layer_tree_host_unittest_picture.cc
index e4fefef..6579662 100644
--- a/cc/trees/layer_tree_host_unittest_picture.cc
+++ b/cc/trees/layer_tree_host_unittest_picture.cc
@@ -224,7 +224,7 @@
 
     // picture_'s transform is going to be changing on the compositor thread, so
     // force it to have a transform node by making it scrollable.
-    picture_->SetScrollClipLayerId(root->id());
+    picture_->SetScrollable(root->bounds());
 
     layer_tree_host()->SetRootLayer(root);
     LayerTreeHostPictureTest::SetupTree();
@@ -412,7 +412,7 @@
 
     pinch_ = Layer::Create();
     pinch_->SetBounds(gfx::Size(500, 500));
-    pinch_->SetScrollClipLayerId(root_clip->id());
+    pinch_->SetScrollable(root_clip->bounds());
     pinch_->SetIsContainerForFixedPositionLayers(true);
     page_scale_layer->AddChild(pinch_);
     root_clip->AddChild(page_scale_layer);
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index d3ac199..767c4e2 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -94,10 +94,8 @@
         num_scrolls_(0) {}
 
   void BeginTest() override {
-    outer_viewport_container_layer_id_ = layer_tree_host()
-                                             ->outer_viewport_scroll_layer()
-                                             ->scroll_clip_layer()
-                                             ->id();
+    outer_viewport_container_layer_id_ =
+        layer_tree_host()->outer_viewport_container_layer()->id();
     layer_tree_host()->outer_viewport_scroll_layer()->SetScrollOffset(
         initial_scroll_);
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
@@ -125,7 +123,6 @@
     LayerImpl* scroll_layer = impl->OuterViewportScrollLayer();
     EXPECT_VECTOR_EQ(gfx::Vector2d(), ScrollDelta(scroll_layer));
 
-    scroll_layer->SetScrollClipLayer(outer_viewport_container_layer_id_);
     scroll_layer->SetBounds(
         gfx::Size(root->bounds().width() + 100, root->bounds().height() + 100));
     scroll_layer->ScrollBy(scroll_amount_);
@@ -505,15 +502,12 @@
   void SetupTree() override {
     LayerTreeHostScrollTest::SetupTree();
     layer_tree_host()
-        ->outer_viewport_scroll_layer()
-        ->scroll_clip_layer()
+        ->outer_viewport_container_layer()
         ->SetForceRenderSurfaceForTesting(true);
     gfx::Transform translate;
     translate.Translate(0.25f, 0.f);
-    layer_tree_host()
-        ->outer_viewport_scroll_layer()
-        ->scroll_clip_layer()
-        ->SetTransform(translate);
+    layer_tree_host()->outer_viewport_container_layer()->SetTransform(
+        translate);
     layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 0.1f, 100.f);
   }
 
@@ -598,11 +592,8 @@
       child_layer_->SetPosition(gfx::PointF(60.f, 5.f));
     }
 
-    scoped_refptr<Layer> outer_container_layer =
-        layer_tree_host()->outer_viewport_scroll_layer()->parent();
-
     child_layer_->SetIsDrawable(true);
-    child_layer_->SetScrollClipLayerId(outer_container_layer->id());
+    child_layer_->SetScrollable(root_layer->bounds());
     child_layer_->SetElementId(
         LayerIdToElementIdForTesting(child_layer_->id()));
     child_layer_->SetBounds(root_scroll_layer_->bounds());
@@ -1102,10 +1093,8 @@
   LayerTreeHostScrollTestScrollZeroMaxScrollOffset() {}
 
   void BeginTest() override {
-    outer_viewport_container_layer_id_ = layer_tree_host()
-                                             ->outer_viewport_scroll_layer()
-                                             ->scroll_clip_layer()
-                                             ->id();
+    outer_viewport_container_layer_id_ =
+        layer_tree_host()->outer_viewport_container_layer()->id();
     PostSetNeedsCommitToMainThread();
   }
 
@@ -1114,7 +1103,7 @@
     Layer* scroll_layer = layer_tree_host()->outer_viewport_scroll_layer();
     switch (layer_tree_host()->SourceFrameNumber()) {
       case 0:
-        scroll_layer->SetScrollClipLayerId(outer_viewport_container_layer_id_);
+        scroll_layer->SetScrollable(root->bounds());
         // Set max_scroll_offset = (100, 100).
         scroll_layer->SetBounds(gfx::Size(root->bounds().width() + 100,
                                           root->bounds().height() + 100));
@@ -1442,10 +1431,9 @@
   Layer* CreateScrollLayer(Layer* parent, FakeLayerScrollClient* client) {
     scoped_refptr<PictureLayer> scroll_layer =
         PictureLayer::Create(&fake_content_layer_client_);
-    scroll_layer->SetBounds(gfx::Size(110, 110));
     scroll_layer->SetPosition(gfx::PointF());
     scroll_layer->SetIsDrawable(true);
-    scroll_layer->SetScrollClipLayerId(parent->id());
+    scroll_layer->SetScrollable(parent->bounds());
     scroll_layer->SetElementId(
         LayerIdToElementIdForTesting(scroll_layer->id()));
     scroll_layer->SetBounds(gfx::Size(parent->bounds().width() + 100,
@@ -1504,10 +1492,8 @@
   }
 
   void BeginTest() override {
-    outer_viewport_container_layer_id_ = layer_tree_host()
-                                             ->outer_viewport_scroll_layer()
-                                             ->scroll_clip_layer()
-                                             ->id();
+    outer_viewport_container_layer_id_ =
+        layer_tree_host()->outer_viewport_container_layer()->id();
     layer_tree_host()->outer_viewport_scroll_layer()->SetScrollOffset(
         initial_scroll_);
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
@@ -1599,7 +1585,6 @@
     LayerImpl* root = impl->active_tree()->root_layer_for_testing();
     LayerImpl* scroll_layer = impl->OuterViewportScrollLayer();
 
-    scroll_layer->SetScrollClipLayer(outer_viewport_container_layer_id_);
     scroll_layer->SetBounds(
         gfx::Size(root->bounds().width() + 100, root->bounds().height() + 100));
     scroll_layer->ScrollBy(scroll_amount_);
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 6f2fcdd..b4a4222 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1248,9 +1248,6 @@
     scale_factor = transform_tree.page_scale_factor();
 
   gfx::SizeF scaled_scroll_bounds = gfx::ScaleSize(scroll_bounds, scale_factor);
-  scaled_scroll_bounds.SetSize(std::floor(scaled_scroll_bounds.width()),
-                               std::floor(scaled_scroll_bounds.height()));
-
   gfx::Size clip_layer_bounds = scroll_clip_layer_bounds(scroll_node->id);
 
   gfx::ScrollOffset max_offset(
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 2f287ca..af37d72 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -140,12 +140,6 @@
   return layer->test_properties()->transform;
 }
 
-static void SetIsScrollClipLayer(Layer* layer) {
-  layer->set_is_scroll_clip_layer();
-}
-
-static void SetIsScrollClipLayer(LayerImpl* layer) {}
-
 // Methods to query state from the AnimationHost ----------------------
 template <typename LayerType>
 bool OpacityIsAnimating(LayerType* layer) {
@@ -394,7 +388,6 @@
     data_for_children->affected_by_outer_viewport_bounds_delta =
         layer == data_from_ancestor.outer_viewport_scroll_layer;
     if (is_scrollable) {
-      DCHECK(!is_root);
       DCHECK(Transform(layer).IsIdentity());
       data_for_children->transform_fixed_parent = Parent(layer);
     } else {
@@ -1001,11 +994,6 @@
   layer->SetHasTransformNode(val);
 }
 
-void SetHasScrollNode(LayerImpl* layer, bool val) {}
-void SetHasScrollNode(Layer* layer, bool val) {
-  layer->SetHasScrollNode(val);
-}
-
 template <typename LayerType>
 void AddScrollNodeIfNeeded(
     const DataForRecursion<LayerType>& data_from_ancestor,
@@ -1037,14 +1025,12 @@
   if (!requires_node) {
     node_id = parent_id;
     data_for_children->scroll_tree_parent = node_id;
-    SetHasScrollNode(layer, false);
   } else {
     ScrollNode node;
     node.owning_layer_id = layer->id();
     node.scrollable = scrollable;
     node.main_thread_scrolling_reasons = main_thread_scrolling_reasons;
     node.non_fast_scrollable_region = layer->non_fast_scrollable_region();
-
     node.scrolls_inner_viewport =
         layer == data_from_ancestor.inner_viewport_scroll_layer;
     node.scrolls_outer_viewport =
@@ -1055,12 +1041,8 @@
       node.max_scroll_offset_affected_by_page_scale = true;
     }
 
-    if (LayerType* scroll_clip_layer = layer->scroll_clip_layer()) {
-      SetIsScrollClipLayer(scroll_clip_layer);
-      node.scroll_clip_layer_bounds = scroll_clip_layer->bounds();
-    }
-
     node.bounds = layer->bounds();
+    node.scroll_clip_layer_bounds = layer->scroll_container_bounds();
     node.offset_to_transform_parent = layer->offset_to_transform_parent();
     node.should_flatten = layer->should_flatten_transform_from_property_tree();
     node.user_scrollable_horizontal = UserScrollableHorizontal(layer);
@@ -1087,7 +1069,6 @@
       data_for_children->property_trees->scroll_tree.SetBaseScrollOffset(
           layer->element_id(), layer->CurrentScrollOffset());
     }
-    SetHasScrollNode(layer, true);
   }
 
   layer->SetScrollTreeIndex(node_id);
diff --git a/cc/trees/scroll_node.h b/cc/trees/scroll_node.h
index 43841bf..60b3293 100644
--- a/cc/trees/scroll_node.h
+++ b/cc/trees/scroll_node.h
@@ -30,6 +30,7 @@
   // The layer id that corresponds to the layer contents that are scrolled.
   // Unlike |id|, this id is stable across frames that don't change the
   // composited layer list.
+  // TODO(pdr): This is no longer used and can be removed.
   int owning_layer_id;
 
   uint32_t main_thread_scrolling_reasons;
@@ -38,6 +39,7 @@
 
   // Size of the clipped area, not including non-overlay scrollbars. Overlay
   // scrollbars do not affect the clipped area.
+  // TODO(pdr): Rename this to scroll_container_bounds.
   gfx::Size scroll_clip_layer_bounds;
 
   // Bounds of the overflow scrolling area.
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc
index 18ab0a7..daf58880 100644
--- a/cc/trees/tree_synchronizer_unittest.cc
+++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -494,22 +494,17 @@
   host_impl->CreatePendingTree();
 
   scoped_refptr<Layer> layer_tree_root = Layer::Create();
-  scoped_refptr<Layer> scroll_clip_layer = Layer::Create();
   scoped_refptr<Layer> scroll_layer = Layer::Create();
-  scoped_refptr<Layer> transient_scroll_clip_layer = Layer::Create();
   scoped_refptr<Layer> transient_scroll_layer = Layer::Create();
 
-  layer_tree_root->AddChild(transient_scroll_clip_layer);
-  transient_scroll_clip_layer->AddChild(transient_scroll_layer);
-  transient_scroll_layer->AddChild(scroll_clip_layer);
-  scroll_clip_layer->AddChild(scroll_layer);
+  layer_tree_root->AddChild(transient_scroll_layer);
+  transient_scroll_layer->AddChild(scroll_layer);
 
   ElementId scroll_element_id = ElementId(5);
   scroll_layer->SetElementId(scroll_element_id);
 
-  transient_scroll_layer->SetScrollClipLayerId(
-      transient_scroll_clip_layer->id());
-  scroll_layer->SetScrollClipLayerId(scroll_clip_layer->id());
+  transient_scroll_layer->SetScrollable(gfx::Size(1, 1));
+  scroll_layer->SetScrollable(gfx::Size(1, 1));
   host_->SetRootLayer(layer_tree_root);
   host_->BuildPropertyTreesForTesting();
   host_->CommitAndCreatePendingTree();
@@ -523,7 +518,7 @@
       host_impl->active_tree()->property_trees()->scroll_tree.Node(
           scroll_layer->scroll_tree_index());
   host_impl->active_tree()->SetCurrentlyScrollingNode(scroll_node);
-  transient_scroll_layer->SetScrollClipLayerId(Layer::INVALID_ID);
+  transient_scroll_layer->SetScrollable(gfx::Size(0, 0));
   host_->BuildPropertyTreesForTesting();
 
   host_impl->CreatePendingTree();
@@ -546,25 +541,20 @@
   host_impl->CreatePendingTree();
 
   scoped_refptr<Layer> layer_tree_root = Layer::Create();
-  scoped_refptr<Layer> scroll_clip_layer = Layer::Create();
   scoped_refptr<Layer> scroll_layer = Layer::Create();
-  scoped_refptr<Layer> transient_scroll_clip_layer = Layer::Create();
   scoped_refptr<Layer> transient_scroll_layer = Layer::Create();
 
   ElementId scroll_element_id = ElementId(5);
   scroll_layer->SetElementId(scroll_element_id);
 
-  layer_tree_root->AddChild(transient_scroll_clip_layer);
-  transient_scroll_clip_layer->AddChild(transient_scroll_layer);
-  transient_scroll_layer->AddChild(scroll_clip_layer);
-  scroll_clip_layer->AddChild(scroll_layer);
+  layer_tree_root->AddChild(transient_scroll_layer);
+  transient_scroll_layer->AddChild(scroll_layer);
 
   ElementId transient_scroll_element_id = ElementId(7);
   transient_scroll_layer->SetElementId(transient_scroll_element_id);
 
-  transient_scroll_layer->SetScrollClipLayerId(
-      transient_scroll_clip_layer->id());
-  scroll_layer->SetScrollClipLayerId(scroll_clip_layer->id());
+  transient_scroll_layer->SetScrollable(gfx::Size(1, 1));
+  scroll_layer->SetScrollable(gfx::Size(1, 1));
   transient_scroll_layer->SetScrollOffset(gfx::ScrollOffset(1, 2));
   scroll_layer->SetScrollOffset(gfx::ScrollOffset(10, 20));
 
@@ -624,8 +614,9 @@
   host_impl->active_tree()->SetCurrentlyScrollingNode(
       scroll_tree.Node(scroll_layer_impl->scroll_tree_index()));
 
-  // Make one layer unscrollable so that scroll tree topology changes
-  transient_scroll_layer->SetScrollClipLayerId(Layer::INVALID_ID);
+  // Change the scroll tree topology by removing transient_scroll_layer.
+  transient_scroll_layer->RemoveFromParent();
+  layer_tree_root->AddChild(scroll_layer);
   host_->BuildPropertyTreesForTesting();
 
   host_impl->CreatePendingTree();
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 36b77b5..d8fe989 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -391,7 +391,6 @@
       "//chrome/common:features",
       "//chrome/install_static:install_static_util",
       "//chrome/install_static:secondary_module",
-      "//chrome/profiling",
       "//chrome_elf",
       "//components/crash/content/app",
       "//components/policy:generated",
@@ -1415,6 +1414,7 @@
   public_deps = [
     "//chrome/browser",
     "//chrome/common",
+    "//chrome/profiling",
     "//components/sync",
   ]
   if (enable_plugins) {
diff --git a/chrome/android/java/res/layout/account_signin_view.xml b/chrome/android/java/res/layout/account_signin_view.xml
index 09c3565..9139aad 100644
--- a/chrome/android/java/res/layout/account_signin_view.xml
+++ b/chrome/android/java/res/layout/account_signin_view.xml
@@ -117,6 +117,7 @@
                 <View style="@style/Divider"/>
 
                 <TextView
+                    style="@style/RobotoMediumStyle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:layout_marginTop="16dp"
@@ -126,7 +127,6 @@
                     android:text="@string/sync_confirmation_chrome_sync_title"
                     android:textColor="@color/default_text_color"
                     android:textSize="@dimen/fre_normal_text_size"
-                    android:fontFamily="sans-serif-medium"
                     android:drawableStart="@drawable/chrome_sync_logo"
                     android:drawablePadding="16dp"/>
 
@@ -147,6 +147,7 @@
                     android:layout_marginEnd="16dp"/>
 
                 <TextView
+                    style="@style/RobotoMediumStyle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:layout_marginTop="16dp"
@@ -156,7 +157,6 @@
                     android:text="@string/sync_confirmation_personalize_services_title"
                     android:textColor="@color/default_text_color"
                     android:textSize="@dimen/fre_normal_text_size"
-                    android:fontFamily="sans-serif-medium"
                     android:drawableStart="@drawable/googleg"
                     android:drawablePadding="16dp"/>
 
diff --git a/chrome/android/java/res/layout/contextual_search_peek_promo_text_view.xml b/chrome/android/java/res/layout/contextual_search_peek_promo_text_view.xml
index 97fcef5..df0772b 100644
--- a/chrome/android/java/res/layout/contextual_search_peek_promo_text_view.xml
+++ b/chrome/android/java/res/layout/contextual_search_peek_promo_text_view.xml
@@ -14,12 +14,12 @@
               android:paddingEnd="@dimen/contextual_search_peek_promo_padding"
               android:visibility="invisible">
     <TextView
+        style="@style/RobotoMediumStyle"
         android:id="@+id/contextual_search_peek_promo_new"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/contextual_search_peek_promo_padding"
         android:background="@drawable/contextual_search_peek_promo_new_background"
-        android:fontFamily="sans-serif-medium"
         android:text="@string/contextual_search_peek_promo_new"
         android:textAllCaps="true"
         android:textColor="@color/light_active_color"
diff --git a/chrome/android/java/res/layout/contextual_search_promo_view.xml b/chrome/android/java/res/layout/contextual_search_promo_view.xml
index e262beb6..18712fc 100644
--- a/chrome/android/java/res/layout/contextual_search_promo_view.xml
+++ b/chrome/android/java/res/layout/contextual_search_promo_view.xml
@@ -39,7 +39,6 @@
         android:layout_gravity="end"
         android:layout_marginTop="16dp"
         android:layout_marginEnd="16dp"
-        android:fontFamily="sans-serif-medium"
         android:paddingStart="8dp"
         android:paddingEnd="8dp"
         android:text="@string/contextual_search_allow_button"
@@ -57,7 +56,6 @@
         android:layout_gravity="end"
         android:layout_marginTop="16dp"
         android:layout_marginEnd="16dp"
-        android:fontFamily="sans-serif-medium"
         android:paddingStart="8dp"
         android:paddingEnd="8dp"
         android:text="@string/contextual_search_no_thanks_button"
diff --git a/chrome/android/java/res/layout/dialog_with_titlebar.xml b/chrome/android/java/res/layout/dialog_with_titlebar.xml
deleted file mode 100644
index 3a4dd68e..0000000
--- a/chrome/android/java/res/layout/dialog_with_titlebar.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/toolbar_height_no_shadow"
-        android:background="@color/default_primary_color" >
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:fontFamily="sans-serif-medium"
-            android:gravity="center_vertical"
-            android:paddingStart="16dp"
-            android:paddingEnd="16dp"
-            android:textColor="#333333"
-            android:textSize="20sp" />
-
-        <ImageButton
-            android:id="@+id/close_button"
-            style="@style/ToolbarButton"
-            android:layout_width="52dp"
-            android:layout_height="match_parent"
-            android:layout_gravity="top"
-            android:contentDescription="@string/close"
-            android:src="@drawable/btn_close" />
-    </LinearLayout>
-
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/toolbar_height_no_shadow"
-        android:contentDescription="@null"
-        android:scaleType="fitXY"
-        android:src="@drawable/toolbar_shadow" />
-
-    <!-- Dialog content will be inserted here from Java code -->
-
-</FrameLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/fre_data_reduction_proxy.xml b/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
index 08c7356..1be505b 100644
--- a/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
+++ b/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
@@ -66,6 +66,7 @@
                         android:textSize="@dimen/fre_normal_text_size" />
 
                     <android.support.v7.widget.SwitchCompat
+                        style="@style/RobotoMediumStyle"
                         android:id="@+id/enable_data_saver_switch"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
@@ -73,8 +74,7 @@
                         android:showText="false"
                         android:text="@string/data_reduction_enabled_switch"
                         android:textColor="@color/fre_text_color"
-                        android:textSize="@dimen/fre_normal_text_size"
-                        android:fontFamily="sans-serif-medium" />
+                        android:textSize="@dimen/fre_normal_text_size" />
                     
                 </LinearLayout>
             </LinearLayout>
diff --git a/chrome/android/java/res/layout/new_tab_page_incognito.xml b/chrome/android/java/res/layout/new_tab_page_incognito.xml
index 84f44dcf..f6be0ba3 100644
--- a/chrome/android/java/res/layout/new_tab_page_incognito.xml
+++ b/chrome/android/java/res/layout/new_tab_page_incognito.xml
@@ -64,7 +64,6 @@
                 style="@style/IncognitoNewTabLearnMoreLink"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:fontFamily="sans-serif-medium"
                 android:text="@string/learn_more"
                 android:textColor="#03a9f4" />
 
diff --git a/chrome/android/java/res/layout/new_tab_page_incognito_md.xml b/chrome/android/java/res/layout/new_tab_page_incognito_md.xml
index 2e1a7708..146fd29 100644
--- a/chrome/android/java/res/layout/new_tab_page_incognito_md.xml
+++ b/chrome/android/java/res/layout/new_tab_page_incognito_md.xml
@@ -78,10 +78,10 @@
                 android:background="?attr/listChoiceBackgroundIndicator"
                 android:clickable="true"
                 android:focusable="true"
-                android:fontFamily="sans-serif-medium"
                 android:text="@string/learn_more"
                 android:textColor="@color/google_blue_300"
-                android:lineSpacingExtra="6sp" />
+                android:lineSpacingExtra="6sp"
+                style="@style/RobotoMediumStyle" />
 
         </LinearLayout>
 
diff --git a/chrome/android/java/res/layout/photo_picker_bitmap_view.xml b/chrome/android/java/res/layout/photo_picker_bitmap_view.xml
index e84c8c3..22c50cc 100644
--- a/chrome/android/java/res/layout/photo_picker_bitmap_view.xml
+++ b/chrome/android/java/res/layout/photo_picker_bitmap_view.xml
@@ -77,11 +77,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/photo_picker_label_gap"
-            android:fontFamily="sans-serif-medium"
             android:gravity="center"
-            android:textStyle="bold"
             android:textSize="14sp"
             android:textAllCaps="true"
-            android:textColor="@color/photo_picker_special_tile_color" />
+            android:textColor="@color/photo_picker_special_tile_color"
+            style="@style/RobotoMediumStyle" />
     </LinearLayout>
 </view>
diff --git a/chrome/android/java/res/layout/snackbar.xml b/chrome/android/java/res/layout/snackbar.xml
index d9ba185..d466784 100644
--- a/chrome/android/java/res/layout/snackbar.xml
+++ b/chrome/android/java/res/layout/snackbar.xml
@@ -41,7 +41,6 @@
         android:layout_gravity="center_vertical"
         android:paddingEnd="24dp"
         android:paddingStart="24dp"
-        android:fontFamily="sans-serif-medium"
         android:textAllCaps="true"
         android:textColor="@color/snackbar_button_color"
         android:textSize="14sp"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 80b0593e..09c67660 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -130,10 +130,9 @@
         <item name="android:paddingTop">16dp</item>
         <item name="android:layout_marginBottom">16dp</item>
     </style>
-    <style name="PreferenceCategoryTextStyle">
+    <style name="PreferenceCategoryTextStyle" parent="RobotoMediumStyle">
         <item name="android:textColor">@color/pref_accent_color</item>
         <item name="android:textSize">12sp</item>
-        <item name="android:textStyle">bold</item>
     </style>
     <style name="PreferenceTitle">
         <item name="android:ellipsize">end</item>
@@ -720,7 +719,7 @@
         <item name="android:textSize">14sp</item>
         <item name="android:lineSpacingMultiplier">1.2</item>
     </style>
-    <style name="IncognitoNewTabLearnMoreLink">
+    <style name="IncognitoNewTabLearnMoreLink" parent="RobotoMediumStyle">
         <item name="android:background">?attr/listChoiceBackgroundIndicator</item>
         <item name="android:clickable">true</item>
         <item name="android:focusable">true</item>
diff --git a/chrome/android/java/res/values-v21/styles.xml b/chrome/android/java/res/values-v21/styles.xml
index 51ade34..725a6828d 100644
--- a/chrome/android/java/res/values-v21/styles.xml
+++ b/chrome/android/java/res/values-v21/styles.xml
@@ -25,8 +25,7 @@
         <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
         <item name="android:paddingEnd">4dp</item>
     </style>
-    <style name="PreferenceCategoryTextStyle">
-        <item name="android:fontFamily">sans-serif-medium</item>
+    <style name="PreferenceCategoryTextStyle" parent="RobotoMediumStyle">
         <item name="android:textColor">@color/pref_accent_color</item>
         <item name="android:textSize">12sp</item>
     </style>
diff --git a/chrome/android/java/res/values-v21/values.xml b/chrome/android/java/res/values-v21/values.xml
deleted file mode 100644
index d903908..0000000
--- a/chrome/android/java/res/values-v21/values.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- Font to use when a spec asks for "Roboto Medium". -->
-    <string name="roboto_medium_typeface">sans-serif-medium</string>
-    <integer name="roboto_medium_textstyle">0</integer>
-</resources>
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index ae55468..ab3adfd 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -22,6 +22,7 @@
     <!-- InfoBar constants -->
     <item type="id" name="button_primary" />
     <item type="id" name="button_secondary" />
+    <item type="id" name="infobar_icon" />
     <item type="id" name="infobar_close_button" />
     <item type="id" name="infobar_extra_check" />
     <item type="id" name="infobar_message" />
diff --git a/chrome/android/java/res/values/values.xml b/chrome/android/java/res/values/values.xml
index ef5aa31..8ea6bc2 100644
--- a/chrome/android/java/res/values/values.xml
+++ b/chrome/android/java/res/values/values.xml
@@ -8,12 +8,6 @@
     xmlns:tools="http://schemas.android.com/tools"
     tools:ignore="MissingTranslation">
 
-    <!-- Font to use when a spec asks for "Roboto Medium".
-         Bolding the text makes it look almost, but not quite, entirely unlike Roboto Medium,
-         which doesn't exist pre-L. -->
-    <string name="roboto_medium_typeface">sans-serif</string>
-    <integer name="roboto_medium_textstyle">1</integer>
-
     <!-- Menu Animation Constants -->
     <item type="fraction" format="fraction" name="menu_animation_pivot_x">95%</item>
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
index ec47df7..312102d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
@@ -8,7 +8,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
 import android.text.SpannableString;
@@ -26,6 +25,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.widget.DualControlLayout;
+import org.chromium.ui.UiUtils;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.widget.ButtonCompat;
 
@@ -259,10 +259,8 @@
         lp.endMargin = mBigIconMargin;
 
         Resources res = getContext().getResources();
-        String typeface = res.getString(R.string.roboto_medium_typeface);
-        int textStyle = res.getInteger(R.integer.roboto_medium_textstyle);
         float textSize = res.getDimension(R.dimen.infobar_big_icon_message_size);
-        mMessageTextView.setTypeface(Typeface.create(typeface, textStyle));
+        mMessageTextView.setTypeface(UiUtils.createRobotoMediumTypeface());
         mMessageTextView.setMaxLines(1);
         mMessageTextView.setEllipsize(TextUtils.TruncateAt.END);
         mMessageTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
@@ -529,6 +527,7 @@
                 iconView.setImageBitmap(iconBitmap);
             }
             iconView.setFocusable(false);
+            iconView.setId(R.id.infobar_icon);
             iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
         }
         return iconView;
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 2eca9f3..14948a1 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
@@ -23,6 +23,16 @@
  */
 public class ReaderModeInfoBar extends InfoBar {
     /**
+     * Navigate to Reader Mode when the icon or the message text is clicked.
+     */
+    private View.OnClickListener mNavigateListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (getReaderModeManager() != null) getReaderModeManager().navigateToReaderMode();
+        }
+    };
+
+    /**
      * Default constructor.
      */
     private ReaderModeInfoBar() {
@@ -44,14 +54,9 @@
         prompt.setTextColor(
                 ApiCompatibilityUtils.getColor(layout.getResources(), R.color.default_text_color));
         prompt.setGravity(Gravity.CENTER_VERTICAL);
+        prompt.setOnClickListener(mNavigateListener);
 
-        prompt.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (getReaderModeManager() != null) getReaderModeManager().navigateToReaderMode();
-            }
-        });
-
+        layout.findViewById(R.id.infobar_icon).setOnClickListener(mNavigateListener);
         layout.addContent(prompt, 1f);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerBridge.java
index 2219914c..5cccb6c5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerBridge.java
@@ -11,6 +11,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.blink_public.platform.modules.remoteplayback.WebRemotePlaybackAvailability;
 import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState;
+import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
 
 /**
  * Acts as a proxy between the remotely playing video and the HTMLMediaElement.
@@ -202,6 +203,11 @@
     @CalledByNative
     private void requestRemotePlayback(long startPositionMillis) {
         Log.d(TAG, "requestRemotePlayback at t=%d", startPositionMillis);
+        // TODO(crbug.com/736568): Re-enable dialog in VR.
+        if (VrShellDelegate.isInVr()) {
+            mMediaStateListener.onRouteDialogCancelled();
+            return;
+        }
         if (mRouteController == null) return;
         // Clear out the state
         mPauseRequested = false;
@@ -217,6 +223,11 @@
     @CalledByNative
     private void requestRemotePlaybackControl() {
         Log.d(TAG, "requestRemotePlaybackControl");
+        // TODO(crbug.com/736568): Re-enable dialog in VR.
+        if (VrShellDelegate.isInVr()) {
+            mMediaStateListener.onRouteDialogCancelled();
+            return;
+        }
         RemoteMediaPlayerController.instance().requestRemotePlaybackControl(mMediaStateListener);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageDialog.java
deleted file mode 100644
index 63f0935..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageDialog.java
+++ /dev/null
@@ -1,61 +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.
-
-package org.chromium.chrome.browser.ntp;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.TextView;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.NativePage;
-import org.chromium.chrome.browser.widget.AlwaysDismissedDialog;
-
-/**
- * Displays a NativePage in a full screen dialog instead of like a regular Chrome page.
- */
-public class NativePageDialog extends AlwaysDismissedDialog {
-    private final NativePage mPage;
-
-    public NativePageDialog(Activity ownerActivity, NativePage page) {
-        super(ownerActivity, R.style.DialogWhenLarge);
-        mPage = page;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        FrameLayout view = (FrameLayout) LayoutInflater.from(getContext()).inflate(
-                R.layout.dialog_with_titlebar, null);
-        view.addView(mPage.getView(), 0);
-        setContentView(view);
-
-        getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-
-        TextView title = (TextView) view.findViewById(R.id.title);
-        title.setText(mPage.getTitle());
-
-        ImageButton closeButton = (ImageButton) view.findViewById(R.id.close_button);
-        closeButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                dismiss();
-            }
-        });
-    }
-
-    @Override
-    public void dismiss() {
-        super.dismiss();
-        if (mPage != null) mPage.destroy();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java
index 3d1741b3..62996a22 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java
@@ -7,23 +7,17 @@
 import android.content.Context;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.SuppressFBWarnings;
-import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.base.library_loader.ProcessInitException;
-import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.background_task_scheduler.BackgroundTask;
 import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback;
 import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
 import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.background_task_scheduler.TaskInfo;
 import org.chromium.components.background_task_scheduler.TaskParameters;
-import org.chromium.content.browser.BrowserStartupController;
 
 import java.util.concurrent.TimeUnit;
 
@@ -34,7 +28,7 @@
  * happens when a task fires.
  */
 @JNINamespace("offline_pages::prefetch")
-public class PrefetchBackgroundTask implements BackgroundTask {
+public class PrefetchBackgroundTask extends NativeBackgroundTask {
     public static final long DEFAULT_START_DELAY_SECONDS = 15 * 60;
 
     private static final String TAG = "OPPrefetchBGTask";
@@ -43,16 +37,9 @@
 
     private long mNativeTask = 0;
     private TaskFinishedCallback mTaskFinishedCallback = null;
+    private Profile mProfile = null;
 
-    private Profile mProfile;
-
-    public PrefetchBackgroundTask() {
-        mProfile = Profile.getLastUsedProfile();
-    }
-
-    public PrefetchBackgroundTask(Profile profile) {
-        mProfile = profile;
-    }
+    public PrefetchBackgroundTask() {}
 
     static BackgroundTaskScheduler getScheduler() {
         if (sSchedulerInstance != null) {
@@ -61,6 +48,11 @@
         return BackgroundTaskSchedulerFactory.getScheduler();
     }
 
+    protected Profile getProfile() {
+        if (mProfile == null) mProfile = Profile.getLastUsedProfile();
+        return mProfile;
+    }
+
     /**
      * Schedules the default 'NWake' task for the prefetching service.
      *
@@ -94,39 +86,47 @@
                 ContextUtils.getApplicationContext(), TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID);
     }
 
-    /**
-     * Initializer that runs when the task wakes up Chrome.
-     *
-     * Loads the native library and then calls into the PrefetchService, which then manages the
-     * lifetime of this task.
-     */
     @Override
-    public boolean onStartTask(
+    public int onStartTaskBeforeNativeLoaded(
             Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
-        assert taskParameters.getTaskId() == TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID;
-        if (mNativeTask != 0) return false;
-
         // TODO(dewittj): Ensure that the conditions are right to do work.  If the maximum time to
         // wait is reached, it is possible the task will fire even if network conditions are
-        // incorrect.
-
-        // Ensures that native potion of the browser is launched.
-        launchBrowserIfNecessary(context);
-
-        mTaskFinishedCallback = callback;
-        return nativeStartPrefetchTask(mProfile);
+        // incorrect.  We want:
+        // * Unmetered WiFi connection
+        // * >50% battery
+        // * Preferences enabled.
+        return NativeBackgroundTask.LOAD_NATIVE;
     }
 
     @Override
-    public boolean onStopTask(Context context, TaskParameters taskParameters) {
+    protected void onStartTaskWithNative(
+            Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
         assert taskParameters.getTaskId() == TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID;
-        if (mNativeTask == 0) return false;
+        if (mNativeTask != 0) return;
+
+        mTaskFinishedCallback = callback;
+        nativeStartPrefetchTask(getProfile());
+    }
+
+    @Override
+    protected boolean onStopTaskBeforeNativeLoaded(Context context, TaskParameters taskParameters) {
+        // TODO(dewittj): Implement this properly.
+        return true;
+    }
+
+    @Override
+    protected boolean onStopTaskWithNative(Context context, TaskParameters taskParameters) {
+        assert taskParameters.getTaskId() == TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID;
+        assert mNativeTask != 0;
 
         return nativeOnStopTask(mNativeTask);
     }
 
     @Override
-    public void reschedule(Context context) {}
+    public void reschedule(Context context) {
+        // TODO(dewittj): Set the backoff time appropriately.
+        scheduleTask(0);
+    }
 
     /**
      * Called during construction of the native task.
@@ -151,26 +151,6 @@
     }
 
     @VisibleForTesting
-    @SuppressFBWarnings("DM_EXIT")
-    void launchBrowserIfNecessary(Context context) {
-        if (BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
-                        .isStartupSuccessfullyCompleted()) {
-            return;
-        }
-
-        // TODO(https://crbug.com/717251): Remove when BackgroundTaskScheduler supports loading the
-        // native library.
-        try {
-            ChromeBrowserInitializer.getInstance(context).handleSynchronousStartup();
-        } catch (ProcessInitException e) {
-            Log.e(TAG, "ProcessInitException while starting the browser process.");
-            // Since the library failed to initialize nothing in the application can work, so kill
-            // the whole application not just the activity.
-            System.exit(-1);
-        }
-    }
-
-    @VisibleForTesting
     static void setSchedulerForTesting(BackgroundTaskScheduler scheduler) {
         sSchedulerInstance = scheduler;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPlaceholderFieldTrial.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPlaceholderFieldTrial.java
index 99a70d5..08eddf5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPlaceholderFieldTrial.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPlaceholderFieldTrial.java
@@ -17,12 +17,9 @@
 public class OmniboxPlaceholderFieldTrial {
     private static final String FIELD_TRIAL_NAME = "OmniboxPlaceholderExperiment";
     private static final String GROUP_SEARCH_OR_TYPE_URL = "SearchOrTypeUrl";
-    private static final String GROUP_SEARCH_OR_TYPE_WEBSITE_NAME = "SearchOrTypeWebsiteName";
-    private static final String GROUP_SEARCH_THE_WEB = "SearchTheWeb";
-    private static final String GROUP_ENTER_A_SEARCH_OR_WEBSITE = "EnterASearchOrWebsite";
-    private static final String GROUP_SEARCH_NEWS = "SearchNews";
-    private static final String GROUP_SEARCH_RECIPES = "SearchRecipes";
-    private static final String GROUP_SEARCH_WEATHER = "SearchWeather";
+    private static final String GROUP_SEARCH_OR_TYPE_WEB_ADDRESS = "SearchOrTypeWebAddress";
+    private static final String GROUP_TYPE_WHAT_YOU_ARE_LOOKING_FOR = "TypeWhatYouAreLookingFor";
+    private static final String GROUP_FIND_NEWS_RECIPES_WEATHER = "FindNewsRecipesWeather";
     private static final String GROUP_BLANK = "Blank";
 
     private static String sCachedHint;
@@ -50,23 +47,14 @@
             case GROUP_SEARCH_OR_TYPE_URL:
                 sCachedHint = resources.getString(R.string.search_or_type_url);
                 break;
-            case GROUP_SEARCH_OR_TYPE_WEBSITE_NAME:
-                sCachedHint = resources.getString(R.string.search_or_type_website_name);
+            case GROUP_SEARCH_OR_TYPE_WEB_ADDRESS:
+                sCachedHint = resources.getString(R.string.search_or_type_web_address);
                 break;
-            case GROUP_SEARCH_THE_WEB:
-                sCachedHint = resources.getString(R.string.search_the_web);
+            case GROUP_TYPE_WHAT_YOU_ARE_LOOKING_FOR:
+                sCachedHint = resources.getString(R.string.type_what_you_are_looking_for);
                 break;
-            case GROUP_ENTER_A_SEARCH_OR_WEBSITE:
-                sCachedHint = resources.getString(R.string.enter_a_search_or_website);
-                break;
-            case GROUP_SEARCH_NEWS:
-                sCachedHint = resources.getString(R.string.search_news);
-                break;
-            case GROUP_SEARCH_RECIPES:
-                sCachedHint = resources.getString(R.string.search_recipes);
-                break;
-            case GROUP_SEARCH_WEATHER:
-                sCachedHint = resources.getString(R.string.search_weather);
+            case GROUP_FIND_NEWS_RECIPES_WEATHER:
+                sCachedHint = resources.getString(R.string.find_news_recipes_weather);
                 break;
             case GROUP_BLANK:
                 sCachedHint = "";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationSnackbarController.java
index 8b1352d..d117f6ca7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationSnackbarController.java
@@ -9,7 +9,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.text.SpannableString;
-import android.text.style.TypefaceSpan;
+import android.text.style.StyleSpan;
 import android.view.View;
 
 import org.chromium.base.ContextUtils;
@@ -74,7 +74,7 @@
         }
 
         Uri searchUri = Uri.parse(TemplateUrlService.getInstance().getUrlForSearchQuery("foo"));
-        TypefaceSpan robotoMediumSpan = new TypefaceSpan("sans-serif-medium");
+        StyleSpan robotoMediumSpan = new StyleSpan(R.style.RobotoMediumStyle);
         String messageWithoutSpans = context.getResources().getString(
                 R.string.omnibox_geolocation_disclosure, "<b>" + searchUri.getHost() + "</b>");
         SpannableString message = SpanApplier.applySpans(messageWithoutSpans,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java
index 51c340d..880cb980 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java
@@ -6,7 +6,6 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Typeface;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
@@ -14,6 +13,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.ui.UiUtils;
 
 import java.util.List;
 
@@ -116,9 +116,7 @@
             // Set the correct appearance, face and style for the text.
             ApiCompatibilityUtils.setTextAppearance(
                     textView, R.style.PaymentsUiSectionAddButtonLabel);
-            textView.setTypeface(
-                    Typeface.create(resources.getString(R.string.roboto_medium_typeface),
-                            R.integer.roboto_medium_textstyle));
+            textView.setTypeface(UiUtils.createRobotoMediumTypeface());
 
             // Padding at the bottom of the dropdown.
             ApiCompatibilityUtils.setPaddingRelative(convertView,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
index 8d5c0f97..834d19b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
@@ -7,7 +7,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.v4.view.animation.LinearOutSlowInInterpolator;
@@ -37,6 +36,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.widget.DualControlLayout;
 import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.ui.UiUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -942,8 +942,6 @@
                     labelView.setEnabled(isEnabled);
                 } else if (mRowType == OPTION_ROW_TYPE_ADD) {
                     // Shows string saying that the user can add a new option, e.g. credit card no.
-                    String typeface = resources.getString(R.string.roboto_medium_typeface);
-                    int textStyle = resources.getInteger(R.integer.roboto_medium_textstyle);
                     int buttonHeight = resources.getDimensionPixelSize(
                             R.dimen.payments_section_add_button_height);
 
@@ -951,7 +949,7 @@
                             labelView, R.style.PaymentsUiSectionAddButtonLabel);
                     labelView.setMinimumHeight(buttonHeight);
                     labelView.setGravity(Gravity.CENTER_VERTICAL);
-                    labelView.setTypeface(Typeface.create(typeface, textStyle));
+                    labelView.setTypeface(UiUtils.createRobotoMediumTypeface());
                 } else if (mRowType == OPTION_ROW_TYPE_DESCRIPTION) {
                     // The description spans all the columns.
                     columnStart = 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ExpandablePreferenceGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ExpandablePreferenceGroup.java
index f709b697..49df02c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ExpandablePreferenceGroup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ExpandablePreferenceGroup.java
@@ -6,13 +6,11 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.preference.PreferenceGroup;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StyleSpan;
-import android.text.style.TypefaceSpan;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ImageView;
@@ -47,18 +45,8 @@
                 new SpannableStringBuilder(getContext().getResources().getString(resourceId));
         String prefCount = String.format(Locale.getDefault(), " - %d", count);
         spannable.append(prefCount);
-
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
-            spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
-                       0,
-                       spannable.length() - prefCount.length(),
-                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-        } else {
-            spannable.setSpan(new TypefaceSpan("sans-serif-medium"),
-                       0,
-                       spannable.length() - prefCount.length(),
-                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-        }
+        spannable.setSpan(new StyleSpan(R.style.RobotoMediumStyle), 0,
+                spannable.length() - prefCount.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
 
         // Color the first part of the title blue.
         ForegroundColorSpan blueSpan = new ForegroundColorSpan(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncErrorCardPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncErrorCardPreference.java
index 399fb26..a1c2350 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncErrorCardPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncErrorCardPreference.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.preferences;
 
 import android.content.Context;
-import android.graphics.Typeface;
 import android.preference.Preference;
 import android.util.AttributeSet;
 import android.util.TypedValue;
@@ -15,6 +14,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.ui.UiUtils;
 
 /**
  * A preference that displays hint message to resolve sync error. Click of it navigates user to
@@ -33,7 +33,7 @@
         View view = super.onCreateView(parent);
         TextView title = (TextView) view.findViewById(android.R.id.title);
         title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
-        title.setTypeface(Typeface.create("sans-serif-medium", Typeface.NORMAL));
+        title.setTypeface(UiUtils.createRobotoMediumTypeface());
         title.setTextColor(ApiCompatibilityUtils.getColor(
                 getContext().getResources(), R.color.input_underline_error_color));
         return view;
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 94f2e2b..d56817e 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1773,7 +1773,7 @@
       <message name="IDS_BROWSER_ACTIONS_SINGLE_LINK_OPEN_NOTIFICATION_TITLE" desc="Content title displayed in the notification when one link is opened in the background from Browser Actions. [CHAR-LIMIT=30]">
         Link opened in Chrome
       </message>
-      <message name="IDS_BROWSER_ACTIONS_MULTI_LINKS_OPEN_NOTIFICATION_TITLE" desc="Content title displayed in the notification when multiple links are opened in the background from Browser Actions. [CHAR-LIMIT=30]">
+      <message name="IDS_BROWSER_ACTIONS_MULTI_LINKS_OPEN_NOTIFICATION_TITLE" desc="Content title displayed in the notification when multiple links are opened in the background from Browser Actions. [CHAR-LIMIT=41]">
         Multiple links opened in Chrome
       </message>
       <message name="IDS_BROWSER_ACTIONS_NOTIFICATION_TEXT" desc="Context text displayed in the notification when links are opened in the background from Browser Actions. [CHAR-LIMIT=30]">
@@ -2541,31 +2541,19 @@
       </message>
 
       <!-- Omnibox -->
-      <message name="IDS_SEARCH_OR_TYPE_URL" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a URL. [CHAR-LIMIT=38]">
-        Search or type URL
-      </message>
       <message name="IDS_LOCATION_BAR_VERBOSE_STATUS_OFFLINE" desc="Verbose indication of offline status in the location bar. [CHAR-LIMIT=20]">
         Offline
       </message>
 
       <!-- Omnibox Placeholder Experiment -->
-      <message name="IDS_SEARCH_OR_TYPE_WEBSITE_NAME" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a URL. [CHAR-LIMIT=38]">
-        Search or type website name
+      <message name="IDS_SEARCH_OR_TYPE_WEB_ADDRESS" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a website. [CHAR-LIMIT=38]">
+        Search or type web address
       </message>
-      <message name="IDS_SEARCH_THE_WEB" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a URL. [CHAR-LIMIT=38]">
-        Search the web
+      <message name="IDS_TYPE_WHAT_YOU_ARE_LOOKING_FOR" desc="Prompt to enter what user is looking for into the text field that will either perform web searches or navigate to a website. [CHAR-LIMIT=38]">
+        Type what you’re looking for
       </message>
-      <message name="IDS_ENTER_A_SEARCH_OR_WEBSITE" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a URL. [CHAR-LIMIT=38]">
-        Enter a search or website
-      </message>
-      <message name="IDS_SEARCH_NEWS" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a URL, with a suggestion of searching news. [CHAR-LIMIT=38]">
-        Search… news
-      </message>
-      <message name="IDS_SEARCH_RECIPES" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a URL, with a suggestion of searching recipes. [CHAR-LIMIT=38]">
-        Search… recipes
-      </message>
-      <message name="IDS_SEARCH_WEATHER" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a URL, with a suggestion of searching weather. [CHAR-LIMIT=38]">
-        Search… weather
+      <message name="IDS_FIND_NEWS_RECIPES_WEATHER" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a website, with suggestions of finding news, recipes or weather. [CHAR-LIMIT=38]">
+         Find news, recipes, weather...
       </message>
 
       <!-- Voice search -->
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index a72e0ac..158ce1e 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -621,7 +621,6 @@
   "java/src/org/chromium/chrome/browser/ntp/LogoDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/ntp/LogoView.java",
   "java/src/org/chromium/chrome/browser/ntp/NativePageAssassin.java",
-  "java/src/org/chromium/chrome/browser/ntp/NativePageDialog.java",
   "java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java",
   "java/src/org/chromium/chrome/browser/ntp/NativePageRootFrameLayout.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPage.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java
index 1dd80a0..84b1de4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java
@@ -101,25 +101,11 @@
     }
 
     /**
-     * Verify that WebShare is missing by default (without a flag).
-     * @throws Exception
-     */
-    @Test
-    @MediumTest
-    @Feature({"WebShare"})
-    public void testWebShareMissingWithoutFlag() throws Exception {
-        mActivityTestRule.loadUrl(mUrl);
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("initiate_share()");
-        Assert.assertEquals("Fail: navigator.share === undefined", mUpdateWaiter.waitForUpdate());
-    }
-
-    /**
      * Verify that WebShare fails if called without a user gesture.
      * @throws Exception
      */
     @Test
     @MediumTest
-    @CommandLineFlags.Add("enable-blink-features=WebShare")
     @Feature({"WebShare"})
     public void testWebShareNoUserGesture() throws Exception {
         mActivityTestRule.loadUrl(mUrl);
@@ -130,28 +116,11 @@
     }
 
     /**
-     * Verify that WebShare fails if the origin trial is disabled.
-     * @throws Exception
-     */
-    @Test
-    @MediumTest
-    @CommandLineFlags.Add({"enable-blink-features=WebShare",
-            "origin-trial-disabled-features=WebShare"})
-    @Feature({"WebShare"})
-    public void testWebShareOriginTrialDisabled() throws Exception {
-        mActivityTestRule.loadUrl(mUrl);
-        TouchCommon.singleClickView(mTab.getView());
-        Assert.assertEquals(
-                "Fail: SecurityError: WebShare is disabled.", mUpdateWaiter.waitForUpdate());
-    }
-
-    /**
      * Verify WebShare fails if share is called from a user gesture, and canceled.
      * @throws Exception
      */
     @Test
     @MediumTest
-    @CommandLineFlags.Add("enable-blink-features=WebShare")
     @Feature({"WebShare"})
     public void testWebShareCancel() throws Exception {
         // This test tests functionality that is only available post Lollipop MR1.
@@ -184,7 +153,6 @@
      */
     @Test
     @MediumTest
-    @CommandLineFlags.Add("enable-blink-features=WebShare")
     @Feature({"WebShare"})
     public void testWebShareSuccess() throws Exception {
         // This test tests functionality that is only available post Lollipop MR1.
@@ -240,7 +208,6 @@
      */
     @Test
     @MediumTest
-    @CommandLineFlags.Add("enable-blink-features=WebShare")
     @Feature({"WebShare"})
     public void testWebShareCancelPreLMR1() throws Exception {
         ShareHelper.setFakeIntentReceiverForTesting(new ShareHelper.FakeIntentReceiver() {
@@ -271,7 +238,6 @@
      */
     @Test
     @MediumTest
-    @CommandLineFlags.Add("enable-blink-features=WebShare")
     @Feature({"WebShare"})
     public void testWebShareSuccessPreLMR1() throws Exception {
         ShareHelper.setFakeIntentReceiverForTesting(new ShareHelper.FakeIntentReceiver() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java
index 437bdd5..ef62836 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java
@@ -22,7 +22,6 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback;
@@ -56,7 +55,6 @@
         private Semaphore mStopSemaphore = new Semaphore(0);
 
         public TestPrefetchBackgroundTask(TaskInfo taskInfo) {
-            super(Profile.getLastUsedProfile());
             mTaskInfo = taskInfo;
         }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
index 3c84028..06c1f88a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
@@ -9,10 +9,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -20,6 +22,9 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
 import org.mockito.invocation.InvocationOnMock;
@@ -30,6 +35,9 @@
 import org.robolectric.internal.ShadowExtractor;
 import org.robolectric.shadows.multidex.ShadowMultiDex;
 
+import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.chrome.browser.init.BrowserParts;
+import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback;
 import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
 import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
@@ -76,12 +84,33 @@
     }
 
     @Spy
-    private PrefetchBackgroundTask mPrefetchBackgroundTask = new PrefetchBackgroundTask(null);
+    private PrefetchBackgroundTask mPrefetchBackgroundTask = new PrefetchBackgroundTask();
+    @Mock
+    private ChromeBrowserInitializer mChromeBrowserInitializer;
+    @Captor
+    ArgumentCaptor<BrowserParts> mBrowserParts;
     private ShadowBackgroundTaskScheduler mShadowTaskScheduler;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        doNothing().when(mChromeBrowserInitializer).handlePreNativeStartup(any(BrowserParts.class));
+        try {
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) {
+                    mBrowserParts.getValue().finishNativeInitialization();
+                    return null;
+                }
+            })
+                    .when(mChromeBrowserInitializer)
+                    .handlePostNativeStartup(eq(true), mBrowserParts.capture());
+        } catch (ProcessInitException ex) {
+            fail("Unexpected exception while initializing mock of ChromeBrowserInitializer.");
+        }
+
+        ChromeBrowserInitializer.setForTesting(mChromeBrowserInitializer);
+
         doAnswer(new Answer() {
             public Object answer(InvocationOnMock invocation) {
                 mPrefetchBackgroundTask.setNativeTask(1);
@@ -91,7 +120,7 @@
                 .when(mPrefetchBackgroundTask)
                 .nativeStartPrefetchTask(any());
         doReturn(true).when(mPrefetchBackgroundTask).nativeOnStopTask(1);
-        doNothing().when(mPrefetchBackgroundTask).launchBrowserIfNecessary(any());
+        doReturn(null).when(mPrefetchBackgroundTask).getProfile();
 
         mShadowTaskScheduler = (ShadowBackgroundTaskScheduler) ShadowExtractor.extract(
                 BackgroundTaskSchedulerFactory.getScheduler());
diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc
index 25b891f3..234c16c 100644
--- a/chrome/app/chrome_main.cc
+++ b/chrome/app/chrome_main.cc
@@ -4,6 +4,7 @@
 
 #include <stdint.h>
 
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -68,7 +69,11 @@
   content::ContentMainParams params(&chrome_main_delegate);
 
 #if defined(OS_WIN)
-  // The process should crash when going through abnormal termination.
+  // The process should crash when going through abnormal termination, but we
+  // must be sure to reset this setting when ChromeMain returns normally.
+  auto crash_on_detach_resetter = base::ScopedClosureRunner(
+      base::Bind(&base::win::SetShouldCrashOnProcessDetach,
+                 base::win::ShouldCrashOnProcessDetach()));
   base::win::SetShouldCrashOnProcessDetach(true);
   base::win::SetAbortBehaviorForCrashReporting();
   params.instance = instance;
@@ -124,9 +129,5 @@
 
   int rv = content::ContentMain(params);
 
-#if defined(OS_WIN)
-  base::win::SetShouldCrashOnProcessDetach(false);
-#endif
-
   return rv;
 }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index dcc4e4d..f7bd9cc 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5406,6 +5406,11 @@
       <message name="IDS_OMNIBOX_CLEAR_ALL" desc="Accessibility text for the button that deletes all text input from the omnibox on touch devices.">
         Clear input
       </message>
+      <if expr="is_android">
+        <message name="IDS_SEARCH_OR_TYPE_URL" desc="Prompt to enter text into the text field that will either perform web searches or navigate to a URL. [CHAR-LIMIT=38]" formatter_data="android_java">
+          Search or type URL
+        </message>
+      </if>
 
       <!--Tooltip strings-->
       <message name="IDS_TOOLTIP_BACK" desc="The tooltip for back button">
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 03df9c6b..028cdb3b 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3176,20 +3176,14 @@
     <message name="IDS_SETTINGS_POWER_IDLE_OTHER" desc="In Device Settings > Power, menu item for custom idle behavior.">
       Other
     </message>
-    <message name="IDS_SETTINGS_POWER_LID_CLOSED_LABEL" desc="In Device Settings > Power, label for behavior when lid is closed.">
-      When lid is closed
+    <message name="IDS_SETTINGS_POWER_LID_CLOSED_SLEEP_LABEL" desc="In Device Settings > Power, label for suspending when lid is closed.">
+      Sleep when lid is closed
     </message>
-    <message name="IDS_SETTINGS_POWER_LID_CLOSED_SLEEP" desc="In Device Settings > Power, menu item for lid-closed behavior that puts the device to sleep.">
-      Sleep
+    <message name="IDS_SETTINGS_POWER_LID_CLOSED_SIGN_OUT_LABEL" desc="In Device Settings > Power, label for signing out when lid is closed.">
+      Sign out when lid is closed
     </message>
-    <message name="IDS_SETTINGS_POWER_LID_CLOSED_STAY_AWAKE" desc="In Device Settings > Power, menu item for lid-closed behavior that prevents the device from sleeping.">
-      Stay awake
-    </message>
-    <message name="IDS_SETTINGS_POWER_LID_CLOSED_SIGN_OUT" desc="In Device Settings > Power, menu item for lid-closed behavior that signs the user out.">
-      Sign out
-    </message>
-    <message name="IDS_SETTINGS_POWER_LID_CLOSED_SHUT_DOWN" desc="In Device Settings > Power, menu item for lid-closed behavior that shuts the system down.">
-      Shut down
+    <message name="IDS_SETTINGS_POWER_LID_CLOSED_SHUT_DOWN_LABEL" desc="In Device Settings > Power, label for shutting down when lid is closed.">
+      Shut down when lid is closed
     </message>
   </if>
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 72df3d62..4616e29 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2181,6 +2181,8 @@
       "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/offline_prefetch_download_client.cc",
+      "offline_pages/prefetch/offline_prefetch_download_client.h",
       "offline_pages/prefetch/prefetch_instance_id_proxy.cc",
       "offline_pages/prefetch/prefetch_instance_id_proxy.h",
       "offline_pages/prefetch/prefetch_service_factory.cc",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index c3bd542..608c851 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -186,10 +186,10 @@
                                   base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kSearchEnginePromoExistingDevice{
-    "SearchEnginePromo.ExistingDevice", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SearchEnginePromo.ExistingDevice", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kSearchEnginePromoNewDevice{
-    "SearchEnginePromo.NewDevice", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SearchEnginePromo.NewDevice", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kNewPhotoPicker{"NewPhotoPicker",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index c58ac362..89c566d 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -34,6 +34,8 @@
     "textures/button_texture.h",
     "textures/close_button_texture.cc",
     "textures/close_button_texture.h",
+    "textures/exclusive_screen_toast_texture.cc",
+    "textures/exclusive_screen_toast_texture.h",
     "textures/exit_prompt_texture.cc",
     "textures/exit_prompt_texture.h",
     "textures/exit_warning_texture.cc",
@@ -44,8 +46,6 @@
     "textures/insecure_content_transient_texture.h",
     "textures/loading_indicator_texture.cc",
     "textures/loading_indicator_texture.h",
-    "textures/presentation_toast_texture.cc",
-    "textures/presentation_toast_texture.h",
     "textures/render_text_wrapper.cc",
     "textures/render_text_wrapper.h",
     "textures/splash_screen_icon_texture.cc",
diff --git a/chrome/browser/android/vr_shell/color_scheme.cc b/chrome/browser/android/vr_shell/color_scheme.cc
index 5545ecd..178fe5b1 100644
--- a/chrome/browser/android/vr_shell/color_scheme.cc
+++ b/chrome/browser/android/vr_shell/color_scheme.cc
@@ -40,9 +40,9 @@
       normal_scheme.exit_warning_foreground;
   normal_scheme.transient_warning_background =
       normal_scheme.exit_warning_background;
-  normal_scheme.presentation_toast_foreground =
+  normal_scheme.exclusive_screen_toast_foreground =
       normal_scheme.exit_warning_foreground;
-  normal_scheme.presentation_toast_background =
+  normal_scheme.exclusive_screen_toast_background =
       normal_scheme.exit_warning_background;
 
   normal_scheme.permanent_warning_foreground = 0xFF444444;
diff --git a/chrome/browser/android/vr_shell/color_scheme.h b/chrome/browser/android/vr_shell/color_scheme.h
index b6516343..ccefb8bb 100644
--- a/chrome/browser/android/vr_shell/color_scheme.h
+++ b/chrome/browser/android/vr_shell/color_scheme.h
@@ -45,8 +45,8 @@
   SkColor exit_warning_background;
   SkColor transient_warning_foreground;
   SkColor transient_warning_background;
-  SkColor presentation_toast_foreground;
-  SkColor presentation_toast_background;
+  SkColor exclusive_screen_toast_foreground;
+  SkColor exclusive_screen_toast_background;
   SkColor permanent_warning_foreground;
   SkColor permanent_warning_background;
   SkColor system_indicator_foreground;
diff --git a/chrome/browser/android/vr_shell/textures/presentation_toast_texture.cc b/chrome/browser/android/vr_shell/textures/exclusive_screen_toast_texture.cc
similarity index 75%
rename from chrome/browser/android/vr_shell/textures/presentation_toast_texture.cc
rename to chrome/browser/android/vr_shell/textures/exclusive_screen_toast_texture.cc
index 2729b85..62cb453b 100644
--- a/chrome/browser/android/vr_shell/textures/presentation_toast_texture.cc
+++ b/chrome/browser/android/vr_shell/textures/exclusive_screen_toast_texture.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/browser/android/vr_shell/textures/presentation_toast_texture.h"
+#include "chrome/browser/android/vr_shell/textures/exclusive_screen_toast_texture.h"
 
 #include "cc/paint/skia_paint_canvas.h"
 #include "chrome/grit/generated_resources.h"
@@ -23,12 +23,12 @@
 
 }  // namespace
 
-PresentationToastTexture::PresentationToastTexture() = default;
+ExclusiveScreenToastTexture::ExclusiveScreenToastTexture() = default;
 
-PresentationToastTexture::~PresentationToastTexture() = default;
+ExclusiveScreenToastTexture::~ExclusiveScreenToastTexture() = default;
 
-void PresentationToastTexture::Draw(SkCanvas* sk_canvas,
-                                    const gfx::Size& texture_size) {
+void ExclusiveScreenToastTexture::Draw(SkCanvas* sk_canvas,
+                                       const gfx::Size& texture_size) {
   cc::SkiaPaintCanvas paint_canvas(sk_canvas);
   gfx::Canvas gfx_canvas(&paint_canvas, 1.0f);
   gfx::Canvas* canvas = &gfx_canvas;
@@ -36,14 +36,14 @@
   size_.set_width(texture_size.width());
   SkPaint paint;
 
-  paint.setColor(color_scheme().presentation_toast_background);
+  paint.setColor(color_scheme().exclusive_screen_toast_background);
   auto text = l10n_util::GetStringUTF16(IDS_PRESS_APP_TO_EXIT);
   gfx::FontList fonts;
   GetFontList(size_.width() * kFontSizeFactor, text, &fonts);
   gfx::Rect text_size(size_.width() * kTextWidthFactor, 0);
 
   std::vector<std::unique_ptr<gfx::RenderText>> lines = PrepareDrawStringRect(
-      text, fonts, color_scheme().presentation_toast_foreground, &text_size,
+      text, fonts, color_scheme().exclusive_screen_toast_foreground, &text_size,
       kTextAlignmentCenter, kWrappingBehaviorWrap);
 
   DCHECK_LE(text_size.height(),
@@ -61,12 +61,12 @@
   canvas->Restore();
 }
 
-gfx::Size PresentationToastTexture::GetPreferredTextureSize(
+gfx::Size ExclusiveScreenToastTexture::GetPreferredTextureSize(
     int maximum_width) const {
   return gfx::Size(maximum_width, maximum_width);
 }
 
-gfx::SizeF PresentationToastTexture::GetDrawnSize() const {
+gfx::SizeF ExclusiveScreenToastTexture::GetDrawnSize() const {
   return size_;
 }
 
diff --git a/chrome/browser/android/vr_shell/textures/exclusive_screen_toast_texture.h b/chrome/browser/android/vr_shell/textures/exclusive_screen_toast_texture.h
new file mode 100644
index 0000000..6f7a49cc
--- /dev/null
+++ b/chrome/browser/android/vr_shell/textures/exclusive_screen_toast_texture.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 CHROME_BROWSER_ANDROID_VR_SHELL_TEXTURES_EXCLUSIVE_SCREEN_TOAST_TEXTURE_H_
+#define CHROME_BROWSER_ANDROID_VR_SHELL_TEXTURES_EXCLUSIVE_SCREEN_TOAST_TEXTURE_H_
+
+#include "base/macros.h"
+#include "chrome/browser/android/vr_shell/textures/ui_texture.h"
+
+namespace vr_shell {
+
+class ExclusiveScreenToastTexture : public UiTexture {
+ public:
+  ExclusiveScreenToastTexture();
+  ~ExclusiveScreenToastTexture() override;
+  gfx::Size GetPreferredTextureSize(int width) const override;
+  gfx::SizeF GetDrawnSize() const override;
+
+ private:
+  void Draw(SkCanvas* canvas, const gfx::Size& texture_size) override;
+
+  gfx::SizeF size_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExclusiveScreenToastTexture);
+};
+
+}  // namespace vr_shell
+
+#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_TEXTURES_EXCLUSIVE_SCREEN_TOAST_TEXTURE_H_
diff --git a/chrome/browser/android/vr_shell/textures/presentation_toast_texture.h b/chrome/browser/android/vr_shell/textures/presentation_toast_texture.h
deleted file mode 100644
index 649b1e6..0000000
--- a/chrome/browser/android/vr_shell/textures/presentation_toast_texture.h
+++ /dev/null
@@ -1,30 +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_BROWSER_ANDROID_VR_SHELL_TEXTURES_PRESENTATION_TOAST_TEXTURE_H_
-#define CHROME_BROWSER_ANDROID_VR_SHELL_TEXTURES_PRESENTATION_TOAST_TEXTURE_H_
-
-#include "base/macros.h"
-#include "chrome/browser/android/vr_shell/textures/ui_texture.h"
-
-namespace vr_shell {
-
-class PresentationToastTexture : public UiTexture {
- public:
-  PresentationToastTexture();
-  ~PresentationToastTexture() override;
-  gfx::Size GetPreferredTextureSize(int width) const override;
-  gfx::SizeF GetDrawnSize() const override;
-
- private:
-  void Draw(SkCanvas* canvas, const gfx::Size& texture_size) override;
-
-  gfx::SizeF size_;
-
-  DISALLOW_COPY_AND_ASSIGN(PresentationToastTexture);
-};
-
-}  // namespace vr_shell
-
-#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_TEXTURES_PRESENTATION_TOAST_TEXTURE_H_
diff --git a/chrome/browser/android/vr_shell/ui_elements/simple_textured_element.h b/chrome/browser/android/vr_shell/ui_elements/simple_textured_element.h
index f170fab4..4494bbc 100644
--- a/chrome/browser/android/vr_shell/ui_elements/simple_textured_element.h
+++ b/chrome/browser/android/vr_shell/ui_elements/simple_textured_element.h
@@ -9,10 +9,10 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "chrome/browser/android/vr_shell/textures/exclusive_screen_toast_texture.h"
 #include "chrome/browser/android/vr_shell/textures/exit_warning_texture.h"
 #include "chrome/browser/android/vr_shell/textures/insecure_content_permanent_texture.h"
 #include "chrome/browser/android/vr_shell/textures/insecure_content_transient_texture.h"
-#include "chrome/browser/android/vr_shell/textures/presentation_toast_texture.h"
 #include "chrome/browser/android/vr_shell/textures/splash_screen_icon_texture.h"
 #include "chrome/browser/android/vr_shell/textures/ui_texture.h"
 #include "chrome/browser/android/vr_shell/ui_elements/textured_element.h"
@@ -42,7 +42,7 @@
     PermanentSecurityWarning;
 typedef SimpleTexturedElement<InsecureContentTransientTexture>
     TransientSecurityWarning;
-typedef SimpleTexturedElement<PresentationToastTexture> PresentationToast;
+typedef SimpleTexturedElement<ExclusiveScreenToastTexture> ExclusiveScreenToast;
 typedef SimpleTexturedElement<SplashScreenIconTexture> SplashScreenIcon;
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h b/chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h
index 26c2f6a7..91e82c4 100644
--- a/chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h
+++ b/chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h
@@ -29,7 +29,7 @@
   kExitPromptBackplane,
   kTransientUrlBar,
   kLocationAccessIndicator,
-  kPresentationToast,
+  kExclusiveScreenToast,
   kSplashScreenIcon,
 };
 
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc
index 1596a3c..a0507a63 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -425,8 +425,9 @@
 }
 
 void UiSceneManager::CreateToasts() {
-  std::unique_ptr<UiElement> element = base::MakeUnique<PresentationToast>(512);
-  element->set_debug_id(kPresentationToast);
+  std::unique_ptr<UiElement> element =
+      base::MakeUnique<ExclusiveScreenToast>(512);
+  element->set_debug_id(kExclusiveScreenToast);
   element->set_id(AllocateId());
   element->set_fill(vr_shell::Fill::NONE);
   element->set_size({kToastWidth, kToastHeight, 1});
@@ -434,7 +435,7 @@
   element->set_visible(false);
   element->set_hit_testable(false);
   element->set_lock_to_fov(true);
-  presentation_toast_ = element.get();
+  exclusive_screen_toast_ = element.get();
   scene_->AddUiElement(std::move(element));
 }
 
@@ -454,7 +455,7 @@
   toast_state_ = SET_FOR_WEB_VR;
   ConfigureScene();
   ConfigureSecurityWarnings();
-  ConfigurePresentationToast();
+  ConfigureExclusiveScreenToast();
 }
 
 void UiSceneManager::ConfigureScene() {
@@ -601,7 +602,7 @@
   fullscreen_ = fullscreen;
   toast_state_ = SET_FOR_FULLSCREEN;
   ConfigureScene();
-  ConfigurePresentationToast();
+  ConfigureExclusiveScreenToast();
 }
 
 void UiSceneManager::ConfigureSecurityWarnings() {
@@ -649,7 +650,7 @@
   }
 }
 
-void UiSceneManager::ConfigurePresentationToast() {
+void UiSceneManager::ConfigureExclusiveScreenToast() {
   bool toast_visible = false;
   switch (toast_state_) {
     case SET_FOR_WEB_VR:
@@ -661,13 +662,13 @@
     case UNCHANGED:
       return;
   }
-  presentation_toast_->set_visible(toast_visible);
+  exclusive_screen_toast_->set_visible(toast_visible);
   if (toast_visible) {
-    presentation_toast_timer_.Start(
+    exclusive_screen_toast_timer_.Start(
         FROM_HERE, base::TimeDelta::FromSeconds(kToastTimeoutSeconds), this,
-        &UiSceneManager::OnPresentationToastTimer);
+        &UiSceneManager::OnExclusiveScreenToastTimer);
   } else {
-    presentation_toast_timer_.Stop();
+    exclusive_screen_toast_timer_.Stop();
   }
   toast_state_ = UNCHANGED;
 }
@@ -692,8 +693,8 @@
   transient_url_bar_->set_visible(false);
 }
 
-void UiSceneManager::OnPresentationToastTimer() {
-  presentation_toast_->set_visible(false);
+void UiSceneManager::OnExclusiveScreenToastTimer() {
+  exclusive_screen_toast_->set_visible(false);
 }
 
 void UiSceneManager::OnBackButtonClicked() {
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.h b/chrome/browser/android/vr_shell/ui_scene_manager.h
index 7901ad99..e57883c 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.h
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.h
@@ -83,12 +83,12 @@
   void ConfigureSecurityWarnings();
   void ConfigureTransientUrlBar();
   void ConfigureIndicators();
-  void ConfigurePresentationToast();
+  void ConfigureExclusiveScreenToast();
   void UpdateBackgroundColor();
   void CloseExitPrompt();
   void OnSecurityWarningTimer();
   void OnTransientUrlBarTimer();
-  void OnPresentationToastTimer();
+  void OnExclusiveScreenToastTimer();
   void OnBackButtonClicked();
   void OnSecurityIconClicked();
   void OnExitPromptPrimaryButtonClicked();
@@ -106,7 +106,7 @@
   // UI element pointers (not owned by the scene manager).
   UiElement* permanent_security_warning_ = nullptr;
   UiElement* transient_security_warning_ = nullptr;
-  UiElement* presentation_toast_ = nullptr;
+  UiElement* exclusive_screen_toast_ = nullptr;
   UiElement* exit_prompt_ = nullptr;
   UiElement* exit_prompt_backplane_ = nullptr;
   UiElement* exit_warning_ = nullptr;
@@ -147,7 +147,7 @@
 
   base::OneShotTimer security_warning_timer_;
   base::OneShotTimer transient_url_bar_timer_;
-  base::OneShotTimer presentation_toast_timer_;
+  base::OneShotTimer exclusive_screen_toast_timer_;
 
   base::WeakPtrFactory<UiSceneManager> weak_ptr_factory_;
 
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
index 93d6988c..5e2efd9 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
@@ -167,28 +167,28 @@
   // Tests toast not showing when directly entering VR though WebVR
   // presentation.
   MakeManager(kNotInCct, kInWebVr);
-  EXPECT_FALSE(IsVisible(kPresentationToast));
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
 
   MakeManager(kNotInCct, kNotInWebVr);
-  EXPECT_FALSE(IsVisible(kPresentationToast));
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
 
   manager_->SetFullscreen(true);
-  EXPECT_TRUE(IsVisible(kPresentationToast));
+  EXPECT_TRUE(IsVisible(kExclusiveScreenToast));
 
   manager_->SetWebVrMode(true, true);
-  EXPECT_TRUE(IsVisible(kPresentationToast));
+  EXPECT_TRUE(IsVisible(kExclusiveScreenToast));
 
   manager_->SetWebVrMode(false, false);
-  EXPECT_FALSE(IsVisible(kPresentationToast));
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
 
   manager_->SetFullscreen(false);
-  EXPECT_FALSE(IsVisible(kPresentationToast));
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
 
   manager_->SetWebVrMode(true, false);
-  EXPECT_FALSE(IsVisible(kPresentationToast));
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
 
   manager_->SetWebVrMode(false, true);
-  EXPECT_TRUE(IsVisible(kPresentationToast));
+  EXPECT_TRUE(IsVisible(kExclusiveScreenToast));
 }
 
 TEST_F(UiSceneManagerTest, CloseButtonVisibleInCctFullscreen) {
@@ -295,7 +295,7 @@
 TEST_F(UiSceneManagerTest, UiUpdatesForFullscreenChanges) {
   std::set<UiElementDebugId> visible_in_fullscreen = {
       kContentQuad, kCloseButton, kBackplane,
-      kCeiling,     kFloor,       kPresentationToast,
+      kCeiling,     kFloor,       kExclusiveScreenToast,
   };
 
   MakeManager(kNotInCct, kNotInWebVr);
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index efc1b8e..1d0f415 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -1466,8 +1466,7 @@
         parsed_command_line_);
   }
 
-  ui::SelectFileDialog::SetFactory(new ChromeSelectFileDialogFactory(
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)));
+  ui::SelectFileDialog::SetFactory(new ChromeSelectFileDialogFactory());
 #endif  // defined(OS_WIN)
 
   if (parsed_command_line().HasSwitch(switches::kMakeDefaultBrowser)) {
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 7e747d52..c5ec45e 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -56,7 +56,6 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/chrome_utility_messages.h"
 #include "chrome/common/conflicts/module_watcher_win.h"
 #include "chrome/common/crash_keys.h"
 #include "chrome/common/env_vars.h"
@@ -70,8 +69,6 @@
 #include "chrome/installer/util/l10n_string_util.h"
 #include "chrome/installer/util/shell_util.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/utility_process_host.h"
-#include "content/public/browser/utility_process_host_client.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/main_function_params.h"
 #include "ui/base/cursor/cursor_loader_win.h"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1b93edb..a6280f1 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -112,7 +112,6 @@
 #include "chrome/common/env_vars.h"
 #include "chrome/common/features.h"
 #include "chrome/common/logging_chrome.h"
-#include "chrome/common/origin_trials/chrome_origin_trial_policy.h"
 #include "chrome/common/pepper_permission_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/render_messages.h"
@@ -3353,14 +3352,12 @@
   }
 #endif
 
-  if (!ChromeOriginTrialPolicy().IsFeatureDisabled("WebShare")) {
 #if defined(OS_ANDROID)
-    frame_interfaces_parameterized_->AddInterface(base::Bind(
-        &ForwardToJavaWebContentsRegistry<blink::mojom::ShareService>));
+  frame_interfaces_parameterized_->AddInterface(base::Bind(
+      &ForwardToJavaWebContentsRegistry<blink::mojom::ShareService>));
 #elif defined(OS_LINUX) || defined(OS_WIN)
-    frame_interfaces_->AddInterface(base::Bind(&ShareServiceImpl::Create));
+  frame_interfaces_->AddInterface(base::Bind(&ShareServiceImpl::Create));
 #endif
-  }
 }
 
 #if BUILDFLAG(ENABLE_WEBRTC)
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index 26322dc..a5e991a 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -413,6 +413,8 @@
   // Do not expect that SetProfile() is called for various Profile instances.
   // At the moment, it is used for testing purposes.
   DCHECK(profile != profile_);
+  // TODO(yusukes): Once Shutdown() is removed, always call RequestStop() with
+  // |true|. We can actually remove the boolean parameter then.
   Shutdown();
 
   profile_ = profile;
@@ -470,7 +472,7 @@
   playstore_launcher_.reset();
   terms_of_service_negotiator_.reset();
   android_management_checker_.reset();
-  arc_session_runner_->RequestStop();
+  arc_session_runner_->RequestStop(false);
   // TODO(hidehiko): The ARC instance's stopping is asynchronous, so it might
   // still be running when we return from this function. Do not set the
   // STOPPED state immediately here.
@@ -624,7 +626,9 @@
   DCHECK(profile_);
 
   if (!enable_requested_) {
-    VLOG(1) << "ARC is already disabled. Do nothing.";
+    VLOG(1) << "ARC is already disabled. "
+            << "Killing an instance for login screen (if any).";
+    arc_session_runner_->RequestStop(true);
     return;
   }
   enable_requested_ = false;
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
index 6541abb..4ffc5b6 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
+++ b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
@@ -638,10 +638,6 @@
   ASSERT_FALSE(registry()->IsHandledProtocol("test"));
 }
 
-// TODO(smckay): This is much more appropriately an integration
-// test. Make that so, then update the
-// ShellIntegretion{Delegate,Callback,Worker} test classes we use to fully
-// isolate this test from the FILE thread.
 TEST_F(ProtocolHandlerRegistryTest, TestOSRegistration) {
   ProtocolHandler ph_do1 = CreateProtocolHandler("do", "test1");
   ProtocolHandler ph_do2 = CreateProtocolHandler("do", "test2");
@@ -671,10 +667,6 @@
 #define MAYBE_TestOSRegistrationFailure TestOSRegistrationFailure
 #endif
 
-// TODO(smckay): This is much more appropriately an integration
-// test. Make that so, then update the
-// ShellIntegretion{Delegate,Callback,Worker} test classes we use to fully
-// isolate this test from the FILE thread.
 TEST_F(ProtocolHandlerRegistryTest, MAYBE_TestOSRegistrationFailure) {
   ProtocolHandler ph_do = CreateProtocolHandler("do", "test1");
   ProtocolHandler ph_dont = CreateProtocolHandler("dont", "test");
diff --git a/chrome/browser/download/download_service_factory.cc b/chrome/browser/download/download_service_factory.cc
index 05a415bc..c37af5b 100644
--- a/chrome/browser/download/download_service_factory.cc
+++ b/chrome/browser/download/download_service_factory.cc
@@ -19,12 +19,17 @@
 #include "components/download/public/download_service.h"
 #include "components/download/public/task_scheduler.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/offline_pages/features/features.h"
 #include "content/public/browser/browser_context.h"
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/download/service/download_task_scheduler.h"
 #endif
 
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
+#include "chrome/browser/offline_pages/prefetch/offline_prefetch_download_client.h"
+#endif
+
 // static
 DownloadServiceFactory* DownloadServiceFactory::GetInstance() {
   return base::Singleton<DownloadServiceFactory>::get();
@@ -48,7 +53,11 @@
     content::BrowserContext* context) const {
   auto clients = base::MakeUnique<download::DownloadClientMap>();
 
-  // TODO(dtrainor): Register all clients here.
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
+  clients->insert(std::make_pair(
+      download::DownloadClient::OFFLINE_PAGE_PREFETCH,
+      base::MakeUnique<offline_pages::OfflinePrefetchDownloadClient>(context)));
+#endif  // BUILDFLAG(ENABLE_OFFLINE_PAGES)
 
   auto* download_manager = content::BrowserContext::GetDownloadManager(context);
 
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc b/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
index 796f1f37..1001d5bf 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
@@ -61,6 +61,30 @@
             std::vector<std::unique_ptr<DesktopMediaList>> source_lists,
             bool request_audio,
             const DoneCallback& done_callback) override {
+    bool show_screens = false;
+    bool show_windows = false;
+    bool show_tabs = false;
+
+    for (auto& source_list : source_lists) {
+      switch (source_list->GetMediaListType()) {
+        case DesktopMediaID::TYPE_NONE:
+          break;
+        case DesktopMediaID::TYPE_SCREEN:
+          show_screens = true;
+          break;
+        case DesktopMediaID::TYPE_WINDOW:
+          show_windows = true;
+          break;
+        case DesktopMediaID::TYPE_WEB_CONTENTS:
+          show_tabs = true;
+          break;
+      }
+    }
+    EXPECT_EQ(expectation_->expect_screens, show_screens);
+    EXPECT_EQ(expectation_->expect_windows, show_windows);
+    EXPECT_EQ(expectation_->expect_tabs, show_tabs);
+    EXPECT_EQ(expectation_->expect_audio, request_audio);
+
     if (!expectation_->cancelled) {
       // Post a task to call the callback asynchronously.
       base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -98,34 +122,6 @@
     current_test_ = 0;
   }
 
-  // DesktopCaptureChooseDesktopMediaFunction::PickerFactory interface.
-  MediaListArray CreateModel(
-      bool show_screens,
-      bool show_windows,
-      bool show_tabs,
-      bool show_audio) override {
-    EXPECT_LE(current_test_, tests_count_);
-    MediaListArray media_lists;
-    if (current_test_ >= tests_count_) {
-      return media_lists;
-    }
-    EXPECT_EQ(test_flags_[current_test_].expect_screens, show_screens);
-    EXPECT_EQ(test_flags_[current_test_].expect_windows, show_windows);
-    EXPECT_EQ(test_flags_[current_test_].expect_tabs, show_tabs);
-    EXPECT_EQ(test_flags_[current_test_].expect_audio, show_audio);
-
-    media_lists[0] = std::unique_ptr<DesktopMediaList>(
-        show_screens ? new FakeDesktopMediaList(DesktopMediaID::TYPE_SCREEN)
-                     : nullptr);
-    media_lists[1] = std::unique_ptr<DesktopMediaList>(
-        show_windows ? new FakeDesktopMediaList(DesktopMediaID::TYPE_WINDOW)
-                     : nullptr);
-    media_lists[2] = std::unique_ptr<DesktopMediaList>(
-        show_tabs ? new FakeDesktopMediaList(DesktopMediaID::TYPE_WEB_CONTENTS)
-                  : nullptr);
-    return media_lists;
-  }
-
   std::unique_ptr<DesktopMediaPicker> CreatePicker() override {
     EXPECT_LE(current_test_, tests_count_);
     if (current_test_ >= tests_count_)
@@ -135,6 +131,12 @@
         new FakeDesktopMediaPicker(test_flags_ + current_test_ - 1));
   }
 
+  std::unique_ptr<DesktopMediaList> CreateMediaList(
+      DesktopMediaID::Type type) override {
+    EXPECT_LE(current_test_, tests_count_);
+    return std::unique_ptr<DesktopMediaList>(new FakeDesktopMediaList(type));
+  }
+
  private:
   TestFlags* test_flags_;
   int tests_count_;
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
index 29763ff..05ca164 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
@@ -82,56 +82,6 @@
   // Register to be notified when the tab is closed.
   Observe(web_contents);
 
-  bool show_screens = false;
-  bool show_windows = false;
-  bool show_tabs = false;
-  bool request_audio = false;
-
-  std::vector<DesktopMediaID::Type> source_types;
-
-  for (auto source_type : sources) {
-    // Avoid duplicates in the input |sources|.
-    if (std::find(source_types.begin(), source_types.end(), source_type) !=
-        source_types.end()) {
-      continue;
-    }
-
-    switch (source_type) {
-      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE:
-        error_ = kInvalidSourceNameError;
-        return false;
-
-      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN:
-        source_types.push_back(DesktopMediaID::TYPE_SCREEN);
-        show_screens = true;
-        break;
-
-      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW:
-        source_types.push_back(DesktopMediaID::TYPE_WINDOW);
-        show_windows = true;
-        break;
-
-      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB:
-        if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-            extensions::switches::kDisableTabForDesktopShare)) {
-          source_types.push_back(DesktopMediaID::TYPE_WEB_CONTENTS);
-          show_tabs = true;
-        }
-        break;
-
-      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_AUDIO:
-        bool has_flag = base::CommandLine::ForCurrentProcess()->HasSwitch(
-            extensions::switches::kDisableDesktopCaptureAudio);
-        request_audio = !has_flag;
-        break;
-    }
-  }
-
-  if (!show_screens && !show_windows && !show_tabs) {
-    error_ = kEmptySourcesListError;
-    return false;
-  }
-
   gfx::NativeWindow parent_window = web_contents->GetTopLevelNativeWindow();
   // In case of coming from background extension page, |parent_window| will
   // be null. We are going to make the picker modal to the current browser
@@ -143,59 +93,110 @@
     if (target_browser)
       parent_window = target_browser->window()->GetNativeWindow();
   }
-  std::unique_ptr<DesktopMediaList> screen_list;
-  std::unique_ptr<DesktopMediaList> window_list;
-  std::unique_ptr<DesktopMediaList> tab_list;
+
+  // Keep same order as the input |sources| and avoid duplicates.
+  std::vector<std::unique_ptr<DesktopMediaList>> source_lists;
+  bool have_screen_list = false;
+  bool have_window_list = false;
+  bool have_tab_list = false;
+  bool request_audio = false;
+  for (auto source_type : sources) {
+    switch (source_type) {
+      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE: {
+        error_ = kInvalidSourceNameError;
+        return false;
+      }
+      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN: {
+        if (have_screen_list) {
+          continue;
+        }
+        std::unique_ptr<DesktopMediaList> screen_list;
+        if (g_picker_factory) {
+          screen_list =
+              g_picker_factory->CreateMediaList(DesktopMediaID::TYPE_SCREEN);
+        } else {
+#if defined(USE_ASH)
+          screen_list = base::MakeUnique<DesktopMediaListAsh>(
+              DesktopMediaID::TYPE_SCREEN);
+#else   // !defined(USE_ASH)
+          webrtc::DesktopCaptureOptions capture_options =
+              webrtc::DesktopCaptureOptions::CreateDefault();
+          capture_options.set_disable_effects(false);
+          screen_list = base::MakeUnique<NativeDesktopMediaList>(
+              DesktopMediaID::TYPE_SCREEN,
+              webrtc::DesktopCapturer::CreateScreenCapturer(capture_options));
+#endif  // !defined(USE_ASH)
+        }
+        have_screen_list = true;
+        source_lists.push_back(std::move(screen_list));
+        break;
+      }
+      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW: {
+        if (have_window_list) {
+          continue;
+        }
+        std::unique_ptr<DesktopMediaList> window_list;
+        if (g_picker_factory) {
+          window_list =
+              g_picker_factory->CreateMediaList(DesktopMediaID::TYPE_WINDOW);
+        } else {
+#if defined(USE_ASH)
+          window_list = base::MakeUnique<DesktopMediaListAsh>(
+              DesktopMediaID::TYPE_WINDOW);
+#else   // !defined(USE_ASH)
+          // NativeDesktopMediaList calls the capturers on a background thread.
+          // This means that the two DesktopCapturer instances (for screens and
+          // windows) created here cannot share the same DesktopCaptureOptions
+          // instance. DesktopCaptureOptions owns X connection, which cannot be
+          // used on multiple threads concurrently.
+          webrtc::DesktopCaptureOptions capture_options =
+              webrtc::DesktopCaptureOptions::CreateDefault();
+          capture_options.set_disable_effects(false);
+          window_list = base::MakeUnique<NativeDesktopMediaList>(
+              DesktopMediaID::TYPE_WINDOW,
+              webrtc::DesktopCapturer::CreateWindowCapturer(capture_options));
+#endif  // !defined(USE_ASH)
+        }
+        have_window_list = true;
+        source_lists.push_back(std::move(window_list));
+        break;
+      }
+      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB: {
+        if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+                extensions::switches::kDisableTabForDesktopShare) ||
+            have_tab_list) {
+          continue;
+        }
+        std::unique_ptr<DesktopMediaList> tab_list;
+        if (g_picker_factory) {
+          tab_list = g_picker_factory->CreateMediaList(
+              DesktopMediaID::TYPE_WEB_CONTENTS);
+        } else {
+          tab_list = base::MakeUnique<TabDesktopMediaList>();
+        }
+        have_tab_list = true;
+        source_lists.push_back(std::move(tab_list));
+        break;
+      }
+      case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_AUDIO: {
+        bool audio_capture_disabled =
+            base::CommandLine::ForCurrentProcess()->HasSwitch(
+                extensions::switches::kDisableDesktopCaptureAudio);
+        if (!audio_capture_disabled) {
+          request_audio = true;
+        }
+        break;
+      }
+    }
+  }
+  if (source_lists.empty()) {
+    error_ = kEmptySourcesListError;
+    return false;
+  }
+
   if (g_picker_factory) {
-    PickerFactory::MediaListArray media_lists =
-        g_picker_factory->CreateModel(show_screens, show_windows, show_tabs,
-                                      request_audio);
-    screen_list = std::move(media_lists[0]);
-    window_list = std::move(media_lists[1]);
-    tab_list = std::move(media_lists[2]);
     picker_ = g_picker_factory->CreatePicker();
   } else {
-    // Create a screens list.
-    if (show_screens) {
-#if defined(USE_ASH)
-      screen_list =
-          base::MakeUnique<DesktopMediaListAsh>(DesktopMediaID::TYPE_SCREEN);
-#else   // !defined(USE_ASH)
-      webrtc::DesktopCaptureOptions capture_options =
-          webrtc::DesktopCaptureOptions::CreateDefault();
-      capture_options.set_disable_effects(false);
-      screen_list = base::MakeUnique<NativeDesktopMediaList>(
-          DesktopMediaID::TYPE_SCREEN,
-          webrtc::DesktopCapturer::CreateScreenCapturer(capture_options));
-#endif  // !defined(USE_ASH)
-    }
-
-    // Create a windows list.
-    if (show_windows) {
-#if defined(USE_ASH)
-      window_list =
-          base::MakeUnique<DesktopMediaListAsh>(DesktopMediaID::TYPE_WINDOW);
-#else   // !defined(USE_ASH)
-      // NativeDesktopMediaList calls the capturers on a background
-      // thread. This means that the two DesktopCapturer instances (for screens
-      // and windows) created here cannot share the same DesktopCaptureOptions
-      // instance. DesktopCaptureOptions owns X connection, which cannot be used
-      // on multiple threads concurrently.
-      webrtc::DesktopCaptureOptions capture_options =
-          webrtc::DesktopCaptureOptions::CreateDefault();
-      capture_options.set_disable_effects(false);
-      window_list = base::MakeUnique<NativeDesktopMediaList>(
-          DesktopMediaID::TYPE_WINDOW,
-          webrtc::DesktopCapturer::CreateWindowCapturer(capture_options));
-#endif  // !defined(USE_ASH)
-    }
-
-    if (show_tabs)
-      tab_list = base::MakeUnique<TabDesktopMediaList>();
-
-    DCHECK(screen_list || window_list || tab_list);
-    DCHECK(!source_types.empty());
-
     // DesktopMediaPicker is implemented only for Windows, OSX and
     // Aura Linux builds.
 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
@@ -206,28 +207,6 @@
 #endif
   }
 
-  // Keep same order as the input |sources|.
-  std::vector<std::unique_ptr<DesktopMediaList>> source_lists;
-  for (auto source_type : source_types) {
-    switch (source_type) {
-      case DesktopMediaID::TYPE_NONE:
-        NOTREACHED();
-        break;
-      case DesktopMediaID::TYPE_SCREEN:
-        if (screen_list)
-          source_lists.push_back(std::move(screen_list));
-        break;
-      case DesktopMediaID::TYPE_WINDOW:
-        if (window_list)
-          source_lists.push_back(std::move(window_list));
-        break;
-      case DesktopMediaID::TYPE_WEB_CONTENTS:
-        if (tab_list)
-          source_lists.push_back(std::move(tab_list));
-        break;
-    }
-  }
-
   DesktopMediaPicker::DoneCallback callback = base::Bind(
       &DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults,
       this);
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h
index f4f03f04..a5643287 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h
@@ -28,13 +28,9 @@
   // Used for tests to supply fake picker.
   class PickerFactory {
    public:
-    typedef std::array<std::unique_ptr<DesktopMediaList>, 3> MediaListArray;
-    virtual MediaListArray CreateModel(
-        bool show_screens,
-        bool show_windows,
-        bool show_tabs,
-        bool show_audio) = 0;
     virtual std::unique_ptr<DesktopMediaPicker> CreatePicker() = 0;
+    virtual std::unique_ptr<DesktopMediaList> CreateMediaList(
+        content::DesktopMediaID::Type type) = 0;
 
    protected:
     PickerFactory() = default;
diff --git a/chrome/browser/extensions/api/socket/socket_api_unittest.cc b/chrome/browser/extensions/api/socket/socket_api_unittest.cc
index c174cc6..34e18a1 100644
--- a/chrome/browser/extensions/api/socket/socket_api_unittest.cc
+++ b/chrome/browser/extensions/api/socket/socket_api_unittest.cc
@@ -5,6 +5,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process_impl.h"
 #include "chrome/browser/extensions/extension_api_unittest.h"
@@ -35,13 +36,9 @@
 };
 
 TEST_F(SocketUnitTest, Create) {
-  // Get BrowserThread
-  content::BrowserThread::ID id;
-  CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id));
-
   // Create SocketCreateFunction and put it on BrowserThread
   SocketCreateFunction* function = new SocketCreateFunction();
-  function->set_work_thread_id(id);
+  function->set_work_task_runner(base::SequencedTaskRunnerHandle::Get());
 
   // Run tests
   std::unique_ptr<base::DictionaryValue> result(
diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc
index 7801b0d..dbcdc6a2 100644
--- a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc
+++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc
@@ -5,6 +5,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process_impl.h"
 #include "chrome/browser/extensions/extension_api_unittest.h"
@@ -48,14 +49,10 @@
 };
 
 TEST_F(SocketsTcpServerUnitTest, Create) {
-  // Get BrowserThread
-  content::BrowserThread::ID id;
-  CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id));
-
   // Create SocketCreateFunction and put it on BrowserThread
   SocketsTcpServerCreateFunction* function =
       new SocketsTcpServerCreateFunction();
-  function->set_work_thread_id(id);
+  function->set_work_task_runner(base::SequencedTaskRunnerHandle::Get());
 
   // Run tests
   std::unique_ptr<base::DictionaryValue> result(RunFunctionAndReturnDictionary(
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index 9e9dd82..dd6db4ce 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -9,6 +9,7 @@
 #include <cctype>
 #include <string>
 
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -40,6 +41,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/image_loader.h"
@@ -67,7 +69,6 @@
 #include "ui/gfx/image/image_family.h"
 
 #if defined(OS_MACOSX)
-#include "base/command_line.h"
 #include "chrome/browser/web_applications/web_app_mac.h"
 #include "chrome/common/chrome_switches.h"
 #endif
@@ -590,8 +591,12 @@
 
   UpdateWebAppInfoFromManifest(manifest, &web_app_info_);
 
-  if (!ChromeOriginTrialPolicy().IsFeatureDisabled("WebShare"))
+  // TODO(mgiuca): Web Share Target should have its own flag, rather than using
+  // the experimental-web-platform-features flag. https://crbug.com/736178.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableExperimentalWebPlatformFeatures)) {
     UpdateShareTargetInPrefs(manifest_url, manifest, profile_->GetPrefs());
+  }
 
   // Add urls from the WebApplicationInfo.
   std::vector<GURL> web_app_info_icon_urls;
diff --git a/chrome/browser/history/top_sites_factory.cc b/chrome/browser/history/top_sites_factory.cc
index 97a0ac9a..dc0163a 100644
--- a/chrome/browser/history/top_sites_factory.cc
+++ b/chrome/browser/history/top_sites_factory.cc
@@ -103,9 +103,7 @@
       profile->GetPrefs(), HistoryServiceFactory::GetForProfile(
                                profile, ServiceAccessType::EXPLICIT_ACCESS),
       prepopulated_page_list, base::Bind(CanAddURLToHistory)));
-  top_sites->Init(context->GetPath().Append(history::kTopSitesFilename),
-                  content::BrowserThread::GetTaskRunnerForThread(
-                      content::BrowserThread::DB));
+  top_sites->Init(context->GetPath().Append(history::kTopSitesFilename));
   return top_sites;
 }
 
diff --git a/chrome/browser/media/android/router/media_router_dialog_controller_android.cc b/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
index ca4ef76..08983f6 100644
--- a/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
+++ b/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
@@ -18,8 +18,13 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "device/vr/features/features.h"
 #include "jni/ChromeMediaRouterDialogController_jni.h"
 
+#if BUILDFLAG(ENABLE_VR)
+#include "chrome/browser/android/vr_shell/vr_tab_helper.h"
+#endif  // BUILDFLAG(ENABLE_VR)
+
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
     media_router::MediaRouterDialogControllerAndroid);
 
@@ -143,6 +148,14 @@
 }
 
 void MediaRouterDialogControllerAndroid::CreateMediaRouterDialog() {
+#if BUILDFLAG(ENABLE_VR)
+  // TODO(crbug.com/736568): Re-enable dialog in VR.
+  if (vr_shell::VrTabHelper::IsInVr(initiator())) {
+    CancelPresentationRequest();
+    return;
+  }
+#endif  // BUILDFLAG(ENABLE_VR)
+
   JNIEnv* env = base::android::AttachCurrentThread();
 
   const std::vector<MediaSource> sources =
diff --git a/chrome/browser/offline_pages/prefetch/offline_prefetch_download_client.cc b/chrome/browser/offline_pages/prefetch/offline_prefetch_download_client.cc
new file mode 100644
index 0000000..a2d49a9
--- /dev/null
+++ b/chrome/browser/offline_pages/prefetch/offline_prefetch_download_client.cc
@@ -0,0 +1,67 @@
+// 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/prefetch/offline_prefetch_download_client.h"
+
+#include "base/logging.h"
+#include "chrome/browser/download/download_service_factory.h"
+#include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h"
+#include "components/offline_pages/core/prefetch/prefetch_downloader.h"
+#include "components/offline_pages/core/prefetch/prefetch_service.h"
+
+namespace offline_pages {
+
+OfflinePrefetchDownloadClient::OfflinePrefetchDownloadClient(
+    content::BrowserContext* context)
+    : context_(context) {}
+
+OfflinePrefetchDownloadClient::~OfflinePrefetchDownloadClient() = default;
+
+void OfflinePrefetchDownloadClient::OnServiceInitialized(
+    const std::vector<std::string>& outstanding_download_guids) {
+  // TODO(jianli): Remove orphaned downloads.
+  PrefetchDownloader* downloader = GetPrefetchDownloader();
+  if (downloader)
+    downloader->OnDownloadServiceReady();
+}
+
+download::Client::ShouldDownload
+OfflinePrefetchDownloadClient::OnDownloadStarted(
+    const std::string& guid,
+    const std::vector<GURL>& url_chain,
+    const scoped_refptr<const net::HttpResponseHeaders>& headers) {
+  return download::Client::ShouldDownload::CONTINUE;
+}
+
+void OfflinePrefetchDownloadClient::OnDownloadUpdated(
+    const std::string& guid,
+    uint64_t bytes_downloaded) {}
+
+void OfflinePrefetchDownloadClient::OnDownloadFailed(
+    const std::string& guid,
+    download::Client::FailureReason reason) {
+  PrefetchDownloader* downloader = GetPrefetchDownloader();
+  if (downloader)
+    downloader->OnDownloadFailed(guid);
+}
+
+void OfflinePrefetchDownloadClient::OnDownloadSucceeded(
+    const std::string& guid,
+    const base::FilePath& path,
+    uint64_t size) {
+  PrefetchDownloader* downloader = GetPrefetchDownloader();
+  if (downloader)
+    downloader->OnDownloadSucceeded(guid, path, size);
+}
+
+PrefetchDownloader* OfflinePrefetchDownloadClient::GetPrefetchDownloader()
+    const {
+  PrefetchService* prefetch_service =
+      PrefetchServiceFactory::GetForBrowserContext(context_);
+  if (!prefetch_service)
+    return nullptr;
+  return prefetch_service->GetPrefetchDownloader();
+}
+
+}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/prefetch/offline_prefetch_download_client.h b/chrome/browser/offline_pages/prefetch/offline_prefetch_download_client.h
new file mode 100644
index 0000000..3a258ff
--- /dev/null
+++ b/chrome/browser/offline_pages/prefetch/offline_prefetch_download_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 CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_OFFLINE_PREFETCH_DOWNLOAD_CLIENT_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_OFFLINE_PREFETCH_DOWNLOAD_CLIENT_H_
+
+#include "base/macros.h"
+#include "components/download/public/client.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace offline_pages {
+
+class PrefetchDownloader;
+
+class OfflinePrefetchDownloadClient : public download::Client {
+ public:
+  explicit OfflinePrefetchDownloadClient(content::BrowserContext* context);
+  ~OfflinePrefetchDownloadClient() override;
+
+ private:
+  // Overridden from Client:
+  void OnServiceInitialized(
+      const std::vector<std::string>& outstanding_download_guids) override;
+  download::Client::ShouldDownload OnDownloadStarted(
+      const std::string& guid,
+      const std::vector<GURL>& url_chain,
+      const scoped_refptr<const net::HttpResponseHeaders>& headers) override;
+  void OnDownloadUpdated(const std::string& guid,
+                         uint64_t bytes_downloaded) override;
+  void OnDownloadFailed(const std::string& guid,
+                        download::Client::FailureReason reason) override;
+  void OnDownloadSucceeded(const std::string& guid,
+                           const base::FilePath& path,
+                           uint64_t size) override;
+
+  PrefetchDownloader* GetPrefetchDownloader() const;
+
+  content::BrowserContext* context_;
+
+  DISALLOW_COPY_AND_ASSIGN(OfflinePrefetchDownloadClient);
+};
+
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_OFFLINE_PREFETCH_DOWNLOAD_CLIENT_H_
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
index 34eaf11..12dbcc3 100644
--- a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
+++ b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
+#include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/offline_pages/prefetch/offline_metrics_collector_impl.h"
 #include "chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h"
 #include "chrome/browser/profiles/profile.h"
@@ -16,6 +17,7 @@
 #include "chrome/common/chrome_content_client.h"
 #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_downloader.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h"
 #include "components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h"
 #include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
@@ -27,7 +29,10 @@
 PrefetchServiceFactory::PrefetchServiceFactory()
     : BrowserContextKeyedServiceFactory(
           "OfflinePagePrefetchService",
-          BrowserContextDependencyManager::GetInstance()) {}
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(DownloadServiceFactory::GetInstance());
+}
+
 // static
 PrefetchServiceFactory* PrefetchServiceFactory::GetInstance() {
   return base::Singleton<PrefetchServiceFactory>::get();
@@ -55,11 +60,15 @@
   auto suggested_articles_observer =
       base::MakeUnique<SuggestedArticlesObserver>();
 
-  return new PrefetchServiceImpl(std::move(offline_metrics_collector),
-                                 std::move(prefetch_dispatcher),
-                                 std::move(prefetch_gcm_app_handler),
-                                 std::move(prefetch_network_request_factory),
-                                 std::move(suggested_articles_observer));
+  auto prefetch_downloader = base::MakeUnique<PrefetchDownloader>(
+      DownloadServiceFactory::GetForBrowserContext(context),
+      chrome::GetChannel());
+
+  return new PrefetchServiceImpl(
+      std::move(offline_metrics_collector), std::move(prefetch_dispatcher),
+      std::move(prefetch_gcm_app_handler),
+      std::move(prefetch_network_request_factory),
+      std::move(suggested_articles_observer), std::move(prefetch_downloader));
 }
 
 }  // namespace offline_pages
diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc
index abab796..f04aef7 100644
--- a/chrome/browser/profiles/off_the_record_profile_io_data.cc
+++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc
@@ -199,21 +199,6 @@
   net::URLRequestContextStorage* main_context_storage =
       main_request_context_storage();
 
-  IOThread* const io_thread = profile_params->io_thread;
-  IOThread::Globals* const io_thread_globals = io_thread->globals();
-
-  ApplyProfileParamsToContext(main_context);
-
-  main_context->set_transport_security_state(transport_security_state());
-  main_context->set_ct_policy_enforcer(
-      io_thread_globals->system_request_context->ct_policy_enforcer());
-
-  main_context->set_net_log(io_thread->net_log());
-
-  main_context->set_http_auth_handler_factory(
-      io_thread_globals->system_request_context->http_auth_handler_factory());
-  main_context->set_proxy_service(proxy_service());
-
   // For incognito, we use the default non-persistent HttpServerPropertiesImpl.
   main_context_storage->set_http_server_properties(
       base::MakeUnique<net::HttpServerPropertiesImpl>());
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 81a5cbf2..f35c5a4 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -469,24 +469,12 @@
   IOThread* const io_thread = profile_params->io_thread;
   IOThread::Globals* const io_thread_globals = io_thread->globals();
 
-  ApplyProfileParamsToContext(main_context);
-
   if (lazy_params_->http_server_properties_manager) {
     lazy_params_->http_server_properties_manager->InitializeOnNetworkSequence();
     main_context_storage->set_http_server_properties(
         std::move(lazy_params_->http_server_properties_manager));
   }
 
-  main_context->set_transport_security_state(transport_security_state());
-  main_context->set_ct_policy_enforcer(
-      io_thread_globals->system_request_context->ct_policy_enforcer());
-
-  main_context->set_net_log(io_thread->net_log());
-
-  main_context->set_http_auth_handler_factory(
-      io_thread_globals->system_request_context->http_auth_handler_factory());
-
-  main_context->set_proxy_service(proxy_service());
   main_context->set_network_quality_estimator(
       io_thread_globals->network_quality_estimator.get());
 
@@ -796,9 +784,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(initialized());
 
-  DCHECK(transport_security_state());
   // Completes synchronously.
-  transport_security_state()->DeleteAllDynamicDataSince(time);
+  main_request_context()->transport_security_state()->DeleteAllDynamicDataSince(
+      time);
   DCHECK(http_server_properties_manager_);
   http_server_properties_manager_->Clear(completion);
 }
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 128b17e..824002c 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -658,39 +658,33 @@
            static_cast<void*>(it->second), sizeof(void*));
   }
 
-  // Prevent the TreeStateTracker from getting any more notifications by
-  // severing the link between it and the CTVerifier and unregistering it from
-  // new STH notifications.
-  //
-  // Only do this if the |cert_transparency_verifier_| is not null.
-  if (cert_transparency_verifier_) {
-    cert_transparency_verifier_->SetObserver(nullptr);
+  if (main_request_context_) {
+    // Prevent the TreeStateTracker from getting any more notifications by
+    // severing the link between it and the CTVerifier and unregistering it from
+    // new STH notifications.
+    main_request_context_->cert_transparency_verifier()->SetObserver(nullptr);
     ct_tree_tracker_unregistration_.Run();
-  }
 
-  // Destroy certificate_report_sender_ before main_request_context_,
-  // since the former has a reference to the latter.
-  if (transport_security_state_)
-    transport_security_state_->SetReportSender(nullptr);
-  certificate_report_sender_.reset();
+    // Destroy certificate_report_sender_ before main_request_context_,
+    // since the former has a reference to the latter.
+    main_request_context_->transport_security_state()->SetReportSender(nullptr);
+    certificate_report_sender_.reset();
 
-  if (transport_security_state_)
-    transport_security_state_->SetExpectCTReporter(nullptr);
-  expect_ct_reporter_.reset();
+    main_request_context_->transport_security_state()->SetExpectCTReporter(
+        nullptr);
+    expect_ct_reporter_.reset();
 
-  if (transport_security_state_)
-    transport_security_state_->SetRequireCTDelegate(nullptr);
+    main_request_context_->transport_security_state()->SetRequireCTDelegate(
+        nullptr);
 
-  // And the same for the ReportingService.
-  if (main_request_context_storage()) {
+    // And the same for the ReportingService.
     main_request_context_storage()->set_reporting_service(
         std::unique_ptr<net::ReportingService>());
-  }
 
-  // This should be shut down last, as any other requests may initiate more
-  // activity when the ProxyService aborts lookups.
-  if (proxy_service_)
-    proxy_service_->OnShutdown();
+    // This should be shut down last, as any other requests may initiate more
+    // activity when the ProxyService aborts lookups.
+    main_request_context_->proxy_service()->OnShutdown();
+  }
 
   // TODO(ajwong): These AssertNoURLRequests() calls are unnecessary since they
   // are already done in the URLRequestContext destructor.
@@ -1004,13 +998,18 @@
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
 
-  // Create the common request contexts.
+  // Create extension request context.  Only used for cookies.
+  extensions_request_context_.reset(new net::URLRequestContext());
+  extensions_request_context_->set_name("extensions");
+
+  // Create the main request context.
   main_request_context_.reset(new net::URLRequestContext());
   main_request_context_storage_.reset(
       new net::URLRequestContextStorage(main_request_context_.get()));
-  extensions_request_context_.reset(new net::URLRequestContext());
   main_request_context_->set_name("main");
-  extensions_request_context_->set_name("extensions");
+
+  ApplyProfileParamsToContext(main_request_context_.get());
+  main_request_context_->set_net_log(io_thread->net_log());
 
   main_request_context_->set_enable_brotli(io_thread_globals->enable_brotli);
 
@@ -1053,20 +1052,24 @@
   main_request_context_->set_host_resolver(
       io_thread_globals->system_request_context->host_resolver());
 
-  // NOTE: Proxy service uses the default io thread network delegate, not the
-  // delegate just created.
-  proxy_service_ = ProxyServiceFactory::CreateProxyService(
-      io_thread->net_log(), main_request_context_.get(), network_delegate.get(),
-      std::move(profile_params_->proxy_config_service), command_line,
-      io_thread->WpadQuickCheckEnabled(),
-      io_thread->PacHttpsUrlStrippingEnabled());
+  main_request_context_->set_http_auth_handler_factory(
+      io_thread_globals->system_request_context->http_auth_handler_factory());
+
+  main_request_context_storage_->set_proxy_service(
+      ProxyServiceFactory::CreateProxyService(
+          io_thread->net_log(), main_request_context_.get(),
+          network_delegate.get(),
+          std::move(profile_params_->proxy_config_service), command_line,
+          io_thread->WpadQuickCheckEnabled(),
+          io_thread->PacHttpsUrlStrippingEnabled()));
 
   main_request_context_storage_->set_network_delegate(
       std::move(network_delegate));
 
-  transport_security_state_.reset(new net::TransportSecurityState());
+  std::unique_ptr<net::TransportSecurityState> transport_security_state(
+      base::MakeUnique<net::TransportSecurityState>());
   transport_security_persister_.reset(new net::TransportSecurityPersister(
-      transport_security_state_.get(), profile_params_->path,
+      transport_security_state.get(), profile_params_->path,
       base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BACKGROUND,
            base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
@@ -1102,14 +1105,16 @@
         })");
   certificate_report_sender_.reset(
       new net::ReportSender(main_request_context_.get(), traffic_annotation));
-  transport_security_state_->SetReportSender(certificate_report_sender_.get());
+  transport_security_state->SetReportSender(certificate_report_sender_.get());
 
   expect_ct_reporter_.reset(
       new ChromeExpectCTReporter(main_request_context_.get()));
-  transport_security_state_->SetExpectCTReporter(expect_ct_reporter_.get());
+  transport_security_state->SetExpectCTReporter(expect_ct_reporter_.get());
 
-  transport_security_state_->SetRequireCTDelegate(
+  transport_security_state->SetRequireCTDelegate(
       ct_policy_manager_->GetDelegate());
+  main_request_context_storage_->set_transport_security_state(
+      std::move(transport_security_state));
 
   // Take ownership over these parameters.
   cookie_settings_ = profile_params_->cookie_settings;
@@ -1169,18 +1174,22 @@
   std::unique_ptr<net::MultiLogCTVerifier> ct_verifier(
       new net::MultiLogCTVerifier());
   ct_verifier->AddLogs(io_thread_globals->ct_logs);
-  main_request_context_->set_cert_transparency_verifier(ct_verifier.get());
 
   ct_tree_tracker_.reset(new certificate_transparency::TreeStateTracker(
       io_thread_globals->ct_logs, io_thread->net_log()));
   ct_verifier->SetObserver(ct_tree_tracker_.get());
 
-  cert_transparency_verifier_ = std::move(ct_verifier);
+  main_request_context_storage_->set_cert_transparency_verifier(
+      std::move(ct_verifier));
+
   io_thread->RegisterSTHObserver(ct_tree_tracker_.get());
   ct_tree_tracker_unregistration_ =
       base::Bind(&IOThread::UnregisterSTHObserver, base::Unretained(io_thread),
                  ct_tree_tracker_.get());
 
+  main_request_context_->set_ct_policy_enforcer(
+      io_thread_globals->system_request_context->ct_policy_enforcer());
+
   InitializeInternal(profile_params_.get(), protocol_handlers,
                      std::move(request_interceptors));
 
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index a25e88d..d626a45 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -77,15 +77,12 @@
 class ChannelIDService;
 class ClientCertStore;
 class CookieStore;
-class CTVerifier;
 class HttpTransactionFactory;
 class ProxyConfigService;
-class ProxyService;
 class ReportingService;
 class ReportSender;
 class SSLConfigService;
 class TransportSecurityPersister;
-class TransportSecurityState;
 class URLRequestContextStorage;
 class URLRequestJobFactoryImpl;
 }  // namespace net
@@ -187,10 +184,6 @@
     return &network_controller_handle_;
   }
 
-  net::TransportSecurityState* transport_security_state() const {
-    return transport_security_state_.get();
-  }
-
 #if defined(OS_CHROMEOS)
   std::string username_hash() const {
     return username_hash_;
@@ -383,10 +376,6 @@
   void set_previews_io_data(
       std::unique_ptr<previews::PreviewsIOData> previews_io_data) const;
 
-  net::ProxyService* proxy_service() const {
-    return proxy_service_.get();
-  }
-
   net::URLRequestContext* main_request_context() const {
     return main_request_context_.get();
   }
@@ -564,10 +553,6 @@
   mutable std::unique_ptr<data_reduction_proxy::DataReductionProxyIOData>
       data_reduction_proxy_io_data_;
 
-  mutable std::unique_ptr<net::ProxyService> proxy_service_;
-  mutable std::unique_ptr<net::TransportSecurityState>
-      transport_security_state_;
-  mutable std::unique_ptr<net::CTVerifier> cert_transparency_verifier_;
   mutable std::unique_ptr<ChromeExpectCTReporter> expect_ct_reporter_;
 #if defined(OS_CHROMEOS)
   // Set to |cert_verifier_| if it references a PolicyCertVerifier. In that
@@ -579,13 +564,6 @@
   mutable std::unique_ptr<chromeos::CertificateProvider> certificate_provider_;
 #endif
 
-  // Pointed to by the TransportSecurityState.
-  mutable std::unique_ptr<net::TransportSecurityPersister>
-      transport_security_persister_;
-  mutable std::unique_ptr<net::ReportSender> certificate_report_sender_;
-  mutable std::unique_ptr<certificate_transparency::CTPolicyManager>
-      ct_policy_manager_;
-
   // Owns the subset of URLRequestContext's elements that are created by
   // subclasses of ProfileImplIOData, to ensure proper destruction ordering.
   // TODO(mmenke):  Move ownship of net objects owned by the ProfileIOData
@@ -594,6 +572,15 @@
       main_request_context_storage_;
   mutable std::unique_ptr<net::URLRequestContext> main_request_context_;
 
+  // Pointed to by the TransportSecurityState (owned by
+  // URLRequestContextStorage), and must be disconnected from it before it's
+  // destroyed.
+  mutable std::unique_ptr<net::TransportSecurityPersister>
+      transport_security_persister_;
+  mutable std::unique_ptr<net::ReportSender> certificate_report_sender_;
+  mutable std::unique_ptr<certificate_transparency::CTPolicyManager>
+      ct_policy_manager_;
+
   mutable std::unique_ptr<net::URLRequestContext> extensions_request_context_;
   // One URLRequestContext per isolated app for main and media requests.
   mutable URLRequestContextMap app_request_context_map_;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 034d12c..3351f16 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -354,10 +354,10 @@
   const size_t kMappingSize = arraysize(kUmaEnumToControlId);
   for (size_t i = 0; i < kMappingSize; ++i) {
     if (kUmaEnumToControlId[i].control_id == id) {
-      if (enum_lookup_type == GENERAL_ENUM_ID) {
+      if (enum_lookup_type == GENERAL_ENUM_ID)
         return kUmaEnumToControlId[i].enum_id;
-      } else if (enum_lookup_type == CONTEXT_SPECIFIC_ENUM_ID &&
-                 kUmaEnumToControlId[i].context_specific_enum_id > -1) {
+      if (enum_lookup_type == CONTEXT_SPECIFIC_ENUM_ID &&
+          kUmaEnumToControlId[i].context_specific_enum_id > -1) {
         return kUmaEnumToControlId[i].context_specific_enum_id;
       }
     }
@@ -514,9 +514,8 @@
 
 // static
 bool RenderViewContextMenu::IsInternalResourcesURL(const GURL& url) {
-  if (!url.SchemeIs(content::kChromeUIScheme))
-    return false;
-  return url.host_piece() == chrome::kChromeUISyncResourcesHost;
+  return url.SchemeIs(content::kChromeUIScheme) &&
+         url.host_piece() == chrome::kChromeUISyncResourcesHost;
 }
 
 // static
@@ -677,14 +676,14 @@
       sorted_menu_titles.end());
 
   int index = 0;
-  for (size_t i = 0; i < sorted_menu_titles.size(); ++i) {
-    std::vector<const Extension*>& extensions =
-        title_to_extensions_map[sorted_menu_titles[i]];
-    for (auto* extension : extensions) {
+  for (const auto& title : sorted_menu_titles) {
+    const std::vector<const Extension*>& extensions =
+        title_to_extensions_map[title];
+    for (const Extension* extension : extensions) {
       MenuItem::ExtensionKey extension_key(extension->id());
       extension_items_.AppendExtensionItems(extension_key,
                                             printable_selection_text, &index,
-                                            false);  // is_action_menu
+                                            /*is_action_menu=*/false);
     }
   }
 }
@@ -712,7 +711,7 @@
   // Only add extension items from this extension.
   int index = 0;
   extension_items_.AppendExtensionItems(key, PrintableSelectionText(), &index,
-                                        false /* is_action_menu */);
+                                        /*is_action_menu=*/false);
 }
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
@@ -733,10 +732,10 @@
       menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
   }
 
-  if (content_type_->SupportsGroup(
-          ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
+  bool media_image = content_type_->SupportsGroup(
+      ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE);
+  if (media_image)
     AppendImageItems();
-  }
 
   if (content_type_->SupportsGroup(
           ContextMenuContentType::ITEM_GROUP_SEARCHWEBFORIMAGE)) {
@@ -765,12 +764,13 @@
 
   // ITEM_GROUP_MEDIA_FILE has no specific items.
 
-  if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_EDITABLE))
+  bool editable =
+      content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_EDITABLE);
+  if (editable)
     AppendEditableItems();
 
   if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_COPY)) {
-    DCHECK(!content_type_->SupportsGroup(
-               ContextMenuContentType::ITEM_GROUP_EDITABLE));
+    DCHECK(!editable);
     AppendCopyItem();
   }
 
@@ -780,15 +780,12 @@
     AppendSearchProvider();
   }
 
-  if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT) &&
-      !content_type_->SupportsGroup(
-          ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
+  if (!media_image &&
+      content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT)) {
     AppendPrintItem();
   }
 
-  if (content_type_->SupportsGroup(
-          ContextMenuContentType::ITEM_GROUP_EDITABLE) &&
-      params_.misspelled_word.empty()) {
+  if (editable && params_.misspelled_word.empty()) {
     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
     AppendLanguageSettings();
     AppendPlatformEditableItems();
@@ -829,10 +826,10 @@
   }
 
   // Remove any redundant trailing separator.
-  if (menu_model_.GetItemCount() > 0 &&
-      menu_model_.GetTypeAt(menu_model_.GetItemCount() - 1) ==
-          ui::MenuModel::TYPE_SEPARATOR) {
-    menu_model_.RemoveItemAt(menu_model_.GetItemCount() - 1);
+  int index = menu_model_.GetItemCount() - 1;
+  if (index >= 0 &&
+      menu_model_.GetTypeAt(index) == ui::MenuModel::TYPE_SEPARATOR) {
+    menu_model_.RemoveItemAt(index);
   }
 }
 
@@ -842,38 +839,37 @@
 
 void RenderViewContextMenu::RecordUsedItem(int id) {
   int enum_id = FindUMAEnumValueForCommand(id, GENERAL_ENUM_ID);
-  if (enum_id != -1) {
-    const size_t kMappingSize = arraysize(kUmaEnumToControlId);
-    UMA_HISTOGRAM_EXACT_LINEAR("RenderViewContextMenu.Used", enum_id,
-                               kUmaEnumToControlId[kMappingSize - 1].enum_id);
-    // Record to additional context specific histograms.
-    enum_id = FindUMAEnumValueForCommand(id, CONTEXT_SPECIFIC_ENUM_ID);
-
-    // Linked image context.
-    if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK) &&
-        content_type_->SupportsGroup(
-            ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
-      UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOption.ImageLink",
-                                 enum_id,
-                                 kUmaEnumToControlId[kMappingSize - 1].enum_id);
-    }
-    // Selected text context.
-    if (content_type_->SupportsGroup(
-            ContextMenuContentType::ITEM_GROUP_SEARCH_PROVIDER) &&
-        content_type_->SupportsGroup(
-            ContextMenuContentType::ITEM_GROUP_PRINT)) {
-      UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOption.SelectedText",
-                                 enum_id,
-                                 kUmaEnumToControlId[kMappingSize - 1].enum_id);
-    }
-    // Misspelled word context.
-    if (!params_.misspelled_word.empty()) {
-      UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOption.MisspelledWord",
-                                 enum_id,
-                                 kUmaEnumToControlId[kMappingSize - 1].enum_id);
-    }
-  } else {
+  if (enum_id == -1) {
     NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id;
+    return;
+  }
+
+  const size_t kMappingSize = arraysize(kUmaEnumToControlId);
+  UMA_HISTOGRAM_EXACT_LINEAR("RenderViewContextMenu.Used", enum_id,
+                             kUmaEnumToControlId[kMappingSize - 1].enum_id);
+  // Record to additional context specific histograms.
+  enum_id = FindUMAEnumValueForCommand(id, CONTEXT_SPECIFIC_ENUM_ID);
+
+  // Linked image context.
+  if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK) &&
+      content_type_->SupportsGroup(
+          ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
+    UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOption.ImageLink", enum_id,
+                               kUmaEnumToControlId[kMappingSize - 1].enum_id);
+  }
+  // Selected text context.
+  if (content_type_->SupportsGroup(
+          ContextMenuContentType::ITEM_GROUP_SEARCH_PROVIDER) &&
+      content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PRINT)) {
+    UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOption.SelectedText",
+                               enum_id,
+                               kUmaEnumToControlId[kMappingSize - 1].enum_id);
+  }
+  // Misspelled word context.
+  if (!params_.misspelled_word.empty()) {
+    UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOption.MisspelledWord",
+                               enum_id,
+                               kUmaEnumToControlId[kMappingSize - 1].enum_id);
   }
 }
 
@@ -909,9 +905,9 @@
 
 void RenderViewContextMenu::AppendPrintPreviewItems() {
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-  if (!print_preview_menu_observer_.get()) {
-    print_preview_menu_observer_.reset(
-        new PrintPreviewContextMenuObserver(source_web_contents_));
+  if (!print_preview_menu_observer_) {
+    print_preview_menu_observer_ =
+        base::MakeUnique<PrintPreviewContextMenuObserver>(source_web_contents_);
   }
 
   observers_.AddObserver(print_preview_menu_observer_.get());
@@ -1114,22 +1110,23 @@
 }
 
 void RenderViewContextMenu::AppendSearchWebForImageItems() {
+  if (!params_.has_image_contents)
+    return;
+
   TemplateURLService* service =
       TemplateURLServiceFactory::GetForProfile(GetProfile());
-  const TemplateURL* const default_provider =
-      service->GetDefaultSearchProvider();
-  if (params_.has_image_contents && default_provider &&
-      !default_provider->image_url().empty() &&
-      default_provider->image_url_ref().IsValid(service->search_terms_data())) {
-    menu_model_.AddItem(
-        IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
-        l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
-                                   default_provider->short_name()));
-    if (default_provider->image_url_ref().HasGoogleBaseURLs(
-            service->search_terms_data())) {
-      AddGoogleIconToLastMenuItem(&menu_model_);
-    }
+  const TemplateURL* const provider = service->GetDefaultSearchProvider();
+  if (!provider || provider->image_url().empty() ||
+      !provider->image_url_ref().IsValid(service->search_terms_data())) {
+    return;
   }
+
+  menu_model_.AddItem(
+      IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
+      l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
+                                 provider->short_name()));
+  if (provider->image_url_ref().HasGoogleBaseURLs(service->search_terms_data()))
+    AddGoogleIconToLastMenuItem(&menu_model_);
 }
 
 void RenderViewContextMenu::AppendAudioItems() {
@@ -1382,8 +1379,9 @@
 #else
   if (!spelling_options_submenu_observer_) {
     const int kLanguageRadioGroup = 1;
-    spelling_options_submenu_observer_.reset(
-        new SpellingOptionsSubMenuObserver(this, this, kLanguageRadioGroup));
+    spelling_options_submenu_observer_ =
+        base::MakeUnique<SpellingOptionsSubMenuObserver>(this, this,
+                                                         kLanguageRadioGroup);
   }
 
   spelling_options_submenu_observer_->InitMenu(params_);
@@ -1392,8 +1390,10 @@
 }
 
 void RenderViewContextMenu::AppendSpellingSuggestionItems() {
-  if (!spelling_suggestions_menu_observer_)
-    spelling_suggestions_menu_observer_.reset(new SpellingMenuObserver(this));
+  if (!spelling_suggestions_menu_observer_) {
+    spelling_suggestions_menu_observer_ =
+        base::MakeUnique<SpellingMenuObserver>(this);
+  }
   observers_.AddObserver(spelling_suggestions_menu_observer_.get());
   spelling_suggestions_menu_observer_->InitMenu(params_);
 }
@@ -1954,13 +1954,13 @@
 bool RenderViewContextMenu::IsDevCommandEnabled(int id) const {
   if (id == IDC_CONTENT_CONTEXT_INSPECTELEMENT ||
       id == IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE) {
-    if (!GetPrefs(browser_context_)
-             ->GetBoolean(prefs::kWebKitJavascriptEnabled))
+    PrefService* prefs = GetPrefs(browser_context_);
+    if (!prefs->GetBoolean(prefs::kWebKitJavascriptEnabled))
       return false;
 
     // Don't enable the web inspector if the developer tools are disabled via
     // the preference dev-tools-disabled.
-    if (GetPrefs(browser_context_)->GetBoolean(prefs::kDevToolsDisabled))
+    if (prefs->GetBoolean(prefs::kDevToolsDisabled))
       return false;
   }
 
@@ -2033,7 +2033,7 @@
                   url.is_valid() &&
                   ProfileIOData::IsHandledProtocol(url.scheme());
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-      // Do not save the preview PDF on the print preview page.
+  // Do not save the preview PDF on the print preview page.
   can_save = can_save &&
       !(printing::PrintPreviewDialogController::IsPrintPreviewURL(url));
 #endif
@@ -2211,11 +2211,11 @@
           source_web_contents_->GetBrowserContext(),
           render_frame_host->GetSiteInstance());
 
-  std::unique_ptr<DownloadUrlParameters> dl_params(new DownloadUrlParameters(
+  auto dl_params = base::MakeUnique<DownloadUrlParameters>(
       url, render_frame_host->GetProcess()->GetID(),
       render_frame_host->GetRenderViewHost()->GetRoutingID(),
       render_frame_host->GetRoutingID(),
-      storage_partition->GetURLRequestContext(), NO_TRAFFIC_ANNOTATION_YET));
+      storage_partition->GetURLRequestContext(), NO_TRAFFIC_ANNOTATION_YET);
   dl_params->set_referrer(CreateReferrer(url, params_));
   dl_params->set_referrer_encoding(params_.frame_charset);
   dl_params->set_suggested_name(params_.suggested_filename);
diff --git a/chrome/browser/resources/offline_pages/offline_internals.html b/chrome/browser/resources/offline_pages/offline_internals.html
index a90859c..7220e63a 100644
--- a/chrome/browser/resources/offline_pages/offline_internals.html
+++ b/chrome/browser/resources/offline_pages/offline_internals.html
@@ -100,6 +100,10 @@
             placeholder="operations/1234-5678">
         <button id="get-operation">Get Operation</button>
       </div>
+      <div>
+        <input id="download-name" type="text" placeholder="us/page/1234-5678">
+        <button id="download-archive">Download</button>
+      </div>
     </div>
     <div id="prefetch-actions-info" class="dump"></div>
   </body>
diff --git a/chrome/browser/resources/offline_pages/offline_internals.js b/chrome/browser/resources/offline_pages/offline_internals.js
index 705eb4ac..5acddb5 100644
--- a/chrome/browser/resources/offline_pages/offline_internals.js
+++ b/chrome/browser/resources/offline_pages/offline_internals.js
@@ -335,6 +335,9 @@
       browserProxy.getOperation($('operation-name').value)
           .then(setPrefetchResult);
     };
+    $('download-archive').onclick = function() {
+      browserProxy.downloadArchive($('download-name').value);
+    };
     if (!incognito)
       refreshAll();
   }
diff --git a/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js b/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
index 4066134..81d58c11 100644
--- a/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
+++ b/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
@@ -145,6 +145,12 @@
      * @return {!Promise<string>} A string describing the result.
      */
     getOperation: function(name) {},
+
+    /**
+     * Downloads an archive.
+     * @param {string} name Name of archive to download.
+     */
+    downloadArchive: function(name) {},
   };
 
   /**
@@ -229,6 +235,11 @@
     getOperation: function(name) {
       return cr.sendWithPromise('getOperation', name);
     },
+
+    /** @override */
+    downloadArchive: function(name) {
+      chrome.send('downloadArchive', [name]);
+    },
   };
 
   return {
diff --git a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
index 52f3ced..beb4b67 100644
--- a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
@@ -130,35 +130,34 @@
   }
 
   /** @interface */
-  function AboutPageBrowserProxy() {}
-
-  AboutPageBrowserProxy.prototype = {
+  class AboutPageBrowserProxy {
     /**
      * Indicates to the browser that the page is ready.
      */
-    pageReady: function() {},
+    pageReady() {}
 
     /**
      * Request update status from the browser. It results in one or more
      * 'update-status-changed' WebUI events.
      */
-    refreshUpdateStatus: function() {},
+    refreshUpdateStatus() {}
 
     /** Opens the help page. */
-    openHelpPage: function() {},
+    openHelpPage() {}
 
     // <if expr="_google_chrome">
     /**
      * Opens the feedback dialog.
      */
-    openFeedbackDialog: function() {},
+    openFeedbackDialog() {}
+
     // </if>
 
     // <if expr="chromeos">
     /**
      * Checks for available update and applies if it exists.
      */
-    requestUpdate: function() {},
+    requestUpdate() {}
 
     /**
      * Checks for the update with specified version and size and applies over
@@ -170,101 +169,102 @@
      * @param {string} target_version
      * @param {string} target_size
      */
-    requestUpdateOverCellular: function(target_version, target_size) {},
+    requestUpdateOverCellular(target_version, target_size) {}
 
     /**
      * @param {!BrowserChannel} channel
      * @param {boolean} isPowerwashAllowed
      */
-    setChannel: function(channel, isPowerwashAllowed) {},
+    setChannel(channel, isPowerwashAllowed) {}
 
     /** @return {!Promise<!ChannelInfo>} */
-    getChannelInfo: function() {},
+    getChannelInfo() {}
 
     /** @return {!Promise<!VersionInfo>} */
-    getVersionInfo: function() {},
+    getVersionInfo() {}
 
     /** @return {!Promise<?RegulatoryInfo>} */
-    getRegulatoryInfo: function() {},
+    getRegulatoryInfo() {}
+
     // </if>
 
     // <if expr="_google_chrome and is_macosx">
     /**
      * Triggers setting up auto-updates for all users.
      */
-    promoteUpdater: function() {},
+    promoteUpdater() {}
     // </if>
-  };
+  }
 
   /**
    * @implements {settings.AboutPageBrowserProxy}
-   * @constructor
    */
-  function AboutPageBrowserProxyImpl() {}
-  cr.addSingletonGetter(AboutPageBrowserProxyImpl);
-
-  AboutPageBrowserProxyImpl.prototype = {
+  class AboutPageBrowserProxyImpl {
     /** @override */
-    pageReady: function() {
+    pageReady() {
       chrome.send('aboutPageReady');
-    },
+    }
 
     /** @override */
-    refreshUpdateStatus: function() {
+    refreshUpdateStatus() {
       chrome.send('refreshUpdateStatus');
-    },
+    }
 
     // <if expr="_google_chrome and is_macosx">
     /** @override */
-    promoteUpdater: function() {
+    promoteUpdater() {
       chrome.send('promoteUpdater');
-    },
+    }
+
     // </if>
 
     /** @override */
-    openHelpPage: function() {
+    openHelpPage() {
       chrome.send('openHelpPage');
-    },
+    }
 
     // <if expr="_google_chrome">
     /** @override */
-    openFeedbackDialog: function() {
+    openFeedbackDialog() {
       chrome.send('openFeedbackDialog');
-    },
+    }
+
     // </if>
 
     // <if expr="chromeos">
     /** @override */
-    requestUpdate: function() {
+    requestUpdate() {
       chrome.send('requestUpdate');
-    },
+    }
 
     /** @override */
-    requestUpdateOverCellular: function(target_version, target_size) {
+    requestUpdateOverCellular(target_version, target_size) {
       chrome.send('requestUpdateOverCellular', [target_version, target_size]);
-    },
+    }
 
     /** @override */
-    setChannel: function(channel, isPowerwashAllowed) {
+    setChannel(channel, isPowerwashAllowed) {
       chrome.send('setChannel', [channel, isPowerwashAllowed]);
-    },
+    }
 
     /** @override */
-    getChannelInfo: function() {
+    getChannelInfo() {
       return cr.sendWithPromise('getChannelInfo');
-    },
+    }
 
     /** @override */
-    getVersionInfo: function() {
+    getVersionInfo() {
       return cr.sendWithPromise('getVersionInfo');
-    },
+    }
 
     /** @override */
-    getRegulatoryInfo: function() {
+    getRegulatoryInfo() {
       return cr.sendWithPromise('getRegulatoryInfo');
     }
     // </if>
-  };
+  }
+
+  cr.addSingletonGetter(AboutPageBrowserProxyImpl);
 
   return {
     AboutPageBrowserProxy: AboutPageBrowserProxy,
diff --git a/chrome/browser/resources/settings/android_apps_page/android_apps_browser_proxy.js b/chrome/browser/resources/settings/android_apps_page/android_apps_browser_proxy.js
index c5b32bd..6cb17f5f 100644
--- a/chrome/browser/resources/settings/android_apps_page/android_apps_browser_proxy.js
+++ b/chrome/browser/resources/settings/android_apps_page/android_apps_browser_proxy.js
@@ -21,40 +21,35 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function AndroidAppsBrowserProxy() {}
-
-  AndroidAppsBrowserProxy.prototype = {
-    requestAndroidAppsInfo: function() {},
+  class AndroidAppsBrowserProxy {
+    requestAndroidAppsInfo() {}
 
     /**
      * @param {boolean} keyboardAction True if the app was opened using a
      *     keyboard action.
      */
-    showAndroidAppsSettings: function(keyboardAction) {},
-  };
+    showAndroidAppsSettings(keyboardAction) {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.AndroidAppsBrowserProxy}
    */
-  function AndroidAppsBrowserProxyImpl() {}
+  class AndroidAppsBrowserProxyImpl {
+    /** @override */
+    requestAndroidAppsInfo() {
+      chrome.send('requestAndroidAppsInfo');
+    }
+
+    /** @override */
+    showAndroidAppsSettings(keyboardAction) {
+      chrome.send('showAndroidAppsSettings', [keyboardAction]);
+    }
+  }
 
   // The singleton instance_ can be replaced with a test version of this wrapper
   // during testing.
   cr.addSingletonGetter(AndroidAppsBrowserProxyImpl);
 
-  AndroidAppsBrowserProxyImpl.prototype = {
-    /** @override */
-    requestAndroidAppsInfo: function() {
-      chrome.send('requestAndroidAppsInfo');
-    },
-
-    /** @override */
-    showAndroidAppsSettings: function(keyboardAction) {
-      chrome.send('showAndroidAppsSettings', [keyboardAction]);
-    },
-  };
-
   return {
     AndroidAppsBrowserProxy: AndroidAppsBrowserProxy,
     AndroidAppsBrowserProxyImpl: AndroidAppsBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js b/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js
index 5006a3f..779818bd 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js
+++ b/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js
@@ -4,90 +4,89 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function AppearanceBrowserProxy() {}
-
-  AppearanceBrowserProxy.prototype = {
+  class AppearanceBrowserProxy {
     /** @return {!Promise<number>} */
-    getDefaultZoom: assertNotReached,
+    getDefaultZoom() {}
 
     /**
      * @param {string} themeId
      * @return {!Promise<!chrome.management.ExtensionInfo>} Theme info.
      */
-    getThemeInfo: assertNotReached,
+    getThemeInfo(themeId) {}
 
     /** @return {boolean} Whether the current profile is supervised. */
-    isSupervised: assertNotReached,
+    isSupervised() {}
 
     // <if expr="chromeos">
-    openWallpaperManager: assertNotReached,
+    openWallpaperManager() {}
+
     // </if>
 
-    useDefaultTheme: assertNotReached,
+    useDefaultTheme() {}
 
     // <if expr="is_linux and not chromeos">
-    useSystemTheme: assertNotReached,
+    useSystemTheme() {}
+
     // </if>
 
     /**
      * @param {string} url The url of which to check validity.
      * @return {!Promise<boolean>}
      */
-    validateStartupPage: assertNotReached,
-  };
+    validateStartupPage(url) {}
+  }
 
   /**
    * @implements {settings.AppearanceBrowserProxy}
-   * @constructor
    */
-  function AppearanceBrowserProxyImpl() {}
-
-  cr.addSingletonGetter(AppearanceBrowserProxyImpl);
-
-  AppearanceBrowserProxyImpl.prototype = {
+  class AppearanceBrowserProxyImpl {
     /** @override */
-    getDefaultZoom: function() {
+    getDefaultZoom() {
       return new Promise(function(resolve) {
         chrome.settingsPrivate.getDefaultZoom(resolve);
       });
-    },
+    }
 
     /** @override */
-    getThemeInfo: function(themeId) {
+    getThemeInfo(themeId) {
       return new Promise(function(resolve) {
         chrome.management.get(themeId, resolve);
       });
-    },
+    }
 
     /** @override */
-    isSupervised: function() {
+    isSupervised() {
       return loadTimeData.getBoolean('isSupervised');
-    },
+    }
 
     // <if expr="chromeos">
     /** @override */
-    openWallpaperManager: function() {
+    openWallpaperManager() {
       chrome.send('openWallpaperManager');
-    },
+    }
+
     // </if>
 
     /** @override */
-    useDefaultTheme: function() {
+    useDefaultTheme() {
       chrome.send('useDefaultTheme');
-    },
+    }
 
     // <if expr="is_linux and not chromeos">
     /** @override */
-    useSystemTheme: function() {
+    useSystemTheme() {
       chrome.send('useSystemTheme');
-    },
+    }
+
     // </if>
 
     /** @override */
-    validateStartupPage: function(url) {
+    validateStartupPage(url) {
       return cr.sendWithPromise('validateStartupPage', url);
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(AppearanceBrowserProxyImpl);
 
   return {
     AppearanceBrowserProxy: AppearanceBrowserProxy,
diff --git a/chrome/browser/resources/settings/appearance_page/fonts_browser_proxy.js b/chrome/browser/resources/settings/appearance_page/fonts_browser_proxy.js
index b8b1263..d513b3ad 100644
--- a/chrome/browser/resources/settings/appearance_page/fonts_browser_proxy.js
+++ b/chrome/browser/resources/settings/appearance_page/fonts_browser_proxy.js
@@ -12,44 +12,39 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function FontsBrowserProxy() {}
-
-  FontsBrowserProxy.prototype = {
+  class FontsBrowserProxy {
     /**
      * @return {!Promise<!FontsData>} Fonts and the advanced font settings
      *     extension URL.
      */
-    fetchFontsData: assertNotReached,
+    fetchFontsData() {}
 
-    observeAdvancedFontExtensionAvailable: assertNotReached,
+    observeAdvancedFontExtensionAvailable() {}
 
-    openAdvancedFontSettings: assertNotReached,
-  };
+    openAdvancedFontSettings() {}
+  }
 
   /**
    * @implements {settings.FontsBrowserProxy}
-   * @constructor
    */
-  function FontsBrowserProxyImpl() {}
-
-  cr.addSingletonGetter(FontsBrowserProxyImpl);
-
-  FontsBrowserProxyImpl.prototype = {
+  class FontsBrowserProxyImpl {
     /** @override */
-    fetchFontsData: function() {
+    fetchFontsData() {
       return cr.sendWithPromise('fetchFontsData');
-    },
+    }
 
     /** @override */
-    observeAdvancedFontExtensionAvailable: function() {
+    observeAdvancedFontExtensionAvailable() {
       chrome.send('observeAdvancedFontExtensionAvailable');
-    },
+    }
 
     /** @override */
-    openAdvancedFontSettings: function() {
+    openAdvancedFontSettings() {
       chrome.send('openAdvancedFontSettings');
     }
-  };
+  }
+
+  cr.addSingletonGetter(FontsBrowserProxyImpl);
 
   return {
     FontsBrowserProxy: FontsBrowserProxy,
diff --git a/chrome/browser/resources/settings/certificate_manager_page/certificates_browser_proxy.js b/chrome/browser/resources/settings/certificate_manager_page/certificates_browser_proxy.js
index eb89020..3501b77 100644
--- a/chrome/browser/resources/settings/certificate_manager_page/certificates_browser_proxy.js
+++ b/chrome/browser/resources/settings/certificate_manager_page/certificates_browser_proxy.js
@@ -83,36 +83,33 @@
 var CertificatesImportError;
 
 cr.define('settings', function() {
-
   /** @interface */
-  function CertificatesBrowserProxy() {}
-
-  CertificatesBrowserProxy.prototype = {
+  class CertificatesBrowserProxy {
     /**
      * Triggers 5 events in the following order
      * 1x 'certificates-model-ready' event.
      * 4x 'certificates-changed' event, one for each certificate category.
      */
-    refreshCertificates: function() {},
+    refreshCertificates() {}
 
     /** @param {string} id */
-    viewCertificate: function(id) {},
+    viewCertificate(id) {}
 
     /** @param {string} id */
-    exportCertificate: function(id) {},
+    exportCertificate(id) {}
 
     /**
      * @param {string} id
      * @return {!Promise} A promise resolved when the certificate has been
      *     deleted successfully or rejected with a CertificatesError.
      */
-    deleteCertificate: function(id) {},
+    deleteCertificate(id) {}
 
     /**
      * @param {string} id
      * @return {!Promise<!CaTrustInfo>}
      */
-    getCaCertificateTrust: function(id) {},
+    getCaCertificateTrust(id) {}
 
     /**
      * @param {string} id
@@ -121,9 +118,9 @@
      * @param {boolean} objSign
      * @return {!Promise}
      */
-    editCaCertificateTrust: function(id, ssl, email, objSign) {},
+    editCaCertificateTrust(id, ssl, email, objSign) {}
 
-    cancelImportExportCertificate: function() {},
+    cancelImportExportCertificate() {}
 
     /**
      * @param {string} id
@@ -133,13 +130,13 @@
      *     passed back via a call to
      *     exportPersonalCertificatePasswordSelected().
      */
-    exportPersonalCertificate: function(id) {},
+    exportPersonalCertificate(id) {}
 
     /**
      * @param {string} password
      * @return {!Promise}
      */
-    exportPersonalCertificatePasswordSelected: function(password) {},
+    exportPersonalCertificatePasswordSelected(password) {}
 
     /**
      * @param {boolean} useHardwareBacked
@@ -148,13 +145,13 @@
      *     the user, and the password should be passed back via a call to
      *     importPersonalCertificatePasswordSelected().
      */
-    importPersonalCertificate: function(useHardwareBacked) {},
+    importPersonalCertificate(useHardwareBacked) {}
 
     /**
      * @param {string} password
      * @return {!Promise}
      */
-    importPersonalCertificatePasswordSelected: function(password) {},
+    importPersonalCertificatePasswordSelected(password) {}
 
     /**
      * @return {!Promise} A promise firing once the user has selected
@@ -163,7 +160,7 @@
      *     trust levels, and that information should be passed back via a call
      *     to importCaCertificateTrustSelected().
      */
-    importCaCertificate: function() {},
+    importCaCertificate() {}
 
     /**
      * @param {boolean} ssl
@@ -174,101 +171,99 @@
      *     error occurred with either a CertificatesError or
      *     CertificatesImportError.
      */
-    importCaCertificateTrustSelected: function(ssl, email, objSign) {},
+    importCaCertificateTrustSelected(ssl, email, objSign) {}
 
     /**
      * @return {!Promise} A promise firing once the certificate has been
      *     imported. The promise is rejected if an error occurred, with either
      *     a CertificatesError or CertificatesImportError.
      */
-    importServerCertificate: function() {},
-  };
+    importServerCertificate() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.CertificatesBrowserProxy}
    */
-  function CertificatesBrowserProxyImpl() {}
+  class CertificatesBrowserProxyImpl {
+    /** @override */
+    refreshCertificates() {
+      chrome.send('refreshCertificates');
+    }
+
+    /** @override */
+    viewCertificate(id) {
+      chrome.send('viewCertificate', [id]);
+    }
+
+    /** @override */
+    exportCertificate(id) {
+      chrome.send('exportCertificate', [id]);
+    }
+
+    /** @override */
+    deleteCertificate(id) {
+      return cr.sendWithPromise('deleteCertificate', id);
+    }
+
+    /** @override */
+    exportPersonalCertificate(id) {
+      return cr.sendWithPromise('exportPersonalCertificate', id);
+    }
+
+    /** @override */
+    exportPersonalCertificatePasswordSelected(password) {
+      return cr.sendWithPromise(
+          'exportPersonalCertificatePasswordSelected', password);
+    }
+
+    /** @override */
+    importPersonalCertificate(useHardwareBacked) {
+      return cr.sendWithPromise('importPersonalCertificate', useHardwareBacked);
+    }
+
+    /** @override */
+    importPersonalCertificatePasswordSelected(password) {
+      return cr.sendWithPromise(
+          'importPersonalCertificatePasswordSelected', password);
+    }
+
+    /** @override */
+    getCaCertificateTrust(id) {
+      return cr.sendWithPromise('getCaCertificateTrust', id);
+    }
+
+    /** @override */
+    editCaCertificateTrust(id, ssl, email, objSign) {
+      return cr.sendWithPromise(
+          'editCaCertificateTrust', id, ssl, email, objSign);
+    }
+
+    /** @override */
+    importCaCertificateTrustSelected(ssl, email, objSign) {
+      return cr.sendWithPromise(
+          'importCaCertificateTrustSelected', ssl, email, objSign);
+    }
+
+    /** @override */
+    cancelImportExportCertificate() {
+      chrome.send('cancelImportExportCertificate');
+    }
+
+    /** @override */
+    importCaCertificate() {
+      return cr.sendWithPromise('importCaCertificate');
+    }
+
+    /** @override */
+    importServerCertificate() {
+      return cr.sendWithPromise('importServerCertificate');
+    }
+  }
+
   // The singleton instance_ is replaced with a test version of this wrapper
   // during testing.
   cr.addSingletonGetter(CertificatesBrowserProxyImpl);
 
-  CertificatesBrowserProxyImpl.prototype = {
-    /** @override */
-    refreshCertificates: function() {
-      chrome.send('refreshCertificates');
-    },
-
-    /** @override */
-    viewCertificate: function(id) {
-      chrome.send('viewCertificate', [id]);
-    },
-
-    /** @override */
-    exportCertificate: function(id) {
-      chrome.send('exportCertificate', [id]);
-    },
-
-    /** @override */
-    deleteCertificate: function(id) {
-      return cr.sendWithPromise('deleteCertificate', id);
-    },
-
-    /** @override */
-    exportPersonalCertificate: function(id) {
-      return cr.sendWithPromise('exportPersonalCertificate', id);
-    },
-
-    /** @override */
-    exportPersonalCertificatePasswordSelected: function(password) {
-      return cr.sendWithPromise(
-          'exportPersonalCertificatePasswordSelected', password);
-    },
-
-    /** @override */
-    importPersonalCertificate: function(useHardwareBacked) {
-      return cr.sendWithPromise('importPersonalCertificate', useHardwareBacked);
-    },
-
-    /** @override */
-    importPersonalCertificatePasswordSelected: function(password) {
-      return cr.sendWithPromise(
-          'importPersonalCertificatePasswordSelected', password);
-    },
-
-    /** @override */
-    getCaCertificateTrust: function(id) {
-      return cr.sendWithPromise('getCaCertificateTrust', id);
-    },
-
-    /** @override */
-    editCaCertificateTrust: function(id, ssl, email, objSign) {
-      return cr.sendWithPromise(
-          'editCaCertificateTrust', id, ssl, email, objSign);
-    },
-
-    /** @override */
-    importCaCertificateTrustSelected: function(ssl, email, objSign) {
-      return cr.sendWithPromise(
-          'importCaCertificateTrustSelected', ssl, email, objSign);
-    },
-
-    /** @override */
-    cancelImportExportCertificate: function() {
-      chrome.send('cancelImportExportCertificate');
-    },
-
-    /** @override */
-    importCaCertificate: function() {
-      return cr.sendWithPromise('importCaCertificate');
-    },
-
-    /** @override */
-    importServerCertificate: function() {
-      return cr.sendWithPromise('importServerCertificate');
-    },
-  };
-
   return {
     CertificatesBrowserProxy: CertificatesBrowserProxy,
     CertificatesBrowserProxyImpl: CertificatesBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_proxy.js b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_proxy.js
index 7edbab00..b032a5cd 100644
--- a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_proxy.js
+++ b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_proxy.js
@@ -4,61 +4,56 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function ChromeCleanupProxy() {}
-
-  ChromeCleanupProxy.prototype = {
+  class ChromeCleanupProxy {
     /**
      * Registers the current ChromeCleanupHandler as an observer of
      * ChromeCleanerController events.
      */
-    registerChromeCleanerObserver: assertNotReached,
+    registerChromeCleanerObserver() {}
 
     /**
      * Starts a cleanup on the user's computer.
      */
-    startCleanup: assertNotReached,
+    startCleanup() {}
 
     /**
      * Restarts the user's computer.
      */
-    restartComputer: assertNotReached,
+    restartComputer() {}
 
     /**
      * Hides the Cleanup page from the settings menu.
      */
-    dismissCleanupPage: assertNotReached,
-  };
+    dismissCleanupPage() {}
+  }
 
   /**
    * @implements {settings.ChromeCleanupProxy}
-   * @constructor
    */
-  function ChromeCleanupProxyImpl() {}
+  class ChromeCleanupProxyImpl {
+    /** @override */
+    registerChromeCleanerObserver() {
+      chrome.send('registerChromeCleanerObserver');
+    }
+
+    /** @override */
+    startCleanup() {
+      chrome.send('startCleanup');
+    }
+
+    /** @override */
+    restartComputer() {
+      chrome.send('restartComputer');
+    }
+
+    /** @override */
+    dismissCleanupPage() {
+      chrome.send('dismissCleanupPage');
+    }
+  }
 
   cr.addSingletonGetter(ChromeCleanupProxyImpl);
 
-  ChromeCleanupProxyImpl.prototype = {
-    /** @override */
-    registerChromeCleanerObserver: function() {
-      chrome.send('registerChromeCleanerObserver');
-    },
-
-    /** @override */
-    startCleanup: function() {
-      chrome.send('startCleanup');
-    },
-
-    /** @override */
-    restartComputer: function() {
-      chrome.send('restartComputer');
-    },
-
-    /** @override */
-    dismissCleanupPage: function() {
-      chrome.send('dismissCleanupPage');
-    },
-  };
-
   return {
     ChromeCleanupProxy: ChromeCleanupProxy,
     ChromeCleanupProxyImpl: ChromeCleanupProxyImpl,
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.js b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.js
index 20e2ffe8..24bdd364 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.js
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.js
@@ -26,52 +26,48 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function ClearBrowsingDataBrowserProxy() {}
-
-  ClearBrowsingDataBrowserProxy.prototype = {
+  class ClearBrowsingDataBrowserProxy {
     /**
      * @param {!Array<!ImportantSite>} importantSites
      * @return {!Promise<void>}
      *     A promise resolved when data clearing has completed.
      */
-    clearBrowsingData: function(importantSites) {},
+    clearBrowsingData(importantSites) {}
 
     /**
      * @return {!Promise<!Array<!ImportantSite>>}
      *     A promise resolved when imporant sites are retrieved.
      */
-    getImportantSites: function() {},
+    getImportantSites() {}
 
     /**
      * Kick off counter updates and return initial state.
      * @return {!Promise<void>} Signal when the setup is complete.
      */
-    initialize: function() {},
-  };
+    initialize() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.ClearBrowsingDataBrowserProxy}
    */
-  function ClearBrowsingDataBrowserProxyImpl() {}
-  cr.addSingletonGetter(ClearBrowsingDataBrowserProxyImpl);
-
-  ClearBrowsingDataBrowserProxyImpl.prototype = {
+  class ClearBrowsingDataBrowserProxyImpl {
     /** @override */
-    clearBrowsingData: function(importantSites) {
+    clearBrowsingData(importantSites) {
       return cr.sendWithPromise('clearBrowsingData', importantSites);
-    },
+    }
 
     /** @override */
-    getImportantSites: function() {
+    getImportantSites() {
       return cr.sendWithPromise('getImportantSites');
-    },
+    }
 
     /** @override */
-    initialize: function() {
+    initialize() {
       return cr.sendWithPromise('initializeClearBrowsingData');
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(ClearBrowsingDataBrowserProxyImpl);
 
   return {
     ClearBrowsingDataBrowserProxy: ClearBrowsingDataBrowserProxy,
diff --git a/chrome/browser/resources/settings/default_browser_page/default_browser_browser_proxy.js b/chrome/browser/resources/settings/default_browser_page/default_browser_browser_proxy.js
index e4f34da..bb8703a 100644
--- a/chrome/browser/resources/settings/default_browser_page/default_browser_browser_proxy.js
+++ b/chrome/browser/resources/settings/default_browser_page/default_browser_browser_proxy.js
@@ -19,42 +19,38 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function DefaultBrowserBrowserProxy() {}
-
-  DefaultBrowserBrowserProxy.prototype = {
+  class DefaultBrowserBrowserProxy {
     /**
      * Get the initial DefaultBrowserInfo and begin sending updates to
      * 'settings.updateDefaultBrowserState'.
      * @return {!Promise<DefaultBrowserInfo>}
      */
-    requestDefaultBrowserState: function() {},
+    requestDefaultBrowserState() {}
 
     /*
      * Try to set the current browser as the default browser. The new status of
      * the settings will be sent to 'settings.updateDefaultBrowserState'.
      */
-    setAsDefaultBrowser: function() {},
-  };
+    setAsDefaultBrowser() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.DefaultBrowserBrowserProxy}
    */
-  function DefaultBrowserBrowserProxyImpl() {}
-  cr.addSingletonGetter(DefaultBrowserBrowserProxyImpl);
-
-  DefaultBrowserBrowserProxyImpl.prototype = {
+  class DefaultBrowserBrowserProxyImpl {
     /** @override */
-    requestDefaultBrowserState: function() {
+    requestDefaultBrowserState() {
       return cr.sendWithPromise(
           'SettingsDefaultBrowser.requestDefaultBrowserState');
-    },
+    }
 
     /** @override */
-    setAsDefaultBrowser: function() {
+    setAsDefaultBrowser() {
       chrome.send('SettingsDefaultBrowser.setAsDefaultBrowser');
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(DefaultBrowserBrowserProxyImpl);
 
   return {
     DefaultBrowserBrowserProxy: DefaultBrowserBrowserProxy,
diff --git a/chrome/browser/resources/settings/device_page/compiled_resources2.gyp b/chrome/browser/resources/settings/device_page/compiled_resources2.gyp
index f93da31..97ba452 100644
--- a/chrome/browser/resources/settings/device_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/device_page/compiled_resources2.gyp
@@ -118,6 +118,7 @@
       'dependencies': [
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior',
+        '<(EXTERNS_GYP):settings_private',
         '../compiled_resources2.gyp:route',
         '../prefs/compiled_resources2.gyp:prefs_types',
         'device_page_browser_proxy'
diff --git a/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js b/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
index 5e63b92..d84e6c06 100644
--- a/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
@@ -76,161 +76,157 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function DevicePageBrowserProxy() {}
-
-  DevicePageBrowserProxy.prototype = {
+  class DevicePageBrowserProxy {
     /** Initializes the mouse and touchpad handler. */
-    initializePointers: function() {},
+    initializePointers() {}
 
     /** Initializes the stylus handler. */
-    initializeStylus: function() {},
+    initializeStylus() {}
 
     /**
      * Override to interact with the on-tap/on-keydown event on the Learn More
      * link.
      * @param {!Event} e
      */
-    handleLinkEvent: function(e) {},
+    handleLinkEvent(e) {}
 
     /** Initializes the keyboard WebUI handler. */
-    initializeKeyboard: function() {},
+    initializeKeyboard() {}
 
     /** Shows the Ash keyboard shortcuts overlay. */
-    showKeyboardShortcutsOverlay: function() {},
+    showKeyboardShortcutsOverlay() {}
 
     /** Requests a power status update. */
-    updatePowerStatus: function() {},
+    updatePowerStatus() {}
 
     /**
      * Sets the ID of the power source to use.
      * @param {string} powerSourceId ID of the power source. '' denotes the
      *     battery (no external power source).
      */
-    setPowerSource: function(powerSourceId) {},
+    setPowerSource(powerSourceId) {}
 
     /** Requests the current power management settings. */
-    requestPowerManagementSettings: function() {},
+    requestPowerManagementSettings() {}
 
     /**
      * Sets the idle power management behavior.
      * @param {settings.IdleBehavior} behavior Idle behavior.
      */
-    setIdleBehavior: function(behavior) {},
+    setIdleBehavior(behavior) {}
 
     /**
      * Sets the lid-closed power management behavior.
      * @param {settings.LidClosedBehavior} behavior Lid-closed behavior.
      */
-    setLidClosedBehavior: function(behavior) {},
+    setLidClosedBehavior(behavior) {}
 
     /**
      * |callback| is run when there is new note-taking app information
      * available or after |requestNoteTakingApps| has been called.
      * @param {function(Array<settings.NoteAppInfo>, boolean):void} callback
      */
-    setNoteTakingAppsUpdatedCallback: function(callback) {},
+    setNoteTakingAppsUpdatedCallback(callback) {}
 
     /**
      * Open up the play store with the given URL.
      * @param {string} url
      */
-    showPlayStore: function(url) {},
+    showPlayStore(url) {}
 
     /**
      * Request current note-taking app info. Invokes any callback registered in
      * |onNoteTakingAppsUpdated|.
      */
-    requestNoteTakingApps: function() {},
+    requestNoteTakingApps() {}
 
     /**
      * Changes the preferred note taking app.
      * @param {string} appId The app id. This should be a value retrieved from a
      *     |onNoteTakingAppsUpdated| callback.
      */
-    setPreferredNoteTakingApp: function(appId) {},
-  };
+    setPreferredNoteTakingApp(appId) {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.DevicePageBrowserProxy}
    */
-  function DevicePageBrowserProxyImpl() {}
-  cr.addSingletonGetter(DevicePageBrowserProxyImpl);
-
-  DevicePageBrowserProxyImpl.prototype = {
+  class DevicePageBrowserProxyImpl {
     /** @override */
-    initializePointers: function() {
+    initializePointers() {
       chrome.send('initializePointerSettings');
-    },
+    }
 
     /** @override */
-    initializeStylus: function() {
+    initializeStylus() {
       chrome.send('initializeStylusSettings');
-    },
+    }
 
     /** override */
-    handleLinkEvent: function(e) {
+    handleLinkEvent(e) {
       // Prevent the link from activating its parent element when tapped or
       // when Enter is pressed.
       if (e.type != 'keydown' || e.keyCode == 13)
         e.stopPropagation();
-    },
+    }
 
     /** @override */
-    initializeKeyboard: function() {
+    initializeKeyboard() {
       chrome.send('initializeKeyboardSettings');
-    },
+    }
 
     /** @override */
-    showKeyboardShortcutsOverlay: function() {
+    showKeyboardShortcutsOverlay() {
       chrome.send('showKeyboardShortcutsOverlay');
-    },
+    }
 
     /** @override */
-    updatePowerStatus: function() {
+    updatePowerStatus() {
       chrome.send('updatePowerStatus');
-    },
+    }
 
     /** @override */
-    setPowerSource: function(powerSourceId) {
+    setPowerSource(powerSourceId) {
       chrome.send('setPowerSource', [powerSourceId]);
-    },
+    }
 
     /** @override */
-    requestPowerManagementSettings: function() {
+    requestPowerManagementSettings() {
       chrome.send('requestPowerManagementSettings');
-    },
+    }
 
     /** @override */
-    setIdleBehavior: function(behavior) {
+    setIdleBehavior(behavior) {
       chrome.send('setIdleBehavior', [behavior]);
-    },
+    }
 
     /** @override */
-    setLidClosedBehavior: function(behavior) {
+    setLidClosedBehavior(behavior) {
       chrome.send('setLidClosedBehavior', [behavior]);
-    },
+    }
 
     /** @override */
-    setNoteTakingAppsUpdatedCallback: function(callback) {
+    setNoteTakingAppsUpdatedCallback(callback) {
       cr.addWebUIListener('onNoteTakingAppsUpdated', callback);
-    },
+    }
 
     /** @override */
-    showPlayStore: function(url) {
+    showPlayStore(url) {
       chrome.send('showPlayStoreApps', [url]);
-    },
+    }
 
     /** @override */
-    requestNoteTakingApps: function() {
+    requestNoteTakingApps() {
       chrome.send('requestNoteTakingApps');
-    },
+    }
 
     /** @override */
-    setPreferredNoteTakingApp: function(appId) {
+    setPreferredNoteTakingApp(appId) {
       chrome.send('setPreferredNoteTakingApp', [appId]);
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(DevicePageBrowserProxyImpl);
 
   return {
     DevicePageBrowserProxy: DevicePageBrowserProxy,
diff --git a/chrome/browser/resources/settings/device_page/power.html b/chrome/browser/resources/settings/device_page/power.html
index f007de7e..f0ee3e45 100644
--- a/chrome/browser/resources/settings/device_page/power.html
+++ b/chrome/browser/resources/settings/device_page/power.html
@@ -5,6 +5,7 @@
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="../controls/settings_toggle_button.html">
 <link rel="import" href="../route.html">
 <link rel="import" href="../settings_shared_css.html">
 
@@ -59,24 +60,11 @@
 
     <div id="lidClosedRow" class="settings-box continuation"
         hidden$="[[!hasLid_]]">
-      <div class="start">$i18n{powerLidClosedLabel}</div>
-      <template is="dom-if" if="[[lidClosedControlled_]]" restamp>
-        <cr-policy-indicator id="lidClosedControlledIndicator"
-            indicator-type="devicePolicy"
-            icon-aria-label="$i18n{powerLidClosedLabel}">
-        </cr-policy-indicator>
-      </template>
-      <div class="md-select-wrapper">
-        <select id="lidClosedSelect" class="md-select"
-            on-change="onLidClosedSelectChange_"
-            disabled="[[lidClosedControlled_]]"
-            aria-label="$i18n{powerLidClosedLabel}">
-          <template is="dom-repeat" items="[[lidClosedOptions_]]">
-            <option value="[[item.value]]">[[item.name]]</option>
-          </template>
-        </select>
-        <span class="md-select-underline"></span>
-      </div>
+      <settings-toggle-button class="start" id="lidClosedToggle"
+          pref="[[lidClosedPref_]]" label="[[lidClosedLabel_]]"
+          on-settings-boolean-control-change="onLidClosedToggleChange_"
+          no-set-pref>
+      </settings-toggle-button>
     </div>
   </template>
   <script src="power.js"></script>
diff --git a/chrome/browser/resources/settings/device_page/power.js b/chrome/browser/resources/settings/device_page/power.js
index 61532061..842ab2dc 100644
--- a/chrome/browser/resources/settings/device_page/power.js
+++ b/chrome/browser/resources/settings/device_page/power.js
@@ -30,10 +30,8 @@
     /** @private {boolean} Whether the idle behavior is controlled by policy. */
     idleControlled_: Boolean,
 
-    /** @private {boolean} Whether the lid-closed behavior is controlled by
-     * policy.
-     */
-    lidClosedControlled_: Boolean,
+    /** @private {string} Text for label describing the lid-closed behavior. */
+    lidClosedLabel_: String,
 
     /** @private {boolean} Whether the system posesses a lid. */
     hasLid_: Boolean,
@@ -73,10 +71,12 @@
       computed: 'computeIdleOptions_(idleControlled_)',
     },
 
-    /** @private */
-    lidClosedOptions_: {
-      type: Array,
-      computed: 'computeLidClosedOptions_(lidClosedControlled_)',
+    /** @private {!chrome.settingsPrivate.PrefObject} */
+    lidClosedPref_: {
+      type: Object,
+      value: function() {
+        return /** @type {!chrome.settingsPrivate.PrefObject} */ ({});
+      },
     },
   },
 
@@ -172,36 +172,6 @@
     return options;
   },
 
-  /**
-   * @param {boolean} lidClosedControlled
-   * @return {!Array<!{value: settings.LidClosedBehavior, name: string}>}
-   *     Options to display in lid-closed-behavior select.
-   * @private
-   */
-  computeLidClosedOptions_: function(lidClosedControlled) {
-    var options = [
-      {
-        value: settings.LidClosedBehavior.SUSPEND,
-        name: loadTimeData.getString('powerLidClosedSleep'),
-      },
-      {
-        value: settings.LidClosedBehavior.DO_NOTHING,
-        name: loadTimeData.getString('powerLidClosedStayAwake'),
-      },
-    ];
-    if (lidClosedControlled) {
-      // Some options are only settable via policy.
-      options.push({
-        value: settings.LidClosedBehavior.STOP_SESSION,
-        name: loadTimeData.getString('powerLidClosedSignOut'),
-      }, {
-        value: settings.LidClosedBehavior.SHUT_DOWN,
-        name: loadTimeData.getString('powerLidClosedShutDown'),
-      });
-    }
-    return options;
-  },
-
   /** @private */
   onPowerSourceChange_: function() {
     settings.DevicePageBrowserProxyImpl.getInstance().setPowerSource(
@@ -216,11 +186,12 @@
   },
 
   /** @private */
-  onLidClosedSelectChange_: function() {
-    var behavior = /** @type {settings.LidClosedBehavior} */
-        (parseInt(this.$.lidClosedSelect.value, 10));
+  onLidClosedToggleChange_: function() {
+    // Other behaviors are only displayed when the setting is controlled, in
+    // which case the toggle can't be changed by the user.
     settings.DevicePageBrowserProxyImpl.getInstance().setLidClosedBehavior(
-        behavior);
+        this.$.lidClosedToggle.checked ? settings.LidClosedBehavior.SUSPEND :
+                                         settings.LidClosedBehavior.DO_NOTHING);
   },
 
   /**
@@ -237,21 +208,58 @@
   },
 
   /**
-   * @param {!settings.PowerManagementSettings} settings Current power
+   * @param {settings.LidClosedBehavior} behavior Current behavior.
+   * @param {boolean} isControlled Whether the underlying pref is controlled.
+   * @private
+   */
+  updateLidClosedLabelAndPref_: function(behavior, isControlled) {
+    var pref = {
+      key: '',
+      type: chrome.settingsPrivate.PrefType.BOOLEAN,
+      // Most behaviors get a dedicated label and appear as checked.
+      value: true,
+    };
+
+    switch (behavior) {
+      case settings.LidClosedBehavior.SUSPEND:
+      case settings.LidClosedBehavior.DO_NOTHING:
+        // "Suspend" and "do nothing" share the "sleep" label and communicate
+        // their state via the toggle state.
+        this.lidClosedLabel_ = loadTimeData.getString('powerLidSleepLabel');
+        pref.value = behavior == settings.LidClosedBehavior.SUSPEND;
+        break;
+      case settings.LidClosedBehavior.STOP_SESSION:
+        this.lidClosedLabel_ = loadTimeData.getString('powerLidSignOutLabel');
+        break;
+      case settings.LidClosedBehavior.SHUT_DOWN:
+        this.lidClosedLabel_ = loadTimeData.getString('powerLidShutDownLabel');
+        break;
+    }
+
+    if (isControlled) {
+      pref.enforcement = chrome.settingsPrivate.Enforcement.ENFORCED;
+      pref.controlledBy = chrome.settingsPrivate.ControlledBy.USER_POLICY;
+    }
+
+    this.lidClosedPref_ = pref;
+  },
+
+  /**
+   * @param {!settings.PowerManagementSettings} browserSettings Current power
    *     management settings.
    * @private
    */
-  powerManagementSettingsChanged_: function(settings) {
-    this.idleControlled_ = settings.idleControlled;
-    this.lidClosedControlled_ = settings.lidClosedControlled;
-    this.hasLid_ = settings.hasLid;
+  powerManagementSettingsChanged_: function(browserSettings) {
+    this.idleControlled_ = browserSettings.idleControlled;
+    this.hasLid_ = browserSettings.hasLid;
+    this.updateLidClosedLabelAndPref_(
+        browserSettings.lidClosedBehavior, browserSettings.lidClosedControlled);
 
-    // The select elements include "Other" options when controlled but omit them
-    // otherwise. Make sure that the options are there before we potentially try
-    // to select them.
+    // The idle behavior select element includes an "Other" option when
+    // controlled but omits it otherwise. Make sure that the option is there
+    // before we potentially try to select it.
     this.async(function() {
-      this.$.idleSelect.value = settings.idleBehavior;
-      this.$.lidClosedSelect.value = settings.lidClosedBehavior;
+      this.$.idleSelect.value = browserSettings.idleBehavior;
     });
   },
 
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js b/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js
index 2f510a1..88c41db 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js
+++ b/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js
@@ -4,41 +4,34 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function DownloadsBrowserProxy() {}
-
-  DownloadsBrowserProxy.prototype = {
-    initializeDownloads: assertNotReached,
-
-    selectDownloadLocation: assertNotReached,
-
-    resetAutoOpenFileTypes: assertNotReached,
-  };
+  class DownloadsBrowserProxy {
+    initializeDownloads() {}
+    selectDownloadLocation() {}
+    resetAutoOpenFileTypes() {}
+  }
 
   /**
    * @implements {settings.DownloadsBrowserProxy}
-   * @constructor
    */
-  function DownloadsBrowserProxyImpl() {}
+  class DownloadsBrowserProxyImpl {
+    /** @override */
+    initializeDownloads() {
+      chrome.send('initializeDownloads');
+    }
+
+    /** @override */
+    selectDownloadLocation() {
+      chrome.send('selectDownloadLocation');
+    }
+
+    /** @override */
+    resetAutoOpenFileTypes() {
+      chrome.send('resetAutoOpenFileTypes');
+    }
+  }
 
   cr.addSingletonGetter(DownloadsBrowserProxyImpl);
 
-  DownloadsBrowserProxyImpl.prototype = {
-    /** @override */
-    initializeDownloads: function() {
-      chrome.send('initializeDownloads');
-    },
-
-    /** @override */
-    selectDownloadLocation: function() {
-      chrome.send('selectDownloadLocation');
-    },
-
-    /** @override */
-    resetAutoOpenFileTypes: function() {
-      chrome.send('resetAutoOpenFileTypes');
-    },
-  };
-
   return {
     DownloadsBrowserProxy: DownloadsBrowserProxy,
     DownloadsBrowserProxyImpl: DownloadsBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/extension_control_browser_proxy.js b/chrome/browser/resources/settings/extension_control_browser_proxy.js
index adbbd79..84b350e 100644
--- a/chrome/browser/resources/settings/extension_control_browser_proxy.js
+++ b/chrome/browser/resources/settings/extension_control_browser_proxy.js
@@ -4,36 +4,32 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function ExtensionControlBrowserProxy() {}
-
-  ExtensionControlBrowserProxy.prototype = {
+  class ExtensionControlBrowserProxy {
     // TODO(dbeam): should be be returning !Promise<boolean> to indicate whether
     // it succeeded?
     /** @param {string} extensionId */
-    disableExtension: assertNotReached,
+    disableExtension(extensionId) {}
 
     /** @param {string} extensionId */
-    manageExtension: assertNotReached,
-  };
+    manageExtension(extensionId) {}
+  }
 
   /**
    * @implements {settings.ExtensionControlBrowserProxy}
-   * @constructor
    */
-  function ExtensionControlBrowserProxyImpl() {}
-  cr.addSingletonGetter(ExtensionControlBrowserProxyImpl);
-
-  ExtensionControlBrowserProxyImpl.prototype = {
+  class ExtensionControlBrowserProxyImpl {
     /** @override */
-    disableExtension: function(extensionId) {
+    disableExtension(extensionId) {
       chrome.send('disableExtension', [extensionId]);
-    },
+    }
 
     /** @override */
-    manageExtension: function(extensionId) {
+    manageExtension(extensionId) {
       window.open('chrome://extensions?id=' + extensionId);
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(ExtensionControlBrowserProxyImpl);
 
   return {
     ExtensionControlBrowserProxy: ExtensionControlBrowserProxy,
diff --git a/chrome/browser/resources/settings/languages_page/languages_browser_proxy.js b/chrome/browser/resources/settings/languages_page/languages_browser_proxy.js
index 5edc03f..e23dd250 100644
--- a/chrome/browser/resources/settings/languages_page/languages_browser_proxy.js
+++ b/chrome/browser/resources/settings/languages_page/languages_browser_proxy.js
@@ -9,65 +9,63 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function LanguagesBrowserProxy() {}
-
-  LanguagesBrowserProxy.prototype = {
+  class LanguagesBrowserProxy {
     // <if expr="chromeos or is_win">
     /**
      * Sets the prospective UI language to the chosen language. This won't
      * affect the actual UI language until a restart.
      * @param {string} languageCode
      */
-    setProspectiveUILanguage: function(languageCode) {},
+    setProspectiveUILanguage(languageCode) {}
 
     /** @return {!Promise<string>} */
-    getProspectiveUILanguage: function() {},
+    getProspectiveUILanguage() {}
+
     // </if>
 
     /** @return {!LanguageSettingsPrivate} */
-    getLanguageSettingsPrivate: function() {},
+    getLanguageSettingsPrivate() {}
 
     // <if expr="chromeos">
     /** @return {!InputMethodPrivate} */
-    getInputMethodPrivate: function() {},
+    getInputMethodPrivate() {}
     // </if>
-  };
+  }
 
   /**
-   * @constructor
    * @implements {settings.LanguagesBrowserProxy}
    */
-  function LanguagesBrowserProxyImpl() {}
-  // The singleton instance_ is replaced with a test version of this wrapper
-  // during testing.
-  cr.addSingletonGetter(LanguagesBrowserProxyImpl);
-
-  LanguagesBrowserProxyImpl.prototype = {
+  class LanguagesBrowserProxyImpl {
     // <if expr="chromeos or is_win">
     /** @override */
-    setProspectiveUILanguage: function(languageCode) {
+    setProspectiveUILanguage(languageCode) {
       chrome.send('setProspectiveUILanguage', [languageCode]);
-    },
+    }
 
     /** @override */
-    getProspectiveUILanguage: function() {
+    getProspectiveUILanguage() {
       return cr.sendWithPromise('getProspectiveUILanguage');
-    },
+    }
+
     // </if>
 
     /** @override */
-    getLanguageSettingsPrivate: function() {
+    getLanguageSettingsPrivate() {
       return /** @type {!LanguageSettingsPrivate} */ (
           chrome.languageSettingsPrivate);
-    },
+    }
 
     // <if expr="chromeos">
     /** @override */
-    getInputMethodPrivate: function() {
+    getInputMethodPrivate() {
       return /** @type {!InputMethodPrivate} */ (chrome.inputMethodPrivate);
-    },
+    }
     // </if>
-  };
+  }
+
+  // The singleton instance_ is replaced with a test version of this wrapper
+  // during testing.
+  cr.addSingletonGetter(LanguagesBrowserProxyImpl);
 
   return {
     LanguagesBrowserProxy: LanguagesBrowserProxy,
diff --git a/chrome/browser/resources/settings/lifetime_browser_proxy.js b/chrome/browser/resources/settings/lifetime_browser_proxy.js
index b9915c0..d97ac2d 100644
--- a/chrome/browser/resources/settings/lifetime_browser_proxy.js
+++ b/chrome/browser/resources/settings/lifetime_browser_proxy.js
@@ -4,54 +4,50 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function LifetimeBrowserProxy() {}
-
-  LifetimeBrowserProxy.prototype = {
+  class LifetimeBrowserProxy {
     // Triggers a browser restart.
-    restart: function() {},
+    restart() {}
 
     // Triggers a browser relaunch.
-    relaunch: function() {},
+    relaunch() {}
 
     // <if expr="chromeos">
     // First signs out current user and then performs a restart.
-    signOutAndRestart: function() {},
+    signOutAndRestart() {}
 
     // Triggers a factory reset.
-    factoryReset: function() {},
+    factoryReset() {}
     // </if>
-  };
+  }
 
   /**
-   * @constructor
    * @implements {settings.LifetimeBrowserProxy}
    */
-  function LifetimeBrowserProxyImpl() {}
-  cr.addSingletonGetter(LifetimeBrowserProxyImpl);
-
-  LifetimeBrowserProxyImpl.prototype = {
+  class LifetimeBrowserProxyImpl {
     /** @override */
-    restart: function() {
+    restart() {
       chrome.send('restart');
-    },
+    }
 
     /** @override */
-    relaunch: function() {
+    relaunch() {
       chrome.send('relaunch');
-    },
+    }
 
     // <if expr="chromeos">
     /** @override */
-    signOutAndRestart: function() {
+    signOutAndRestart() {
       chrome.send('signOutAndRestart');
-    },
+    }
 
     /** @override */
-    factoryReset: function() {
+    factoryReset() {
       chrome.send('factoryReset');
-    },
+    }
     // </if>
-  };
+  }
+
+  cr.addSingletonGetter(LifetimeBrowserProxyImpl);
 
   return {
     LifetimeBrowserProxy: LifetimeBrowserProxy,
diff --git a/chrome/browser/resources/settings/on_startup_page/on_startup_browser_proxy.js b/chrome/browser/resources/settings/on_startup_page/on_startup_browser_proxy.js
index a99f618..917029b 100644
--- a/chrome/browser/resources/settings/on_startup_page/on_startup_browser_proxy.js
+++ b/chrome/browser/resources/settings/on_startup_page/on_startup_browser_proxy.js
@@ -7,26 +7,22 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function OnStartupBrowserProxy() {}
-
-  OnStartupBrowserProxy.prototype = {
+  class OnStartupBrowserProxy {
     /** @return {!Promise<?NtpExtension>} */
-    getNtpExtension: assertNotReached,
-  };
+    getNtpExtension() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.OnStartupBrowserProxy}
    */
-  function OnStartupBrowserProxyImpl() {}
-  cr.addSingletonGetter(OnStartupBrowserProxyImpl);
-
-  OnStartupBrowserProxyImpl.prototype = {
+  class OnStartupBrowserProxyImpl {
     /** @override */
-    getNtpExtension: function() {
+    getNtpExtension() {
       return cr.sendWithPromise('getNtpExtension');
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(OnStartupBrowserProxyImpl);
 
   return {
     OnStartupBrowserProxy: OnStartupBrowserProxy,
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_urls_page_browser_proxy.js b/chrome/browser/resources/settings/on_startup_page/startup_urls_page_browser_proxy.js
index b33e31a..ff13b19 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_urls_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/on_startup_page/startup_urls_page_browser_proxy.js
@@ -14,25 +14,22 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function StartupUrlsPageBrowserProxy() {}
-
-  StartupUrlsPageBrowserProxy.prototype = {
-    loadStartupPages: assertNotReached,
-
-    useCurrentPages: assertNotReached,
+  class StartupUrlsPageBrowserProxy {
+    loadStartupPages() {}
+    useCurrentPages() {}
 
     /**
      * @param {string} url
      * @return {!Promise<boolean>} Whether the URL is valid.
      */
-    validateStartupPage: assertNotReached,
+    validateStartupPage(url) {}
 
     /**
      * @param {string} url
      * @return {!Promise<boolean>} Whether the URL was actually added, or
      *     ignored because it was invalid.
      */
-    addStartupPage: assertNotReached,
+    addStartupPage(url) {}
 
     /**
      * @param {number} modelIndex
@@ -40,52 +37,49 @@
      * @return {!Promise<boolean>} Whether the URL was actually edited, or
      *     ignored because it was invalid.
      */
-    editStartupPage: assertNotReached,
+    editStartupPage(modelIndex, url) {}
 
     /** @param {number} index */
-    removeStartupPage: assertNotReached,
-  };
+    removeStartupPage(index) {}
+  }
 
   /**
    * @implements {settings.StartupUrlsPageBrowserProxy}
-   * @constructor
    */
-  function StartupUrlsPageBrowserProxyImpl() {}
+  class StartupUrlsPageBrowserProxyImpl {
+    /** @override */
+    loadStartupPages() {
+      chrome.send('onStartupPrefsPageLoad');
+    }
+
+    /** @override */
+    useCurrentPages() {
+      chrome.send('setStartupPagesToCurrentPages');
+    }
+
+    /** @override */
+    validateStartupPage(url) {
+      return cr.sendWithPromise('validateStartupPage', url);
+    }
+
+    /** @override */
+    addStartupPage(url) {
+      return cr.sendWithPromise('addStartupPage', url);
+    }
+
+    /** @override */
+    editStartupPage(modelIndex, url) {
+      return cr.sendWithPromise('editStartupPage', modelIndex, url);
+    }
+
+    /** @override */
+    removeStartupPage(index) {
+      chrome.send('removeStartupPage', [index]);
+    }
+  }
 
   cr.addSingletonGetter(StartupUrlsPageBrowserProxyImpl);
 
-  StartupUrlsPageBrowserProxyImpl.prototype = {
-    /** @override */
-    loadStartupPages: function() {
-      chrome.send('onStartupPrefsPageLoad');
-    },
-
-    /** @override */
-    useCurrentPages: function() {
-      chrome.send('setStartupPagesToCurrentPages');
-    },
-
-    /** @override */
-    validateStartupPage: function(url) {
-      return cr.sendWithPromise('validateStartupPage', url);
-    },
-
-    /** @override */
-    addStartupPage: function(url) {
-      return cr.sendWithPromise('addStartupPage', url);
-    },
-
-    /** @override */
-    editStartupPage: function(modelIndex, url) {
-      return cr.sendWithPromise('editStartupPage', modelIndex, url);
-    },
-
-    /** @override */
-    removeStartupPage: function(index) {
-      chrome.send('removeStartupPage', [index]);
-    },
-  };
-
   return {
     StartupUrlsPageBrowserProxy: StartupUrlsPageBrowserProxy,
     StartupUrlsPageBrowserProxyImpl: StartupUrlsPageBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js b/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
index 05e3dc6..d6dc9b9 100644
--- a/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
@@ -21,91 +21,87 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function ChangePictureBrowserProxy() {}
-
-  ChangePictureBrowserProxy.prototype = {
+  class ChangePictureBrowserProxy {
     /**
      * Retrieves the initial set of default images, profile image, etc. As a
      * response, the C++ sends these WebUIListener events:
      * 'default-images-changed', 'profile-image-changed', 'old-image-changed',
      * and 'selected-image-changed'
      */
-    initialize: function() {},
+    initialize() {}
 
     /**
      * Sets the user image to one of the default images. As a response, the C++
      * sends the 'default-images-changed' WebUIListener event.
      * @param {string} imageUrl
      */
-    selectDefaultImage: function(imageUrl) {},
+    selectDefaultImage(imageUrl) {}
 
     /**
      * Sets the user image to the 'old' image. As a response, the C++ sends the
      * 'old-image-changed' WebUIListener event.
      */
-    selectOldImage: function() {},
+    selectOldImage() {}
 
     /**
      * Sets the user image to the profile image. As a response, the C++ sends
      * the 'profile-image-changed' WebUIListener event.
      */
-    selectProfileImage: function() {},
+    selectProfileImage() {}
 
     /**
      * Provides the taken photo as a data URL to the C++. No response is
      * expected.
      * @param {string} photoDataUrl
      */
-    photoTaken: function(photoDataUrl) {},
+    photoTaken(photoDataUrl) {}
 
     /**
      * Requests a file chooser to select a new user image. No response is
      * expected.
      */
-    chooseFile: function() {},
-  };
+    chooseFile() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.ChangePictureBrowserProxy}
    */
-  function ChangePictureBrowserProxyImpl() {}
+  class ChangePictureBrowserProxyImpl {
+    /** @override */
+    initialize() {
+      chrome.send('onChangePicturePageInitialized');
+    }
+
+    /** @override */
+    selectDefaultImage(imageUrl) {
+      chrome.send('selectImage', [imageUrl, 'default']);
+    }
+
+    /** @override */
+    selectOldImage() {
+      chrome.send('selectImage', ['', 'old']);
+    }
+
+    /** @override */
+    selectProfileImage() {
+      chrome.send('selectImage', ['', 'profile']);
+    }
+
+    /** @override */
+    photoTaken(photoDataUrl) {
+      chrome.send('photoTaken', [photoDataUrl]);
+    }
+
+    /** @override */
+    chooseFile() {
+      chrome.send('chooseFile');
+    }
+  }
+
   // The singleton instance_ is replaced with a test version of this wrapper
   // during testing.
   cr.addSingletonGetter(ChangePictureBrowserProxyImpl);
 
-  ChangePictureBrowserProxyImpl.prototype = {
-    /** @override */
-    initialize: function() {
-      chrome.send('onChangePicturePageInitialized');
-    },
-
-    /** @override */
-    selectDefaultImage: function(imageUrl) {
-      chrome.send('selectImage', [imageUrl, 'default']);
-    },
-
-    /** @override */
-    selectOldImage: function() {
-      chrome.send('selectImage', ['', 'old']);
-    },
-
-    /** @override */
-    selectProfileImage: function() {
-      chrome.send('selectImage', ['', 'profile']);
-    },
-
-    /** @override */
-    photoTaken: function(photoDataUrl) {
-      chrome.send('photoTaken', [photoDataUrl]);
-    },
-
-    /** @override */
-    chooseFile: function() {
-      chrome.send('chooseFile');
-    },
-  };
-
   return {
     ChangePictureBrowserProxy: ChangePictureBrowserProxy,
     ChangePictureBrowserProxyImpl: ChangePictureBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/people_page/easy_unlock_browser_proxy.js b/chrome/browser/resources/settings/people_page/easy_unlock_browser_proxy.js
index f43dfb6..008e6c94 100644
--- a/chrome/browser/resources/settings/people_page/easy_unlock_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/easy_unlock_browser_proxy.js
@@ -10,73 +10,69 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function EasyUnlockBrowserProxy() {}
-
-  EasyUnlockBrowserProxy.prototype = {
+  class EasyUnlockBrowserProxy {
     /**
      * Returns a true promise if Easy Unlock is already enabled on the device.
      * @return {!Promise<boolean>}
      */
-    getEnabledStatus: function() {},
+    getEnabledStatus() {}
 
     /**
      * Starts the Easy Unlock setup flow.
      */
-    startTurnOnFlow: function() {},
+    startTurnOnFlow() {}
 
     /**
      * Returns the Easy Unlock turn off flow status.
      * @return {!Promise<string>}
      */
-    getTurnOffFlowStatus: function() {},
+    getTurnOffFlowStatus() {}
 
     /**
      * Begins the Easy Unlock turn off flow.
      */
-    startTurnOffFlow: function() {},
+    startTurnOffFlow() {}
 
     /**
      * Cancels any in-progress Easy Unlock turn-off flows.
      */
-    cancelTurnOffFlow: function() {},
-  };
+    cancelTurnOffFlow() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.EasyUnlockBrowserProxy}
    */
-  function EasyUnlockBrowserProxyImpl() {}
+  class EasyUnlockBrowserProxyImpl {
+    /** @override */
+    getEnabledStatus() {
+      return cr.sendWithPromise('easyUnlockGetEnabledStatus');
+    }
+
+    /** @override */
+    startTurnOnFlow() {
+      chrome.send('easyUnlockStartTurnOnFlow');
+    }
+
+    /** @override */
+    getTurnOffFlowStatus() {
+      return cr.sendWithPromise('easyUnlockGetTurnOffFlowStatus');
+    }
+
+    /** @override */
+    startTurnOffFlow() {
+      chrome.send('easyUnlockStartTurnOffFlow');
+    }
+
+    /** @override */
+    cancelTurnOffFlow() {
+      chrome.send('easyUnlockCancelTurnOffFlow');
+    }
+  }
+
   // The singleton instance_ is replaced with a test version of this wrapper
   // during testing.
   cr.addSingletonGetter(EasyUnlockBrowserProxyImpl);
 
-  EasyUnlockBrowserProxyImpl.prototype = {
-    /** @override */
-    getEnabledStatus: function() {
-      return cr.sendWithPromise('easyUnlockGetEnabledStatus');
-    },
-
-    /** @override */
-    startTurnOnFlow: function() {
-      chrome.send('easyUnlockStartTurnOnFlow');
-    },
-
-    /** @override */
-    getTurnOffFlowStatus: function() {
-      return cr.sendWithPromise('easyUnlockGetTurnOffFlowStatus');
-    },
-
-    /** @override */
-    startTurnOffFlow: function() {
-      chrome.send('easyUnlockStartTurnOffFlow');
-    },
-
-    /** @override */
-    cancelTurnOffFlow: function() {
-      chrome.send('easyUnlockCancelTurnOffFlow');
-    },
-  };
-
   return {
     EasyUnlockBrowserProxy: EasyUnlockBrowserProxy,
     EasyUnlockBrowserProxyImpl: EasyUnlockBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/people_page/fingerprint_browser_proxy.js b/chrome/browser/resources/settings/people_page/fingerprint_browser_proxy.js
index 09e2f8c0..71941a3a 100644
--- a/chrome/browser/resources/settings/people_page/fingerprint_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/fingerprint_browser_proxy.js
@@ -52,112 +52,106 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function FingerprintBrowserProxy() {}
-
-  FingerprintBrowserProxy.prototype = {
+  class FingerprintBrowserProxy {
     /**
      * @return {!Promise<!settings.FingerprintInfo>}
      */
-    getFingerprintsList: function() {},
+    getFingerprintsList() {}
 
     /**
      * @return {!Promise<number>}
      */
-    getNumFingerprints: function() {},
+    getNumFingerprints() {}
 
-    startEnroll: function() {},
-
-    cancelCurrentEnroll: function() {},
+    startEnroll() {}
+    cancelCurrentEnroll() {}
 
     /**
      * @param {number} index
      * @return {!Promise<string>}
      */
-    getEnrollmentLabel: function(index) {},
+    getEnrollmentLabel(index) {}
 
     /**
      * @param {number} index
      * @return {!Promise<boolean>}
      */
-    removeEnrollment: function(index) {},
+    removeEnrollment(index) {}
 
     /**
      * @param {number} index
      * @param {string} newLabel
      * @return {!Promise<boolean>}
      */
-    changeEnrollmentLabel: function(index, newLabel) {},
+    changeEnrollmentLabel(index, newLabel) {}
 
-    startAuthentication: function() {},
-
-    endCurrentAuthentication: function() {},
+    startAuthentication() {}
+    endCurrentAuthentication() {}
 
     /**
      * TODO(sammiequon): Temporary function to let the handler know when a
      * completed scan has been sent via click on the setup fingerprint dialog.
      * Remove this when real scans are implemented.
      */
-    fakeScanComplete: function() {},
-  };
+    fakeScanComplete() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.FingerprintBrowserProxy}
    */
-  function FingerprintBrowserProxyImpl() {}
-  cr.addSingletonGetter(FingerprintBrowserProxyImpl);
-
-  FingerprintBrowserProxyImpl.prototype = {
+  class FingerprintBrowserProxyImpl {
     /** @override */
-    getFingerprintsList: function() {
+    getFingerprintsList() {
       return cr.sendWithPromise('getFingerprintsList');
-    },
+    }
 
     /** @override */
-    getNumFingerprints: function() {
+    getNumFingerprints() {
       return cr.sendWithPromise('getNumFingerprints');
-    },
+    }
 
     /** @override */
-    startEnroll: function() {
+    startEnroll() {
       chrome.send('startEnroll');
-    },
+    }
 
     /** @override */
-    cancelCurrentEnroll: function() {
+    cancelCurrentEnroll() {
       chrome.send('cancelCurrentEnroll');
-    },
+    }
 
     /** @override */
-    getEnrollmentLabel: function(index) {
+    getEnrollmentLabel(index) {
       return cr.sendWithPromise('getEnrollmentLabel');
-    },
+    }
 
     /** @override */
-    removeEnrollment: function(index) {
+    removeEnrollment(index) {
       return cr.sendWithPromise('removeEnrollment', index);
-    },
+    }
 
     /** @override */
-    changeEnrollmentLabel: function(index, newLabel) {
+    changeEnrollmentLabel(index, newLabel) {
       return cr.sendWithPromise('changeEnrollmentLabel', index, newLabel);
-    },
+    }
 
     /** @override */
-    startAuthentication: function() {
+    startAuthentication() {
       chrome.send('startAuthentication');
-    },
+    }
 
     /** @override */
-    endCurrentAuthentication: function() {
+    endCurrentAuthentication() {
       chrome.send('endCurrentAuthentication');
-    },
+    }
 
     /** @override */
-    fakeScanComplete: function() {
+    fakeScanComplete() {
       chrome.send('fakeScanComplete');
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(FingerprintBrowserProxyImpl);
 
   return {
     FingerprintBrowserProxy: FingerprintBrowserProxy,
diff --git a/chrome/browser/resources/settings/people_page/import_data_browser_proxy.js b/chrome/browser/resources/settings/people_page/import_data_browser_proxy.js
index 24c108ec..5458bab 100644
--- a/chrome/browser/resources/settings/people_page/import_data_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/import_data_browser_proxy.js
@@ -36,54 +36,50 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function ImportDataBrowserProxy() {}
-
-  ImportDataBrowserProxy.prototype = {
+  class ImportDataBrowserProxy {
     /**
      * Returns the source profiles available for importing from other browsers.
      * @return {!Promise<!Array<!settings.BrowserProfile>>}
      */
-    initializeImportDialog: function() {},
+    initializeImportDialog() {}
 
     /**
      * Starts importing data for the specificed source browser profile. The C++
      * responds with the 'import-data-status-changed' WebUIListener event.
      * @param {number} sourceBrowserProfileIndex
      */
-    importData: function(sourceBrowserProfileIndex) {},
+    importData(sourceBrowserProfileIndex) {}
 
     /**
      * Prompts the user to choose a bookmarks file to import bookmarks from.
      */
-    importFromBookmarksFile: function() {},
-  };
+    importFromBookmarksFile() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.ImportDataBrowserProxy}
    */
-  function ImportDataBrowserProxyImpl() {}
+  class ImportDataBrowserProxyImpl {
+    /** @override */
+    initializeImportDialog() {
+      return cr.sendWithPromise('initializeImportDialog');
+    }
+
+    /** @override */
+    importData(sourceBrowserProfileIndex) {
+      chrome.send('importData', [sourceBrowserProfileIndex]);
+    }
+
+    /** @override */
+    importFromBookmarksFile() {
+      chrome.send('importFromBookmarksFile');
+    }
+  }
+
   // The singleton instance_ is replaced with a test version of this wrapper
   // during testing.
   cr.addSingletonGetter(ImportDataBrowserProxyImpl);
 
-  ImportDataBrowserProxyImpl.prototype = {
-    /** @override */
-    initializeImportDialog: function() {
-      return cr.sendWithPromise('initializeImportDialog');
-    },
-
-    /** @override */
-    importData: function(sourceBrowserProfileIndex) {
-      chrome.send('importData', [sourceBrowserProfileIndex]);
-    },
-
-    /** @override */
-    importFromBookmarksFile: function() {
-      chrome.send('importFromBookmarksFile');
-    },
-  };
-
   return {
     ImportDataBrowserProxy: ImportDataBrowserProxy,
     ImportDataBrowserProxyImpl: ImportDataBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/people_page/manage_profile_browser_proxy.js b/chrome/browser/resources/settings/people_page/manage_profile_browser_proxy.js
index 34e2d341..85a7259 100644
--- a/chrome/browser/resources/settings/people_page/manage_profile_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/manage_profile_browser_proxy.js
@@ -20,95 +20,91 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function ManageProfileBrowserProxy() {}
-
-  ManageProfileBrowserProxy.prototype = {
+  class ManageProfileBrowserProxy {
     /**
      * Gets the available profile icons to choose from.
      * @return {!Promise<!Array<!AvatarIcon>>}
      */
-    getAvailableIcons: function() {},
+    getAvailableIcons() {}
 
     /**
      * Sets the profile's icon to the GAIA avatar.
      */
-    setProfileIconToGaiaAvatar: function() {},
+    setProfileIconToGaiaAvatar() {}
 
     /**
      * Sets the profile's icon to one of the default avatars.
      * @param {string} iconUrl The new profile URL.
      */
-    setProfileIconToDefaultAvatar: function(iconUrl) {},
+    setProfileIconToDefaultAvatar(iconUrl) {}
 
     /**
      * Sets the profile's name.
      * @param {string} name The new profile name.
      */
-    setProfileName: function(name) {},
+    setProfileName(name) {}
 
     /**
      * Returns whether the current profile has a shortcut.
      * @return {!Promise<ProfileShortcutStatus>}
      */
-    getProfileShortcutStatus: function() {},
+    getProfileShortcutStatus() {}
 
     /**
      * Adds a shortcut for the current profile.
      */
-    addProfileShortcut: function() {},
+    addProfileShortcut() {}
 
     /**
      * Removes the shortcut of the current profile.
      */
-    removeProfileShortcut: function() {},
-  };
+    removeProfileShortcut() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.ManageProfileBrowserProxy}
    */
-  function ManageProfileBrowserProxyImpl() {}
+  class ManageProfileBrowserProxyImpl {
+    /** @override */
+    getAvailableIcons() {
+      return cr.sendWithPromise('getAvailableIcons');
+    }
+
+    /** @override */
+    setProfileIconToGaiaAvatar() {
+      chrome.send('setProfileIconToGaiaAvatar');
+    }
+
+    /** @override */
+    setProfileIconToDefaultAvatar(iconUrl) {
+      chrome.send('setProfileIconToDefaultAvatar', [iconUrl]);
+    }
+
+    /** @override */
+    setProfileName(name) {
+      chrome.send('setProfileName', [name]);
+    }
+
+    /** @override */
+    getProfileShortcutStatus() {
+      return cr.sendWithPromise('requestProfileShortcutStatus');
+    }
+
+    /** @override */
+    addProfileShortcut() {
+      chrome.send('addProfileShortcut');
+    }
+
+    /** @override */
+    removeProfileShortcut() {
+      chrome.send('removeProfileShortcut');
+    }
+  }
+
   // The singleton instance_ is replaced with a test version of this wrapper
   // during testing.
   cr.addSingletonGetter(ManageProfileBrowserProxyImpl);
 
-  ManageProfileBrowserProxyImpl.prototype = {
-    /** @override */
-    getAvailableIcons: function() {
-      return cr.sendWithPromise('getAvailableIcons');
-    },
-
-    /** @override */
-    setProfileIconToGaiaAvatar: function() {
-      chrome.send('setProfileIconToGaiaAvatar');
-    },
-
-    /** @override */
-    setProfileIconToDefaultAvatar: function(iconUrl) {
-      chrome.send('setProfileIconToDefaultAvatar', [iconUrl]);
-    },
-
-    /** @override */
-    setProfileName: function(name) {
-      chrome.send('setProfileName', [name]);
-    },
-
-    /** @override */
-    getProfileShortcutStatus: function() {
-      return cr.sendWithPromise('requestProfileShortcutStatus');
-    },
-
-    /** @override */
-    addProfileShortcut: function() {
-      chrome.send('addProfileShortcut');
-    },
-
-    /** @override */
-    removeProfileShortcut: function() {
-      chrome.send('removeProfileShortcut');
-    },
-  };
-
   return {
     ManageProfileBrowserProxy: ManageProfileBrowserProxy,
     ManageProfileBrowserProxyImpl: ManageProfileBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js b/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js
index 81eb71a..31f4824 100644
--- a/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js
@@ -20,51 +20,47 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function ProfileInfoBrowserProxy() {}
-
-  ProfileInfoBrowserProxy.prototype = {
+  class ProfileInfoBrowserProxy {
     /**
      * Returns a Promise for the profile info.
      * @return {!Promise<!settings.ProfileInfo>}
      */
-    getProfileInfo: function() {},
+    getProfileInfo() {}
 
     /**
      * Requests the profile stats count. The result is returned by the
      * 'profile-stats-count-ready' WebUI listener event.
      */
-    getProfileStatsCount: function() {},
+    getProfileStatsCount() {}
 
     /**
      * Returns a Promise that's true if the profile manages supervised users.
      * @return {!Promise<boolean>}
      */
-    getProfileManagesSupervisedUsers: function() {},
-  };
+    getProfileManagesSupervisedUsers() {}
+  }
 
   /**
-   * @constructor
    * @implements {ProfileInfoBrowserProxy}
    */
-  function ProfileInfoBrowserProxyImpl() {}
-  cr.addSingletonGetter(ProfileInfoBrowserProxyImpl);
-
-  ProfileInfoBrowserProxyImpl.prototype = {
+  class ProfileInfoBrowserProxyImpl {
     /** @override */
-    getProfileInfo: function() {
+    getProfileInfo() {
       return cr.sendWithPromise('getProfileInfo');
-    },
+    }
 
     /** @override */
-    getProfileStatsCount: function() {
+    getProfileStatsCount() {
       chrome.send('getProfileStatsCount');
-    },
+    }
 
     /** @override */
-    getProfileManagesSupervisedUsers: function() {
+    getProfileManagesSupervisedUsers() {
       return cr.sendWithPromise('getProfileManagesSupervisedUsers');
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(ProfileInfoBrowserProxyImpl);
 
   return {
     ProfileInfoBrowserProxyImpl: ProfileInfoBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
index 648d22f..05f345f3 100644
--- a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
@@ -105,138 +105,138 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function SyncBrowserProxy() {}
-
-  SyncBrowserProxy.prototype = {
+  class SyncBrowserProxy {
     // <if expr="not chromeos">
     /**
      * Starts the signin process for the user. Does nothing if the user is
      * already signed in.
      */
-    startSignIn: function() {},
+    startSignIn() {}
 
     /**
      * Signs out the signed-in user.
      * @param {boolean} deleteProfile
      */
-    signOut: function(deleteProfile) {},
+    signOut(deleteProfile) {}
 
     /**
      * Opens the multi-profile user manager.
      */
-    manageOtherPeople: function() {},
+    manageOtherPeople() {}
+
     // </if>
 
     // <if expr="chromeos">
     /**
      * Signs the user out.
      */
-    attemptUserExit: function() {},
+    attemptUserExit() {}
+
     // </if>
 
     /**
      * Gets the current sync status.
      * @return {!Promise<!settings.SyncStatus>}
      */
-    getSyncStatus: function() {},
+    getSyncStatus() {}
 
     /**
      * Function to invoke when the sync page has been navigated to. This
      * registers the UI as the "active" sync UI so that if the user tries to
      * open another sync UI, this one will be shown instead.
      */
-    didNavigateToSyncPage: function() {},
+    didNavigateToSyncPage() {}
 
     /**
      * Function to invoke when leaving the sync page so that the C++ layer can
      * be notified that the sync UI is no longer open.
      */
-    didNavigateAwayFromSyncPage: function() {},
+    didNavigateAwayFromSyncPage() {}
 
     /**
      * Sets which types of data to sync.
      * @param {!settings.SyncPrefs} syncPrefs
      * @return {!Promise<!settings.PageStatus>}
      */
-    setSyncDatatypes: function(syncPrefs) {},
+    setSyncDatatypes(syncPrefs) {}
 
     /**
      * Sets the sync encryption options.
      * @param {!settings.SyncPrefs} syncPrefs
      * @return {!Promise<!settings.PageStatus>}
      */
-    setSyncEncryption: function(syncPrefs) {},
+    setSyncEncryption(syncPrefs) {}
 
     /**
      * Opens the Google Activity Controls url in a new tab.
      */
-    openActivityControlsUrl: function() {},
-  };
+    openActivityControlsUrl() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.SyncBrowserProxy}
    */
-  function SyncBrowserProxyImpl() {}
-  cr.addSingletonGetter(SyncBrowserProxyImpl);
-
-  SyncBrowserProxyImpl.prototype = {
+  class SyncBrowserProxyImpl {
     // <if expr="not chromeos">
     /** @override */
-    startSignIn: function() {
+    startSignIn() {
       chrome.send('SyncSetupStartSignIn');
-    },
+    }
 
     /** @override */
-    signOut: function(deleteProfile) {
+    signOut(deleteProfile) {
       chrome.send('SyncSetupStopSyncing', [deleteProfile]);
-    },
+    }
 
     /** @override */
-    manageOtherPeople: function() {
+    manageOtherPeople() {
       chrome.send('SyncSetupManageOtherPeople');
-    },
+    }
+
     // </if>
     // <if expr="chromeos">
     /** @override */
-    attemptUserExit: function() {
+    attemptUserExit() {
       return chrome.send('AttemptUserExit');
-    },
+    }
+
     // </if>
 
     /** @override */
-    getSyncStatus: function() {
+    getSyncStatus() {
       return cr.sendWithPromise('SyncSetupGetSyncStatus');
-    },
+    }
 
     /** @override */
-    didNavigateToSyncPage: function() {
+    didNavigateToSyncPage() {
       chrome.send('SyncSetupShowSetupUI');
-    },
+    }
 
     /** @override */
-    didNavigateAwayFromSyncPage: function() {
+    didNavigateAwayFromSyncPage() {
       chrome.send('SyncSetupDidClosePage');
-    },
+    }
 
     /** @override */
-    setSyncDatatypes: function(syncPrefs) {
+    setSyncDatatypes(syncPrefs) {
       return cr.sendWithPromise(
           'SyncSetupSetDatatypes', JSON.stringify(syncPrefs));
-    },
+    }
 
     /** @override */
-    setSyncEncryption: function(syncPrefs) {
+    setSyncEncryption(syncPrefs) {
       return cr.sendWithPromise(
           'SyncSetupSetEncryption', JSON.stringify(syncPrefs));
-    },
+    }
 
     /** @override */
-    openActivityControlsUrl: function() {
+    openActivityControlsUrl() {
       chrome.metricsPrivate.recordUserAction(
           'Signin_AccountSettings_GoogleActivityControlsClicked');
     }
-  };
+  }
+
+  cr.addSingletonGetter(SyncBrowserProxyImpl);
 
   return {
     SyncBrowserProxy: SyncBrowserProxy,
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js b/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
index 3af40d9..2a5eb45 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
+++ b/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
@@ -65,117 +65,111 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function CupsPrintersBrowserProxy() {}
-
-  CupsPrintersBrowserProxy.prototype = {
-
+  class CupsPrintersBrowserProxy {
     /**
      * @return {!Promise<!CupsPrintersList>}
      */
-    getCupsPrintersList: function() {},
+    getCupsPrintersList() {}
 
     /**
      * @param {string} printerId
      * @param {string} printerName
      */
-    updateCupsPrinter: function(printerId, printerName) {},
+    updateCupsPrinter(printerId, printerName) {}
 
     /**
      * @param {string} printerId
      * @param {string} printerName
      */
-    removeCupsPrinter: function(printerId, printerName) {},
+    removeCupsPrinter(printerId, printerName) {}
 
     /**
      * @return {!Promise<string>} The full path of the printer PPD file.
      */
-    getCupsPrinterPPDPath: function() {},
+    getCupsPrinterPPDPath() {}
 
     /**
      * @param {!CupsPrinterInfo} newPrinter
      */
-    addCupsPrinter: function(newPrinter) {},
+    addCupsPrinter(newPrinter) {}
 
-    startDiscoveringPrinters: function() {},
-
-    stopDiscoveringPrinters: function() {},
+    startDiscoveringPrinters() {}
+    stopDiscoveringPrinters() {}
 
     /**
      * @return {!Promise<!ManufacturersInfo>}
      */
-    getCupsPrinterManufacturersList: function() {},
+    getCupsPrinterManufacturersList() {}
 
     /**
      * @param {string} manufacturer
      * @return {!Promise<!ModelsInfo>}
      */
-    getCupsPrinterModelsList: function(manufacturer) {},
+    getCupsPrinterModelsList(manufacturer) {}
 
     /**
      * @param {!CupsPrinterInfo} newPrinter
      * @return {!Promise<!PrinterMakeModel>}
      */
-    getPrinterInfo: function(newPrinter) {},
-  };
+    getPrinterInfo(newPrinter) {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.CupsPrintersBrowserProxy}
    */
-  function CupsPrintersBrowserProxyImpl() {}
-  cr.addSingletonGetter(CupsPrintersBrowserProxyImpl);
-
-  CupsPrintersBrowserProxyImpl.prototype = {
+  class CupsPrintersBrowserProxyImpl {
     /** @override */
-    getCupsPrintersList: function() {
+    getCupsPrintersList() {
       return cr.sendWithPromise('getCupsPrintersList');
-    },
+    }
 
     /** @override */
-    updateCupsPrinter: function(printerId, printerName) {
+    updateCupsPrinter(printerId, printerName) {
       chrome.send('updateCupsPrinter', [printerId, printerName]);
-    },
+    }
 
     /** @override */
-    removeCupsPrinter: function(printerId, printerName) {
+    removeCupsPrinter(printerId, printerName) {
       chrome.send('removeCupsPrinter', [printerId, printerName]);
-    },
+    }
 
     /** @override */
-    addCupsPrinter: function(newPrinter) {
+    addCupsPrinter(newPrinter) {
       chrome.send('addCupsPrinter', [newPrinter]);
-    },
+    }
 
     /** @override */
-    getCupsPrinterPPDPath: function() {
+    getCupsPrinterPPDPath() {
       return cr.sendWithPromise('selectPPDFile');
-    },
+    }
 
     /** @override */
-    startDiscoveringPrinters: function() {
+    startDiscoveringPrinters() {
       chrome.send('startDiscoveringPrinters');
-    },
+    }
 
     /** @override */
-    stopDiscoveringPrinters: function() {
+    stopDiscoveringPrinters() {
       chrome.send('stopDiscoveringPrinters');
-    },
+    }
 
     /** @override */
-    getCupsPrinterManufacturersList: function() {
+    getCupsPrinterManufacturersList() {
       return cr.sendWithPromise('getCupsPrinterManufacturersList');
-    },
+    }
 
     /** @override */
-    getCupsPrinterModelsList: function(manufacturer) {
+    getCupsPrinterModelsList(manufacturer) {
       return cr.sendWithPromise('getCupsPrinterModelsList', manufacturer);
-    },
+    }
 
     /** @override */
-    getPrinterInfo: function(newPrinter) {
+    getPrinterInfo(newPrinter) {
       return cr.sendWithPromise('getPrinterInfo', newPrinter);
-    },
-  };
+    }
+  }
+
+  cr.addSingletonGetter(CupsPrintersBrowserProxyImpl);
 
   return {
     CupsPrintersBrowserProxy: CupsPrintersBrowserProxy,
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js b/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js
index 38f544d..9d384a2 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js
@@ -9,66 +9,65 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function PrivacyPageBrowserProxy() {}
-
-  PrivacyPageBrowserProxy.prototype = {
+  class PrivacyPageBrowserProxy {
     // <if expr="_google_chrome and not chromeos">
     /** @return {!Promise<!MetricsReporting>} */
-    getMetricsReporting: assertNotReached,
+    getMetricsReporting() {}
 
     /** @param {boolean} enabled */
-    setMetricsReportingEnabled: assertNotReached,
+    setMetricsReportingEnabled(enabled) {}
+
     // </if>
 
     // <if expr="is_win or is_macosx">
     /** Invokes the native certificate manager (used by win and mac). */
-    showManageSSLCertificates: function() {},
+    showManageSSLCertificates() {}
+
     // </if>
 
     /** @return {!Promise<boolean>} */
-    getSafeBrowsingExtendedReporting: assertNotReached,
+    getSafeBrowsingExtendedReporting() {}
 
     /** @param {boolean} enabled */
-    setSafeBrowsingExtendedReportingEnabled: assertNotReached,
-  };
+    setSafeBrowsingExtendedReportingEnabled(enabled) {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.PrivacyPageBrowserProxy}
    */
-  function PrivacyPageBrowserProxyImpl() {}
-  cr.addSingletonGetter(PrivacyPageBrowserProxyImpl);
-
-  PrivacyPageBrowserProxyImpl.prototype = {
+  class PrivacyPageBrowserProxyImpl {
     // <if expr="_google_chrome and not chromeos">
     /** @override */
-    getMetricsReporting: function() {
+    getMetricsReporting() {
       return cr.sendWithPromise('getMetricsReporting');
-    },
+    }
 
     /** @override */
-    setMetricsReportingEnabled: function(enabled) {
+    setMetricsReportingEnabled(enabled) {
       chrome.send('setMetricsReportingEnabled', [enabled]);
-    },
+    }
+
     // </if>
 
     /** @override */
-    getSafeBrowsingExtendedReporting: function() {
+    getSafeBrowsingExtendedReporting() {
       return cr.sendWithPromise('getSafeBrowsingExtendedReporting');
-    },
+    }
 
     /** @override */
-    setSafeBrowsingExtendedReportingEnabled: function(enabled) {
+    setSafeBrowsingExtendedReportingEnabled(enabled) {
       chrome.send('setSafeBrowsingExtendedReportingEnabled', [enabled]);
-    },
+    }
 
     // <if expr="is_win or is_macosx">
     /** @override */
-    showManageSSLCertificates: function() {
+    showManageSSLCertificates() {
       chrome.send('showManageSSLCertificates');
-    },
+    }
     // </if>
-  };
+  }
+
+  cr.addSingletonGetter(PrivacyPageBrowserProxyImpl);
 
   return {
     PrivacyPageBrowserProxy: PrivacyPageBrowserProxy,
diff --git a/chrome/browser/resources/settings/reset_page/reset_browser_proxy.js b/chrome/browser/resources/settings/reset_page/reset_browser_proxy.js
index ef80889..f43c9a0 100644
--- a/chrome/browser/resources/settings/reset_page/reset_browser_proxy.js
+++ b/chrome/browser/resources/settings/reset_page/reset_browser_proxy.js
@@ -4,89 +4,83 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function ResetBrowserProxy() {}
-
-  ResetBrowserProxy.prototype = {
+  class ResetBrowserProxy {
     /**
      * @param {boolean} sendSettings Whether the user gave consent to upload
      *     broken settings to Google for analysis.
      * @param {string} requestOrigin The origin of the reset request.
      * @return {!Promise} A promise firing once resetting has completed.
      */
-    performResetProfileSettings: function(sendSettings, requestOrigin) {},
+    performResetProfileSettings(sendSettings, requestOrigin) {}
 
     /**
      * A method to be called when the reset profile dialog is hidden.
      */
-    onHideResetProfileDialog: function() {},
+    onHideResetProfileDialog() {}
 
     /**
      * A method to be called when the reset profile banner is hidden.
      */
-    onHideResetProfileBanner: function() {},
+    onHideResetProfileBanner() {}
 
     /**
      * A method to be called when the reset profile dialog is shown.
      */
-    onShowResetProfileDialog: function() {},
+    onShowResetProfileDialog() {}
 
     /**
      * Shows the settings that are about to be reset and which will be reported
      * to Google for analysis, in a new tab.
      */
-    showReportedSettings: function() {},
+    showReportedSettings() {}
 
     /**
      * Retrieves the triggered reset tool name.
      * @return {!Promise<string>} A promise firing with the tool name, once it
      *     has been retrieved.
      */
-    getTriggeredResetToolName: function() {},
+    getTriggeredResetToolName() {}
 
     // <if expr="chromeos">
     /**
      * A method to be called when the reset powerwash dialog is shown.
      */
-    onPowerwashDialogShow: function() {},
+    onPowerwashDialogShow() {}
 
     /**
      * Initiates a factory reset and restarts ChromeOS.
      */
-    requestFactoryResetRestart: function() {},
+    requestFactoryResetRestart() {}
     // </if>
-  };
+  }
 
   /**
-   * @constructor
    * @implements {settings.ResetBrowserProxy}
    */
-  function ResetBrowserProxyImpl() {}
-  cr.addSingletonGetter(ResetBrowserProxyImpl);
-
-  ResetBrowserProxyImpl.prototype = {
+  class ResetBrowserProxyImpl {
     /** @override */
-    performResetProfileSettings: function(sendSettings, requestOrigin) {
+    performResetProfileSettings(sendSettings, requestOrigin) {
       return cr.sendWithPromise(
           'performResetProfileSettings', sendSettings, requestOrigin);
-    },
+    }
 
     /** @override */
-    onHideResetProfileDialog: function() {
+    onHideResetProfileDialog() {
       chrome.send('onHideResetProfileDialog');
-    },
+    }
 
     /** @override */
-    onHideResetProfileBanner: function() {
+    onHideResetProfileBanner() {
       chrome.send('onHideResetProfileBanner');
-    },
+    }
 
     /** @override */
-    onShowResetProfileDialog: function() {
+    onShowResetProfileDialog() {
       chrome.send('onShowResetProfileDialog');
-    },
+    }
 
     /** @override */
-    showReportedSettings: function() {
+    showReportedSettings() {
       cr.sendWithPromise('getReportedSettings').then(function(settings) {
         var output = settings.map(function(entry) {
           return entry.key + ': ' + entry.value.replace(/\n/g, ', ');
@@ -97,25 +91,27 @@
         div.style.whiteSpace = 'pre';
         win.document.body.appendChild(div);
       });
-    },
+    }
 
     /** @override */
-    getTriggeredResetToolName: function() {
+    getTriggeredResetToolName() {
       return cr.sendWithPromise('getTriggeredResetToolName');
-    },
+    }
 
     // <if expr="chromeos">
     /** @override */
-    onPowerwashDialogShow: function() {
+    onPowerwashDialogShow() {
       chrome.send('onPowerwashDialogShow');
-    },
+    }
 
     /** @override */
-    requestFactoryResetRestart: function() {
+    requestFactoryResetRestart() {
       chrome.send('requestFactoryResetRestart');
-    },
+    }
     // </if>
-  };
+  }
+
+  cr.addSingletonGetter(ResetBrowserProxyImpl);
 
   return {
     ResetBrowserProxyImpl: ResetBrowserProxyImpl,
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js b/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js
index 5423903..76642485 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js
@@ -51,112 +51,108 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function SearchEnginesBrowserProxy() {}
-
-  SearchEnginesBrowserProxy.prototype = {
+  class SearchEnginesBrowserProxy {
     /** @param {number} modelIndex */
-    setDefaultSearchEngine: function(modelIndex) {},
+    setDefaultSearchEngine(modelIndex) {}
 
     /** @param {number} modelIndex */
-    removeSearchEngine: function(modelIndex) {},
+    removeSearchEngine(modelIndex) {}
 
     /** @param {number} modelIndex */
-    searchEngineEditStarted: function(modelIndex) {},
+    searchEngineEditStarted(modelIndex) {}
 
-    searchEngineEditCancelled: function() {},
+    searchEngineEditCancelled() {}
 
     /**
      * @param {string} searchEngine
      * @param {string} keyword
      * @param {string} queryUrl
      */
-    searchEngineEditCompleted: function(searchEngine, keyword, queryUrl) {},
+    searchEngineEditCompleted(searchEngine, keyword, queryUrl) {}
 
     /** @return {!Promise<!SearchEnginesInfo>} */
-    getSearchEnginesList: function() {},
+    getSearchEnginesList() {}
 
     /**
      * @param {string} fieldName
      * @param {string} fieldValue
      * @return {!Promise<boolean>}
      */
-    validateSearchEngineInput: function(fieldName, fieldValue) {},
+    validateSearchEngineInput(fieldName, fieldValue) {}
 
     /** @return {!Promise<!SearchPageHotwordInfo>} */
-    getHotwordInfo: function() {},
+    getHotwordInfo() {}
 
     /** @param {boolean} enabled */
-    setHotwordSearchEnabled: function(enabled) {},
+    setHotwordSearchEnabled(enabled) {}
 
     /** @return {!Promise<boolean>} */
-    getGoogleNowAvailability: function() {},
-  };
+    getGoogleNowAvailability() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.SearchEnginesBrowserProxy}
    */
-  function SearchEnginesBrowserProxyImpl() {}
-  // The singleton instance_ is replaced with a test version of this wrapper
-  // during testing.
-  cr.addSingletonGetter(SearchEnginesBrowserProxyImpl);
-
-  SearchEnginesBrowserProxyImpl.prototype = {
+  class SearchEnginesBrowserProxyImpl {
     /** @override */
-    setDefaultSearchEngine: function(modelIndex) {
+    setDefaultSearchEngine(modelIndex) {
       chrome.send('setDefaultSearchEngine', [modelIndex]);
-    },
+    }
 
     /** @override */
-    removeSearchEngine: function(modelIndex) {
+    removeSearchEngine(modelIndex) {
       chrome.send('removeSearchEngine', [modelIndex]);
-    },
+    }
 
     /** @override */
-    searchEngineEditStarted: function(modelIndex) {
+    searchEngineEditStarted(modelIndex) {
       chrome.send('searchEngineEditStarted', [modelIndex]);
-    },
+    }
 
     /** @override */
-    searchEngineEditCancelled: function() {
+    searchEngineEditCancelled() {
       chrome.send('searchEngineEditCancelled');
-    },
+    }
 
     /** @override */
-    searchEngineEditCompleted: function(searchEngine, keyword, queryUrl) {
+    searchEngineEditCompleted(searchEngine, keyword, queryUrl) {
       chrome.send('searchEngineEditCompleted', [
         searchEngine,
         keyword,
         queryUrl,
       ]);
-    },
+    }
 
     /** @override */
-    getSearchEnginesList: function() {
+    getSearchEnginesList() {
       return cr.sendWithPromise('getSearchEnginesList');
-    },
+    }
 
     /** @override */
-    validateSearchEngineInput: function(fieldName, fieldValue) {
+    validateSearchEngineInput(fieldName, fieldValue) {
       return cr.sendWithPromise(
           'validateSearchEngineInput', fieldName, fieldValue);
-    },
+    }
 
     /** @override */
-    getHotwordInfo: function() {
+    getHotwordInfo() {
       return cr.sendWithPromise('getHotwordInfo');
-    },
+    }
 
     /** @override */
-    setHotwordSearchEnabled: function(enabled) {
+    setHotwordSearchEnabled(enabled) {
       chrome.send('setHotwordSearchEnabled', [enabled]);
-    },
+    }
 
     /** @override */
-    getGoogleNowAvailability: function() {
+    getGoogleNowAvailability() {
       return cr.sendWithPromise('getGoogleNowAvailability');
-    },
-  };
+    }
+  }
+
+  // The singleton instance_ is replaced with a test version of this wrapper
+  // during testing.
+  cr.addSingletonGetter(SearchEnginesBrowserProxyImpl);
 
   return {
     SearchEnginesBrowserProxy: SearchEnginesBrowserProxy,
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index e2b6b6cc..395a62c 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -6,6 +6,12 @@
 <dom-module id="settings-shared">
   <template>
     <style include="settings-icons cr-shared-style">
+      /* Prevent action-links from being selected to avoid accidental
+       * selection when trying to click it. */
+      a[is=action-link] {
+        -webkit-user-select: none;
+      }
+
       /* Use <h2> as the "sub-header" mentioned in the UX design docs. */
       h2 {
         align-items: center;
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
index ecbd1c1..e335cdf 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -98,43 +98,41 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function SiteSettingsPrefsBrowserProxy() {}
-
-  SiteSettingsPrefsBrowserProxy.prototype = {
+  class SiteSettingsPrefsBrowserProxy {
     /**
      * Sets the default value for a site settings category.
      * @param {string} contentType The name of the category to change.
      * @param {string} defaultValue The name of the value to set as default.
      */
-    setDefaultValueForContentType: function(contentType, defaultValue) {},
+    setDefaultValueForContentType(contentType, defaultValue) {}
 
     /**
      * Gets the cookie details for a particular site.
      * @param {string} site The name of the site.
      * @return {!Promise<!CookieList>}
      */
-    getCookieDetails: function(site) {},
+    getCookieDetails(site) {}
 
     /**
      * Gets the default value for a site settings category.
      * @param {string} contentType The name of the category to query.
      * @return {!Promise<!DefaultContentSetting>}
      */
-    getDefaultValueForContentType: function(contentType) {},
+    getDefaultValueForContentType(contentType) {}
 
     /**
      * Gets the exceptions (site list) for a particular category.
      * @param {string} contentType The name of the category to query.
      * @return {!Promise<!Array<!RawSiteException>>}
      */
-    getExceptionList: function(contentType) {},
+    getExceptionList(contentType) {}
 
     /**
      * Gets the exception details for a particular site.
      * @param {string} site The name of the site.
      * @return {!Promise<!RawSiteException>}
      */
-    getSiteDetails: function(site) {},
+    getSiteDetails(site) {}
 
     /**
      * Resets the category permission for a given origin (expressed as primary
@@ -146,8 +144,8 @@
      * @param {boolean} incognito Whether this applies only to a current
      *     incognito session exception.
      */
-    resetCategoryPermissionForOrigin: function(
-        primaryPattern, secondaryPattern, contentType, incognito) {},
+    resetCategoryPermissionForOrigin(
+        primaryPattern, secondaryPattern, contentType, incognito) {}
 
     /**
      * Sets the category permission for a given origin (expressed as primary
@@ -160,36 +158,36 @@
      * @param {boolean} incognito Whether this rule applies only to the current
      *     incognito session.
      */
-    setCategoryPermissionForOrigin: function(
-        primaryPattern, secondaryPattern, contentType, value, incognito) {},
+    setCategoryPermissionForOrigin(
+        primaryPattern, secondaryPattern, contentType, value, incognito) {}
 
     /**
      * Checks whether a pattern is valid.
      * @param {string} pattern The pattern to check
      * @return {!Promise<boolean>} True if the pattern is valid.
      */
-    isPatternValid: function(pattern) {},
+    isPatternValid(pattern) {}
 
     /**
      * Gets the list of default capture devices for a given type of media. List
      * is returned through a JS call to updateDevicesMenu.
      * @param {string} type The type to look up.
      */
-    getDefaultCaptureDevices: function(type) {},
+    getDefaultCaptureDevices(type) {}
 
     /**
      * Sets a default devices for a given type of media.
      * @param {string} type The type of media to configure.
      * @param {string} defaultValue The id of the media device to set.
      */
-    setDefaultCaptureDevice: function(type, defaultValue) {},
+    setDefaultCaptureDevice(type, defaultValue) {}
 
     /**
      * Reloads all cookies.
      * @return {!Promise<!CookieList>} Returns the full cookie
      *     list.
      */
-    reloadCookies: function() {},
+    reloadCookies() {}
 
     /**
      * Fetches all children of a given cookie.
@@ -197,20 +195,20 @@
      * @return {!Promise<!Array<!CookieDataSummaryItem>>} Returns a cookie list
      *     for the given path.
      */
-    loadCookieChildren: function(path) {},
+    loadCookieChildren(path) {}
 
     /**
      * Removes a given cookie.
      * @param {string} path The path to the parent cookie.
      */
-    removeCookie: function(path) {},
+    removeCookie(path) {}
 
     /**
      * Removes all cookies.
      * @return {!Promise<!CookieList>} Returns the up to date
      *     cookie list once deletion is complete (empty list).
      */
-    removeAllCookies: function() {},
+    removeAllCookies() {}
 
     /**
      * observes _all_ of the the protocol handler state, which includes a list
@@ -218,7 +216,7 @@
      * other state sent with the messages 'setIgnoredProtocolHandler' and
      * 'setHandlersEnabled'.
      */
-    observeProtocolHandlers: function() {},
+    observeProtocolHandlers() {}
 
     /**
      * Observes one aspect of the protocol handler so that updates to the
@@ -229,34 +227,34 @@
      * If |observeProtocolHandlers| is called, there's no need to call this
      * observe as well.
      */
-    observeProtocolHandlersEnabledState: function() {},
+    observeProtocolHandlersEnabledState() {}
 
     /**
      * Enables or disables the ability for sites to ask to become the default
      * protocol handlers.
      * @param {boolean} enabled Whether sites can ask to become default.
      */
-    setProtocolHandlerDefault: function(enabled) {},
+    setProtocolHandlerDefault(enabled) {}
 
     /**
      * Sets a certain url as default for a given protocol handler.
      * @param {string} protocol The protocol to set a default for.
      * @param {string} url The url to use as the default.
      */
-    setProtocolDefault: function(protocol, url) {},
+    setProtocolDefault(protocol, url) {}
 
     /**
      * Deletes a certain protocol handler by url.
      * @param {string} protocol The protocol to delete the url from.
      * @param {string} url The url to delete.
      */
-    removeProtocolHandler: function(protocol, url) {},
+    removeProtocolHandler(protocol, url) {}
 
     /**
      * Fetches a list of all USB devices and the sites permitted to use them.
      * @return {!Promise<!Array<!UsbDeviceEntry>>} The list of USB devices.
      */
-    fetchUsbDevices: function() {},
+    fetchUsbDevices() {}
 
     /**
      * Removes a particular USB device object permission by origin and embedding
@@ -266,73 +264,66 @@
      * @param {!UsbDeviceDetails} usbDevice The USB device to revoke permission
      *     for.
      */
-    removeUsbDevice: function(origin, embeddingOrigin, usbDevice) {},
+    removeUsbDevice(origin, embeddingOrigin, usbDevice) {}
 
     /**
      * Fetches the incognito status of the current profile (whether an icognito
      * profile exists). Returns the results via onIncognitoStatusChanged.
      */
-    updateIncognitoStatus: function() {},
+    updateIncognitoStatus() {}
 
     /**
      * Fetches the currently defined zoom levels for sites. Returns the results
      * via onZoomLevelsChanged.
      */
-    fetchZoomLevels: function() {},
+    fetchZoomLevels() {}
 
     /**
      * Removes a zoom levels for a given host.
      * @param {string} host The host to remove zoom levels for.
      */
-    removeZoomLevel: function(host) {},
-  };
+    removeZoomLevel(host) {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.SiteSettingsPrefsBrowserProxy}
    */
-  function SiteSettingsPrefsBrowserProxyImpl() {}
-
-  // The singleton instance_ is replaced with a test version of this wrapper
-  // during testing.
-  cr.addSingletonGetter(SiteSettingsPrefsBrowserProxyImpl);
-
-  SiteSettingsPrefsBrowserProxyImpl.prototype = {
+  class SiteSettingsPrefsBrowserProxyImpl {
     /** @override */
-    setDefaultValueForContentType: function(contentType, defaultValue) {
+    setDefaultValueForContentType(contentType, defaultValue) {
       chrome.send('setDefaultValueForContentType', [contentType, defaultValue]);
-    },
+    }
 
     /** @override */
-    getCookieDetails: function(site) {
+    getCookieDetails(site) {
       return cr.sendWithPromise('getCookieDetails', site);
-    },
+    }
 
     /** @override */
-    getDefaultValueForContentType: function(contentType) {
+    getDefaultValueForContentType(contentType) {
       return cr.sendWithPromise('getDefaultValueForContentType', contentType);
-    },
+    }
 
     /** @override */
-    getExceptionList: function(contentType) {
+    getExceptionList(contentType) {
       return cr.sendWithPromise('getExceptionList', contentType);
-    },
+    }
 
     /** @override */
-    getSiteDetails: function(site) {
+    getSiteDetails(site) {
       return cr.sendWithPromise('getSiteDetails', site);
-    },
+    }
 
     /** @override */
-    resetCategoryPermissionForOrigin: function(
+    resetCategoryPermissionForOrigin(
         primaryPattern, secondaryPattern, contentType, incognito) {
       chrome.send(
           'resetCategoryPermissionForOrigin',
           [primaryPattern, secondaryPattern, contentType, incognito]);
-    },
+    }
 
     /** @override */
-    setCategoryPermissionForOrigin: function(
+    setCategoryPermissionForOrigin(
         primaryPattern, secondaryPattern, contentType, value, incognito) {
       // TODO(dschuyler): It may be incorrect for JS to send the embeddingOrigin
       // pattern. Look into removing this parameter from site_settings_handler.
@@ -340,93 +331,97 @@
       chrome.send(
           'setCategoryPermissionForOrigin',
           [primaryPattern, '', contentType, value, incognito]);
-    },
+    }
 
     /** @override */
-    isPatternValid: function(pattern) {
+    isPatternValid(pattern) {
       return cr.sendWithPromise('isPatternValid', pattern);
-    },
+    }
 
     /** @override */
-    getDefaultCaptureDevices: function(type) {
+    getDefaultCaptureDevices(type) {
       chrome.send('getDefaultCaptureDevices', [type]);
-    },
+    }
 
     /** @override */
-    setDefaultCaptureDevice: function(type, defaultValue) {
+    setDefaultCaptureDevice(type, defaultValue) {
       chrome.send('setDefaultCaptureDevice', [type, defaultValue]);
-    },
+    }
 
     /** @override */
-    reloadCookies: function() {
+    reloadCookies() {
       return cr.sendWithPromise('reloadCookies');
-    },
+    }
 
     /** @override */
-    loadCookieChildren: function(path) {
+    loadCookieChildren(path) {
       return cr.sendWithPromise('loadCookie', path);
-    },
+    }
 
     /** @override */
-    removeCookie: function(path) {
+    removeCookie(path) {
       chrome.send('removeCookie', [path]);
-    },
+    }
 
     /** @override */
-    removeAllCookies: function() {
+    removeAllCookies() {
       return cr.sendWithPromise('removeAllCookies');
-    },
+    }
 
     /** @override */
-    observeProtocolHandlers: function() {
+    observeProtocolHandlers() {
       chrome.send('observeProtocolHandlers');
-    },
+    }
 
     /** @override */
-    observeProtocolHandlersEnabledState: function() {
+    observeProtocolHandlersEnabledState() {
       chrome.send('observeProtocolHandlersEnabledState');
-    },
+    }
 
     /** @override */
-    setProtocolHandlerDefault: function(enabled) {
+    setProtocolHandlerDefault(enabled) {
       chrome.send('setHandlersEnabled', [enabled]);
-    },
+    }
 
     /** @override */
-    setProtocolDefault: function(protocol, url) {
+    setProtocolDefault(protocol, url) {
       chrome.send('setDefault', [[protocol, url]]);
-    },
+    }
 
     /** @override */
-    removeProtocolHandler: function(protocol, url) {
+    removeProtocolHandler(protocol, url) {
       chrome.send('removeHandler', [[protocol, url]]);
-    },
+    }
 
     /** @override */
-    fetchUsbDevices: function() {
+    fetchUsbDevices() {
       return cr.sendWithPromise('fetchUsbDevices');
-    },
+    }
 
     /** @override */
-    removeUsbDevice: function(origin, embeddingOrigin, usbDevice) {
+    removeUsbDevice(origin, embeddingOrigin, usbDevice) {
       chrome.send('removeUsbDevice', [origin, embeddingOrigin, usbDevice]);
-    },
+    }
 
     /** @override */
-    updateIncognitoStatus: function() {
+    updateIncognitoStatus() {
       chrome.send('updateIncognitoStatus');
-    },
+    }
 
     /** @override */
-    fetchZoomLevels: function() {
+    fetchZoomLevels() {
       chrome.send('fetchZoomLevels');
-    },
+    }
 
     /** @override */
-    removeZoomLevel: function(host) {
+    removeZoomLevel(host) {
       chrome.send('removeZoomLevel', [host]);
-    },
-  };
+    }
+  }
+
+  // The singleton instance_ is replaced with a test version of this wrapper
+  // during testing.
+  cr.addSingletonGetter(SiteSettingsPrefsBrowserProxyImpl);
 
   return {
     SiteSettingsPrefsBrowserProxy: SiteSettingsPrefsBrowserProxy,
diff --git a/chrome/browser/resources/settings/system_page/system_page_browser_proxy.js b/chrome/browser/resources/settings/system_page/system_page_browser_proxy.js
index b9ac51b3..3b4d509 100644
--- a/chrome/browser/resources/settings/system_page/system_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/system_page/system_page_browser_proxy.js
@@ -6,39 +6,34 @@
 
 cr.define('settings', function() {
   /** @interface */
-  function SystemPageBrowserProxy() {}
-
-  SystemPageBrowserProxy.prototype = {
+  class SystemPageBrowserProxy {
     /** Shows the native system proxy settings. */
-    showProxySettings: function() {},
+    showProxySettings() {}
 
     /**
      * @return {boolean} Whether hardware acceleration was enabled when the user
      *     started Chrome.
      */
-    wasHardwareAccelerationEnabledAtStartup: function() {},
-  };
+    wasHardwareAccelerationEnabledAtStartup() {}
+  }
 
   /**
-   * @constructor
    * @implements {settings.SystemPageBrowserProxy}
    */
-  function SystemPageBrowserProxyImpl() {}
+  class SystemPageBrowserProxyImpl {
+    /** @override */
+    showProxySettings() {
+      chrome.send('showProxySettings');
+    }
+
+    /** @override */
+    wasHardwareAccelerationEnabledAtStartup() {
+      return loadTimeData.getBoolean('hardwareAccelerationEnabledAtStartup');
+    }
+  }
 
   cr.addSingletonGetter(SystemPageBrowserProxyImpl);
 
-  SystemPageBrowserProxyImpl.prototype = {
-    /** @override */
-    showProxySettings: function() {
-      chrome.send('showProxySettings');
-    },
-
-    /** @override */
-    wasHardwareAccelerationEnabledAtStartup: function() {
-      return loadTimeData.getBoolean('hardwareAccelerationEnabledAtStartup');
-    },
-  };
-
   return {
     SystemPageBrowserProxy: SystemPageBrowserProxy,
     SystemPageBrowserProxyImpl: SystemPageBrowserProxyImpl,
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index f4123b4..7605395c 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -137,8 +137,9 @@
   if (net::GetValueForKeyInQuery(web_contents->GetURL(),
                                  "url",
                                  &url_param)) {
-    if (GURL(url_param).is_valid())
+    if (GURL(url_param).is_valid()) {
       request_url = GURL(url_param);
+    }
   }
   std::string overridable_param;
   if (net::GetValueForKeyInQuery(web_contents->GetURL(),
@@ -152,6 +153,12 @@
                                  &strict_enforcement_param)) {
     strict_enforcement = strict_enforcement_param == "1";
   }
+  std::string type_param;
+  if (net::GetValueForKeyInQuery(web_contents->GetURL(), "type", &type_param)) {
+    if (type_param == "hpkp_failure") {
+      cert_error = net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
+    }
+  }
   net::SSLInfo ssl_info;
   ssl_info.cert = ssl_info.unverified_cert = CreateFakeCert();
   // This delegate doesn't create an interstitial.
@@ -224,8 +231,9 @@
   if (net::GetValueForKeyInQuery(web_contents->GetURL(),
                                  "url",
                                  &url_param)) {
-    if (GURL(url_param).is_valid())
+    if (GURL(url_param).is_valid()) {
       request_url = GURL(url_param);
+    }
   }
   GURL main_frame_url(request_url);
   // TODO(mattm): add flag to change main_frame_url or add dedicated flag to
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
index 87e4fcd4..9147c8d 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
@@ -52,6 +52,11 @@
       "Privacy error");
 }
 
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, PinnedCertInterstitial) {
+  TestInterstitial(GURL("chrome://interstitials/ssl?type=hpkp_failure"),
+                   "Privacy error");
+}
+
 IN_PROC_BROWSER_TEST_F(InterstitialUITest, MalwareInterstitial) {
   TestInterstitial(
       GURL("chrome://interstitials/safebrowsing?type=malware"),
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
index ab599f61..de7fec70 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
@@ -29,6 +29,7 @@
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/offline_pages/core/prefetch/generate_page_bundle_request.h"
 #include "components/offline_pages/core/prefetch/get_operation_request.h"
+#include "components/offline_pages/core/prefetch/prefetch_downloader.h"
 #include "components/offline_pages/core/prefetch/prefetch_service.h"
 #include "content/public/browser/web_ui.h"
 #include "net/base/network_change_notifier.h"
@@ -348,6 +349,19 @@
           weak_ptr_factory_.GetWeakPtr(), callback_id)));
 }
 
+void OfflineInternalsUIMessageHandler::HandleDownloadArchive(
+    const base::ListValue* args) {
+  AllowJavascript();
+  std::string name;
+  CHECK(args->GetString(0, &name));
+  base::TrimWhitespaceASCII(name, base::TRIM_ALL, &name);
+
+  if (prefetch_service_) {
+    prefetch_service_->GetPrefetchDownloader()->StartDownload(
+        base::GenerateGUID(), name);
+  }
+}
+
 void OfflineInternalsUIMessageHandler::HandlePrefetchRequestCallback(
     std::string callback_id,
     offline_pages::PrefetchRequestStatus status,
@@ -510,6 +524,10 @@
       "getOperation",
       base::Bind(&OfflineInternalsUIMessageHandler::HandleGetOperation,
                  weak_ptr_factory_.GetWeakPtr()));
+  web_ui()->RegisterMessageCallback(
+      "downloadArchive",
+      base::Bind(&OfflineInternalsUIMessageHandler::HandleDownloadArchive,
+                 weak_ptr_factory_.GetWeakPtr()));
 
   // Get the offline page model associated with this web ui.
   Profile* profile = Profile::FromWebUI(web_ui());
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
index 61da1724..00a595de 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
@@ -82,6 +82,9 @@
   // Sends and processes a request to get the info about an operation.
   void HandleGetOperation(const base::ListValue* args);
 
+  // Downloads an archive.
+  void HandleDownloadArchive(const base::ListValue* args);
+
   // Callback for async GetAllPages calls.
   void HandleStoredPagesCallback(
       std::string callback_id,
@@ -109,12 +112,6 @@
       const std::string& operation_name,
       const std::vector<offline_pages::RenderPageInfo>& pages);
 
-  // Callback for GetOperation calls.
-  void HandleGetOperationCallback(
-      std::string callback_id,
-      offline_pages::PrefetchRequestStatus status,
-      const std::vector<offline_pages::RenderPageInfo>& pages);
-
   // Offline page model to call methods on.
   offline_pages::OfflinePageModel* offline_page_model_;
 
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 928d1b9..c6fe9e83 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -754,11 +754,9 @@
       {"powerIdleDisplayOff", IDS_SETTINGS_POWER_IDLE_DISPLAY_OFF},
       {"powerIdleDisplayOn", IDS_SETTINGS_POWER_IDLE_DISPLAY_ON},
       {"powerIdleOther", IDS_SETTINGS_POWER_IDLE_OTHER},
-      {"powerLidClosedLabel", IDS_SETTINGS_POWER_LID_CLOSED_LABEL},
-      {"powerLidClosedSleep", IDS_SETTINGS_POWER_LID_CLOSED_SLEEP},
-      {"powerLidClosedStayAwake", IDS_SETTINGS_POWER_LID_CLOSED_STAY_AWAKE},
-      {"powerLidClosedSignOut", IDS_SETTINGS_POWER_LID_CLOSED_SIGN_OUT},
-      {"powerLidClosedShutDown", IDS_SETTINGS_POWER_LID_CLOSED_SHUT_DOWN},
+      {"powerLidSleepLabel", IDS_SETTINGS_POWER_LID_CLOSED_SLEEP_LABEL},
+      {"powerLidSignOutLabel", IDS_SETTINGS_POWER_LID_CLOSED_SIGN_OUT_LABEL},
+      {"powerLidShutDownLabel", IDS_SETTINGS_POWER_LID_CLOSED_SHUT_DOWN_LABEL},
   };
   AddLocalizedStringsBulk(html_source, power_strings, arraysize(power_strings));
 
diff --git a/chrome/browser/win/chrome_select_file_dialog_factory.cc b/chrome/browser/win/chrome_select_file_dialog_factory.cc
index 87338fe..a0ea71c2 100644
--- a/chrome/browser/win/chrome_select_file_dialog_factory.cc
+++ b/chrome/browser/win/chrome_select_file_dialog_factory.cc
@@ -4,319 +4,113 @@
 
 #include "chrome/browser/win/chrome_select_file_dialog_factory.h"
 
-#include <Windows.h>
-#include <commdlg.h>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/metrics/field_trial.h"
+#include "base/feature_list.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
-#include "base/synchronization/waitable_event.h"
-#include "chrome/common/chrome_utility_messages.h"
+#include "base/strings/string_util.h"
+#include "base/win/win_util.h"
+#include "chrome/common/shell_handler_win.mojom.h"
 #include "chrome/grit/generated_resources.h"
-#include "content/public/browser/utility_process_host.h"
-#include "content/public/browser/utility_process_host_client.h"
-#include "ipc/ipc_message_macros.h"
+#include "content/public/browser/utility_process_mojo_client.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/win/open_file_name_win.h"
 #include "ui/shell_dialogs/select_file_dialog_win.h"
 
 namespace {
 
-bool ShouldIsolateShellOperations() {
-  return base::FieldTrialList::FindFullName("IsolateShellOperations") ==
-         "Enabled";
-}
+constexpr base::Feature kIsolateShellOperations{
+    "IsolateShellOperations", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Receives the GetOpenFileName result from the utility process.
-class GetOpenFileNameClient : public content::UtilityProcessHostClient {
- public:
-  GetOpenFileNameClient();
+using UtilityProcessClient =
+    content::UtilityProcessMojoClient<chrome::mojom::ShellHandler>;
 
-  // Blocks until the GetOpenFileName result is received (including failure to
-  // launch or a crash of the utility process).
-  void WaitForCompletion();
+std::unique_ptr<UtilityProcessClient> StartUtilityProcess() {
+  auto utility_process_client = base::MakeUnique<UtilityProcessClient>(
+      l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_FILE_DIALOG_NAME));
 
-  // Returns the selected directory.
-  const base::FilePath& directory() const { return directory_; }
+  // TODO(crbug.com/618459): should we change the mojo utility client
+  // to allow an empty error callback?  Currently, the client DCHECKs
+  // if no error callback is set when Start() is called.
+  utility_process_client->set_error_callback(base::Bind(&base::DoNothing));
 
-  // Returns the list of selected filenames. Each should be interpreted as a
-  // child of directory().
-  const std::vector<base::FilePath>& filenames() const { return filenames_; }
+  utility_process_client->set_disable_sandbox();
 
-  // UtilityProcessHostClient implementation
-  void OnProcessCrashed(int exit_code) override;
-  void OnProcessLaunchFailed(int error_code) override;
-  bool OnMessageReceived(const IPC::Message& message) override;
+  utility_process_client->Start();
 
- protected:
-  ~GetOpenFileNameClient() override;
-
- private:
-  void OnResult(const base::FilePath& directory,
-                const std::vector<base::FilePath>& filenames);
-  void OnFailure();
-
-  base::FilePath directory_;
-  std::vector<base::FilePath> filenames_;
-  base::WaitableEvent event_;
-
-  DISALLOW_COPY_AND_ASSIGN(GetOpenFileNameClient);
-};
-
-GetOpenFileNameClient::GetOpenFileNameClient()
-    : event_(base::WaitableEvent::ResetPolicy::MANUAL,
-             base::WaitableEvent::InitialState::NOT_SIGNALED) {}
-
-void GetOpenFileNameClient::WaitForCompletion() {
-  event_.Wait();
-}
-
-void GetOpenFileNameClient::OnProcessCrashed(int exit_code) {
-  event_.Signal();
-}
-
-void GetOpenFileNameClient::OnProcessLaunchFailed(int error_code) {
-  event_.Signal();
-}
-
-bool GetOpenFileNameClient::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(GetOpenFileNameClient, message)
-    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Failed,
-                        OnFailure)
-    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetOpenFileName_Result,
-                        OnResult)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-GetOpenFileNameClient::~GetOpenFileNameClient() {}
-
-void GetOpenFileNameClient::OnResult(
-    const base::FilePath& directory,
-    const std::vector<base::FilePath>& filenames) {
-  directory_ = directory;
-  filenames_ = filenames;
-  event_.Signal();
-}
-
-void GetOpenFileNameClient::OnFailure() {
-  event_.Signal();
-}
-
-// Initiates IPC with a new utility process using |client|. Instructs the
-// utility process to call GetOpenFileName with |ofn|. |current_task_runner|
-// must be the currently executing task runner.
-void DoInvokeGetOpenFileName(
-    OPENFILENAME* ofn,
-    scoped_refptr<GetOpenFileNameClient> client,
-    const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) {
-  DCHECK(current_task_runner->RunsTasksInCurrentSequence());
-
-  base::WeakPtr<content::UtilityProcessHost> utility_process_host(
-      content::UtilityProcessHost::Create(client, current_task_runner)
-      ->AsWeakPtr());
-  utility_process_host->SetName(l10n_util::GetStringUTF16(
-      IDS_UTILITY_PROCESS_FILE_DIALOG_NAME));
-  utility_process_host->DisableSandbox();
-  utility_process_host->Send(new ChromeUtilityMsg_GetOpenFileName(
-      ofn->hwndOwner,
-      ofn->Flags & ~OFN_ENABLEHOOK,  // We can't send a hook function over IPC.
-      ui::win::OpenFileName::GetFilters(ofn),
-      base::FilePath(ofn->lpstrInitialDir ? ofn->lpstrInitialDir
-                                          : base::string16()),
-      base::FilePath(ofn->lpstrFile)));
-}
-
-// Invokes GetOpenFileName in a utility process. Blocks until the result is
-// received. Uses |blocking_task_runner| for IPC.
-bool GetOpenFileNameInUtilityProcess(
-    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
-    OPENFILENAME* ofn) {
-  scoped_refptr<GetOpenFileNameClient> client(new GetOpenFileNameClient);
-  blocking_task_runner->PostTask(
-      FROM_HERE,
-      base::Bind(&DoInvokeGetOpenFileName,
-                 base::Unretained(ofn), client, blocking_task_runner));
-  client->WaitForCompletion();
-
-  if (client->filenames().empty())
-    return false;
-
-  ui::win::OpenFileName::SetResult(
-      client->directory(), client->filenames(), ofn);
-  return true;
-}
-
-// Implements GetOpenFileName for CreateWinSelectFileDialog by delegating to a
-// utility process.
-bool GetOpenFileNameImpl(
-    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
-    OPENFILENAME* ofn) {
-  if (ShouldIsolateShellOperations())
-    return GetOpenFileNameInUtilityProcess(blocking_task_runner, ofn);
-
-  return ::GetOpenFileName(ofn) == TRUE;
-}
-
-class GetSaveFileNameClient : public content::UtilityProcessHostClient {
- public:
-  GetSaveFileNameClient();
-
-  // Blocks until the GetSaveFileName result is received (including failure to
-  // launch or a crash of the utility process).
-  void WaitForCompletion();
-
-  // Returns the selected path.
-  const base::FilePath& path() const { return path_; }
-
-  // Returns the index of the user-selected filter.
-  int one_based_filter_index() const { return one_based_filter_index_; }
-
-  // UtilityProcessHostClient implementation
-  void OnProcessCrashed(int exit_code) override;
-  void OnProcessLaunchFailed(int error_code) override;
-  bool OnMessageReceived(const IPC::Message& message) override;
-
- protected:
-  ~GetSaveFileNameClient() override;
-
- private:
-  void OnResult(const base::FilePath& path, int one_based_filter_index);
-  void OnFailure();
-
-  base::FilePath path_;
-  int one_based_filter_index_;
-  base::WaitableEvent event_;
-
-  DISALLOW_COPY_AND_ASSIGN(GetSaveFileNameClient);
-};
-
-GetSaveFileNameClient::GetSaveFileNameClient()
-    : one_based_filter_index_(0),
-      event_(base::WaitableEvent::ResetPolicy::MANUAL,
-             base::WaitableEvent::InitialState::NOT_SIGNALED) {}
-
-void GetSaveFileNameClient::WaitForCompletion() {
-  event_.Wait();
-}
-
-void GetSaveFileNameClient::OnProcessCrashed(int exit_code) {
-  event_.Signal();
-}
-
-void GetSaveFileNameClient::OnProcessLaunchFailed(int error_code) {
-  event_.Signal();
-}
-
-bool GetSaveFileNameClient::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(GetSaveFileNameClient, message)
-    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Failed,
-                        OnFailure)
-    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetSaveFileName_Result,
-                        OnResult)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-GetSaveFileNameClient::~GetSaveFileNameClient() {}
-
-void GetSaveFileNameClient::OnResult(const base::FilePath& path,
-                                     int one_based_filter_index) {
-  path_ = path;
-  one_based_filter_index_ = one_based_filter_index;
-  event_.Signal();
-}
-
-void GetSaveFileNameClient::OnFailure() {
-  event_.Signal();
-}
-
-// Initiates IPC with a new utility process using |client|. Instructs the
-// utility process to call GetSaveFileName with |ofn|. |current_task_runner|
-// must be the currently executing task runner.
-void DoInvokeGetSaveFileName(
-    OPENFILENAME* ofn,
-    scoped_refptr<GetSaveFileNameClient> client,
-    const scoped_refptr<base::SequencedTaskRunner>& current_task_runner) {
-  DCHECK(current_task_runner->RunsTasksInCurrentSequence());
-
-  base::WeakPtr<content::UtilityProcessHost> utility_process_host(
-      content::UtilityProcessHost::Create(client, current_task_runner)
-      ->AsWeakPtr());
-  utility_process_host->SetName(l10n_util::GetStringUTF16(
-      IDS_UTILITY_PROCESS_FILE_DIALOG_NAME));
-  utility_process_host->DisableSandbox();
-  ChromeUtilityMsg_GetSaveFileName_Params params;
-  params.owner = ofn->hwndOwner;
-  // We can't pass the hook function over IPC.
-  params.flags = ofn->Flags & ~OFN_ENABLEHOOK;
-  params.filters = ui::win::OpenFileName::GetFilters(ofn);
-  params.one_based_filter_index = ofn->nFilterIndex;
-  params.suggested_filename = base::FilePath(ofn->lpstrFile);
-  params.initial_directory = base::FilePath(
-      ofn->lpstrInitialDir ? ofn->lpstrInitialDir : base::string16());
-  params.default_extension =
-      ofn->lpstrDefExt ? base::string16(ofn->lpstrDefExt) : base::string16();
-
-  utility_process_host->Send(new ChromeUtilityMsg_GetSaveFileName(params));
-}
-
-// Invokes GetSaveFileName in a utility process. Blocks until the result is
-// received. Uses |blocking_task_runner| for IPC.
-bool GetSaveFileNameInUtilityProcess(
-    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
-    OPENFILENAME* ofn) {
-  scoped_refptr<GetSaveFileNameClient> client(new GetSaveFileNameClient);
-  blocking_task_runner->PostTask(
-      FROM_HERE,
-      base::Bind(&DoInvokeGetSaveFileName,
-                 base::Unretained(ofn), client, blocking_task_runner));
-  client->WaitForCompletion();
-
-  if (client->path().empty())
-    return false;
-
-  base::wcslcpy(ofn->lpstrFile, client->path().value().c_str(), ofn->nMaxFile);
-  ofn->nFilterIndex = client->one_based_filter_index();
-
-  return true;
-}
-
-// Implements GetSaveFileName for CreateWinSelectFileDialog by delegating to a
-// utility process.
-bool GetSaveFileNameImpl(
-    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
-    OPENFILENAME* ofn) {
-  if (ShouldIsolateShellOperations())
-    return GetSaveFileNameInUtilityProcess(blocking_task_runner, ofn);
-
-  return ::GetSaveFileName(ofn) == TRUE;
+  return utility_process_client;
 }
 
 }  // namespace
 
-ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory(
-    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
-    : blocking_task_runner_(blocking_task_runner) {
-}
+ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory() = default;
 
-ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() {}
+ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() = default;
 
 ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create(
     ui::SelectFileDialog::Listener* listener,
     ui::SelectFilePolicy* policy) {
   return ui::CreateWinSelectFileDialog(
-      listener,
-      policy,
-      base::Bind(GetOpenFileNameImpl, blocking_task_runner_),
-      base::Bind(GetSaveFileNameImpl, blocking_task_runner_));
+      listener, policy,
+      base::Bind(&ChromeSelectFileDialogFactory::BlockingGetOpenFileName),
+      base::Bind(&ChromeSelectFileDialogFactory::BlockingGetSaveFileName));
+}
+
+// static
+bool ChromeSelectFileDialogFactory::BlockingGetOpenFileName(OPENFILENAME* ofn) {
+  if (!base::FeatureList::IsEnabled(kIsolateShellOperations))
+    return ::GetOpenFileName(ofn) == TRUE;
+
+  auto utility_process_client = StartUtilityProcess();
+
+  mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
+  std::vector<base::FilePath> files;
+  base::FilePath directory;
+
+  utility_process_client->service()->CallGetOpenFileName(
+      base::win::HandleToUint32(ofn->hwndOwner),
+      static_cast<uint32_t>(ofn->Flags & ~OFN_ENABLEHOOK),
+      ui::win::OpenFileName::GetFilters(ofn),
+      ofn->lpstrInitialDir ? base::FilePath(ofn->lpstrInitialDir)
+                           : base::FilePath(),
+      base::FilePath(ofn->lpstrFile), &directory, &files);
+
+  if (files.empty())
+    return false;
+
+  ui::win::OpenFileName::SetResult(directory, files, ofn);
+  return true;
+}
+
+// static
+bool ChromeSelectFileDialogFactory::BlockingGetSaveFileName(OPENFILENAME* ofn) {
+  if (!base::FeatureList::IsEnabled(kIsolateShellOperations))
+    return ::GetSaveFileName(ofn) == TRUE;
+
+  auto utility_process_client = StartUtilityProcess();
+
+  mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
+  uint32_t filter_index = 0;
+  base::FilePath path;
+
+  utility_process_client->service()->CallGetSaveFileName(
+      base::win::HandleToUint32(ofn->hwndOwner),
+      static_cast<uint32_t>(ofn->Flags & ~OFN_ENABLEHOOK),
+      ui::win::OpenFileName::GetFilters(ofn), ofn->nFilterIndex,
+      ofn->lpstrInitialDir ? base::FilePath(ofn->lpstrInitialDir)
+                           : base::FilePath(),
+      base::FilePath(ofn->lpstrFile),
+      ofn->lpstrDefExt ? base::string16(ofn->lpstrDefExt) : base::string16(),
+      &path, &filter_index);
+
+  if (path.empty())
+    return false;
+
+  base::wcslcpy(ofn->lpstrFile, path.value().c_str(), ofn->nMaxFile);
+  ofn->nFilterIndex = static_cast<DWORD>(filter_index);
+  return true;
 }
diff --git a/chrome/browser/win/chrome_select_file_dialog_factory.h b/chrome/browser/win/chrome_select_file_dialog_factory.h
index b1d8745f..6c0f1c0 100644
--- a/chrome/browser/win/chrome_select_file_dialog_factory.h
+++ b/chrome/browser/win/chrome_select_file_dialog_factory.h
@@ -5,32 +5,27 @@
 #ifndef CHROME_BROWSER_WIN_CHROME_SELECT_FILE_DIALOG_FACTORY_H_
 #define CHROME_BROWSER_WIN_CHROME_SELECT_FILE_DIALOG_FACTORY_H_
 
-#include "base/compiler_specific.h"
+#include <Windows.h>
+#include <commdlg.h>
+
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "ui/shell_dialogs/select_file_dialog_factory.h"
 
-namespace base {
-class SequencedTaskRunner;
-}  // namespace base
-
-// Implements a Select File dialog that delegates to a Metro file picker on
-// Metro and to a utility process otherwise. The utility process is used in
-// order to isolate the Chrome browser process from potential instability
-// caused by Shell extension modules loaded by GetOpenFileName.
+// Implements a file Open / Save dialog in a utility process. The utility
+// process is used to isolate the Chrome browser process from potential
+// instability caused by Shell extension modules loaded by ::GetOpenFileName
+// and ::GetSaveFileName.
 class ChromeSelectFileDialogFactory : public ui::SelectFileDialogFactory {
  public:
-  // Uses |blocking_task_runner| to perform IPC with the utility process.
-  explicit ChromeSelectFileDialogFactory(
-      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner);
+  ChromeSelectFileDialogFactory();
   ~ChromeSelectFileDialogFactory() override;
 
-  // ui::SelectFileDialogFactory implementation
+  // ui::SelectFileDialogFactory:
   ui::SelectFileDialog* Create(ui::SelectFileDialog::Listener* listener,
                                ui::SelectFilePolicy* policy) override;
-
  private:
-  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+  static bool BlockingGetOpenFileName(OPENFILENAME* ofn);
+  static bool BlockingGetSaveFileName(OPENFILENAME* ofn);
 
   DISALLOW_COPY_AND_ASSIGN(ChromeSelectFileDialogFactory);
 };
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 5a5cd912..be73029 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -659,7 +659,6 @@
 mojom("mojo_bindings") {
   sources = [
     "cache_stats_recorder.mojom",
-    "conflicts/module_event_sink_win.mojom",
     "file_patcher.mojom",
     "image_context_menu_renderer.mojom",
     "insecure_content_renderer.mojom",
@@ -669,10 +668,16 @@
     "prerender.mojom",
     "renderer_configuration.mojom",
     "resource_usage_reporter.mojom",
-    "shell_handler_win.mojom",
     "thumbnail_capturer.mojom",
   ]
 
+  if (is_win) {
+    sources += [
+      "conflicts/module_event_sink_win.mojom",
+      "shell_handler_win.mojom",
+    ]
+  }
+
   if (is_chromeos) {
     sources += [ "zip_file_creator.mojom" ]
   }
diff --git a/chrome/common/chrome_utility_messages.h b/chrome/common/chrome_utility_messages.h
index 045a338..a60c443 100644
--- a/chrome/common/chrome_utility_messages.h
+++ b/chrome/common/chrome_utility_messages.h
@@ -4,17 +4,9 @@
 
 // Multiply-included message file, so no include guard.
 
-#if defined(OS_WIN)
-#include <Windows.h>
-#endif  // defined(OS_WIN)
+// TODO(noel): rename this file to a param traits variant for the full safe
+// browsing feature, and remove all remaining #include of this file.
 
-#include <string>
-#include <tuple>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/strings/string16.h"
-#include "base/values.h"
 #include "build/build_config.h"
 #include "ipc/ipc_message_macros.h"
 
@@ -24,21 +16,6 @@
 #include "chrome/common/safe_browsing/protobuf_message_param_traits.h"
 #endif
 
-// Singly-included section for typedefs.
-#ifndef CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_
-#define CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_
-
-#if defined(OS_WIN)
-// A vector of filters, each being a tuple containing a display string (i.e.
-// "Text Files") and a filter pattern (i.e. "*.txt").
-typedef std::vector<std::tuple<base::string16, base::string16>>
-    GetOpenFileNameFilter;
-#endif  // OS_WIN
-
-#endif  // CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_
-
-#define IPC_MESSAGE_START ChromeUtilityMsgStart
-
 #if defined(FULL_SAFE_BROWSING)
 IPC_ENUM_TRAITS_VALIDATE(
     safe_browsing::ClientDownloadRequest_DownloadType,
@@ -120,53 +97,3 @@
   IPC_STRUCT_TRAITS_MEMBER(archived_archive_filenames)
 IPC_STRUCT_TRAITS_END()
 #endif  // FULL_SAFE_BROWSING
-
-#if defined(OS_WIN)
-IPC_STRUCT_BEGIN(ChromeUtilityMsg_GetSaveFileName_Params)
-  IPC_STRUCT_MEMBER(HWND, owner)
-  IPC_STRUCT_MEMBER(DWORD, flags)
-  IPC_STRUCT_MEMBER(GetOpenFileNameFilter, filters)
-  IPC_STRUCT_MEMBER(int, one_based_filter_index)
-  IPC_STRUCT_MEMBER(base::FilePath, suggested_filename)
-  IPC_STRUCT_MEMBER(base::FilePath, initial_directory)
-  IPC_STRUCT_MEMBER(base::string16, default_extension)
-IPC_STRUCT_END()
-#endif  // OS_WIN
-
-//------------------------------------------------------------------------------
-// Utility process messages:
-// These are messages from the browser to the utility process.
-
-#if defined(OS_WIN)
-// Instructs the utility process to invoke GetOpenFileName. |owner| is the
-// parent of the modal dialog, |flags| are OFN_* flags. |filter| constrains the
-// user's file choices. |initial_directory| and |filename| select the directory
-// to be displayed and the file to be initially selected.
-//
-// Either ChromeUtilityHostMsg_GetOpenFileName_Failed or
-// ChromeUtilityHostMsg_GetOpenFileName_Result will be returned when the
-// operation completes whether due to error or user action.
-IPC_MESSAGE_CONTROL5(ChromeUtilityMsg_GetOpenFileName,
-                     HWND /* owner */,
-                     DWORD /* flags */,
-                     GetOpenFileNameFilter /* filter */,
-                     base::FilePath /* initial_directory */,
-                     base::FilePath /* filename */)
-IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_GetSaveFileName,
-                     ChromeUtilityMsg_GetSaveFileName_Params /* params */)
-#endif  // defined(OS_WIN)
-
-//------------------------------------------------------------------------------
-// Utility process host messages:
-// These are messages from the utility process to the browser.
-
-#if defined(OS_WIN)
-IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_GetOpenFileName_Failed)
-IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_GetOpenFileName_Result,
-                     base::FilePath /* directory */,
-                     std::vector<base::FilePath> /* filenames */)
-IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_GetSaveFileName_Failed)
-IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_GetSaveFileName_Result,
-                     base::FilePath /* path */,
-                     int /* one_based_filter_index  */)
-#endif  // defined(OS_WIN)
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index efd6292..fc7203d 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -57,6 +57,12 @@
 // A preference that indicates that user accepted Assistant Value Prop.
 const char kArcVoiceInteractionValuePropAccepted[] =
     "arc.voice_interaction_value_prop.accepted";
+// A preference that indicates the user has enabled voice interaction services.
+const char kVoiceInteractionEnabled[] = "settings.voice_interaction.enabled";
+// A preference that indicates the user has enabled providing context to
+// voice interaction services.
+const char kVoiceInteractionContextEnabled[] =
+    "settings.voice_interaction.context.enabled";
 #endif
 
 // A bool pref that keeps whether the child status for this profile was already
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 184079a..a788216a 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -34,6 +34,8 @@
 extern const char kArcSignedIn[];
 extern const char kArcCompatibleFilesystemChosen[];
 extern const char kArcVoiceInteractionValuePropAccepted[];
+extern const char kVoiceInteractionEnabled[];
+extern const char kVoiceInteractionContextEnabled[];
 #endif
 extern const char kChildAccountStatusKnown[];
 extern const char kDefaultApps[];
diff --git a/chrome/common/profiling/BUILD.gn b/chrome/common/profiling/BUILD.gn
index 16464eea..b455adbb 100644
--- a/chrome/common/profiling/BUILD.gn
+++ b/chrome/common/profiling/BUILD.gn
@@ -12,6 +12,8 @@
       "memlog_sender.cc",
       "memlog_sender.h",
       "memlog_sender_pipe.h",
+      "memlog_sender_pipe_posix.cc",
+      "memlog_sender_pipe_posix.h",
       "memlog_sender_pipe_win.cc",
       "memlog_sender_pipe_win.h",
       "memlog_stream.cc",
diff --git a/chrome/common/profiling/memlog_allocator_shim.cc b/chrome/common/profiling/memlog_allocator_shim.cc
index 9ff84cf..346837f5 100644
--- a/chrome/common/profiling/memlog_allocator_shim.cc
+++ b/chrome/common/profiling/memlog_allocator_shim.cc
@@ -5,10 +5,12 @@
 #include "chrome/common/profiling/memlog_allocator_shim.h"
 
 #include "base/allocator/allocator_shim.h"
+#include "base/allocator/features.h"
 #include "base/debug/debugging_flags.h"
 #include "base/debug/stack_trace.h"
 #include "base/synchronization/lock.h"
 #include "base/trace_event/heap_profiler_allocation_register.h"
+#include "build/build_config.h"
 #include "chrome/common/profiling/memlog_stream.h"
 
 namespace profiling {
@@ -71,6 +73,7 @@
   g_send_buffers[bin_to_use].Send(data, size);
 }
 
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
 void* HookAlloc(const AllocatorDispatch* self, size_t size, void* context) {
   const AllocatorDispatch* const next = self->next;
   void* ptr = next->alloc_function(next, size, context);
@@ -167,6 +170,7 @@
     &HookFreeDefiniteSize,  // free_definite_size_function
     nullptr,                // next
 };
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
 
 }  // namespace
 
@@ -174,7 +178,7 @@
   g_send_buffers = new SendBuffer[kNumSendBuffers];
 
   g_sender_pipe = sender_pipe;
-#ifdef NDEBUG
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
   base::allocator::InsertAllocatorDispatch(&g_memlog_hooks);
 #endif
 }
diff --git a/chrome/common/profiling/memlog_sender_pipe.h b/chrome/common/profiling/memlog_sender_pipe.h
index ba2653e..5123ff0 100644
--- a/chrome/common/profiling/memlog_sender_pipe.h
+++ b/chrome/common/profiling/memlog_sender_pipe.h
@@ -9,6 +9,8 @@
 
 #if defined(OS_WIN)
 #include "chrome/common/profiling/memlog_sender_pipe_win.h"
+#else
+#include "chrome/common/profiling/memlog_sender_pipe_posix.h"
 #endif
 
 #endif  // CHROME_COMMON_PROFILING_MEMLOG_SENDER_PIPE_H_
diff --git a/chrome/common/profiling/memlog_sender_pipe_posix.cc b/chrome/common/profiling/memlog_sender_pipe_posix.cc
new file mode 100644
index 0000000..23cd27761
--- /dev/null
+++ b/chrome/common/profiling/memlog_sender_pipe_posix.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/profiling/memlog_sender_pipe_posix.h"
+
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "chrome/common/profiling/memlog_stream.h"
+
+namespace profiling {
+
+MemlogSenderPipe::MemlogSenderPipe(const std::string& pipe_id)
+    : pipe_id_(pipe_id), fd_(-1) {}
+
+MemlogSenderPipe::~MemlogSenderPipe() {
+  if (fd_ != -1)
+    IGNORE_EINTR(::close(fd_));
+}
+
+bool MemlogSenderPipe::Connect() {
+  return false;
+}
+
+bool MemlogSenderPipe::Send(const void* data, size_t sz) {
+  return false;
+}
+
+}  // namespace profiling
diff --git a/chrome/common/profiling/memlog_sender_pipe_posix.h b/chrome/common/profiling/memlog_sender_pipe_posix.h
new file mode 100644
index 0000000..41aef2f
--- /dev/null
+++ b/chrome/common/profiling/memlog_sender_pipe_posix.h
@@ -0,0 +1,33 @@
+// 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_COMMON_PROFILING_MEMLOG_SENDER_PIPE_POSIX_H_
+#define CHROME_COMMON_PROFILING_MEMLOG_SENDER_PIPE_POSIX_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace profiling {
+
+class MemlogSenderPipe {
+ public:
+  explicit MemlogSenderPipe(const std::string& pipe_id);
+  ~MemlogSenderPipe();
+
+  bool Connect();
+
+  bool Send(const void* data, size_t sz);
+
+ private:
+  std::string pipe_id_;
+
+  int fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemlogSenderPipe);
+};
+
+}  // namespace profiling
+
+#endif  // CHROME_COMMON_PROFILING_MEMLOG_SENDER_PIPE_POSIX_H_
diff --git a/chrome/common/shell_handler_win.mojom b/chrome/common/shell_handler_win.mojom
index 0739aad99..099e084 100644
--- a/chrome/common/shell_handler_win.mojom
+++ b/chrome/common/shell_handler_win.mojom
@@ -2,9 +2,59 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// Utility process interface exposed to the browser process on OS_WIN, for
+// calling OS_WIN task pinning and file open/save dialog API.
+
 module chrome.mojom;
 
+import "mojo/common/file_path.mojom";
+import "mojo/common/string16.mojom";
+
 interface ShellHandler {
   // Returns the pinned state of the current executable.
   IsPinnedToTaskbar() => (bool succeeded, bool is_pinned_to_taskbar);
+
+  // Calls OS_WIN ::GetOpenFileName() with parameters:
+  // |owner| HWND to use as the parent of the modal dialog.
+  // |flags| OFN_* flags to use with OPENFILENAME.
+  // |filters| constrains the user's file type selections.
+  // |initial_directory| initial directory to be displayed.
+  // |initial_filename| file name initially selected.
+  //
+  // Returns the list of selected |files| from |directory|. On cancelation
+  // or failure, |files| will be empty.
+  [Sync]
+  CallGetOpenFileName(uint32 owner,
+                      uint32 flags,
+                      FileExtensionFilters filters,
+                      mojo.common.mojom.FilePath initial_directory,
+                      mojo.common.mojom.FilePath initial_filename) =>
+      (mojo.common.mojom.FilePath directory,
+       array<mojo.common.mojom.FilePath> files);
+
+  // Calls OS_WIN ::GetSaveFileName() with parameters:
+  // |owner| HWND to use as the parent of the modal dialog.
+  // |flags| OFN_* flags to use with OPENFILENAME.
+  // |filters| constrains the user's file type selections.
+  // |one_based_filter_index| index of the selected filter in |filters|.
+  // |initial_directory| initial directory to be displayed.
+  // |suggested_filename| save file name to suggest.
+  // |default_extension| file extension to use if the user doesn't type one.
+  //
+  // Returns the save file |path| selected, and the |filter_index| of the
+  // filter selected, by the user. On cancelation or failure, |path| will
+  // be empty.
+  [Sync]
+  CallGetSaveFileName(uint32 owner,
+                      uint32 flags,
+                      FileExtensionFilters filters,
+                      uint32 one_based_filter_index,
+                      mojo.common.mojom.FilePath initial_directory,
+                      mojo.common.mojom.FilePath suggested_filename,
+                      mojo.common.mojom.String16 default_extension) =>
+      (mojo.common.mojom.FilePath path,
+       uint32 filter_index);
 };
+
+[Native]
+struct FileExtensionFilters;
diff --git a/chrome/common/shell_handler_win.typemap b/chrome/common/shell_handler_win.typemap
new file mode 100644
index 0000000..0a596ac
--- /dev/null
+++ b/chrome/common/shell_handler_win.typemap
@@ -0,0 +1,14 @@
+# 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 = "//chrome/common/shell_handler_win.mojom"
+
+public_headers = [ "//base/strings/string16.h" ]
+
+deps = [
+  "//base",
+]
+
+type_mappings = [ "chrome.mojom.FileExtensionFilters=" +
+                  "std::vector<std::tuple<base::string16, base::string16>>" ]
diff --git a/chrome/profiling/BUILD.gn b/chrome/profiling/BUILD.gn
index 1adceab..5cd6cc4 100644
--- a/chrome/profiling/BUILD.gn
+++ b/chrome/profiling/BUILD.gn
@@ -17,7 +17,11 @@
       "memlog_connection_manager.cc",
       "memlog_connection_manager.h",
       "memlog_receiver.h",
+      "memlog_receiver_pipe_posix.cc",
+      "memlog_receiver_pipe_posix.h",
       "memlog_receiver_pipe_server.h",
+      "memlog_receiver_pipe_server_posix.cc",
+      "memlog_receiver_pipe_server_posix.h",
       "memlog_receiver_pipe_server_win.cc",
       "memlog_receiver_pipe_server_win.h",
       "memlog_receiver_pipe_win.cc",
diff --git a/chrome/profiling/README.md b/chrome/profiling/README.md
index 4501ecf3..39beecd 100644
--- a/chrome/profiling/README.md
+++ b/chrome/profiling/README.md
@@ -9,3 +9,5 @@
 is enabled by setting the GN flag `enable_oop_heap_profiling`. The in-process
 code that communicates with the profiling process is in
 `//chrome/common/profiling`.
+
+The browser must be started with `--memlog`.
diff --git a/chrome/profiling/allocation_tracker.cc b/chrome/profiling/allocation_tracker.cc
index 89c3b4b0..69fab82d 100644
--- a/chrome/profiling/allocation_tracker.cc
+++ b/chrome/profiling/allocation_tracker.cc
@@ -13,6 +13,10 @@
 AllocationTracker::Alloc::Alloc(size_t sz, BacktraceStorage::Key key)
     : size(sz), backtrace_key(key) {}
 
+AllocationTracker::Alloc::~Alloc() {}
+AllocationTracker::Alloc::Alloc(const Alloc& other)
+    : size(other.size), backtrace_key(other.backtrace_key) {}
+
 AllocationTracker::AllocationTracker(CompleteCallback complete_cb)
     : complete_callback_(std::move(complete_cb)),
       backtrace_storage_(ProfilingGlobals::Get()->GetBacktraceStorage()) {}
diff --git a/chrome/profiling/allocation_tracker.h b/chrome/profiling/allocation_tracker.h
index 5aa8ea4..3ee5f64 100644
--- a/chrome/profiling/allocation_tracker.h
+++ b/chrome/profiling/allocation_tracker.h
@@ -34,6 +34,8 @@
 
   struct Alloc {
     Alloc(size_t sz, BacktraceStorage::Key key);
+    Alloc(const Alloc& other);
+    ~Alloc();
 
     size_t size;
     BacktraceStorage::Key backtrace_key;
diff --git a/chrome/profiling/memlog_receiver_pipe_posix.cc b/chrome/profiling/memlog_receiver_pipe_posix.cc
new file mode 100644
index 0000000..dbce9ca
--- /dev/null
+++ b/chrome/profiling/memlog_receiver_pipe_posix.cc
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/profiling/memlog_receiver_pipe_posix.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread.h"
+#include "chrome/profiling/memlog_stream_receiver.h"
+
+namespace profiling {
+
+MemlogReceiverPipe::CompletionThunk::CompletionThunk(int fd, Callback cb)
+    : controller_(FROM_HERE), fd_(fd), callback_(cb) {
+  base::MessageLoopForIO::current()->WatchFileDescriptor(
+      fd_, true, base::MessageLoopForIO::WATCH_READ, &controller_, this);
+}
+
+MemlogReceiverPipe::CompletionThunk::~CompletionThunk() {
+  if (fd_ != -1)
+    IGNORE_EINTR(::close(fd_));
+}
+
+void MemlogReceiverPipe::CompletionThunk::OnFileCanReadWithoutBlocking(int fd) {
+  callback_.Run(fd);
+}
+
+void MemlogReceiverPipe::CompletionThunk::OnFileCanWriteWithoutBlocking(
+    int fd) {
+  NOTREACHED();
+}
+
+MemlogReceiverPipe::MemlogReceiverPipe(std::unique_ptr<CompletionThunk> thunk) {
+}
+
+MemlogReceiverPipe::~MemlogReceiverPipe() {}
+
+void MemlogReceiverPipe::StartReadingOnIOThread() {
+  // TODO(ajwong): Implement with something useful.
+}
+
+int MemlogReceiverPipe::GetRemoteProcessID() {
+  // TODO(ajwong): Implement with something useful.
+  return 0;
+}
+
+void MemlogReceiverPipe::SetReceiver(
+    scoped_refptr<base::TaskRunner> task_runner,
+    scoped_refptr<MemlogStreamReceiver> receiver) {
+  receiver_task_runner_ = task_runner;
+  receiver_ = receiver;
+}
+
+}  // namespace profiling
diff --git a/chrome/profiling/memlog_receiver_pipe_posix.h b/chrome/profiling/memlog_receiver_pipe_posix.h
new file mode 100644
index 0000000..a5abb43
--- /dev/null
+++ b/chrome/profiling/memlog_receiver_pipe_posix.h
@@ -0,0 +1,67 @@
+// 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_PROFILING_MEMLOG_RECEIVER_PIPE_POSIX_H_
+#define CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_POSIX_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "build/build_config.h"
+
+namespace base {
+class TaskRunner;
+}  // namespace base
+
+namespace profiling {
+
+class MemlogStreamReceiver;
+
+class MemlogReceiverPipe
+    : public base::RefCountedThreadSafe<MemlogReceiverPipe> {
+ public:
+  class CompletionThunk : public base::MessageLoopForIO::Watcher {
+   public:
+    using Callback = base::RepeatingCallback<void(int)>;
+
+    CompletionThunk(int fd, Callback cb);
+    ~CompletionThunk() override;
+
+    void set_callback(Callback cb) { callback_ = cb; }
+
+    void OnFileCanReadWithoutBlocking(int fd) override;
+    void OnFileCanWriteWithoutBlocking(int fd) override;
+
+   private:
+    base::MessageLoopForIO::FileDescriptorWatcher controller_;
+
+    int fd_;
+    Callback callback_;
+  };
+
+  explicit MemlogReceiverPipe(std::unique_ptr<CompletionThunk> thunk);
+
+  void StartReadingOnIOThread();
+
+  int GetRemoteProcessID();
+  void SetReceiver(scoped_refptr<base::TaskRunner> task_runner,
+                   scoped_refptr<MemlogStreamReceiver> receiver);
+
+ private:
+  friend class base::RefCountedThreadSafe<MemlogReceiverPipe>;
+  ~MemlogReceiverPipe();
+
+  std::unique_ptr<CompletionThunk> thunk_;
+
+  scoped_refptr<base::TaskRunner> receiver_task_runner_;
+  scoped_refptr<MemlogStreamReceiver> receiver_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemlogReceiverPipe);
+};
+
+}  // namespace profiling
+
+#endif  // CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_POSIX_H_
diff --git a/chrome/profiling/memlog_receiver_pipe_server.h b/chrome/profiling/memlog_receiver_pipe_server.h
index 5ef925ac3..8650479 100644
--- a/chrome/profiling/memlog_receiver_pipe_server.h
+++ b/chrome/profiling/memlog_receiver_pipe_server.h
@@ -9,6 +9,8 @@
 
 #if defined(OS_WIN)
 #include "memlog_receiver_pipe_server_win.h"
+#else
+#include "memlog_receiver_pipe_server_posix.h"
 #endif
 
 #endif  // CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_SERVER_H_
diff --git a/chrome/profiling/memlog_receiver_pipe_server_posix.cc b/chrome/profiling/memlog_receiver_pipe_server_posix.cc
new file mode 100644
index 0000000..86540d1
--- /dev/null
+++ b/chrome/profiling/memlog_receiver_pipe_server_posix.cc
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/profiling/memlog_receiver_pipe_server_posix.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/common/profiling/memlog_stream.h"
+
+namespace profiling {
+
+MemlogReceiverPipeServer::MemlogReceiverPipeServer(
+    base::TaskRunner* io_runner,
+    const std::string& pipe_id,
+    NewConnectionCallback on_new_conn)
+    : io_runner_(io_runner),
+      pipe_id_(pipe_id),
+      on_new_connection_(on_new_conn) {}
+
+MemlogReceiverPipeServer::~MemlogReceiverPipeServer() {}
+
+void MemlogReceiverPipeServer::Start() {}
+
+}  // namespace profiling
diff --git a/chrome/profiling/memlog_receiver_pipe_server_posix.h b/chrome/profiling/memlog_receiver_pipe_server_posix.h
new file mode 100644
index 0000000..8cc511a
--- /dev/null
+++ b/chrome/profiling/memlog_receiver_pipe_server_posix.h
@@ -0,0 +1,58 @@
+// 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_PROFILING_MEMLOG_RECEIVER_PIPE_SERVER_POSIX_H_
+#define CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_SERVER_POSIX_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_pump_libevent.h"
+#include "chrome/profiling/memlog_receiver_pipe_posix.h"
+
+namespace base {
+class TaskRunner;
+}  // namespace base
+
+namespace profiling {
+
+class MemlogReceiverPipe;
+
+// This class listens for new pipe connections and creates new
+// MemlogReceiverPipe objects for each one.
+class MemlogReceiverPipeServer
+    : public base::RefCountedThreadSafe<MemlogReceiverPipeServer> {
+ public:
+  using NewConnectionCallback =
+      base::RepeatingCallback<void(scoped_refptr<MemlogReceiverPipe>)>;
+
+  // |io_runner| is the task runner for the I/O thread. When a new connection is
+  // established, the |on_new_conn| callback is called with the pipe.
+  MemlogReceiverPipeServer(base::TaskRunner* io_runner,
+                           const std::string& pipe_id,
+                           NewConnectionCallback on_new_conn);
+
+  void set_on_new_connection(NewConnectionCallback on_new_connection) {
+    on_new_connection_ = on_new_connection;
+  }
+
+  // Starts the server which opens the pipe and begins accepting connections.
+  void Start();
+
+ private:
+  friend class base::RefCountedThreadSafe<MemlogReceiverPipeServer>;
+  ~MemlogReceiverPipeServer();
+
+  scoped_refptr<base::TaskRunner> io_runner_;
+  std::string pipe_id_;
+  NewConnectionCallback on_new_connection_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemlogReceiverPipeServer);
+};
+
+}  // namespace profiling
+
+#endif  // CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_SERVER_POSIX_H_
diff --git a/chrome/profiling/memlog_receiver_pipe_server_win.h b/chrome/profiling/memlog_receiver_pipe_server_win.h
index c6ace10..8f12a7f 100644
--- a/chrome/profiling/memlog_receiver_pipe_server_win.h
+++ b/chrome/profiling/memlog_receiver_pipe_server_win.h
@@ -60,6 +60,8 @@
 
   // Current connection we're waiting on creation for.
   std::unique_ptr<MemlogReceiverPipe::CompletionThunk> current_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemlogReceiverPipeServer);
 };
 
 }  // namespace profiling
diff --git a/chrome/profiling/memlog_stream_parser.cc b/chrome/profiling/memlog_stream_parser.cc
index b67d0d7..3fbc60f 100644
--- a/chrome/profiling/memlog_stream_parser.cc
+++ b/chrome/profiling/memlog_stream_parser.cc
@@ -24,6 +24,8 @@
 MemlogStreamParser::Block::Block(std::unique_ptr<char[]> d, size_t s)
     : data(std::move(d)), size(s) {}
 
+MemlogStreamParser::Block::~Block() {}
+
 MemlogStreamParser::MemlogStreamParser(MemlogReceiver* receiver)
     : receiver_(receiver) {}
 
diff --git a/chrome/profiling/memlog_stream_parser.h b/chrome/profiling/memlog_stream_parser.h
index e2d0e48a..1418d0b 100644
--- a/chrome/profiling/memlog_stream_parser.h
+++ b/chrome/profiling/memlog_stream_parser.h
@@ -18,7 +18,6 @@
  public:
   // Receiver must outlive this class.
   explicit MemlogStreamParser(MemlogReceiver* receiver);
-  ~MemlogStreamParser() override;
 
   // StreamReceiver implementation.
   void OnStreamData(std::unique_ptr<char[]> data, size_t sz) override;
@@ -27,6 +26,7 @@
  private:
   struct Block {
     Block(std::unique_ptr<char[]> d, size_t s);
+    ~Block();
 
     std::unique_ptr<char[]> data;
     size_t size;
@@ -38,6 +38,8 @@
     READ_NO_DATA  // Not enough data, try again when we get more
   };
 
+  ~MemlogStreamParser() override;
+
   // Returns true if the given number of bytes are available now.
   bool AreBytesAvailable(size_t count) const;
 
diff --git a/chrome/profiling/memlog_stream_receiver.h b/chrome/profiling/memlog_stream_receiver.h
index ea8a6d6..bd21d11a 100644
--- a/chrome/profiling/memlog_stream_receiver.h
+++ b/chrome/profiling/memlog_stream_receiver.h
@@ -17,7 +17,6 @@
     : public base::RefCountedThreadSafe<MemlogStreamReceiver> {
  public:
   MemlogStreamReceiver() {}
-  virtual ~MemlogStreamReceiver() {}
 
   // Returns true on success, false on unrecoverable error (in which case no
   // more blocks will be sent). May take a ref to the block, so the caller
@@ -25,6 +24,10 @@
   virtual void OnStreamData(std::unique_ptr<char[]> data, size_t sz) = 0;
 
   virtual void OnStreamComplete() = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<MemlogStreamReceiver>;
+  ~MemlogStreamReceiver() override {}
 };
 
 }  // namespace profiling
diff --git a/chrome/profiling/profiling_browsertest.cc b/chrome/profiling/profiling_browsertest.cc
new file mode 100644
index 0000000..0f1f968
--- /dev/null
+++ b/chrome/profiling/profiling_browsertest.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/process/launch.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+
+namespace profiling {
+
+#if !defined(OS_MACOSX)
+class ProfilingBrowserTest : public InProcessBrowserTest {
+ protected:
+  void RelaunchWithMemlog() {
+    // TODO(ajwong): Remove this once Brett lands is process model change so the
+    // browser process actually launches the profiling process. Until then, it's
+    // okay to have this skip on Mac (which has a different launch setup such
+    // that this sort of relaunch doesn't work). See GetCommandLineForRelaunch()
+    // function for details.
+    // TODO(awong): Can we do this with just SetUpCommandLine() and no Relaunch?
+    base::CommandLine new_command_line(GetCommandLineForRelaunch());
+    new_command_line.AppendSwitch(switches::kMemlog);
+
+    ui_test_utils::BrowserAddedObserver observer;
+    base::LaunchProcess(new_command_line, base::LaunchOptionsForTest());
+
+    observer.WaitForSingleNewBrowser();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(ProfilingBrowserTest, InterceptsNew) {
+  RelaunchWithMemlog();
+}
+#endif
+
+}  // namespace profiling
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 9294036..0db81e04b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1568,6 +1568,7 @@
       "../common/mac/mock_launchd.cc",
       "../common/mac/mock_launchd.h",
       "../common/time_format_browsertest.cc",
+      "../profiling/profiling_browsertest.cc",
       "../renderer/autofill/autofill_renderer_browsertest.cc",
       "../renderer/autofill/fake_content_password_manager_driver.cc",
       "../renderer/autofill/fake_content_password_manager_driver.h",
@@ -1733,6 +1734,9 @@
     if (!enable_one_click_signin) {
       sources -= [ "../browser/ui/sync/one_click_signin_links_delegate_impl_browsertest.cc" ]
     }
+    if (!enable_oop_heap_profiling) {
+      sources -= [ "../profiling/profiling_browsertest.cc" ]
+    }
     if (enable_nacl) {
       sources += [
         "../browser/chrome_service_worker_browsertest.cc",
diff --git a/chrome/test/data/webui/settings/device_page_tests.js b/chrome/test/data/webui/settings/device_page_tests.js
index 1fdbe06..c818b96 100644
--- a/chrome/test/data/webui/settings/device_page_tests.js
+++ b/chrome/test/data/webui/settings/device_page_tests.js
@@ -682,7 +682,7 @@
         var powerSourceWrapper;
         var powerSourceSelect;
         var idleSelect;
-        var lidClosedSelect;
+        var lidClosedToggle;
 
         suiteSetup(function() {
           // Always show power settings.
@@ -705,7 +705,7 @@
                         .updatePowerStatusCalled_);
 
                 idleSelect = assert(powerPage.$$('#idleSelect'));
-                lidClosedSelect = assert(powerPage.$$('#lidClosedSelect'));
+                lidClosedToggle = assert(powerPage.$$('#lidClosedToggle'));
 
                 assertEquals(
                     1,
@@ -802,17 +802,31 @@
         });
 
         test('set lid behavior', function() {
-          selectValue(lidClosedSelect, settings.LidClosedBehavior.DO_NOTHING);
+          var sendLid = function(lidBehavior) {
+            sendPowerManagementSettings(
+                settings.IdleBehavior.DISPLAY_OFF,
+                false /* idleControlled */, lidBehavior,
+                false /* lidClosedControlled */, true /* hasLid */);
+          };
+
+          sendLid(settings.LidClosedBehavior.SUSPEND);
+          assertTrue(lidClosedToggle.checked);
+
+          MockInteractions.tap(lidClosedToggle.$$('#control'));
           expectEquals(
               settings.LidClosedBehavior.DO_NOTHING,
               settings.DevicePageBrowserProxyImpl.getInstance()
                   .lidClosedBehavior_);
+          sendLid(settings.LidClosedBehavior.DO_NOTHING);
+          expectFalse(lidClosedToggle.checked);
 
-          selectValue(lidClosedSelect, settings.LidClosedBehavior.SUSPEND);
+          MockInteractions.tap(lidClosedToggle.$$('#control'));
           expectEquals(
               settings.LidClosedBehavior.SUSPEND,
               settings.DevicePageBrowserProxyImpl.getInstance()
                   .lidClosedBehavior_);
+          sendLid(settings.LidClosedBehavior.SUSPEND);
+          expectTrue(lidClosedToggle.checked);
         });
 
         test('display idle and lid behavior', function() {
@@ -827,11 +841,10 @@
                 settings.IdleBehavior.DISPLAY_ON.toString(), idleSelect.value);
             expectFalse(idleSelect.disabled);
             expectEquals(null, powerPage.$$('#idleControlledIndicator'));
-            expectEquals(
-                settings.LidClosedBehavior.DO_NOTHING.toString(),
-                lidClosedSelect.value);
-            expectFalse(lidClosedSelect.disabled);
-            expectEquals(null, powerPage.$$('#lidClosedControlledIndicator'));
+            expectEquals(loadTimeData.getString('powerLidSleepLabel'),
+                         lidClosedToggle.label);
+            expectFalse(lidClosedToggle.checked);
+            expectFalse(lidClosedToggle.isPrefEnforced());
           }).then(function() {
             sendPowerManagementSettings(
                 settings.IdleBehavior.DISPLAY_OFF,
@@ -843,21 +856,20 @@
                          idleSelect.value);
             expectFalse(idleSelect.disabled);
             expectEquals(null, powerPage.$$('#idleControlledIndicator'));
-            expectEquals(
-                settings.LidClosedBehavior.SUSPEND.toString(),
-                lidClosedSelect.value);
-            expectFalse(lidClosedSelect.disabled);
-            expectEquals(null, powerPage.$$('#lidClosedControlledIndicator'));
+            expectEquals(loadTimeData.getString('powerLidSleepLabel'),
+                         lidClosedToggle.label);
+            expectTrue(lidClosedToggle.checked);
+            expectFalse(lidClosedToggle.isPrefEnforced());
           });
         });
 
         test('display controlled idle and lid behavior', function() {
-          // When settings are controlled, the selects should be disabled and
+          // When settings are controlled, the controls should be disabled and
           // the indicators should be shown.
           return new Promise(function(resolve) {
             sendPowerManagementSettings(
                 settings.IdleBehavior.OTHER, true /* idleControlled */,
-                settings.LidClosedBehavior.SUSPEND,
+                settings.LidClosedBehavior.SHUT_DOWN,
                 true /* lidClosedControlled */, true /* hasLid */);
             powerPage.async(resolve);
           }).then(function() {
@@ -865,12 +877,27 @@
                 settings.IdleBehavior.OTHER.toString(), idleSelect.value);
             expectTrue(idleSelect.disabled);
             expectNotEquals(null, powerPage.$$('#idleControlledIndicator'));
+            expectEquals(loadTimeData.getString('powerLidShutDownLabel'),
+                         lidClosedToggle.label);
+            expectTrue(lidClosedToggle.checked);
+            expectTrue(lidClosedToggle.isPrefEnforced());
+          }).then(function() {
+            sendPowerManagementSettings(
+                settings.IdleBehavior.DISPLAY_OFF,
+                true /* idleControlled */,
+                settings.LidClosedBehavior.STOP_SESSION,
+                true /* lidClosedControlled */, true /* hasLid */);
+            return new Promise(function(resolve) { powerPage.async(resolve); });
+          }).then(function() {
             expectEquals(
-                settings.LidClosedBehavior.SUSPEND.toString(),
-                lidClosedSelect.value);
-            expectTrue(lidClosedSelect.disabled);
-            expectNotEquals(
-                null, powerPage.$$('#lidClosedControlledIndicator'));
+                settings.IdleBehavior.DISPLAY_OFF.toString(),
+                idleSelect.value);
+            expectTrue(idleSelect.disabled);
+            expectNotEquals(null, powerPage.$$('#idleControlledIndicator'));
+            expectEquals(loadTimeData.getString('powerLidSignOutLabel'),
+                         lidClosedToggle.label);
+            expectTrue(lidClosedToggle.checked);
+            expectTrue(lidClosedToggle.isPrefEnforced());
           });
         });
 
diff --git a/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_perf_pages.py b/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_perf_pages.py
index 803bb6f..1be7da6 100644
--- a/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_perf_pages.py
+++ b/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_perf_pages.py
@@ -31,11 +31,11 @@
   """Cast page to open a cast-enabled page and open media router dialog."""
 
   def __init__(self, page_set, url='file://basic_test.html',
-               shared_page_state_class=shared_page_state.SharedPageState):
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name='basic_test.html'):
     super(CastDialogPage, self).__init__(
         url=url, page_set=page_set,
-        shared_page_state_class=shared_page_state_class,
-        name='basic_test.html')
+        shared_page_state_class=shared_page_state_class, name=name)
 
   def RunPageInteractions(self, action_runner):
     # Wait for 5s after Chrome is opened in order to get consistent results.
diff --git a/chrome/typemaps.gni b/chrome/typemaps.gni
index b26165c..539ad98 100644
--- a/chrome/typemaps.gni
+++ b/chrome/typemaps.gni
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 typemaps = [
-  "//chrome/common/safe_archive_analyzer.typemap",
   "//chrome/common/instant.typemap",
+  "//chrome/common/safe_archive_analyzer.typemap",
+  "//chrome/common/shell_handler_win.typemap",
 ]
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index ad67a975..3c64214 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -16,8 +16,6 @@
     "cloud_print/bitmap_image.h",
     "cloud_print/pwg_encoder.cc",
     "cloud_print/pwg_encoder.h",
-    "ipc_shell_handler_win.cc",
-    "ipc_shell_handler_win.h",
     "printing_handler.cc",
     "printing_handler.h",
     "shell_handler_impl_win.cc",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 4f308fc7..50f5ee4 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -43,7 +43,6 @@
 #endif
 
 #if defined(OS_WIN)
-#include "chrome/utility/ipc_shell_handler_win.h"
 #include "chrome/utility/shell_handler_impl_win.h"
 #endif
 
@@ -241,10 +240,6 @@
     (BUILDFLAG(ENABLE_BASIC_PRINTING) && defined(OS_WIN))
   handlers_.push_back(base::MakeUnique<printing::PrintingHandler>());
 #endif
-
-#if defined(OS_WIN)
-  handlers_.push_back(base::MakeUnique<IPCShellHandler>());
-#endif
 }
 
 ChromeContentUtilityClient::~ChromeContentUtilityClient() = default;
diff --git a/chrome/utility/ipc_shell_handler_win.cc b/chrome/utility/ipc_shell_handler_win.cc
deleted file mode 100644
index cfdb765b..0000000
--- a/chrome/utility/ipc_shell_handler_win.cc
+++ /dev/null
@@ -1,79 +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 "chrome/utility/ipc_shell_handler_win.h"
-
-#include <commdlg.h>
-
-#include <memory>
-
-#include "base/files/file_path.h"
-#include "chrome/common/chrome_utility_messages.h"
-#include "content/public/utility/utility_thread.h"
-#include "ui/base/win/open_file_name_win.h"
-
-IPCShellHandler::IPCShellHandler() {}
-IPCShellHandler::~IPCShellHandler() {}
-
-bool IPCShellHandler::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(IPCShellHandler, message)
-    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetOpenFileName, OnGetOpenFileName)
-    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetSaveFileName, OnGetSaveFileName)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-void IPCShellHandler::OnGetOpenFileName(HWND owner,
-                                        DWORD flags,
-                                        const GetOpenFileNameFilter& filter,
-                                        const base::FilePath& initial_directory,
-                                        const base::FilePath& filename) {
-  ui::win::OpenFileName open_file_name(owner, flags);
-  open_file_name.SetInitialSelection(initial_directory, filename);
-  open_file_name.SetFilters(filter);
-
-  base::FilePath directory;
-  std::vector<base::FilePath> filenames;
-
-  if (::GetOpenFileName(open_file_name.GetOPENFILENAME()))
-    open_file_name.GetResult(&directory, &filenames);
-
-  if (filenames.size()) {
-    content::UtilityThread::Get()->Send(
-        new ChromeUtilityHostMsg_GetOpenFileName_Result(directory, filenames));
-  } else {
-    content::UtilityThread::Get()->Send(
-        new ChromeUtilityHostMsg_GetOpenFileName_Failed());
-  }
-}
-
-void IPCShellHandler::OnGetSaveFileName(
-    const ChromeUtilityMsg_GetSaveFileName_Params& params) {
-  ui::win::OpenFileName open_file_name(params.owner, params.flags);
-  open_file_name.SetInitialSelection(params.initial_directory,
-                                     params.suggested_filename);
-  open_file_name.SetFilters(params.filters);
-  open_file_name.GetOPENFILENAME()->nFilterIndex =
-      params.one_based_filter_index;
-  open_file_name.GetOPENFILENAME()->lpstrDefExt =
-      params.default_extension.c_str();
-
-  if (::GetSaveFileName(open_file_name.GetOPENFILENAME())) {
-    content::UtilityThread::Get()->Send(
-        new ChromeUtilityHostMsg_GetSaveFileName_Result(
-            base::FilePath(open_file_name.GetOPENFILENAME()->lpstrFile),
-            open_file_name.GetOPENFILENAME()->nFilterIndex));
-    return;
-  }
-
-  // Zero means the dialog was closed, otherwise we had an error.
-  DWORD error_code = ::CommDlgExtendedError();
-  if (error_code != 0)
-    NOTREACHED() << "GetSaveFileName failed with code: " << error_code;
-
-  content::UtilityThread::Get()->Send(
-      new ChromeUtilityHostMsg_GetSaveFileName_Failed());
-}
diff --git a/chrome/utility/ipc_shell_handler_win.h b/chrome/utility/ipc_shell_handler_win.h
deleted file mode 100644
index 5c71df7..0000000
--- a/chrome/utility/ipc_shell_handler_win.h
+++ /dev/null
@@ -1,53 +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 CHROME_UTILITY_IPC_SHELL_HANDLER_WIN_H_
-#define CHROME_UTILITY_IPC_SHELL_HANDLER_WIN_H_
-
-#include <Windows.h>
-
-#include <tuple>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "chrome/utility/utility_message_handler.h"
-
-namespace base {
-class FilePath;
-}  // namespace base
-
-using GetOpenFileNameFilter =
-    std::vector<std::tuple<base::string16, base::string16>>;
-
-struct ChromeUtilityMsg_GetSaveFileName_Params;
-
-// Handles requests to execute shell operations. Used to protect the browser
-// process from instability due to 3rd-party shell extensions. Must be invoked
-// in a non-sandboxed utility process.
-// Note: This class is deprecated in favor of the Mojo version.
-//       See chrome/common/shell_handler_win.mojom and
-//       chrome/utility/shell_handler_impl_win.h
-class IPCShellHandler : public UtilityMessageHandler {
- public:
-  IPCShellHandler();
-  ~IPCShellHandler() override;
-
-  // IPC::Listener implementation
-  bool OnMessageReceived(const IPC::Message& message) override;
-
- private:
-  void OnGetOpenFileName(HWND owner,
-                         DWORD flags,
-                         const GetOpenFileNameFilter& filter,
-                         const base::FilePath& initial_directory,
-                         const base::FilePath& filename);
-
-  void OnGetSaveFileName(const ChromeUtilityMsg_GetSaveFileName_Params& params);
-
-  DISALLOW_COPY_AND_ASSIGN(IPCShellHandler);
-};
-
-#endif  // CHROME_UTILITY_IPC_SHELL_HANDLER_WIN_H_
diff --git a/chrome/utility/shell_handler_impl_win.cc b/chrome/utility/shell_handler_impl_win.cc
index 33119500..e4879a36 100644
--- a/chrome/utility/shell_handler_impl_win.cc
+++ b/chrome/utility/shell_handler_impl_win.cc
@@ -12,14 +12,17 @@
 #include "base/memory/ptr_util.h"
 #include "base/path_service.h"
 #include "base/scoped_native_library.h"
+#include "base/strings/string16.h"
 #include "base/win/scoped_bstr.h"
 #include "base/win/scoped_com_initializer.h"
 #include "base/win/scoped_comptr.h"
 #include "base/win/scoped_variant.h"
 #include "base/win/shortcut.h"
+#include "base/win/win_util.h"
 #include "chrome/installer/util/install_util.h"
 #include "content/public/utility/utility_thread.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "ui/base/win/open_file_name_win.h"
 
 namespace {
 
@@ -230,3 +233,58 @@
   bool is_pinned_to_taskbar = helper.GetResult();
   callback.Run(!helper.error_occured(), is_pinned_to_taskbar);
 }
+
+void ShellHandlerImpl::CallGetOpenFileName(
+    uint32_t owner,
+    uint32_t flags,
+    const std::vector<std::tuple<base::string16, base::string16>>& filters,
+    const base::FilePath& initial_directory,
+    const base::FilePath& initial_filename,
+    const CallGetOpenFileNameCallback& callback) {
+  ui::win::OpenFileName open_file_name(
+      reinterpret_cast<HWND>(base::win::Uint32ToHandle(owner)), flags);
+
+  open_file_name.SetInitialSelection(initial_directory, initial_filename);
+  open_file_name.SetFilters(filters);
+
+  base::FilePath directory;
+  std::vector<base::FilePath> files;
+  if (::GetOpenFileName(open_file_name.GetOPENFILENAME()))
+    open_file_name.GetResult(&directory, &files);
+
+  if (!files.empty()) {
+    callback.Run(directory, files);
+  } else {
+    callback.Run(base::FilePath(), std::vector<base::FilePath>());
+  }
+}
+
+void ShellHandlerImpl::CallGetSaveFileName(
+    uint32_t owner,
+    uint32_t flags,
+    const std::vector<std::tuple<base::string16, base::string16>>& filters,
+    uint32_t one_based_filter_index,
+    const base::FilePath& initial_directory,
+    const base::FilePath& suggested_filename,
+    const base::string16& default_extension,
+    const CallGetSaveFileNameCallback& callback) {
+  ui::win::OpenFileName open_file_name(
+      reinterpret_cast<HWND>(base::win::Uint32ToHandle(owner)), flags);
+
+  open_file_name.SetInitialSelection(initial_directory, suggested_filename);
+  open_file_name.SetFilters(filters);
+  open_file_name.GetOPENFILENAME()->nFilterIndex = one_based_filter_index;
+  open_file_name.GetOPENFILENAME()->lpstrDefExt = default_extension.c_str();
+
+  if (::GetSaveFileName(open_file_name.GetOPENFILENAME())) {
+    callback.Run(base::FilePath(open_file_name.GetOPENFILENAME()->lpstrFile),
+                 open_file_name.GetOPENFILENAME()->nFilterIndex);
+    return;
+  }
+
+  // Error code 0 means the dialog was closed, otherwise there was an error.
+  if (DWORD error_code = ::CommDlgExtendedError())
+    NOTREACHED() << "::GetSaveFileName() failed: error code " << error_code;
+
+  callback.Run(base::FilePath(), 0);
+}
diff --git a/chrome/utility/shell_handler_impl_win.h b/chrome/utility/shell_handler_impl_win.h
index 1badd896..788152b 100644
--- a/chrome/utility/shell_handler_impl_win.h
+++ b/chrome/utility/shell_handler_impl_win.h
@@ -7,9 +7,11 @@
 
 #include "base/macros.h"
 #include "chrome/common/shell_handler_win.mojom.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
 
-// Implements the ShellHandler mojo interface.
+namespace service_manager {
+struct BindSourceInfo;
+}
+
 class ShellHandlerImpl : public chrome::mojom::ShellHandler {
  public:
   ShellHandlerImpl();
@@ -22,6 +24,24 @@
   // chrome::mojom::ShellHandler:
   void IsPinnedToTaskbar(const IsPinnedToTaskbarCallback& callback) override;
 
+  void CallGetOpenFileName(
+      uint32_t owner,
+      uint32_t flags,
+      const std::vector<std::tuple<base::string16, base::string16>>& filters,
+      const base::FilePath& initial_directory,
+      const base::FilePath& initial_filename,
+      const CallGetOpenFileNameCallback& callback) override;
+
+  void CallGetSaveFileName(
+      uint32_t owner,
+      uint32_t flags,
+      const std::vector<std::tuple<base::string16, base::string16>>& filters,
+      uint32_t one_based_filter_index,
+      const base::FilePath& initial_directory,
+      const base::FilePath& suggested_filename,
+      const base::string16& default_extension,
+      const CallGetSaveFileNameCallback& callback) override;
+
   DISALLOW_COPY_AND_ASSIGN(ShellHandlerImpl);
 };
 
diff --git a/chromeos/cryptohome/cryptohome_parameters.h b/chromeos/cryptohome/cryptohome_parameters.h
index bf3d290..8525f7f 100644
--- a/chromeos/cryptohome/cryptohome_parameters.h
+++ b/chromeos/cryptohome/cryptohome_parameters.h
@@ -174,6 +174,9 @@
   // If |true|, mounts the existing ecryptfs vault to a temporary location while
   // setting up a new dircrypto directory.
   bool to_migrate_from_ecryptfs = false;
+
+  // If |true|, the home dir will be mounted as public mount.
+  bool public_mount = false;
 };
 
 // This function returns true if cryptohome of |account_id| is migrated to
diff --git a/chromeos/cryptohome/homedir_methods.cc b/chromeos/cryptohome/homedir_methods.cc
index e4ba8755..9671ae1 100644
--- a/chromeos/cryptohome/homedir_methods.cc
+++ b/chromeos/cryptohome/homedir_methods.cc
@@ -225,6 +225,9 @@
     if (request.to_migrate_from_ecryptfs)
       request_proto.set_to_migrate_from_ecryptfs(true);
 
+    if (request.public_mount)
+      request_proto.set_public_mount(true);
+
     DBusThreadManager::Get()->GetCryptohomeClient()->MountEx(
         id, auth_proto, request_proto,
         base::Bind(&HomedirMethodsImpl::OnMountExCallback,
diff --git a/chromeos/dbus/fake_session_manager_client.cc b/chromeos/dbus/fake_session_manager_client.cc
index 232d77a..763d491 100644
--- a/chromeos/dbus/fake_session_manager_client.cc
+++ b/chromeos/dbus/fake_session_manager_client.cc
@@ -84,6 +84,8 @@
 }
 
 void FakeSessionManagerClient::EmitLoginPromptVisible() {
+  for (auto& observer : observers_)
+    observer.EmitLoginPromptVisibleCalled();
 }
 
 void FakeSessionManagerClient::RestartJob(
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index f43aafc..e766360 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -40,6 +40,9 @@
 // The label used for the key derived from the user's GAIA credentials.
 const char kCryptohomeGAIAKeyLabel[] = "gaia";
 
+// The label used for the key generated by Cryptohome for public mount.
+const char kCryptohomePublicMountKeyLabel[] = "publicmount";
+
 // The name under which the type of key generated from the user's GAIA
 // credentials is stored.
 const char kKeyProviderDataTypeName[] = "type";
@@ -132,8 +135,14 @@
              bool success,
              cryptohome::MountError return_code,
              const std::string& mount_hash) {
-  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End",
-                                                          false);
+  const bool public_mount = attempt->user_context.GetUserType() ==
+                                user_manager::USER_TYPE_KIOSK_APP ||
+                            attempt->user_context.GetUserType() ==
+                                user_manager::USER_TYPE_ARC_KIOSK_APP;
+
+  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
+      public_mount ? "CryptohomeMountPublic-End" : "CryptohomeMount-End",
+      false);
   attempt->RecordCryptohomeStatus(success, return_code);
   if (success)
     attempt->RecordUsernameHash(mount_hash);
@@ -403,17 +412,25 @@
       base::Bind(&TriggerResolveHash, attempt, resolver));
 }
 
-// Calls cryptohome's MountPublic method
+// Calls cryptohome's MountEx method with the public_mount option.
 void MountPublic(const base::WeakPtr<AuthAttemptState>& attempt,
                  scoped_refptr<CryptohomeAuthenticator> resolver,
-                 int flags) {
-  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic(
-      cryptohome::Identification(attempt->user_context.GetAccountId()), flags,
-      base::Bind(&TriggerResolveWithLoginTimeMarker,
-                 "CryptohomeMountPublic-End", attempt, resolver));
-  cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
+                 bool force_dircrypto_if_available) {
+  cryptohome::MountParameters mount(false /* ephemeral */);
+  mount.force_dircrypto_if_available = force_dircrypto_if_available;
+  mount.public_mount = true;
+  // Set the request to create a new homedir when missing.
+  mount.create_keys.push_back(cryptohome::KeyDefinition(
+      std::string(), kCryptohomePublicMountKeyLabel, cryptohome::PRIV_DEFAULT));
+
+  // For public mounts, authorization secret is filled by cryptohomed, hence it
+  // is left empty. Authentication's key label is also set to an empty string,
+  // which is a wildcard allowing any key to match to allow cryptohomes created
+  // in a legacy way. (See comments in DoMount.)
+  cryptohome::HomedirMethods::GetInstance()->MountEx(
       cryptohome::Identification(attempt->user_context.GetAccountId()),
-      base::Bind(&TriggerResolveHash, attempt, resolver));
+      cryptohome::Authorization(cryptohome::KeyDefinition()), mount,
+      base::Bind(&OnMount, attempt, resolver));
 }
 
 // Calls cryptohome's key migration method.
@@ -612,7 +629,7 @@
   if (!use_guest_mount) {
     MountPublic(current_state_->AsWeakPtr(),
                 scoped_refptr<CryptohomeAuthenticator>(this),
-                cryptohome::CREATE_IF_MISSING);
+                false);  // force_dircrypto_if_available
   } else {
     ephemeral_mount_attempted_ = true;
     MountGuestAndGetHash(current_state_->AsWeakPtr(),
@@ -633,7 +650,7 @@
   remove_user_data_on_failure_ = true;
   MountPublic(current_state_->AsWeakPtr(),
               scoped_refptr<CryptohomeAuthenticator>(this),
-              cryptohome::CREATE_IF_MISSING);
+              true);  // force_dircrypto_if_available
 }
 
 void CryptohomeAuthenticator::OnAuthSuccess() {
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 98b413b..73dbdd4 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -225,6 +225,7 @@
       "//components/subresource_filter/content/common:unit_tests",
       "//components/subresource_filter/content/renderer:unit_tests",
       "//components/tracing:unit_tests",
+      "//components/translate/content/renderer:unit_tests",
       "//components/visitedlink/test:unit_tests",
       "//components/viz/host:unit_tests",
       "//components/viz/service:unit_tests",
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index d228c05..d80c79b 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -233,6 +233,7 @@
     "//base",
     "//base/test:test_support",
     "//chromeos",
+    "//chromeos:test_support_without_gmock",
     "//components/signin/core/account_id",
     "//components/user_manager",
     "//components/user_manager:test_support",
diff --git a/components/arc/arc_session.cc b/components/arc/arc_session.cc
index f589ed3..69e0423 100644
--- a/components/arc/arc_session.cc
+++ b/components/arc/arc_session.cc
@@ -17,6 +17,7 @@
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/sys_info.h"
 #include "base/task_runner_util.h"
@@ -125,9 +126,26 @@
   //   ConnectMojo() -> OnMojoConnected() ->
   // RUNNING
   //
-  // At any state, Stop() can be called. It does not immediately stop the
-  // instance, but will eventually stop it.
-  // The actual stop will be notified via ArcSession::Observer::OnStopped().
+  // Also, StartForLoginScreen() may start ARC instance with
+  // |login_screen_instance_requested_| set to |true|. In that case, the state
+  // changes like the following:
+  //
+  // NOT_STARTED
+  //   StartForLoginScreen() ->
+  // CREATING_SOCKET
+  //   CreateSocket() -> OnSocketCreated() ->
+  // STARTING_INSTANCE
+  //   -> OnInstanceStarted() ->
+  // RUNNING_FOR_LOGIN_SCREEN
+  //
+  // Start() can also be used at any of these 3 state (from CREATING_SOCKET to
+  // RUNNING_FOR_LOGIN_SCREEN) to turn the instance for login screen into a
+  // fully functional one.
+  //
+  // Regardless of whether the instance is for login screen or not, at any
+  // state, Stop() can be called. It may not immediately stop the instance,
+  // but will eventually stop it. The actual stop will be notified via
+  // ArcSession::Observer::OnSessionStopped().
   //
   // When Stop() is called, it makes various behavior based on the current
   // phase.
@@ -153,6 +171,7 @@
   //   whose read side is also polled. Then, in its callback, similar to
   //   STARTING_INSTANCE, a request to stop the ARC instance is sent to
   //   SessionManager, and ArcInstanceStopped handles remaining procedure.
+  // RUNNING_FOR_LOGIN_SCREEN:
   // RUNNING:
   //   There is no more callback which runs on normal flow, so Stop() requests
   //   to stop the ARC instance via SessionManager.
@@ -161,19 +180,6 @@
   // is an event ArcInstanceStopped() sent from SessionManager, when ARC
   // instace unexpectedly terminates. ArcInstanceStopped() turns the state into
   // STOPPED immediately.
-  // This happens only when STARTING_INSTANCE, CONNECTING_MOJO or RUNNING
-  // state.
-  //
-  // STARTING_INSTANCE:
-  //   In OnInstanceStarted(), |state_| is checked at the beginning. If it is
-  //   STOPPED, then ArcInstanceStopped() is called. Do nothing in that case.
-  // CONNECTING_MOJO:
-  //   Similar to Stop() case above, ArcInstanceStopped() also notifies to
-  //   BlockingPool thread to cancel it to unblock the thread. In
-  //   OnMojoConnected(), similar to OnInstanceStarted(), check if |state_| is
-  //   STOPPED, then do nothing.
-  // RUNNING:
-  //   It is not necessary to do anything special here.
   //
   // In NOT_STARTED or STOPPED state, the instance can be safely destructed.
   // Specifically, in STOPPED state, there may be inflight operations or
@@ -189,9 +195,13 @@
     // An UNIX socket is being created.
     CREATING_SOCKET,
 
-    // The request to start the instance has been sent.
+    // The request to start or resume the instance has been sent.
     STARTING_INSTANCE,
 
+    // The instance is set up, but only a handful of processes NOT including
+    // arcbridgeservice (i.e. mojo endpoint) are running.
+    RUNNING_FOR_LOGIN_SCREEN,
+
     // The instance has started. Waiting for it to connect to the IPC bridge.
     CONNECTING_MOJO,
 
@@ -207,6 +217,8 @@
   ~ArcSessionImpl() override;
 
   // ArcSession overrides:
+  void StartForLoginScreen() override;
+  bool IsForLoginScreen() override;
   void Start() override;
   void Stop() override;
   void OnShutdown() override;
@@ -215,10 +227,12 @@
   // Creates the UNIX socket on a worker pool and then processes its file
   // descriptor.
   static mojo::edk::ScopedPlatformHandle CreateSocket();
-  void OnSocketCreated(mojo::edk::ScopedPlatformHandle fd);
+  void OnSocketCreated(bool instance_is_for_login_screen,
+                       mojo::edk::ScopedPlatformHandle fd);
 
   // DBus callback for StartArcInstance().
-  void OnInstanceStarted(mojo::edk::ScopedPlatformHandle socket_fd,
+  void OnInstanceStarted(bool instance_is_for_login_screen,
+                         mojo::edk::ScopedPlatformHandle socket_fd,
                          StartArcInstanceResult result,
                          const std::string& container_instance_id);
 
@@ -236,9 +250,15 @@
   void ArcInstanceStopped(bool clean,
                           const std::string& container_instance_id) override;
 
-  // Completes the termination procedure.
+  // Completes the termination procedure. Note that calling this may end up with
+  // deleting |this| because the function calls observers' OnSessionStopped().
   void OnStopped(ArcStopReason reason);
 
+  // Sends a StartArcInstance D-Bus request to session_manager.
+  static void SendStartArcInstanceDBusMessage(
+      bool instance_is_for_login_screen,
+      const chromeos::SessionManagerClient::StartArcInstanceCallback& cb);
+
   // Checks whether a function runs on the thread where the instance is
   // created.
   THREAD_CHECKER(thread_checker_);
@@ -255,6 +275,14 @@
   // When Stop() is called, this flag is set.
   bool stop_requested_ = false;
 
+  // When StartForLoginScreen() is called, this flag is set. After
+  // that, when Start() is called to resume the boot, the flag is unset.
+  bool login_screen_instance_requested_ = false;
+
+  // The handle StartForLoginScreen() has created. The variable has a
+  // valid handle only when |state_| is RUNNING_FOR_LOGIN_SCREEN.
+  mojo::edk::ScopedPlatformHandle socket_fd_;
+
   // Container instance id passed from session_manager.
   // Should be available only after OnInstanceStarted().
   std::string container_instance_id_;
@@ -296,15 +324,42 @@
 
 void ArcSessionImpl::Start() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(state_, State::NOT_STARTED);
-  VLOG(2) << "Starting ARC session.";
-  VLOG(2) << "Creating socket...";
+  // Start() can be called either for starting ARC from scratch or for
+  // resuming an existing one. Start() must be able to start a fully
+  // functional instance from all of |state_| up to and including
+  // RUNNING_FOR_LOGIN_SCREEN.
+  DCHECK_GE(State::RUNNING_FOR_LOGIN_SCREEN, state_);
 
-  state_ = State::CREATING_SOCKET;
-  base::PostTaskAndReplyWithResult(
-      blocking_task_runner_.get(), FROM_HERE,
-      base::Bind(&ArcSessionImpl::CreateSocket),
-      base::Bind(&ArcSessionImpl::OnSocketCreated, weak_factory_.GetWeakPtr()));
+  // Flip the flag now so that callback functions like OnSocketCreated()
+  // can do the right thing.
+  login_screen_instance_requested_ = false;
+
+  if (state_ == State::NOT_STARTED) {
+    // An instance for login screen does not exist. Start a new one from
+    // scratch.
+    VLOG(2) << "Starting ARC session";
+    VLOG(2) << "Creating socket...";
+    state_ = State::CREATING_SOCKET;
+    base::PostTaskAndReplyWithResult(
+        blocking_task_runner_.get(), FROM_HERE,
+        base::Bind(&ArcSessionImpl::CreateSocket),
+        base::Bind(&ArcSessionImpl::OnSocketCreated, weak_factory_.GetWeakPtr(),
+                   false /* not for login screen */));
+  } else if (state_ == State::CREATING_SOCKET) {
+    VLOG(2) << "Requested to start ARC instance with an existing socket";
+    // OnSocketCreated() will start a fully featured instance.
+  } else if (state_ == State::STARTING_INSTANCE) {
+    VLOG(2) << "Requested to resume an existing ARC instance";
+    // OnInstanceStarted() will start a fully featured instance.
+  } else if (state_ == State::RUNNING_FOR_LOGIN_SCREEN) {
+    VLOG(2) << "Resuming an existing ARC instance";
+    state_ = State::STARTING_INSTANCE;
+    SendStartArcInstanceDBusMessage(
+        false /* not for login screen */,
+        base::Bind(&ArcSessionImpl::OnInstanceStarted,
+                   weak_factory_.GetWeakPtr(), false /* the same */,
+                   base::Passed(&socket_fd_)));
+  }
 }
 
 // static
@@ -346,6 +401,7 @@
 }
 
 void ArcSessionImpl::OnSocketCreated(
+    bool instance_is_for_login_screen,
     mojo::edk::ScopedPlatformHandle socket_fd) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_EQ(state_, State::CREATING_SOCKET);
@@ -362,8 +418,29 @@
     return;
   }
 
-  VLOG(2) << "Socket is created. Starting ARC instance...";
+  VLOG(2) << "Socket is created. Starting ARC instance"
+          << (instance_is_for_login_screen ? " for login screen" : "");
   state_ = State::STARTING_INSTANCE;
+  SendStartArcInstanceDBusMessage(
+      instance_is_for_login_screen,
+      base::Bind(&ArcSessionImpl::OnInstanceStarted, weak_factory_.GetWeakPtr(),
+                 instance_is_for_login_screen, base::Passed(&socket_fd)));
+}
+
+// static
+void ArcSessionImpl::SendStartArcInstanceDBusMessage(
+    bool instance_is_for_login_screen,
+    const chromeos::SessionManagerClient::StartArcInstanceCallback& cb) {
+  chromeos::SessionManagerClient* session_manager_client =
+      chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
+  if (instance_is_for_login_screen) {
+    session_manager_client->StartArcInstance(
+        chromeos::SessionManagerClient::ArcStartupMode::LOGIN_SCREEN,
+        // All variables below except |cb| will be ignored.
+        cryptohome::Identification(), false, false, cb);
+    return;
+  }
+
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
   DCHECK(user_manager->GetPrimaryUser());
   const cryptohome::Identification cryptohome_id(
@@ -377,22 +454,29 @@
   const bool scan_vendor_priv_app =
       chromeos::switches::IsVoiceInteractionEnabled();
 
-  chromeos::SessionManagerClient* session_manager_client =
-      chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
   session_manager_client->StartArcInstance(
       chromeos::SessionManagerClient::ArcStartupMode::FULL, cryptohome_id,
-      skip_boot_completed_broadcast, scan_vendor_priv_app,
-      base::Bind(&ArcSessionImpl::OnInstanceStarted, weak_factory_.GetWeakPtr(),
-                 base::Passed(&socket_fd)));
+      skip_boot_completed_broadcast, scan_vendor_priv_app, cb);
 }
 
 void ArcSessionImpl::OnInstanceStarted(
+    bool instance_is_for_login_screen,
     mojo::edk::ScopedPlatformHandle socket_fd,
     StartArcInstanceResult result,
     const std::string& container_instance_id) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_EQ(state_, State::STARTING_INSTANCE);
-  container_instance_id_ = container_instance_id;
+
+  bool resumed = false;
+  if (!container_instance_id_.empty()) {
+    // |container_instance_id_| has already been initialized when the instance
+    // for login screen was started.
+    DCHECK(container_instance_id.empty());
+    DCHECK(!instance_is_for_login_screen);
+    resumed = true;
+  } else {
+    container_instance_id_ = container_instance_id;
+  }
 
   if (stop_requested_) {
     if (result == StartArcInstanceResult::SUCCESS) {
@@ -412,7 +496,26 @@
     return;
   }
 
-  VLOG(2) << "ARC instance is successfully started. Connecting Mojo...";
+  if (instance_is_for_login_screen) {
+    VLOG(2) << "ARC instance for login screen is successfully started.";
+    if (login_screen_instance_requested_) {
+      state_ = State::RUNNING_FOR_LOGIN_SCREEN;
+      socket_fd_ = std::move(socket_fd);
+    } else {
+      // Start() has been called.
+      VLOG(2) << "Resuming an existing ARC instance";
+      state_ = State::STARTING_INSTANCE;
+      SendStartArcInstanceDBusMessage(
+          false /* not for login screen */,
+          base::Bind(&ArcSessionImpl::OnInstanceStarted,
+                     weak_factory_.GetWeakPtr(), false /* the same */,
+                     base::Passed(&socket_fd_)));
+    }
+    return;
+  }
+
+  VLOG(2) << "ARC instance is successfully "
+          << (resumed ? "resumed" : "started") << ". Connecting Mojo...";
   state_ = State::CONNECTING_MOJO;
 
   // Prepare a pipe so that AcceptInstanceConnection can be interrupted on
@@ -539,6 +642,11 @@
       // clean it up.
       return;
 
+    case State::RUNNING_FOR_LOGIN_SCREEN:
+      // An ARC instance for login screen is running. Request to stop it.
+      StopArcInstance();
+      return;
+
     case State::CONNECTING_MOJO:
       // Mojo connection is being waited on a BlockingPool thread.
       // Request to cancel it. Following stopping procedure will run
@@ -560,10 +668,13 @@
 void ArcSessionImpl::StopArcInstance() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(state_ == State::STARTING_INSTANCE ||
+         state_ == State::RUNNING_FOR_LOGIN_SCREEN ||
          state_ == State::CONNECTING_MOJO || state_ == State::RUNNING);
 
-  // Notification will arrive through ArcInstanceStopped().
-  VLOG(2) << "Requesting to stop ARC instance";
+  VLOG(2) << "Requesting session_manager to stop ARC instance";
+
+  // When the instance is not for login screen, change the |state_| in
+  // ArcInstanceStopped().
   chromeos::SessionManagerClient* session_manager_client =
       chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
   session_manager_client->StopArcInstance(
@@ -578,6 +689,8 @@
           << (clean ? "cleanly" : "uncleanly");
 
   if (container_instance_id != container_instance_id_) {
+    // This path is taken e.g. when an instance for login screen is Stop()ped
+    // by ArcSessionRunner.
     VLOG(1) << "Container instance id mismatch. Do nothing."
             << container_instance_id << " vs " << container_instance_id_;
     return;
@@ -607,6 +720,24 @@
   OnStopped(reason);
 }
 
+void ArcSessionImpl::StartForLoginScreen() {
+  DCHECK_EQ(State::NOT_STARTED, state_);
+
+  VLOG(2) << "Starting ARC session for login screen";
+  VLOG(2) << "Creating socket...";
+  login_screen_instance_requested_ = true;
+  state_ = State::CREATING_SOCKET;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_.get(), FROM_HERE,
+      base::Bind(&ArcSessionImpl::CreateSocket),
+      base::Bind(&ArcSessionImpl::OnSocketCreated, weak_factory_.GetWeakPtr(),
+                 true /* for login screen */));
+}
+
+bool ArcSessionImpl::IsForLoginScreen() {
+  return login_screen_instance_requested_;
+}
+
 void ArcSessionImpl::OnStopped(ArcStopReason reason) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // OnStopped() should be called once per instance.
@@ -634,10 +765,12 @@
   // Note that this may fail if ARC container is not actually running, but
   // ignore an error as described below.
   if (state_ == State::STARTING_INSTANCE ||
-      state_ == State::CONNECTING_MOJO || state_ == State::RUNNING)
+      state_ == State::RUNNING_FOR_LOGIN_SCREEN ||
+      state_ == State::CONNECTING_MOJO || state_ == State::RUNNING) {
     StopArcInstance();
+  }
 
-  // Directly set to the STOPPED stateby OnStopped(). Note that calling
+  // Directly set to the STOPPED state by OnStopped(). Note that calling
   // StopArcInstance() may not work well. At least, because the UI thread is
   // already stopped here, ArcInstanceStopped() callback cannot be invoked.
   OnStopped(ArcStopReason::SHUTDOWN);
diff --git a/components/arc/arc_session.h b/components/arc/arc_session.h
index 4f9f387..7ae7a19 100644
--- a/components/arc/arc_session.h
+++ b/components/arc/arc_session.h
@@ -8,10 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
-#include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
 #include "base/task_runner.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_stop_reason.h"
@@ -21,8 +18,8 @@
 // Starts the ARC instance and bootstraps the bridge connection.
 // Clients should implement the Delegate to be notified upon communications
 // being available.
-// The instance can be safely removed 1) before Start() is called, or 2) after
-// OnStopped() is called.
+// The instance can be safely removed 1) before Start*() is called, or 2) after
+// OnSessionStopped() is called.
 // The number of instances must be at most one. Otherwise, ARC instances will
 // conflict.
 class ArcSession {
@@ -47,17 +44,29 @@
       const scoped_refptr<base::TaskRunner>& blocking_task_runner);
   virtual ~ArcSession();
 
+  // Starts an instance for login screen. The instance is not a fully functional
+  // one, and Observer::OnSessionReady() will *never* be called.
+  virtual void StartForLoginScreen() = 0;
+
+  // Returns true if StartForLoginScreen() has been called but Start() hasn't.
+  virtual bool IsForLoginScreen() = 0;
+
   // Starts and bootstraps a connection with the instance. The Observer's
-  // OnReady() will be called if the bootstrapping is successful, or
-  // OnStopped() if it is not. Start() should not be called twice or more.
+  // OnSessionReady() will be called if the bootstrapping is successful, or
+  // OnSessionStopped() if it is not. Start() should not be called twice or
+  // more. When StartForLoginScreen() has already been called, Start() turns
+  // the mini instance to a fully functional one.
   virtual void Start() = 0;
 
-  // Requests to stop the currently-running instance.
-  // The completion is notified via OnStopped() of the Delegate.
+  // Requests to stop the currently-running instance whether or not it is for
+  // login screen.
+  // The completion is notified via OnSessionStopped() of the Observer.
   virtual void Stop() = 0;
 
   // Called when Chrome is in shutdown state. This is called when the message
-  // loop is already stopped, and the instance will soon be deleted.
+  // loop is already stopped, and the instance will soon be deleted. Caller
+  // may expect that OnSessionStopped() is synchronously called back except
+  // when it has already been called before.
   virtual void OnShutdown() = 0;
 
   void AddObserver(Observer* observer);
diff --git a/components/arc/arc_session_runner.cc b/components/arc/arc_session_runner.cc
index 4c3e8d9..d16f5efc 100644
--- a/components/arc/arc_session_runner.cc
+++ b/components/arc/arc_session_runner.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/task_runner.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
 
 namespace arc {
 
@@ -15,17 +16,34 @@
 constexpr base::TimeDelta kDefaultRestartDelay =
     base::TimeDelta::FromSeconds(5);
 
+chromeos::SessionManagerClient* GetSessionManagerClient() {
+  // If the DBusThreadManager or the SessionManagerClient aren't available,
+  // there isn't much we can do. This should only happen when running tests.
+  if (!chromeos::DBusThreadManager::IsInitialized() ||
+      !chromeos::DBusThreadManager::Get() ||
+      !chromeos::DBusThreadManager::Get()->GetSessionManagerClient())
+    return nullptr;
+  return chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
+}
+
 }  // namespace
 
 ArcSessionRunner::ArcSessionRunner(const ArcSessionFactory& factory)
     : restart_delay_(kDefaultRestartDelay),
       factory_(factory),
-      weak_ptr_factory_(this) {}
+      weak_ptr_factory_(this) {
+  chromeos::SessionManagerClient* client = GetSessionManagerClient();
+  if (client)
+    client->AddObserver(this);
+}
 
 ArcSessionRunner::~ArcSessionRunner() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (arc_session_)
     arc_session_->RemoveObserver(this);
+  chromeos::SessionManagerClient* client = GetSessionManagerClient();
+  if (client)
+    client->RemoveObserver(this);
 }
 
 void ArcSessionRunner::AddObserver(Observer* observer) {
@@ -52,30 +70,33 @@
   // previous RequestStop() call).
   DCHECK(!restart_timer_.IsRunning());
 
-  if (arc_session_) {
+  if (arc_session_ && state_ >= State::STARTING) {
     // In this case, RequestStop() was called, and before |arc_session_| had
     // finished stopping, RequestStart() was called. Do nothing in that case,
     // since when |arc_session_| does actually stop, OnSessionStopped() will
     // be called, where it should automatically restart.
     DCHECK_EQ(state_, State::STOPPING);
   } else {
-    DCHECK_EQ(state_, State::STOPPED);
+    DCHECK_LE(state_, State::STARTING_FOR_LOGIN_SCREEN);
     StartArcSession();
   }
 }
 
-void ArcSessionRunner::RequestStop() {
+void ArcSessionRunner::RequestStop(bool always_stop_session) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  // Consecutive RequestStop() call. Do nothing.
-  if (!run_requested_)
-    return;
+  if (!run_requested_) {
+    // Call Stop() to stop an instance for login screen (if any.) If this is
+    // just a consecutive RequestStop() call, Stop() does nothing.
+    if (!always_stop_session || !arc_session_)
+      return;
+  }
 
   VLOG(1) << "Session ended";
   run_requested_ = false;
 
   if (arc_session_) {
-    // The |state_| could be either STARTING, RUNNING or STOPPING.
+    // The |state_| could be either STARTING*, RUNNING or STOPPING.
     DCHECK_NE(state_, State::STOPPED);
 
     if (state_ == State::STOPPING) {
@@ -124,20 +145,22 @@
 void ArcSessionRunner::SetRestartDelayForTesting(
     const base::TimeDelta& restart_delay) {
   DCHECK_EQ(state_, State::STOPPED);
-  DCHECK(!arc_session_);
   DCHECK(!restart_timer_.IsRunning());
   restart_delay_ = restart_delay;
 }
 
 void ArcSessionRunner::StartArcSession() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(state_, State::STOPPED);
-  DCHECK(!arc_session_);
   DCHECK(!restart_timer_.IsRunning());
 
   VLOG(1) << "Starting ARC instance";
-  arc_session_ = factory_.Run();
-  arc_session_->AddObserver(this);
+  if (!arc_session_) {
+    DCHECK_EQ(state_, State::STOPPED);
+    arc_session_ = factory_.Run();
+    arc_session_->AddObserver(this);
+  } else {
+    DCHECK_EQ(state_, State::STARTING_FOR_LOGIN_SCREEN);
+  }
   state_ = State::STARTING;
   arc_session_->Start();
 }
@@ -159,6 +182,11 @@
   DCHECK(!restart_timer_.IsRunning());
 
   VLOG(0) << "ARC stopped: " << stop_reason;
+
+  // The observers should be agnostic to the existence of the limited-purpose
+  // instance.
+  const bool notify_observers = !arc_session_->IsForLoginScreen();
+
   arc_session_->RemoveObserver(this);
   arc_session_.reset();
 
@@ -188,8 +216,23 @@
   }
 
   state_ = State::STOPPED;
-  for (auto& observer : observer_list_)
-    observer.OnSessionStopped(stop_reason, restarting);
+  if (notify_observers) {
+    for (auto& observer : observer_list_)
+      observer.OnSessionStopped(stop_reason, restarting);
+  }
+}
+
+void ArcSessionRunner::EmitLoginPromptVisibleCalled() {
+  DCHECK(!arc_session_);
+  // Since 'login-prompt-visible' Upstart signal starts all Upstart jobs the
+  // container may depend on such as cras, EmitLoginPromptVisibleCalled() is the
+  // safe place to start the container for login screen.
+  // TODO(yusukes): Once Chrome OS side is ready, uncomment the following:
+
+  // arc_session_ = factory_.Run();
+  // arc_session_->AddObserver(this);
+  // state_ = State::STARTING_FOR_LOGIN_SCREEN;
+  // arc_session_->StartForLoginScreen();
 }
 
 }  // namespace arc
diff --git a/components/arc/arc_session_runner.h b/components/arc/arc_session_runner.h
index f5898b9..13bf3ea 100644
--- a/components/arc/arc_session_runner.h
+++ b/components/arc/arc_session_runner.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "chromeos/dbus/session_manager_client.h"
 #include "components/arc/arc_session.h"
 #include "components/arc/arc_stop_reason.h"
 
@@ -18,7 +19,8 @@
 
 // Accept requests to start/stop ARC instance. Also supports automatic
 // restarting on unexpected ARC instance crash.
-class ArcSessionRunner : public ArcSession::Observer {
+class ArcSessionRunner : public ArcSession::Observer,
+                         public chromeos::SessionManagerClient::Observer {
  public:
   // Observer to notify events across multiple ARC session runs.
   class Observer {
@@ -50,7 +52,8 @@
   void RequestStart();
 
   // Stops the ARC service.
-  void RequestStop();
+  // TODO(yusukes): Remove the parameter.
+  void RequestStop(bool always_stop_session);
 
   // OnShutdown() should be called when the browser is shutting down. This can
   // only be called on the thread that this class was created on. We assume that
@@ -94,6 +97,10 @@
     // ARC instance is not currently running.
     STOPPED,
 
+    // Request to start ARC instance for login screen is received. Starting an
+    // ARC instance.
+    STARTING_FOR_LOGIN_SCREEN,
+
     // Request to start ARC instance is received. Starting an ARC instance.
     STARTING,
 
@@ -112,6 +119,9 @@
   void OnSessionReady() override;
   void OnSessionStopped(ArcStopReason reason) override;
 
+  // chromeos::SessionManagerClient::Observer:
+  void EmitLoginPromptVisibleCalled() override;
+
   THREAD_CHECKER(thread_checker_);
 
   // Observers for the ARC instance state change events.
diff --git a/components/arc/arc_session_runner_unittest.cc b/components/arc/arc_session_runner_unittest.cc
index c407a09..144d0b0b 100644
--- a/components/arc/arc_session_runner_unittest.cc
+++ b/components/arc/arc_session_runner_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/test/scoped_task_environment.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_session_manager_client.h"
 #include "components/arc/arc_session_runner.h"
 #include "components/arc/test/fake_arc_session.h"
 #include "mojo/public/cpp/system/message_pipe.h"
@@ -40,10 +41,13 @@
             base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
 
   void SetUp() override {
+    chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
+        base::MakeUnique<chromeos::FakeSessionManagerClient>());
     chromeos::DBusThreadManager::Initialize();
 
     stop_reason_ = ArcStopReason::SHUTDOWN;
     restarting_ = false;
+    stopped_called_ = false;
 
     // We inject FakeArcSession here so we do not need task_runner.
     arc_session_runner_ =
@@ -65,8 +69,17 @@
         arc_session_runner_->GetArcSessionForTesting());
   }
 
-  ArcStopReason stop_reason() { return stop_reason_; }
-  bool restarting() { return restarting_; }
+  ArcStopReason stop_reason() {
+    EXPECT_TRUE(stopped_called());
+    return stop_reason_;
+  }
+
+  bool restarting() {
+    EXPECT_TRUE(stopped_called());
+    return restarting_;
+  }
+
+  bool stopped_called() { return stopped_called_; }
 
   void ResetArcSessionFactory(
       const ArcSessionRunner::ArcSessionFactory& factory) {
@@ -95,10 +108,12 @@
     // ArcSessionRunner::OnSessionStopped().
     stop_reason_ = stop_reason;
     restarting_ = restarting;
+    stopped_called_ = true;
   }
 
   ArcStopReason stop_reason_;
   bool restarting_;
+  bool stopped_called_;
   std::unique_ptr<ArcSessionRunner> arc_session_runner_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
@@ -138,7 +153,7 @@
   arc_session_runner()->RequestStart();
   EXPECT_TRUE(arc_session_runner()->IsRunning());
 
-  arc_session_runner()->RequestStop();
+  arc_session_runner()->RequestStop(false);
   EXPECT_TRUE(arc_session_runner()->IsStopped());
   EXPECT_TRUE(observer.stopped_called());
 }
@@ -154,7 +169,7 @@
   EXPECT_FALSE(arc_session_runner()->IsStopped());
   EXPECT_FALSE(arc_session_runner()->IsRunning());
 
-  arc_session_runner()->RequestStop();
+  arc_session_runner()->RequestStop(false);
   EXPECT_TRUE(arc_session_runner()->IsStopped());
 }
 
@@ -171,6 +186,47 @@
   EXPECT_TRUE(arc_session_runner()->IsStopped());
 }
 
+// Does the same with the mini instance for login screen.
+// TODO(yusukes): Enable the test once EmitLoginPromptVisibleCalled() is fully
+// enabled.
+TEST_F(ArcSessionRunnerTest, DISABLED_BootFailureForLoginScreen) {
+  ResetArcSessionFactory(
+      base::Bind(&ArcSessionRunnerTest::CreateBootFailureArcSession,
+                 ArcStopReason::CRASH));
+  EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+  chromeos::DBusThreadManager::Get()
+      ->GetSessionManagerClient()
+      ->EmitLoginPromptVisible();
+  // If starting the mini instance fails, arc_session_runner()'s state goes back
+  // to STOPPED, but its observers won't be notified.
+  EXPECT_TRUE(arc_session_runner()->IsStopped());
+  EXPECT_FALSE(stopped_called());
+
+  // Also make sure that RequestStart() works just fine after the boot
+  // failure.
+  ResetArcSessionFactory(base::Bind(FakeArcSession::Create));
+  arc_session_runner()->RequestStart();
+  EXPECT_TRUE(arc_session_runner()->IsRunning());
+}
+
+// Tests that RequestStart() works even after EmitLoginPromptVisibleCalled()
+// is called.
+// TODO(yusukes): Enable the test once EmitLoginPromptVisibleCalled() is fully
+// enabled.
+TEST_F(ArcSessionRunnerTest, DISABLED_StartWithLoginScreenInstance) {
+  EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+  chromeos::DBusThreadManager::Get()
+      ->GetSessionManagerClient()
+      ->EmitLoginPromptVisible();
+  EXPECT_FALSE(arc_session_runner()->IsStopped());
+  EXPECT_FALSE(arc_session_runner()->IsRunning());
+
+  arc_session_runner()->RequestStart();
+  EXPECT_TRUE(arc_session_runner()->IsRunning());
+}
+
 // If the instance is stopped, it should be re-started.
 TEST_F(ArcSessionRunnerTest, Restart) {
   arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
@@ -186,7 +242,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(arc_session_runner()->IsRunning());
 
-  arc_session_runner()->RequestStop();
+  arc_session_runner()->RequestStop(false);
   EXPECT_TRUE(arc_session_runner()->IsStopped());
 }
 
@@ -217,7 +273,7 @@
   EXPECT_TRUE(arc_session_runner()->IsRunning());
 
   // Graceful stop.
-  arc_session_runner()->RequestStop();
+  arc_session_runner()->RequestStop(false);
   EXPECT_EQ(ArcStopReason::SHUTDOWN, stop_reason());
   EXPECT_FALSE(restarting());
   EXPECT_TRUE(arc_session_runner()->IsStopped());
diff --git a/components/arc/test/fake_arc_session.cc b/components/arc/test/fake_arc_session.cc
index 93a99967..6b3b5c1 100644
--- a/components/arc/test/fake_arc_session.cc
+++ b/components/arc/test/fake_arc_session.cc
@@ -15,7 +15,20 @@
 
 FakeArcSession::~FakeArcSession() = default;
 
+void FakeArcSession::StartForLoginScreen() {
+  is_for_login_screen_ = true;
+  if (boot_failure_emulation_enabled_) {
+    for (auto& observer : observer_list_)
+      observer.OnSessionStopped(boot_failure_reason_);
+  }
+}
+
+bool FakeArcSession::IsForLoginScreen() {
+  return is_for_login_screen_;
+}
+
 void FakeArcSession::Start() {
+  is_for_login_screen_ = false;
   if (boot_failure_emulation_enabled_) {
     for (auto& observer : observer_list_)
       observer.OnSessionStopped(boot_failure_reason_);
diff --git a/components/arc/test/fake_arc_session.h b/components/arc/test/fake_arc_session.h
index 132c86a..9e272d2 100644
--- a/components/arc/test/fake_arc_session.h
+++ b/components/arc/test/fake_arc_session.h
@@ -20,6 +20,8 @@
   ~FakeArcSession() override;
 
   // ArcSession overrides:
+  void StartForLoginScreen() override;
+  bool IsForLoginScreen() override;
   void Start() override;
   void Stop() override;
   void OnShutdown() override;
@@ -29,11 +31,12 @@
 
   // The following control Start() behavior for testing various situations.
 
-  // Enables/disables boot failure emulation, in which OnStopped(reason) will
-  // be called when Start() is called.
+  // Enables/disables boot failure emulation, in which OnSessionStopped(reason)
+  // will be called when Start() or StartForLoginScreen() is called.
   void EnableBootFailureEmulation(ArcStopReason reason);
 
-  // Emulate Start() is suspended at some phase, before OnReady() is invoked.
+  // Emulate Start() is suspended at some phase, before OnSessionReady() is
+  // invoked.
   void SuspendBoot();
 
   // Returns FakeArcSession instance. This can be used for a factory
@@ -45,6 +48,7 @@
   ArcStopReason boot_failure_reason_;
 
   bool boot_suspended_ = false;
+  bool is_for_login_screen_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FakeArcSession);
 };
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc
index df5890d..cba036d 100644
--- a/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_table.cc
@@ -2621,23 +2621,27 @@
 }
 
 bool AutofillTable::MigrateToVersion73AddMaskedCardBankName() {
-  sql::Transaction transaction(db_);
-  if (!transaction.Begin())
-    return false;
-
   // Add the new bank_name column to the masked_credit_cards table.
-  if (!db_->DoesColumnExist("masked_credit_cards", "bank_name") &&
-      !db_->Execute("ALTER TABLE masked_credit_cards ADD COLUMN "
-                    "bank_name VARCHAR")) {
-    return false;
-  }
-
-  return transaction.Commit();
+  return db_->DoesColumnExist("masked_credit_cards", "bank_name") ||
+         db_->Execute(
+             "ALTER TABLE masked_credit_cards ADD COLUMN bank_name VARCHAR");
 }
 
 bool AutofillTable::MigrateToVersion74AddServerCardTypeColumn() {
-  return db_->Execute(
-      "ALTER TABLE masked_credit_cards ADD COLUMN type INTEGER DEFAULT 0");
+  // Version 73 was actually used by two different schemas; an attempt to add
+  // the "type" column (as in this version 74) was landed and reverted, and then
+  // the "bank_name" column was added (and stuck). Some clients may have been
+  // upgraded to one and some the other. Figure out which is the case.
+  const bool added_type_column_in_v73 =
+      db_->DoesColumnExist("masked_credit_cards", "type");
+
+  // If we previously added the "type" column, then it's already present with
+  // the correct semantics, but we now need to run the "bank_name" migration.
+  // Otherwise, we need to add "type" now.
+  return added_type_column_in_v73 ? MigrateToVersion73AddMaskedCardBankName()
+                                  : db_->Execute(
+                                        "ALTER TABLE masked_credit_cards ADD "
+                                        "COLUMN type INTEGER DEFAULT 0");
 }
 
 }  // namespace autofill
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc
index 5ae9136..5ca4fee 100644
--- a/components/exo/wayland/clients/client_base.cc
+++ b/components/exo/wayland/clients/client_base.cc
@@ -16,6 +16,7 @@
 
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/message_loop/message_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -260,7 +261,7 @@
       LOG(ERROR) << "Can't create gbm device";
       return false;
     }
-
+    ui_loop_.reset(new base::MessageLoopForUI);
     ui::OzonePlatform::InitParams params;
     params.single_process = true;
     ui::OzonePlatform::InitializeForGPU(params);
diff --git a/components/exo/wayland/clients/client_base.h b/components/exo/wayland/clients/client_base.h
index be00a8c..2fef99a 100644
--- a/components/exo/wayland/clients/client_base.h
+++ b/components/exo/wayland/clients/client_base.h
@@ -19,6 +19,7 @@
 
 namespace base {
 class CommandLine;
+class MessageLoopForUI;
 }
 
 namespace exo {
@@ -92,6 +93,7 @@
   std::unique_ptr<wl_shell_surface> shell_surface_;
   Globals globals_;
 #if defined(OZONE_PLATFORM_GBM)
+  std::unique_ptr<base::MessageLoopForUI> ui_loop_;
   base::ScopedFD drm_fd_;
   std::unique_ptr<gbm_device> device_;
 #endif
diff --git a/components/history/core/browser/expire_history_backend_unittest.cc b/components/history/core/browser/expire_history_backend_unittest.cc
index 11cd46f..0a554fa8 100644
--- a/components/history/core/browser/expire_history_backend_unittest.cc
+++ b/components/history/core/browser/expire_history_backend_unittest.cc
@@ -16,11 +16,11 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/scoped_observer.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/history/core/browser/history_backend_client.h"
 #include "components/history/core/browser/history_backend_notifier.h"
 #include "components/history/core/browser/history_constants.h"
@@ -55,7 +55,9 @@
  public:
   ExpireHistoryTest()
       : backend_client_(history_client_.CreateBackendClient()),
-        expirer_(this, backend_client_.get(), message_loop_.task_runner()),
+        expirer_(this,
+                 backend_client_.get(),
+                 scoped_task_environment_.GetMainThreadTaskRunner()),
         now_(base::Time::Now()) {}
 
  protected:
@@ -96,11 +98,11 @@
   // This must be destroyed last.
   base::ScopedTempDir tmp_dir_;
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
   HistoryClientFakeBookmarks history_client_;
   std::unique_ptr<HistoryBackendClient> backend_client_;
 
-  base::MessageLoopForUI message_loop_;
-
   ExpireHistoryBackend expirer_;
 
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
@@ -139,8 +141,7 @@
                                   PrepopulatedPageList(),
                                   base::Bind(MockCanAddURLToHistory));
     WaitTopSitesLoadedObserver wait_top_sites_observer(top_sites_);
-    top_sites_->Init(path().Append(kTopSitesFilename),
-                     message_loop_.task_runner());
+    top_sites_->Init(path().Append(kTopSitesFilename));
     wait_top_sites_observer.Run();
   }
 
diff --git a/components/history/core/browser/top_sites_backend.cc b/components/history/core/browser/top_sites_backend.cc
index 7124e82..7b0437e 100644
--- a/components/history/core/browser/top_sites_backend.cc
+++ b/components/history/core/browser/top_sites_backend.cc
@@ -14,6 +14,8 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "components/history/core/browser/top_sites_database.h"
@@ -21,9 +23,10 @@
 
 namespace history {
 
-TopSitesBackend::TopSitesBackend(
-    const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner)
-    : db_(new TopSitesDatabase()), db_task_runner_(db_task_runner) {
+TopSitesBackend::TopSitesBackend()
+    : db_(new TopSitesDatabase()),
+      db_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::TaskPriority::USER_VISIBLE, base::MayBlock()})) {
   DCHECK(db_task_runner_);
 }
 
@@ -83,7 +86,7 @@
 }
 
 void TopSitesBackend::InitDBOnDBThread(const base::FilePath& path) {
-  DCHECK(db_task_runner_->BelongsToCurrentThread());
+  DCHECK(db_task_runner_->RunsTasksOnCurrentThread());
   if (!db_->Init(path)) {
     LOG(ERROR) << "Failed to initialize database.";
     db_.reset();
@@ -91,13 +94,13 @@
 }
 
 void TopSitesBackend::ShutdownDBOnDBThread() {
-  DCHECK(db_task_runner_->BelongsToCurrentThread());
+  DCHECK(db_task_runner_->RunsTasksOnCurrentThread());
   db_.reset();
 }
 
 void TopSitesBackend::GetMostVisitedThumbnailsOnDBThread(
     scoped_refptr<MostVisitedThumbnails> thumbnails) {
-  DCHECK(db_task_runner_->BelongsToCurrentThread());
+  DCHECK(db_task_runner_->RunsTasksOnCurrentThread());
 
   if (db_) {
     db_->GetPageThumbnails(&(thumbnails->most_visited),
@@ -139,7 +142,7 @@
 }
 
 void TopSitesBackend::ResetDatabaseOnDBThread(const base::FilePath& file_path) {
-  DCHECK(db_task_runner_->BelongsToCurrentThread());
+  DCHECK(db_task_runner_->RunsTasksOnCurrentThread());
   db_.reset(NULL);
   sql::Connection::Delete(db_path_);
   db_.reset(new TopSitesDatabase());
diff --git a/components/history/core/browser/top_sites_backend.h b/components/history/core/browser/top_sites_backend.h
index 807f337..620a76ac 100644
--- a/components/history/core/browser/top_sites_backend.h
+++ b/components/history/core/browser/top_sites_backend.h
@@ -16,7 +16,7 @@
 namespace base {
 class CancelableTaskTracker;
 class FilePath;
-class SingleThreadTaskRunner;
+class SequencedTaskRunner;
 }
 
 namespace history {
@@ -42,8 +42,7 @@
   typedef base::Callback<void(const scoped_refptr<MostVisitedThumbnails>&)>
       GetMostVisitedThumbnailsCallback;
 
-  explicit TopSitesBackend(
-      const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner);
+  TopSitesBackend();
 
   void Init(const base::FilePath& path);
 
@@ -103,7 +102,7 @@
   base::FilePath db_path_;
 
   std::unique_ptr<TopSitesDatabase> db_;
-  scoped_refptr<base::SingleThreadTaskRunner> db_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> db_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(TopSitesBackend);
 };
diff --git a/components/history/core/browser/top_sites_impl.cc b/components/history/core/browser/top_sites_impl.cc
index f983265..aa758327 100644
--- a/components/history/core/browser/top_sites_impl.cc
+++ b/components/history/core/browser/top_sites_impl.cc
@@ -120,12 +120,10 @@
   DCHECK(!can_add_url_to_history_.is_null());
 }
 
-void TopSitesImpl::Init(
-    const base::FilePath& db_name,
-    const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner) {
+void TopSitesImpl::Init(const base::FilePath& db_name) {
   // Create the backend here, rather than in the constructor, so that
   // unit tests that do not need the backend can run without a problem.
-  backend_ = new TopSitesBackend(db_task_runner);
+  backend_ = new TopSitesBackend();
   backend_->Init(db_name);
   backend_->GetMostVisitedThumbnails(
       base::Bind(&TopSitesImpl::OnGotMostVisitedThumbnails,
diff --git a/components/history/core/browser/top_sites_impl.h b/components/history/core/browser/top_sites_impl.h
index 98bc72f..e8d1023 100644
--- a/components/history/core/browser/top_sites_impl.h
+++ b/components/history/core/browser/top_sites_impl.h
@@ -39,7 +39,6 @@
 class FilePath;
 class RefCountedBytes;
 class RefCountedMemory;
-class SingleThreadTaskRunner;
 }
 
 namespace history {
@@ -64,8 +63,7 @@
                const CanAddURLToHistoryFn& can_add_url_to_history);
 
   // Initializes TopSitesImpl.
-  void Init(const base::FilePath& db_name,
-            const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner);
+  void Init(const base::FilePath& db_name);
 
   // TopSites implementation.
   bool SetPageThumbnail(const GURL& url,
diff --git a/components/history/core/browser/top_sites_impl_unittest.cc b/components/history/core/browser/top_sites_impl_unittest.cc
index 25c83ff..00d1e107 100644
--- a/components/history/core/browser/top_sites_impl_unittest.cc
+++ b/components/history/core/browser/top_sites_impl_unittest.cc
@@ -11,10 +11,10 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "components/history/core/browser/history_client.h"
 #include "components/history/core/browser/history_constants.h"
@@ -72,13 +72,14 @@
                         bool wait,
                         bool include_forced_urls) {
     int start_number_of_callbacks = number_of_callbacks_;
+    base::RunLoop run_loop;
     top_sites->GetMostVisitedURLs(
         base::Bind(&TopSitesQuerier::OnTopSitesAvailable,
-                   weak_ptr_factory_.GetWeakPtr()),
+                   weak_ptr_factory_.GetWeakPtr(), &run_loop),
         include_forced_urls);
     if (wait && start_number_of_callbacks == number_of_callbacks_) {
       waiting_ = true;
-      base::RunLoop().Run();
+      run_loop.Run();
     }
   }
 
@@ -91,11 +92,12 @@
 
  private:
   // Callback for TopSitesImpl::GetMostVisitedURLs.
-  void OnTopSitesAvailable(const history::MostVisitedURLList& data) {
+  void OnTopSitesAvailable(base::RunLoop* run_loop,
+                           const history::MostVisitedURLList& data) {
     urls_ = data;
     number_of_callbacks_++;
     if (waiting_) {
-      base::MessageLoop::current()->QuitWhenIdle();
+      run_loop->QuitWhenIdle();
       waiting_ = false;
     }
   }
@@ -166,15 +168,6 @@
     BlockUntilHistoryProcessesPendingRequests(history_service());
   }
 
-  // Waits for top sites to finish processing a task. This is useful if you need
-  // to wait until top sites finishes processing a task.
-  void WaitForTopSites() {
-    top_sites()->backend_->DoEmptyRequest(
-        base::Bind(&TopSitesImplTest::QuitCallback, base::Unretained(this)),
-        &top_sites_tracker_);
-    base::RunLoop().Run();
-  }
-
   TopSitesImpl* top_sites() { return top_sites_impl_.get(); }
 
   HistoryService* history_service() { return history_service_.get(); }
@@ -196,10 +189,6 @@
     }
   }
 
-  // Quit the current message loop when invoked. Useful when running a nested
-  // message loop.
-  void QuitCallback() { base::MessageLoop::current()->QuitWhenIdle(); }
-
   // Adds a page to history.
   void AddPageToHistory(const GURL& url) {
     RedirectList redirects;
@@ -299,8 +288,7 @@
     top_sites_impl_ = new TopSitesImpl(
         pref_service_.get(), history_service_.get(),
         prepopulated_pages, base::Bind(MockCanAddURLToHistory));
-    top_sites_impl_->Init(scoped_temp_dir_.GetPath().Append(kTopSitesFilename),
-                          message_loop_.task_runner());
+    top_sites_impl_->Init(scoped_temp_dir_.GetPath().Append(kTopSitesFilename));
   }
 
   void DestroyTopSites() {
@@ -308,8 +296,7 @@
       top_sites_impl_->ShutdownOnUIThread();
       top_sites_impl_ = nullptr;
 
-      if (base::MessageLoop::current())
-        base::RunLoop().RunUntilIdle();
+      scoped_task_environment_.RunUntilIdle();
     }
   }
 
@@ -320,8 +307,9 @@
   }
 
  private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
   base::ScopedTempDir scoped_temp_dir_;
-  base::MessageLoopForUI message_loop_;
 
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   std::unique_ptr<HistoryService> history_service_;
diff --git a/components/offline_pages/core/prefetch/BUILD.gn b/components/offline_pages/core/prefetch/BUILD.gn
index e9c5d80..4604e9c4 100644
--- a/components/offline_pages/core/prefetch/BUILD.gn
+++ b/components/offline_pages/core/prefetch/BUILD.gn
@@ -23,6 +23,8 @@
     "prefetch_dispatcher.h",
     "prefetch_dispatcher_impl.cc",
     "prefetch_dispatcher_impl.h",
+    "prefetch_downloader.cc",
+    "prefetch_downloader.h",
     "prefetch_gcm_app_handler.cc",
     "prefetch_gcm_app_handler.h",
     "prefetch_gcm_handler.h",
@@ -35,6 +37,8 @@
     "prefetch_proto_utils.h",
     "prefetch_request_fetcher.cc",
     "prefetch_request_fetcher.h",
+    "prefetch_server_urls.cc",
+    "prefetch_server_urls.h",
     "prefetch_service.h",
     "prefetch_service_impl.cc",
     "prefetch_service_impl.h",
@@ -50,6 +54,7 @@
 
   deps = [
     "//base",
+    "//components/download/public",
     "//components/gcm_driver",
     "//components/gcm_driver/common",
     "//components/keyed_service/core",
@@ -109,6 +114,7 @@
     "get_operation_request_unittest.cc",
     "get_operation_task_unittest.cc",
     "prefetch_dispatcher_impl_unittest.cc",
+    "prefetch_downloader_unittest.cc",
     "prefetch_gcm_app_handler_unittest.cc",
     "prefetch_item_unittest.cc",
     "prefetch_network_request_factory_impl_unittest.cc",
@@ -120,6 +126,7 @@
   deps = [
     ":prefetch",
     ":test_support",
+    "//components/download/public",
     "//components/gcm_driver/instance_id",
     "//components/offline_pages/core",
     "//components/offline_pages/core:switches",
diff --git a/components/offline_pages/core/prefetch/DEPS b/components/offline_pages/core/prefetch/DEPS
index 4a85e6c..5214a88 100644
--- a/components/offline_pages/core/prefetch/DEPS
+++ b/components/offline_pages/core/prefetch/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/download/public",
   "+google_apis",
   "+components/gcm_driver",
   "+components/ntp_snippets",
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_request.cc b/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
index 94f9155..3cb6b2b6 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
@@ -9,16 +9,13 @@
 #include "base/logging.h"
 #include "components/offline_pages/core/prefetch/prefetch_proto_utils.h"
 #include "components/offline_pages/core/prefetch/prefetch_request_fetcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
 #include "components/offline_pages/core/prefetch/proto/offline_pages.pb.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
 
 namespace offline_pages {
 
-namespace {
-const char kGeneratePageBundleRequestURLPath[] = "v1:GeneratePageBundle";
-}  // namespace
-
 GeneratePageBundleRequest::GeneratePageBundleRequest(
     const std::string& user_agent,
     const std::string& gcm_registration_id,
@@ -44,7 +41,7 @@
   request.SerializeToString(&upload_data);
 
   fetcher_ = PrefetchRequestFetcher::CreateForPost(
-      kGeneratePageBundleRequestURLPath, upload_data, channel,
+      GeneratePageBundleRequestURL(channel), upload_data,
       request_context_getter,
       base::Bind(&GeneratePageBundleRequest::OnCompleted,
                  // Fetcher is owned by this instance.
diff --git a/components/offline_pages/core/prefetch/get_operation_request.cc b/components/offline_pages/core/prefetch/get_operation_request.cc
index 77d2cd2..a8541ce8 100644
--- a/components/offline_pages/core/prefetch/get_operation_request.cc
+++ b/components/offline_pages/core/prefetch/get_operation_request.cc
@@ -9,25 +9,20 @@
 #include "base/logging.h"
 #include "components/offline_pages/core/prefetch/prefetch_proto_utils.h"
 #include "components/offline_pages/core/prefetch/prefetch_request_fetcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
 
 namespace offline_pages {
 
-namespace {
-const char kGetOperationURLPath[] = "v1/";
-}  // namespace
-
 GetOperationRequest::GetOperationRequest(
     const std::string& name,
     version_info::Channel channel,
     net::URLRequestContextGetter* request_context_getter,
     const PrefetchRequestFinishedCallback& callback)
     : callback_(callback) {
-  std::string path(kGetOperationURLPath);
-  path += name;
   fetcher_ = PrefetchRequestFetcher::CreateForGet(
-      path, channel, request_context_getter,
+      GetOperationRequestURL(name, channel), request_context_getter,
       base::Bind(&GetOperationRequest::OnCompleted,
                  // Fetcher is owned by this instance.
                  base::Unretained(this)));
diff --git a/components/offline_pages/core/prefetch/prefetch_downloader.cc b/components/offline_pages/core/prefetch/prefetch_downloader.cc
new file mode 100644
index 0000000..8f03b67
--- /dev/null
+++ b/components/offline_pages/core/prefetch/prefetch_downloader.cc
@@ -0,0 +1,113 @@
+// 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_downloader.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "components/download/public/download_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+PrefetchDownloader::PrefetchDownloader(
+    download::DownloadService* download_service,
+    version_info::Channel channel)
+    : download_service_(download_service),
+      channel_(channel),
+      weak_ptr_factory_(this) {
+  DCHECK(download_service);
+  service_started_ = download_service->GetStatus() ==
+                     download::DownloadService::ServiceStatus::READY;
+}
+
+PrefetchDownloader::PrefetchDownloader(version_info::Channel channel)
+    : channel_(channel), weak_ptr_factory_(this) {}
+
+PrefetchDownloader::~PrefetchDownloader() = default;
+
+void PrefetchDownloader::SetCompletedCallback(
+    const PrefetchDownloadCompletedCallback& callback) {
+  callback_ = callback;
+}
+
+void PrefetchDownloader::StartDownload(const std::string& download_id,
+                                       const std::string& download_location) {
+  if (!service_started_) {
+    pending_downloads_.push_back(
+        std::make_pair(download_id, download_location));
+    return;
+  }
+
+  // TODO(jianli): Specify scheduling parameters, i.e. battery, network and etc.
+  // http://crbug.com/736156
+  download::DownloadParams params;
+  params.client = download::DownloadClient::OFFLINE_PAGE_PREFETCH;
+  // TODO(jianli): Remove the uppercase after the download service fixes
+  // this issue.
+  params.guid = base::ToUpperASCII(download_id);
+  params.callback = base::Bind(&PrefetchDownloader::OnStartDownload,
+                               weak_ptr_factory_.GetWeakPtr());
+  params.request_params.url = PrefetchDownloadURL(download_location, channel_);
+  download_service_->StartDownload(params);
+}
+
+void PrefetchDownloader::CancelDownload(const std::string& download_id) {
+  if (service_started_) {
+    download_service_->CancelDownload(download_id);
+    return;
+  }
+  for (auto iter = pending_downloads_.begin(); iter != pending_downloads_.end();
+       ++iter) {
+    if (iter->first == download_id) {
+      pending_downloads_.erase(iter);
+      return;
+    }
+  }
+  pending_cancellations_.push_back(download_id);
+}
+
+void PrefetchDownloader::OnDownloadServiceReady() {
+  DCHECK_EQ(download::DownloadService::ServiceStatus::READY,
+            download_service_->GetStatus());
+  service_started_ = true;
+
+  for (const auto& entry : pending_downloads_)
+    StartDownload(entry.first, entry.second);
+  pending_downloads_.clear();
+
+  for (const auto& entry : pending_cancellations_)
+    download_service_->CancelDownload(entry);
+  pending_cancellations_.clear();
+}
+
+void PrefetchDownloader::OnDownloadServiceShutdown() {
+  service_started_ = false;
+}
+
+void PrefetchDownloader::OnDownloadSucceeded(const std::string& download_id,
+                                             const base::FilePath& file_path,
+                                             uint64_t file_size) {
+  if (callback_)
+    callback_.Run(PrefetchDownloadResult(download_id, file_path, file_size));
+}
+
+void PrefetchDownloader::OnDownloadFailed(const std::string& download_id) {
+  if (callback_) {
+    PrefetchDownloadResult result;
+    result.download_id = download_id;
+    callback_.Run(result);
+  }
+}
+
+void PrefetchDownloader::OnStartDownload(
+    const std::string& download_id,
+    download::DownloadParams::StartResult result) {
+  if (result != download::DownloadParams::StartResult::ACCEPTED)
+    OnDownloadFailed(download_id);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_downloader.h b/components/offline_pages/core/prefetch/prefetch_downloader.h
new file mode 100644
index 0000000..4dd0f24
--- /dev/null
+++ b/components/offline_pages/core/prefetch/prefetch_downloader.h
@@ -0,0 +1,96 @@
+// 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_DOWNLOADER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DOWNLOADER_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/download/public/download_params.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/version_info/channel.h"
+
+namespace download {
+class DownloadService;
+}  // namespace download
+
+namespace offline_pages {
+
+class PrefetchServiceTestTaco;
+
+// Asynchronously downloads the archive.
+class PrefetchDownloader {
+ public:
+  PrefetchDownloader(download::DownloadService* download_service,
+                     version_info::Channel channel);
+  ~PrefetchDownloader();
+
+  void SetCompletedCallback(const PrefetchDownloadCompletedCallback& callback);
+
+  // Starts to download an archive from |download_location|.
+  void StartDownload(const std::string& download_id,
+                     const std::string& download_location);
+
+  // Cancels a previous scheduled download.
+  void CancelDownload(const std::string& download_id);
+
+  // Responding to download client event.
+
+  // Called when the download service is initialized and can accept the
+  // downloads.
+  void OnDownloadServiceReady();
+
+  // Called when the download service is tearing down.
+  void OnDownloadServiceShutdown();
+
+  // Called when a download is completed successfully. Note that the download
+  // can be scheduled in preious sessions.
+  void OnDownloadSucceeded(const std::string& download_id,
+                           const base::FilePath& file_path,
+                           uint64_t file_size);
+
+  // Called when a download fails.
+  void OnDownloadFailed(const std::string& download_id);
+
+ private:
+  friend class PrefetchServiceTestTaco;
+
+  // For test only.
+  explicit PrefetchDownloader(version_info::Channel channel);
+
+  // Callback for StartDownload.
+  void OnStartDownload(const std::string& download_id,
+                       download::DownloadParams::StartResult result);
+
+  // Unowned. It is valid until |this| instance is disposed.
+  download::DownloadService* download_service_;
+
+  version_info::Channel channel_;
+  PrefetchDownloadCompletedCallback callback_;
+
+  // Flag to indicate if the download service is ready to take downloads.
+  bool service_started_ = false;
+
+  // TODO(jianli): Investigate making PrefetchService waits for DownloadService
+  // ready in order to avoid queueing.
+  // List of downloads pending to start after the download service starts. Each
+  // item is a pair of download id and download location.
+  std::vector<std::pair<std::string, std::string>> pending_downloads_;
+  // List of ids of downloads waiting to be cancelled after the download service
+  // starts.
+  std::vector<std::string> pending_cancellations_;
+
+  base::WeakPtrFactory<PrefetchDownloader> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefetchDownloader);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DOWNLOADER_H_
diff --git a/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc b/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc
new file mode 100644
index 0000000..9823617
--- /dev/null
+++ b/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc
@@ -0,0 +1,285 @@
+// 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_downloader.h"
+
+#include <list>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/public/download_service.h"
+#include "components/download/public/service_config.h"
+#include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_service_test_taco.h"
+#include "net/base/url_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
+const char kDownloadId[] = "1234";
+const char kDownloadId2[] = "ABCD";
+const char kFailedDownloadId[] = "FFFFFF";
+const char kDownloadLocation[] = "page/1";
+const char kDownloadLocation2[] = "page/zz";
+const char kServerPathForDownload[] = "/v1/media/page/1";
+const uint64_t kTestFileSize = 12345678u;
+}  // namespace
+
+namespace download {
+class TestServiceConfig : public ServiceConfig {
+ public:
+  TestServiceConfig() = default;
+  ~TestServiceConfig() override = default;
+
+  uint32_t GetMaxScheduledDownloadsPerClient() const override { return 0; }
+  const base::TimeDelta& GetFileKeepAliveTime() const override {
+    return time_delta_;
+  }
+
+ private:
+  base::TimeDelta time_delta_;
+};
+
+class TestDownloadService : public DownloadService {
+ public:
+  TestDownloadService() = default;
+  ~TestDownloadService() override = default;
+
+  // DownloadService implementation.
+  const ServiceConfig& GetConfig() override { return service_config_; }
+  void OnStartScheduledTask(DownloadTaskType task_type,
+                            const TaskFinishedCallback& callback) override {}
+  bool OnStopScheduledTask(DownloadTaskType task_type) override { return true; }
+  ServiceStatus GetStatus() override {
+    return ready_ ? DownloadService::ServiceStatus::READY
+                  : DownloadService::ServiceStatus::STARTING_UP;
+  }
+
+  void StartDownload(const DownloadParams& download_params) override {
+    if (!ready_) {
+      OnDownloadFailed(download_params.guid);
+      return;
+    }
+    downloads_.push_back(download_params);
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&TestDownloadService::ProcessDownload,
+                              base::Unretained(this)));
+  }
+
+  void PauseDownload(const std::string& guid) override {}
+  void ResumeDownload(const std::string& guid) override {}
+
+  void CancelDownload(const std::string& guid) override {
+    for (auto iter = downloads_.begin(); iter != downloads_.end(); ++iter) {
+      if (iter->guid == guid) {
+        downloads_.erase(iter);
+        return;
+      }
+    }
+  }
+
+  void ChangeDownloadCriteria(const std::string& guid,
+                              const SchedulingParams& params) override {}
+
+  DownloadParams GetDownload(const std::string& guid) const {
+    for (auto iter = downloads_.begin(); iter != downloads_.end(); ++iter) {
+      if (iter->guid == guid)
+        return *iter;
+    }
+    return DownloadParams();
+  }
+
+  void set_ready(bool ready) { ready_ = ready; }
+  void set_prefetch_downloader(
+      offline_pages::PrefetchDownloader* prefetch_downloader) {
+    prefetch_downloader_ = prefetch_downloader;
+  }
+
+ private:
+  void ProcessDownload() {
+    if (!ready_ || downloads_.empty())
+      return;
+    DownloadParams params = downloads_.front();
+    downloads_.pop_front();
+    if (params.guid == kFailedDownloadId)
+      OnDownloadFailed(params.guid);
+    else
+      OnDownloadSucceeded(params.guid, base::FilePath(), kTestFileSize);
+  }
+
+  void OnDownloadSucceeded(const std::string& guid,
+                           const base::FilePath& file_path,
+                           uint64_t file_size) {
+    if (prefetch_downloader_)
+      prefetch_downloader_->OnDownloadSucceeded(guid, file_path, file_size);
+  }
+
+  void OnDownloadFailed(const std::string& guid) {
+    if (prefetch_downloader_)
+      prefetch_downloader_->OnDownloadFailed(guid);
+  }
+
+  bool ready_ = false;
+  offline_pages::PrefetchDownloader* prefetch_downloader_ = nullptr;
+  TestServiceConfig service_config_;
+  std::list<DownloadParams> downloads_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDownloadService);
+};
+}  // namespace download
+
+namespace offline_pages {
+
+class PrefetchDownloaderTest : public testing::Test {
+ public:
+  PrefetchDownloaderTest()
+      : task_runner_(new base::TestSimpleTaskRunner),
+        task_runner_handle_(task_runner_) {}
+
+  void SetUp() override {
+    auto downloader =
+        base::MakeUnique<PrefetchDownloader>(&download_service_, kTestChannel);
+    download_service_.set_prefetch_downloader(downloader.get());
+    prefetch_service_taco_.SetPrefetchDownloader(std::move(downloader));
+
+    prefetch_service_taco_.CreatePrefetchService();
+    GetPrefetchDownloader()->SetCompletedCallback(base::Bind(
+        &PrefetchDownloaderTest::OnDownloadCompleted, base::Unretained(this)));
+  }
+
+  void SetDownloadServiceReady(bool ready) {
+    download_service_.set_ready(ready);
+    if (ready)
+      GetPrefetchDownloader()->OnDownloadServiceReady();
+    else
+      GetPrefetchDownloader()->OnDownloadServiceShutdown();
+  }
+
+  void StartDownload(const std::string& download_id,
+                     const std::string& download_location) {
+    GetPrefetchDownloader()->StartDownload(download_id, download_location);
+  }
+
+  void CancelDownload(const std::string& download_id) {
+    GetPrefetchDownloader()->CancelDownload(download_id);
+  }
+
+  download::DownloadParams GetDownload(const std::string& guid) const {
+    return download_service_.GetDownload(guid);
+  }
+
+  void PumpLoop() { task_runner_->RunUntilIdle(); }
+
+  const std::vector<PrefetchDownloadResult>& completed_downloads() const {
+    return completed_downloads_;
+  }
+
+ private:
+  void OnDownloadCompleted(const PrefetchDownloadResult& result) {
+    completed_downloads_.push_back(result);
+  }
+
+  PrefetchDownloader* GetPrefetchDownloader() const {
+    return prefetch_service_taco_.prefetch_service()->GetPrefetchDownloader();
+  }
+
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+  download::TestDownloadService download_service_;
+  PrefetchServiceTestTaco prefetch_service_taco_;
+  std::vector<PrefetchDownloadResult> completed_downloads_;
+};
+
+TEST_F(PrefetchDownloaderTest, DownloadParams) {
+  SetDownloadServiceReady(true);
+  StartDownload(kDownloadId, kDownloadLocation);
+  download::DownloadParams params = GetDownload(kDownloadId);
+  EXPECT_EQ(kDownloadId, params.guid);
+  EXPECT_EQ(download::DownloadClient::OFFLINE_PAGE_PREFETCH, params.client);
+  GURL download_url = params.request_params.url;
+  EXPECT_TRUE(download_url.SchemeIs(url::kHttpsScheme));
+  EXPECT_EQ(kServerPathForDownload, download_url.path());
+  std::string key_value;
+  EXPECT_TRUE(net::GetValueForKeyInQuery(download_url, "key", &key_value));
+  EXPECT_FALSE(key_value.empty());
+  std::string alt_value;
+  EXPECT_TRUE(net::GetValueForKeyInQuery(download_url, "alt", &alt_value));
+  EXPECT_EQ("media", alt_value);
+}
+
+TEST_F(PrefetchDownloaderTest, StartDownloadBeforeServiceReady) {
+  SetDownloadServiceReady(false);
+  StartDownload(kDownloadId, kDownloadLocation);
+  StartDownload(kDownloadId2, kDownloadLocation2);
+  PumpLoop();
+  ASSERT_EQ(0u, completed_downloads().size());
+  SetDownloadServiceReady(true);
+  PumpLoop();
+  ASSERT_EQ(2u, completed_downloads().size());
+  EXPECT_EQ(kDownloadId, completed_downloads()[0].download_id);
+  EXPECT_TRUE(completed_downloads()[0].success);
+  EXPECT_EQ(kDownloadId2, completed_downloads()[1].download_id);
+  EXPECT_TRUE(completed_downloads()[1].success);
+}
+
+TEST_F(PrefetchDownloaderTest, StartDownloadAfterServiceReady) {
+  SetDownloadServiceReady(true);
+  StartDownload(kDownloadId, kDownloadLocation);
+  StartDownload(kDownloadId2, kDownloadLocation2);
+  PumpLoop();
+  ASSERT_EQ(2u, completed_downloads().size());
+  EXPECT_EQ(kDownloadId, completed_downloads()[0].download_id);
+  EXPECT_TRUE(completed_downloads()[0].success);
+  EXPECT_EQ(kDownloadId2, completed_downloads()[1].download_id);
+  EXPECT_TRUE(completed_downloads()[1].success);
+}
+
+TEST_F(PrefetchDownloaderTest, DownloadFailed) {
+  SetDownloadServiceReady(true);
+  StartDownload(kFailedDownloadId, kDownloadLocation);
+  PumpLoop();
+  ASSERT_EQ(1u, completed_downloads().size());
+  EXPECT_EQ(kFailedDownloadId, completed_downloads()[0].download_id);
+  EXPECT_FALSE(completed_downloads()[0].success);
+}
+
+TEST_F(PrefetchDownloaderTest, CancelPendingDownloadBeforeServiceReady) {
+  SetDownloadServiceReady(false);
+  StartDownload(kDownloadId, kDownloadLocation);
+  StartDownload(kDownloadId2, kDownloadLocation2);
+  PumpLoop();
+  ASSERT_EQ(0u, completed_downloads().size());
+  CancelDownload(kDownloadId);
+  SetDownloadServiceReady(true);
+  PumpLoop();
+  ASSERT_EQ(1u, completed_downloads().size());
+  EXPECT_EQ(kDownloadId2, completed_downloads()[0].download_id);
+  EXPECT_TRUE(completed_downloads()[0].success);
+}
+
+TEST_F(PrefetchDownloaderTest, CancelStartedDownloadBeforeServiceReady) {
+  SetDownloadServiceReady(true);
+  StartDownload(kDownloadId, kDownloadLocation);
+  SetDownloadServiceReady(false);
+  CancelDownload(kDownloadId);
+  SetDownloadServiceReady(true);
+  PumpLoop();
+  ASSERT_EQ(0u, completed_downloads().size());
+}
+
+TEST_F(PrefetchDownloaderTest, CancelDownloadAfterServiceReady) {
+  SetDownloadServiceReady(true);
+  StartDownload(kDownloadId, kDownloadLocation);
+  StartDownload(kDownloadId2, kDownloadLocation2);
+  CancelDownload(kDownloadId);
+  PumpLoop();
+  ASSERT_EQ(1u, completed_downloads().size());
+  EXPECT_EQ(kDownloadId2, completed_downloads()[0].download_id);
+  EXPECT_TRUE(completed_downloads()[0].success);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc b/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
index cda960a3..d533ab1 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
+++ b/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/load_flags.h"
 #include "net/base/url_util.h"
@@ -21,57 +22,34 @@
 
 namespace {
 
-const char kPrefetchServer[] = "https://offlinepages-pa.googleapis.com/";
-const char kPrefetchStagingServer[] =
-    "https://staging-offlinepages-pa.sandbox.googleapis.com/";
-
-// Used in all offline prefetch request URLs to specify API Key.
-const char kApiKeyName[] = "key";
-
 // Content type needed in order to communicate with the server in binary
 // proto format.
 const char kRequestContentType[] = "application/x-protobuf";
 
-GURL CompleteURL(const std::string& url_path, version_info::Channel channel) {
-  bool is_stable_channel = channel == version_info::Channel::STABLE;
-  GURL server_url(is_stable_channel ? kPrefetchServer : kPrefetchStagingServer);
-
-  GURL::Replacements replacements;
-  replacements.SetPathStr(url_path);
-  GURL url = server_url.ReplaceComponents(replacements);
-
-  std::string api_key = is_stable_channel ? google_apis::GetAPIKey()
-                                          : google_apis::GetNonStableAPIKey();
-  return net::AppendQueryParameter(url, kApiKeyName, api_key);
-}
-
 }  // namespace
 
 // static
 std::unique_ptr<PrefetchRequestFetcher> PrefetchRequestFetcher::CreateForGet(
-    const std::string& url_path,
-    version_info::Channel channel,
+    const GURL& url,
     net::URLRequestContextGetter* request_context_getter,
     const FinishedCallback& callback) {
   return base::WrapUnique(new PrefetchRequestFetcher(
-      url_path, std::string(), channel, request_context_getter, callback));
+      url, std::string(), request_context_getter, callback));
 }
 
 // static
 std::unique_ptr<PrefetchRequestFetcher> PrefetchRequestFetcher::CreateForPost(
-    const std::string& url_path,
+    const GURL& url,
     const std::string& message,
-    version_info::Channel channel,
     net::URLRequestContextGetter* request_context_getter,
     const FinishedCallback& callback) {
   return base::WrapUnique(new PrefetchRequestFetcher(
-      url_path, message, channel, request_context_getter, callback));
+      url, message, request_context_getter, callback));
 }
 
 PrefetchRequestFetcher::PrefetchRequestFetcher(
-    const std::string& url_path,
+    const GURL& url,
     const std::string& message,
-    version_info::Channel channel,
     net::URLRequestContextGetter* request_context_getter,
     const FinishedCallback& callback)
     : request_context_getter_(request_context_getter), callback_(callback) {
@@ -97,8 +75,7 @@
             "Not implemented, considered not useful."
         })");
   url_fetcher_ = net::URLFetcher::Create(
-      CompleteURL(url_path, channel),
-      message.empty() ? net::URLFetcher::GET : net::URLFetcher::POST, this,
+      url, message.empty() ? net::URLFetcher::GET : net::URLFetcher::POST, this,
       traffic_annotation);
   url_fetcher_->SetRequestContext(request_context_getter_.get());
   url_fetcher_->SetAutomaticallyRetryOn5xx(false);
diff --git a/components/offline_pages/core/prefetch/prefetch_request_fetcher.h b/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
index ea3d7c1..4ac8640 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
+++ b/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
@@ -9,8 +9,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "components/offline_pages/core/prefetch/prefetch_types.h"
-#include "components/version_info/channel.h"
 #include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
 
 namespace net {
 class URLRequestContextGetter;
@@ -26,16 +26,14 @@
 
   // Creates a fetcher that will sends a GET request to the server.
   static std::unique_ptr<PrefetchRequestFetcher> CreateForGet(
-      const std::string& url_path,
-      version_info::Channel channel,
+      const GURL& url,
       net::URLRequestContextGetter* request_context_getter,
       const FinishedCallback& callback);
 
   // Creates a fetcher that will sends a POST request to the server.
   static std::unique_ptr<PrefetchRequestFetcher> CreateForPost(
-      const std::string& url_path,
+      const GURL& url,
       const std::string& message,
-      version_info::Channel channel,
       net::URLRequestContextGetter* request_context_getter,
       const FinishedCallback& callback);
 
@@ -47,9 +45,8 @@
  private:
   // If |message| is empty, the GET request is sent. Otherwise, the POST request
   // is sent with |message| as post data.
-  PrefetchRequestFetcher(const std::string& url_path,
+  PrefetchRequestFetcher(const GURL& url,
                          const std::string& message,
-                         version_info::Channel channel,
                          net::URLRequestContextGetter* request_context_getter,
                          const FinishedCallback& callback);
 
diff --git a/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc b/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
index 8464c3c..39b5d218 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
@@ -20,8 +20,7 @@
 namespace offline_pages {
 
 namespace {
-const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
-const char kTestURLPath[] = "/test";
+const GURL kTestURL("http://exmaple.org");
 const char kTestMessage[] = "Testing";
 }  // namespace
 
@@ -73,9 +72,8 @@
     std::string* data_received) {
   base::MockCallback<PrefetchRequestFetcher::FinishedCallback> callback;
   std::unique_ptr<PrefetchRequestFetcher> fetcher =
-      PrefetchRequestFetcher::CreateForPost(kTestURLPath, kTestMessage,
-                                            kTestChannel, request_context(),
-                                            callback.Get());
+      PrefetchRequestFetcher::CreateForPost(kTestURL, kTestMessage,
+                                            request_context(), callback.Get());
 
   PrefetchRequestStatus status;
   std::string data;
diff --git a/components/offline_pages/core/prefetch/prefetch_server_urls.cc b/components/offline_pages/core/prefetch/prefetch_server_urls.cc
new file mode 100644
index 0000000..84135e6ba
--- /dev/null
+++ b/components/offline_pages/core/prefetch/prefetch_server_urls.cc
@@ -0,0 +1,71 @@
+// 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_server_urls.h"
+
+#include "google_apis/google_api_keys.h"
+#include "net/base/url_util.h"
+
+namespace offline_pages {
+
+namespace {
+
+const char kPrefetchServer[] = "https://offlinepages-pa.googleapis.com/";
+const char kPrefetchStagingServer[] =
+    "https://staging-offlinepages-pa.sandbox.googleapis.com/";
+
+const char kGeneratePageBundleRequestURLPath[] = "v1:GeneratePageBundle";
+const char kGetOperationLeadingURLPath[] = "v1/";
+const char kDownloadLeadingURLPath[] = "v1/media/";
+
+// Used in all offline prefetch request URLs to specify API Key.
+const char kApiKeyName[] = "key";
+// Needed to download as a file.
+const char kAltKeyName[] = "alt";
+const char kAltKeyValueForDownload[] = "media";
+
+GURL GetServerURLForPath(const std::string& url_path,
+                         version_info::Channel channel) {
+  bool is_stable_channel = channel == version_info::Channel::STABLE;
+  GURL server_url(is_stable_channel ? kPrefetchServer : kPrefetchStagingServer);
+
+  GURL::Replacements replacements;
+  replacements.SetPathStr(url_path);
+  return server_url.ReplaceComponents(replacements);
+}
+
+GURL AppendApiKeyToURL(const GURL& url, version_info::Channel channel) {
+  bool is_stable_channel = channel == version_info::Channel::STABLE;
+  std::string api_key = is_stable_channel ? google_apis::GetAPIKey()
+                                          : google_apis::GetNonStableAPIKey();
+  return net::AppendQueryParameter(url, kApiKeyName, api_key);
+}
+
+}  // namespace
+
+GURL GeneratePageBundleRequestURL(version_info::Channel channel) {
+  GURL server_url =
+      GetServerURLForPath(kGeneratePageBundleRequestURLPath, channel);
+  return AppendApiKeyToURL(server_url, channel);
+}
+
+GURL GetOperationRequestURL(const std::string& name,
+                            version_info::Channel channel) {
+  std::string url_path = kGetOperationLeadingURLPath + name;
+  GURL server_url = GetServerURLForPath(url_path, channel);
+  return AppendApiKeyToURL(server_url, channel);
+}
+
+GURL PrefetchDownloadURL(const std::string& download_location,
+                         version_info::Channel channel) {
+  std::string url_path = kDownloadLeadingURLPath + download_location;
+  GURL server_url = GetServerURLForPath(url_path, channel);
+
+  server_url = net::AppendQueryParameter(server_url, kAltKeyName,
+                                         kAltKeyValueForDownload);
+
+  return AppendApiKeyToURL(server_url, channel);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_server_urls.h b/components/offline_pages/core/prefetch/prefetch_server_urls.h
new file mode 100644
index 0000000..626b4684
--- /dev/null
+++ b/components/offline_pages/core/prefetch/prefetch_server_urls.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVER_URLS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVER_URLS_H_
+
+#include <string>
+#include "components/version_info/channel.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+// Returns the URL to send a request to generate page bundle.
+GURL GeneratePageBundleRequestURL(version_info::Channel channel);
+
+// Returns the URL to send a request to get operation info.
+GURL GetOperationRequestURL(const std::string& name,
+                            version_info::Channel channel);
+
+// Returns the URL to download an archive.
+GURL PrefetchDownloadURL(const std::string& download_location,
+                         version_info::Channel channel);
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVER_URLS_H_
diff --git a/components/offline_pages/core/prefetch/prefetch_service.h b/components/offline_pages/core/prefetch/prefetch_service.h
index a168a1d..10e7105 100644
--- a/components/offline_pages/core/prefetch/prefetch_service.h
+++ b/components/offline_pages/core/prefetch/prefetch_service.h
@@ -11,6 +11,7 @@
 class OfflineEventLogger;
 class OfflineMetricsCollector;
 class PrefetchDispatcher;
+class PrefetchDownloader;
 class PrefetchGCMHandler;
 class PrefetchNetworkRequestFactory;
 class SuggestedArticlesObserver;
@@ -31,6 +32,7 @@
   virtual PrefetchDispatcher* GetPrefetchDispatcher() = 0;
   virtual PrefetchGCMHandler* GetPrefetchGCMHandler() = 0;
   virtual PrefetchNetworkRequestFactory* GetPrefetchNetworkRequestFactory() = 0;
+  virtual PrefetchDownloader* GetPrefetchDownloader() = 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 2c07a03..478d4109 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_service_impl.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #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_downloader.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
 #include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
@@ -21,15 +22,19 @@
     std::unique_ptr<PrefetchDispatcher> dispatcher,
     std::unique_ptr<PrefetchGCMHandler> gcm_handler,
     std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory,
-    std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer)
+    std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer,
+    std::unique_ptr<PrefetchDownloader> prefetch_downloader)
     : offline_metrics_collector_(std::move(offline_metrics_collector)),
       prefetch_dispatcher_(std::move(dispatcher)),
       prefetch_gcm_handler_(std::move(gcm_handler)),
       network_request_factory_(std::move(network_request_factory)),
-      suggested_articles_observer_(std::move(suggested_articles_observer)) {
+      suggested_articles_observer_(std::move(suggested_articles_observer)),
+      prefetch_downloader_(std::move(prefetch_downloader)) {
   prefetch_dispatcher_->SetService(this);
   prefetch_gcm_handler_->SetService(this);
   suggested_articles_observer_->SetPrefetchService(this);
+  prefetch_downloader_->SetCompletedCallback(base::Bind(
+      &PrefetchServiceImpl::OnDownloadCompleted, base::Unretained(this)));
 }
 
 PrefetchServiceImpl::~PrefetchServiceImpl() = default;
@@ -59,8 +64,23 @@
   return &logger_;
 }
 
+PrefetchDownloader* PrefetchServiceImpl::GetPrefetchDownloader() {
+  return prefetch_downloader_.get();
+}
+
 void PrefetchServiceImpl::Shutdown() {
   suggested_articles_observer_.reset();
+  prefetch_downloader_.reset();
+}
+
+void PrefetchServiceImpl::OnDownloadCompleted(
+    const PrefetchDownloadResult& result) {
+  logger_.RecordActivity("Download " + result.download_id +
+                         (result.success ? " succeeded" : " failed"));
+  if (result.success) {
+    logger_.RecordActivity("Downloaded as " + result.file_path.MaybeAsASCII() +
+                           " with size " + std::to_string(result.file_size));
+  }
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.h b/components/offline_pages/core/prefetch/prefetch_service_impl.h
index ed1b629..98e5d61 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_impl.h
+++ b/components/offline_pages/core/prefetch/prefetch_service_impl.h
@@ -10,10 +10,12 @@
 #include "base/macros.h"
 #include "components/offline_pages/core/offline_event_logger.h"
 #include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
 
 namespace offline_pages {
 class OfflineMetricsCollector;
 class PrefetchDispatcher;
+class PrefetchDownloader;
 class PrefetchGCMHandler;
 class PrefetchNetworkRequestFactory;
 class SuggestedArticlesObserver;
@@ -25,7 +27,8 @@
       std::unique_ptr<PrefetchDispatcher> dispatcher,
       std::unique_ptr<PrefetchGCMHandler> gcm_handler,
       std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory,
-      std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer);
+      std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer,
+      std::unique_ptr<PrefetchDownloader> prefetch_downloader);
   ~PrefetchServiceImpl() override;
 
   // PrefetchService implementation:
@@ -35,11 +38,15 @@
   PrefetchNetworkRequestFactory* GetPrefetchNetworkRequestFactory() override;
   SuggestedArticlesObserver* GetSuggestedArticlesObserver() override;
   OfflineEventLogger* GetLogger() override;
+  PrefetchDownloader* GetPrefetchDownloader() override;
 
   // KeyedService implementation:
   void Shutdown() override;
 
  private:
+  // Called when a download completes.
+  void OnDownloadCompleted(const PrefetchDownloadResult& result);
+
   OfflineEventLogger logger_;
 
   std::unique_ptr<OfflineMetricsCollector> offline_metrics_collector_;
@@ -47,6 +54,7 @@
   std::unique_ptr<PrefetchGCMHandler> prefetch_gcm_handler_;
   std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory_;
   std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_;
+  std::unique_ptr<PrefetchDownloader> prefetch_downloader_;
 
   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 f16cd86..d18c8b9 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
+++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
@@ -10,6 +10,7 @@
 #include "base/test/test_simple_task_runner.h"
 #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_downloader.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
 #include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
@@ -20,6 +21,10 @@
 
 namespace offline_pages {
 
+namespace {
+const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
+}  // namespace
+
 PrefetchServiceTestTaco::PrefetchServiceTestTaco() {
   metrics_collector_ = base::MakeUnique<TestOfflineMetricsCollector>();
   dispatcher_ = base::MakeUnique<TestPrefetchDispatcher>();
@@ -28,6 +33,7 @@
       base::MakeUnique<TestPrefetchNetworkRequestFactory>();
 
   suggested_articles_observer_ = base::MakeUnique<SuggestedArticlesObserver>();
+  prefetch_downloader_ = base::WrapUnique(new PrefetchDownloader(kTestChannel));
   // This sets up the testing articles as an empty vector, we can ignore the
   // result here.  This allows us to not create a ContentSuggestionsService.
   suggested_articles_observer_->GetTestingArticles();
@@ -65,13 +71,20 @@
   suggested_articles_observer_ = std::move(suggested_articles_observer);
 }
 
+void PrefetchServiceTestTaco::SetPrefetchDownloader(
+    std::unique_ptr<PrefetchDownloader> prefetch_downloader) {
+  CHECK(!prefetch_service_);
+  prefetch_downloader_ = std::move(prefetch_downloader);
+}
+
 void PrefetchServiceTestTaco::CreatePrefetchService() {
   CHECK(metrics_collector_ && dispatcher_ && gcm_handler_ &&
-        suggested_articles_observer_ && network_request_factory_);
+        suggested_articles_observer_ && network_request_factory_ &&
+        prefetch_downloader_);
   prefetch_service_ = base::MakeUnique<PrefetchServiceImpl>(
       std::move(metrics_collector_), std::move(dispatcher_),
       std::move(gcm_handler_), std::move(network_request_factory_),
-      std::move(suggested_articles_observer_));
+      std::move(suggested_articles_observer_), std::move(prefetch_downloader_));
 }
 
 std::unique_ptr<PrefetchService>
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 4b7668e3..aa56544 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
+++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
@@ -14,6 +14,7 @@
 namespace offline_pages {
 class OfflineMetricsCollector;
 class PrefetchDispatcher;
+class PrefetchDownloader;
 class PrefetchGCMHandler;
 class PrefetchService;
 class PrefetchNetworkRequestFactory;
@@ -46,6 +47,8 @@
   // by default, so no ContentSuggestionsService is required..
   void SetSuggestedArticlesObserver(
       std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer);
+  void SetPrefetchDownloader(
+      std::unique_ptr<PrefetchDownloader> prefetch_downloader);
 
   // Creates and caches an instance of PrefetchService, using default or
   // overridden test dependencies.
@@ -53,7 +56,7 @@
 
   // Once CreatePrefetchService() is called, this accessor method starts
   // returning the PrefetchService.
-  PrefetchService* prefetch_service() {
+  PrefetchService* prefetch_service() const {
     CHECK(prefetch_service_);
     return prefetch_service_.get();
   }
@@ -68,6 +71,7 @@
   std::unique_ptr<PrefetchGCMHandler> gcm_handler_;
   std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory_;
   std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_;
+  std::unique_ptr<PrefetchDownloader> prefetch_downloader_;
 
   std::unique_ptr<PrefetchService> prefetch_service_;
 };
diff --git a/components/offline_pages/core/prefetch/prefetch_types.cc b/components/offline_pages/core/prefetch/prefetch_types.cc
index 8f06ddd..7f68f57 100644
--- a/components/offline_pages/core/prefetch/prefetch_types.cc
+++ b/components/offline_pages/core/prefetch/prefetch_types.cc
@@ -6,8 +6,21 @@
 
 namespace offline_pages {
 
-RenderPageInfo::RenderPageInfo() {}
+RenderPageInfo::RenderPageInfo() = default;
 
 RenderPageInfo::RenderPageInfo(const RenderPageInfo& other) = default;
 
+PrefetchDownloadResult::PrefetchDownloadResult() = default;
+
+PrefetchDownloadResult::PrefetchDownloadResult(const std::string& download_id,
+                                               const base::FilePath& file_path,
+                                               uint64_t file_size)
+    : download_id(download_id),
+      success(true),
+      file_path(file_path),
+      file_size(file_size) {}
+
+PrefetchDownloadResult::PrefetchDownloadResult(
+    const PrefetchDownloadResult& other) = default;
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_types.h b/components/offline_pages/core/prefetch/prefetch_types.h
index 20d9abea..1fdb49a 100644
--- a/components/offline_pages/core/prefetch/prefetch_types.h
+++ b/components/offline_pages/core/prefetch/prefetch_types.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/files/file_path.h"
 #include "base/time/time.h"
 #include "components/offline_pages/core/client_id.h"
 #include "url/gurl.h"
@@ -123,6 +124,24 @@
   GURL url;
 };
 
+// Result of a completed download.
+struct PrefetchDownloadResult {
+  PrefetchDownloadResult();
+  PrefetchDownloadResult(const std::string& download_id,
+                         const base::FilePath& file_path,
+                         uint64_t file_size);
+  PrefetchDownloadResult(const PrefetchDownloadResult& other);
+
+  std::string download_id;
+  bool success = false;
+  base::FilePath file_path;
+  uint64_t file_size = 0u;
+};
+
+// Callback invoked upon completion of a download.
+using PrefetchDownloadCompletedCallback =
+    base::Callback<void(const PrefetchDownloadResult& result)>;
+
 }  // namespace offline_pages
 
 #endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_TYPES_H_
diff --git a/components/security_interstitials/core/browser/resources/list_of_interstitials.html b/components/security_interstitials/core/browser/resources/list_of_interstitials.html
index a759c82..9f53597 100644
--- a/components/security_interstitials/core/browser/resources/list_of_interstitials.html
+++ b/components/security_interstitials/core/browser/resources/list_of_interstitials.html
@@ -40,6 +40,9 @@
     <li>
       <a href="clock?clock_manipulation=-2">Clock is behind</a>
     </li>
+    <li>
+      <a href="ssl?type=hpkp_failure">Pinned certificate error</a>
+    </li>
   </ul>
   <h3>SafeBrowsing</h3>
   <h4>Loud</h4>
diff --git a/components/test/data/web_database/version_73_with_type_column.sql b/components/test/data/web_database/version_73_with_type_column.sql
new file mode 100644
index 0000000..5da24ee
--- /dev/null
+++ b/components/test/data/web_database/version_73_with_type_column.sql
@@ -0,0 +1,25 @@
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO "meta" VALUES('mmap_status','-1');
+INSERT INTO "meta" VALUES('version','73');
+INSERT INTO "meta" VALUES('last_compatible_version','72');
+CREATE TABLE token_service (service VARCHAR PRIMARY KEY NOT NULL,encrypted_token BLOB);
+CREATE TABLE keywords (id INTEGER PRIMARY KEY,short_name VARCHAR NOT NULL,keyword VARCHAR NOT NULL,favicon_url VARCHAR NOT NULL,url VARCHAR NOT NULL,safe_for_autoreplace INTEGER,originating_url VARCHAR,date_created INTEGER DEFAULT 0,usage_count INTEGER DEFAULT 0,input_encodings VARCHAR,suggest_url VARCHAR,prepopulate_id INTEGER DEFAULT 0,created_by_policy INTEGER DEFAULT 0,instant_url VARCHAR,last_modified INTEGER DEFAULT 0,sync_guid VARCHAR,alternate_urls VARCHAR,search_terms_replacement_key VARCHAR,image_url VARCHAR,search_url_post_params VARCHAR,suggest_url_post_params VARCHAR,instant_url_post_params VARCHAR,image_url_post_params VARCHAR,new_tab_url VARCHAR, last_visited INTEGER DEFAULT 0);
+CREATE TABLE autofill (name VARCHAR, value VARCHAR, value_lower VARCHAR, date_created INTEGER DEFAULT 0, date_last_used INTEGER DEFAULT 0, count INTEGER DEFAULT 1, PRIMARY KEY (name, value));
+CREATE TABLE credit_cards ( guid VARCHAR PRIMARY KEY, name_on_card VARCHAR, expiration_month INTEGER, expiration_year INTEGER, card_number_encrypted BLOB, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
+CREATE TABLE autofill_profiles ( guid VARCHAR PRIMARY KEY, company_name VARCHAR, street_address VARCHAR, dependent_locality VARCHAR, city VARCHAR, state VARCHAR, zipcode VARCHAR, sorting_code VARCHAR, country_code VARCHAR, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', language_code VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
+CREATE TABLE autofill_profile_names ( guid VARCHAR, first_name VARCHAR, middle_name VARCHAR, last_name VARCHAR, full_name VARCHAR);
+CREATE TABLE autofill_profile_emails ( guid VARCHAR, email VARCHAR);
+CREATE TABLE autofill_profile_phones ( guid VARCHAR, number VARCHAR);
+CREATE TABLE autofill_profiles_trash ( guid VARCHAR);
+CREATE TABLE masked_credit_cards (id VARCHAR,status VARCHAR,name_on_card VARCHAR,network VARCHAR,last_four VARCHAR,exp_month INTEGER DEFAULT 0,exp_year INTEGER DEFAULT 0, type INTEGER DEFAULT 0);
+CREATE TABLE unmasked_credit_cards (id VARCHAR,card_number_encrypted VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, unmask_date INTEGER NOT NULL DEFAULT 0);
+CREATE TABLE server_card_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
+CREATE TABLE server_addresses (id VARCHAR,company_name VARCHAR,street_address VARCHAR,address_1 VARCHAR,address_2 VARCHAR,address_3 VARCHAR,address_4 VARCHAR,postal_code VARCHAR,sorting_code VARCHAR,country_code VARCHAR,language_code VARCHAR, recipient_name VARCHAR, phone_number VARCHAR);
+CREATE TABLE server_address_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, has_converted BOOL NOT NULL DEFAULT FALSE);
+CREATE TABLE autofill_sync_metadata (storage_key VARCHAR PRIMARY KEY NOT NULL,value BLOB);
+CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value BLOB);
+CREATE INDEX autofill_name ON autofill (name);
+CREATE INDEX autofill_name_value_lower ON autofill (name, value_lower);
+COMMIT;
diff --git a/components/translate/content/renderer/BUILD.gn b/components/translate/content/renderer/BUILD.gn
index 17ab7fa..773039fa0 100644
--- a/components/translate/content/renderer/BUILD.gn
+++ b/components/translate/content/renderer/BUILD.gn
@@ -24,3 +24,15 @@
     "//v8",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "translate_helper_unittest.cc",
+  ]
+  deps = [
+    ":renderer",
+    "//base",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/translate/content/renderer/translate_helper.cc b/components/translate/content/renderer/translate_helper.cc
index f1b8ce4..14251b6b 100644
--- a/components/translate/content/renderer/translate_helper.cc
+++ b/components/translate/content/renderer/translate_helper.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/compiler_specific.h"
+#include "base/json/string_escape.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
@@ -167,12 +168,8 @@
 }
 
 bool TranslateHelper::StartTranslation() {
-  std::string script = "cr.googleTranslate.translate('" +
-                       source_lang_ +
-                       "','" +
-                       target_lang_ +
-                       "')";
-  return ExecuteScriptAndGetBoolResult(script, false);
+  return ExecuteScriptAndGetBoolResult(
+      BuildTranslationScript(source_lang_, target_lang_), false);
 }
 
 std::string TranslateHelper::GetOriginalPageLanguage() {
@@ -430,4 +427,13 @@
   delete this;
 }
 
+/* static */
+std::string TranslateHelper::BuildTranslationScript(
+    const std::string& source_lang,
+    const std::string& target_lang) {
+  return "cr.googleTranslate.translate(" +
+         base::GetQuotedJSONString(source_lang) + "," +
+         base::GetQuotedJSONString(target_lang) + ")";
+}
+
 }  // namespace translate
diff --git a/components/translate/content/renderer/translate_helper.h b/components/translate/content/renderer/translate_helper.h
index 622e718..58f2c88 100644
--- a/components/translate/content/renderer/translate_helper.h
+++ b/components/translate/content/renderer/translate_helper.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
@@ -99,9 +100,16 @@
   virtual double ExecuteScriptAndGetDoubleResult(const std::string& script);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(TranslateHelperTest, TestBuildTranslationScript);
+
   // Converts language code to the one used in server supporting list.
   static void ConvertLanguageCodeSynonym(std::string* code);
 
+  // Builds the translation JS used to translate from source_lang to
+  // target_lang.
+  static std::string BuildTranslationScript(const std::string& source_lang,
+                                            const std::string& target_lang);
+
   const mojom::ContentTranslateDriverPtr& GetTranslateDriver();
 
   // Cleanups all states and pending callbacks associated with the current
diff --git a/components/translate/content/renderer/translate_helper_unittest.cc b/components/translate/content/renderer/translate_helper_unittest.cc
new file mode 100644
index 0000000..f62df230
--- /dev/null
+++ b/components/translate/content/renderer/translate_helper_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/translate/content/renderer/translate_helper.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace translate {
+
+class TranslateHelperTest : public testing::Test {};
+
+TEST_F(TranslateHelperTest, TestBuildTranslationScript) {
+  // Test expected cases.
+  EXPECT_EQ(TranslateHelper::BuildTranslationScript("en", "es"),
+            "cr.googleTranslate.translate(\"en\",\"es\")");
+  EXPECT_EQ(TranslateHelper::BuildTranslationScript("en-US", "zh-TW"),
+            "cr.googleTranslate.translate(\"en-US\",\"zh-TW\")");
+
+  // Test that quote gets quoted.
+  EXPECT_EQ(TranslateHelper::BuildTranslationScript("en\"", "es"),
+            "cr.googleTranslate.translate(\"en\\\"\",\"es\")");
+
+  // Test that < gets quoted.
+  EXPECT_EQ(TranslateHelper::BuildTranslationScript("en<", "es"),
+            "cr.googleTranslate.translate(\"en\\u003C\",\"es\")");
+}
+
+}  // namespace translate
diff --git a/components/translate/core/browser/translate_browser_metrics.h b/components/translate/core/browser/translate_browser_metrics.h
index c83c6bb9..e15759a 100644
--- a/components/translate/core/browser/translate_browser_metrics.h
+++ b/components/translate/core/browser/translate_browser_metrics.h
@@ -42,6 +42,8 @@
   INITIATION_STATUS_DISABLED_BY_KEY,
   INITIATION_STATUS_LANGUAGE_IN_ULP,
   INITIATION_STATUS_ABORTED_BY_RANKER,
+  INITIATION_STATUS_ABORTED_BY_TOO_OFTEN_DENIED,
+  INITIATION_STATUS_ABORTED_BY_MATCHES_PREVIOUS_LANGUAGE,
   // Insert new items here.
   INITIATION_STATUS_MAX,
 };
diff --git a/components/translate/core/browser/translate_browser_metrics_unittest.cc b/components/translate/core/browser/translate_browser_metrics_unittest.cc
index 283dfd5..b5bfc35 100644
--- a/components/translate/core/browser/translate_browser_metrics_unittest.cc
+++ b/components/translate/core/browser/translate_browser_metrics_unittest.cc
@@ -29,17 +29,22 @@
       base_samples_ = histogram->SnapshotSamples();
   }
 
-  void CheckInitiationStatus(int expected_disabled_by_prefs,
-                             int expected_disabled_by_config,
-                             int expected_disabled_by_build,
-                             int expected_language_is_not_supported,
-                             int expected_mime_type_is_not_supported,
-                             int expected_url_is_not_supported,
-                             int expected_similar_languages,
-                             int expected_accept_languages,
-                             int expected_auto_by_config,
-                             int expected_auto_by_link,
-                             int expected_show_infobar) {
+  void CheckInitiationStatus(
+      int expected_disabled_by_prefs,
+      int expected_disabled_by_config,
+      int expected_disabled_by_build,
+      int expected_language_is_not_supported,
+      int expected_mime_type_is_not_supported,
+      int expected_url_is_not_supported,
+      int expected_similar_languages,
+      int expected_accept_languages,
+      int expected_auto_by_config,
+      int expected_auto_by_link,
+      int expected_show_infobar,
+      int expected_language_in_ulp,
+      int expected_aborted_by_ranker,
+      int expected_aborted_by_too_often_denied,
+      int expected_aborted_by_matches_previous_language) {
     Snapshot();
 
     EXPECT_EQ(expected_disabled_by_prefs,
@@ -80,6 +85,20 @@
     EXPECT_EQ(expected_show_infobar,
               GetCountWithoutSnapshot(translate::TranslateBrowserMetrics::
                                           INITIATION_STATUS_SHOW_INFOBAR));
+    EXPECT_EQ(expected_language_in_ulp,
+              GetCountWithoutSnapshot(translate::TranslateBrowserMetrics::
+                                          INITIATION_STATUS_LANGUAGE_IN_ULP));
+    EXPECT_EQ(expected_aborted_by_ranker,
+              GetCountWithoutSnapshot(translate::TranslateBrowserMetrics::
+                                          INITIATION_STATUS_ABORTED_BY_RANKER));
+    EXPECT_EQ(expected_aborted_by_too_often_denied,
+              GetCountWithoutSnapshot(
+                  translate::TranslateBrowserMetrics::
+                      INITIATION_STATUS_ABORTED_BY_TOO_OFTEN_DENIED));
+    EXPECT_EQ(expected_aborted_by_matches_previous_language,
+              GetCountWithoutSnapshot(
+                  translate::TranslateBrowserMetrics::
+                      INITIATION_STATUS_ABORTED_BY_MATCHES_PREVIOUS_LANGUAGE));
   }
 
   HistogramBase::Count GetTotalCount() {
@@ -127,43 +146,57 @@
   MetricsRecorder recorder(translate::TranslateBrowserMetrics::GetMetricsName(
       translate::TranslateBrowserMetrics::UMA_INITIATION_STATUS));
 
-  recorder.CheckInitiationStatus(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+  recorder.CheckInitiationStatus(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_PREFS);
-  recorder.CheckInitiationStatus(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+  recorder.CheckInitiationStatus(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
-  recorder.CheckInitiationStatus(1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+  recorder.CheckInitiationStatus(1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_KEY);
-  recorder.CheckInitiationStatus(1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+  recorder.CheckInitiationStatus(1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::
           INITIATION_STATUS_LANGUAGE_IS_NOT_SUPPORTED);
-  recorder.CheckInitiationStatus(1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::
           INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED);
-  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::
           INITIATION_STATUS_URL_IS_NOT_SUPPORTED);
-  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::INITIATION_STATUS_SIMILAR_LANGUAGES);
-  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::INITIATION_STATUS_ACCEPT_LANGUAGES);
-  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_CONFIG);
-  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_LINK);
-  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0);
   translate::TranslateBrowserMetrics::ReportInitiationStatus(
       translate::TranslateBrowserMetrics::INITIATION_STATUS_SHOW_INFOBAR);
-  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0);
+  translate::TranslateBrowserMetrics::ReportInitiationStatus(
+      translate::TranslateBrowserMetrics::INITIATION_STATUS_LANGUAGE_IN_ULP);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0);
+  translate::TranslateBrowserMetrics::ReportInitiationStatus(
+      translate::TranslateBrowserMetrics::INITIATION_STATUS_ABORTED_BY_RANKER);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0);
+  translate::TranslateBrowserMetrics::ReportInitiationStatus(
+      translate::TranslateBrowserMetrics::
+          INITIATION_STATUS_ABORTED_BY_TOO_OFTEN_DENIED);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0);
+  translate::TranslateBrowserMetrics::ReportInitiationStatus(
+      translate::TranslateBrowserMetrics::
+          INITIATION_STATUS_ABORTED_BY_MATCHES_PREVIOUS_LANGUAGE);
+  recorder.CheckInitiationStatus(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
 }
 
 TEST(TranslateBrowserMetricsTest, ReportLanguageDetectionError) {
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index 06812c57..538b3215 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -595,6 +595,9 @@
       !language_state_.HasLanguageChanged() &&
       !ShouldOverrideDecision(
           metrics::TranslateEventProto::MATCHES_PREVIOUS_LANGUAGE)) {
+    TranslateBrowserMetrics::ReportInitiationStatus(
+        TranslateBrowserMetrics::
+            INITIATION_STATUS_ABORTED_BY_MATCHES_PREVIOUS_LANGUAGE);
     return true;
   }
 
@@ -605,6 +608,8 @@
           source_language) &&
       !ShouldOverrideDecision(
           metrics::TranslateEventProto::LANGUAGE_DISABLED_BY_AUTO_BLACKLIST)) {
+    TranslateBrowserMetrics::ReportInitiationStatus(
+        TranslateBrowserMetrics::INITIATION_STATUS_ABORTED_BY_TOO_OFTEN_DENIED);
     return true;
   }
 
diff --git a/components/translate/core/browser/translate_manager_unittest.cc b/components/translate/core/browser/translate_manager_unittest.cc
index 5e6537f1..83970f1f 100644
--- a/components/translate/core/browser/translate_manager_unittest.cc
+++ b/components/translate/core/browser/translate_manager_unittest.cc
@@ -39,6 +39,7 @@
 namespace {
 
 const char kTrialName[] = "MyTrial";
+const char kInitiationStatusName[] = "Translate.InitiationStatus.v2";
 
 // Overrides NetworkChangeNotifier, simulating connection type changes
 // for tests.
@@ -299,7 +300,6 @@
 
   // The test measures that the "Translate was disabled" exit can only be
   // reached after the early-out tests including IsOffline() passed.
-  const char kMetricName[] = "Translate.InitiationStatus.v2";
   base::HistogramTester histogram_tester;
 
   prefs_.SetBoolean(prefs::kEnableTranslate, false);
@@ -310,13 +310,13 @@
   // key test.
   network_notifier_.SimulateOffline();
   translate_manager_->InitiateTranslation("de");
-  histogram_tester.ExpectTotalCount(kMetricName, 0);
+  histogram_tester.ExpectTotalCount(kInitiationStatusName, 0);
 
   // In the online case, InitiateTranslation will proceed past early out tests.
   network_notifier_.SimulateOnline();
   translate_manager_->InitiateTranslation("de");
   histogram_tester.ExpectUniqueSample(
-      kMetricName,
+      kInitiationStatusName,
       translate::TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_PREFS,
       1);
 }
@@ -505,8 +505,10 @@
 TEST_F(TranslateManagerTest, ShouldSuppressBubbleUI_Default) {
   PrepareTranslateManager();
   SetHasLanguageChanged(true);
+  base::HistogramTester histogram_tester;
   EXPECT_FALSE(translate_manager_->ShouldSuppressBubbleUI(false, "en"));
   EXPECT_FALSE(translate_manager_->ShouldSuppressBubbleUI(true, "en"));
+  histogram_tester.ExpectTotalCount(kInitiationStatusName, 0);
 }
 
 TEST_F(TranslateManagerTest, ShouldSuppressBubbleUI_HasLanguageChangedFalse) {
@@ -517,20 +519,33 @@
       ShouldOverrideDecision(
           metrics::TranslateEventProto::MATCHES_PREVIOUS_LANGUAGE, _, _))
       .WillOnce(Return(false));
+  base::HistogramTester histogram_tester;
   EXPECT_TRUE(translate_manager_->ShouldSuppressBubbleUI(false, "en"));
+  histogram_tester.ExpectUniqueSample(
+      kInitiationStatusName,
+      translate::TranslateBrowserMetrics::
+          INITIATION_STATUS_ABORTED_BY_MATCHES_PREVIOUS_LANGUAGE,
+      1);
 
   EXPECT_CALL(mock_translate_ranker_, ShouldOverrideDecision(_, _, _))
       .WillOnce(Return(false));
 
   EXPECT_TRUE(translate_manager_->ShouldSuppressBubbleUI(true, "en"));
+  histogram_tester.ExpectUniqueSample(
+      kInitiationStatusName,
+      translate::TranslateBrowserMetrics::
+          INITIATION_STATUS_ABORTED_BY_MATCHES_PREVIOUS_LANGUAGE,
+      2);
 }
 
 TEST_F(TranslateManagerTest, ShouldSuppressBubbleUI_NewUI) {
   PrepareTranslateManager();
   base::test::ScopedFeatureList scoped_feature_list;
+  base::HistogramTester histogram_tester;
   scoped_feature_list.InitAndEnableFeature(translate::kTranslateUI2016Q2);
   SetHasLanguageChanged(false);
   EXPECT_FALSE(translate_manager_->ShouldSuppressBubbleUI(false, "en"));
+  histogram_tester.ExpectTotalCount(kInitiationStatusName, 0);
 }
 
 TEST_F(TranslateManagerTest, ShouldSuppressBubbleUI_IsTooOftenDenied) {
@@ -543,13 +558,20 @@
           metrics::TranslateEventProto::LANGUAGE_DISABLED_BY_AUTO_BLACKLIST, _,
           _))
       .WillOnce(Return(false));
+  base::HistogramTester histogram_tester;
   EXPECT_TRUE(translate_manager_->ShouldSuppressBubbleUI(false, "en"));
   EXPECT_FALSE(translate_manager_->ShouldSuppressBubbleUI(false, "de"));
   EXPECT_FALSE(translate_manager_->ShouldSuppressBubbleUI(true, "en"));
+  histogram_tester.ExpectUniqueSample(
+      kInitiationStatusName,
+      translate::TranslateBrowserMetrics::
+          INITIATION_STATUS_ABORTED_BY_TOO_OFTEN_DENIED,
+      1);
 }
 
 TEST_F(TranslateManagerTest, ShouldSuppressBubbleUI_Override) {
   PrepareTranslateManager();
+  base::HistogramTester histogram_tester;
   EXPECT_CALL(
       mock_translate_ranker_,
       ShouldOverrideDecision(
@@ -564,6 +586,7 @@
   SetHasLanguageChanged(false);
   SetLanguageTooOftenDenied("en");
   EXPECT_FALSE(translate_manager_->ShouldSuppressBubbleUI(false, "en"));
+  histogram_tester.ExpectTotalCount(kInitiationStatusName, 0);
 }
 
 }  // namespace testing
diff --git a/components/webdata/common/BUILD.gn b/components/webdata/common/BUILD.gn
index 7a11220..921d444 100644
--- a/components/webdata/common/BUILD.gn
+++ b/components/webdata/common/BUILD.gn
@@ -60,6 +60,7 @@
     "//components/test/data/web_database/version_71.sql",
     "//components/test/data/web_database/version_72.sql",
     "//components/test/data/web_database/version_73.sql",
+    "//components/test/data/web_database/version_73_with_type_column.sql",
   ]
   outputs = [
     "{{bundle_resources_dir}}/" +
diff --git a/components/webdata/common/web_database_migration_unittest.cc b/components/webdata/common/web_database_migration_unittest.cc
index 8c6a30f5..a119b444 100644
--- a/components/webdata/common/web_database_migration_unittest.cc
+++ b/components/webdata/common/web_database_migration_unittest.cc
@@ -1359,3 +1359,52 @@
     EXPECT_EQ(CreditCard::CARD_TYPE_UNKNOWN, cards.ColumnInt(2));
   }
 }
+
+// Tests that version 73 with "type" column instead of "bank_name" column can be
+// migrated to version 74 with both of these columns. This is necessary to
+// verify that the version 73 collision resolves itself.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion73WithTypeColumnToCurrent) {
+  ASSERT_NO_FATAL_FAILURE(
+      LoadDatabase(FILE_PATH_LITERAL("version_73_with_type_column.sql")));
+
+  // Verify pre-conditions.
+  {
+    sql::Connection connection;
+    ASSERT_TRUE(connection.Open(GetDatabasePath()));
+    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+    sql::MetaTable meta_table;
+    ASSERT_TRUE(meta_table.Init(&connection, 73, 73));
+
+    EXPECT_FALSE(
+        connection.DoesColumnExist("masked_credit_cards", "bank_name"));
+    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "type"));
+
+    EXPECT_TRUE(connection.Execute(
+        "INSERT INTO masked_credit_cards (type) VALUES (2)"));
+  }
+
+  DoMigration();
+
+  // Verify post-conditions.
+  {
+    sql::Connection connection;
+    ASSERT_TRUE(connection.Open(GetDatabasePath()));
+    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+    // Check version.
+    EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+    // The bank_name column should exist.
+    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "bank_name"));
+
+    // The type column should exist.
+    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "type"));
+
+    // Make sure that the existing value of the type column is preserved.
+    sql::Statement s_masked_cards(
+        connection.GetUniqueStatement("SELECT type FROM masked_credit_cards"));
+    ASSERT_TRUE(s_masked_cards.Step());
+    EXPECT_EQ(2, s_masked_cards.ColumnInt(0));
+  }
+}
diff --git a/content/app/strings/content_strings.grd b/content/app/strings/content_strings.grd
index 8b521fc..bed0ca1 100644
--- a/content/app/strings/content_strings.grd
+++ b/content/app/strings/content_strings.grd
@@ -171,24 +171,12 @@
       <message name="IDS_DOWNLOAD_BUTTON_LABEL" desc="Clickable button label to download media file.">
         Save
       </message>
-      <message name="IDS_SEARCHABLE_INDEX_INTRO" desc="Text that appears at the start of nearly-obsolete webpages in the form of a 'searchable index'.">
-        This is a searchable index. Enter search keywords: '''
-      </message>
       <message name="IDS_FORM_CALENDAR_CLEAR" desc="Label for a button which clears a date input field.">
         Clear
       </message>
       <message name="IDS_FORM_CALENDAR_TODAY" desc="Label for a button which sets today to a date input field.">
         Today
       </message>
-      <message name="IDS_FORM_DATE_FORMAT_DAY_IN_MONTH" desc="Short word to indicate which is the day-in-month part in a date format. e.g. 'Day' in 'Month/Day/Year'">
-        Day
-      </message>
-      <message name="IDS_FORM_DATE_FORMAT_MONTH" desc="Short word to indicate which is the month part in a date format. e.g. 'Month' in 'Month/Day/Year'">
-        Month
-      </message>
-      <message name="IDS_FORM_DATE_FORMAT_YEAR" desc="Short word to indicate which is the year part in a date format. e.g. 'Year' in 'Month/Day/Year'">
-        Year
-      </message>
       <message name="IDS_FORM_SUBMIT_LABEL" desc="Default label for Submit buttons in forms on webpages.">
         Submit
       </message>
@@ -219,9 +207,6 @@
       <message name="IDS_FORM_OTHER_MONTH_LABEL" desc="Label for button that opens a full month picker so the user can choose dates other than the ones in the list." meaning="for month label">
         Other...
       </message>
-      <message name="IDS_FORM_OTHER_TIME_LABEL" desc="Label for button that opens a full time picker so the user can choose dates other than the ones in the list." meaning="for time label">
-        Other...
-      </message>
       <message name="IDS_FORM_OTHER_WEEK_LABEL" desc="Label for button that opens a full week picker so the user can choose dates other than the ones in the list." meaning="for week label">
         Other...
       </message>
@@ -247,16 +232,6 @@
         Week
       </message>
 
-      <message name="IDS_RECENT_SEARCHES_NONE" desc="Label for only item in menu that appears when clicking on the search field image, when no searches have been performed">
-        No recent searches
-      </message>
-      <message name="IDS_RECENT_SEARCHES" desc="label for first item in the menu that appears when clicking on the search field image, used as embedded menu title">
-        Recent Searches
-      </message>
-      <message name="IDS_RECENT_SEARCHES_CLEAR" desc="menu item in Recent Searches menu that empties menu's contents">
-        Clear Recent Searches
-      </message>
-
       <message name="IDS_AX_CALENDAR_SHOW_MONTH_SELECTOR" desc="Accessible description of a button to show month selection panel in a calendar picker.">
         Show month selection panel
       </message>
@@ -320,15 +295,9 @@
       <message name="IDS_AX_ROLE_HEADING" desc="Accessibility role description for headings">
         heading
       </message>
-      <message name="IDS_AX_ROLE_IMAGE_MAP" desc="Accessibility role description for image map">
-        image map
-      </message>
       <message name="IDS_AX_ROLE_LINK" desc="Accessibility role description for link">
         link
       </message>
-      <message name="IDS_AX_ROLE_LIST_MARKER" desc="Accessibility role description for list marker">
-        list marker
-      </message>
       <message name="IDS_AX_ROLE_MAIN_CONTENT" desc="Accessibility role description for main content of the document.">
         main
       </message>
@@ -558,14 +527,6 @@
         pause
       </message>
 
-      <message name="IDS_AX_MEDIA_SLIDER" desc="Accessibility role description for timeline slider">
-        movie time
-      </message>
-
-      <message name="IDS_AX_MEDIA_SLIDER_THUMB" desc="Accessibility role description for timeline thumb">
-        movie timeline thumb
-      </message>
-
       <message name="IDS_AX_MEDIA_CURRENT_TIME_DISPLAY" desc="Accessibility role description for elapsed time display">
         elapsed time
       </message>
@@ -574,10 +535,6 @@
         remaining time
       </message>
 
-      <message name="IDS_AX_MEDIA_STATUS_DISPLAY" desc="Accessibility role description for movie status">
-        status
-      </message>
-
       <message name="IDS_AX_MEDIA_ENTER_FULL_SCREEN_BUTTON" desc="Accessibility role description for enter fullscreen button">
         enter full screen
       </message>
@@ -642,10 +599,6 @@
         movie time scrubber
       </message>
 
-      <message name="IDS_AX_MEDIA_SLIDER_THUMB_HELP" desc="Accessibility help description for timeline thumb">
-        movie time scrubber thumb
-      </message>
-
       <message name="IDS_AX_MEDIA_CURRENT_TIME_DISPLAY_HELP" desc="Accessibility help description for elapsed time display">
         current time in seconds
       </message>
@@ -654,10 +607,6 @@
         number of seconds of movie remaining
       </message>
 
-      <message name="IDS_AX_MEDIA_STATUS_DISPLAY_HELP" desc="Accessibility help description for movie status">
-        current movie status
-      </message>
-
       <message name="IDS_AX_MEDIA_ENTER_FULL_SCREEN_BUTTON_HELP" desc="Accessibility help description for enter fullscreen button">
         play movie in full screen mode
       </message>
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 36cd4f8..06f8106 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -743,13 +743,14 @@
 
 BrowserAccessibility* BrowserAccessibility::GetTable() const {
   BrowserAccessibility* table = const_cast<BrowserAccessibility*>(this);
-  while (table && !table->IsTableLikeRole())
+  while (table && !ui::IsTableLikeRole(table->GetRole()))
     table = table->PlatformGetParent();
   return table;
 }
 
 BrowserAccessibility* BrowserAccessibility::GetTableCell(int index) const {
-  if (!IsTableLikeRole() && !IsCellOrTableHeaderRole())
+  if (!ui::IsTableLikeRole(GetRole()) &&
+      !ui::IsCellOrTableHeaderRole(GetRole()))
     return nullptr;
 
   BrowserAccessibility* table = GetTable();
@@ -764,7 +765,8 @@
 
 BrowserAccessibility* BrowserAccessibility::GetTableCell(int row,
                                                          int column) const {
-  if (!IsTableLikeRole() && !IsCellOrTableHeaderRole())
+  if (!ui::IsTableLikeRole(GetRole()) &&
+      !ui::IsCellOrTableHeaderRole(GetRole()))
     return nullptr;
   if (row < 0 || row >= GetTableRowCount() || column < 0 ||
       column >= GetTableColumnCount()) {
@@ -788,7 +790,7 @@
 }
 
 int BrowserAccessibility::GetTableCellIndex() const {
-  if (!IsCellOrTableHeaderRole())
+  if (!ui::IsCellOrTableHeaderRole(GetRole()))
     return -1;
 
   BrowserAccessibility* table = GetTable();
@@ -818,7 +820,7 @@
 }
 
 int BrowserAccessibility::GetTableColumnSpan() const {
-  if (!IsCellOrTableHeaderRole())
+  if (!ui::IsCellOrTableHeaderRole(GetRole()))
     return 0;
 
   int column_span;
@@ -840,7 +842,7 @@
 }
 
 int BrowserAccessibility::GetTableRowSpan() const {
-  if (!IsCellOrTableHeaderRole())
+  if (!ui::IsCellOrTableHeaderRole(GetRole()))
     return 0;
 
   int row_span;
@@ -861,18 +863,6 @@
   return GetData().HasAction(action_enum);
 }
 
-bool BrowserAccessibility::IsCellOrTableHeaderRole() const {
-  return (GetRole() == ui::AX_ROLE_CELL ||
-          GetRole() == ui::AX_ROLE_COLUMN_HEADER ||
-          GetRole() == ui::AX_ROLE_ROW_HEADER);
-}
-
-bool BrowserAccessibility::IsTableLikeRole() const {
-  return (GetRole() == ui::AX_ROLE_TABLE ||
-          GetRole() == ui::AX_ROLE_GRID ||
-          GetRole() == ui::AX_ROLE_TREE_GRID);
-}
-
 bool BrowserAccessibility::HasCaret() const {
   if (IsSimpleTextControl() && HasIntAttribute(ui::AX_ATTR_TEXT_SEL_START) &&
       HasIntAttribute(ui::AX_ATTR_TEXT_SEL_END)) {
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 74b5d89..b7a098d 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -344,12 +344,6 @@
   bool HasState(ui::AXState state_enum) const;
   bool HasAction(ui::AXAction action_enum) const;
 
-  // Returns true if this node is a cell or a table header.
-  bool IsCellOrTableHeaderRole() const;
-
-  // Returns true if this node is a table, a grid or a treegrid.
-  bool IsTableLikeRole() const;
-
   // Returns true if the caret is active on this object.
   bool HasCaret() const;
 
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 52b84c6..4e9d9362 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -14,6 +14,7 @@
 #include "content/common/accessibility_messages.h"
 #include "content/public/common/content_client.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/platform/ax_android_constants.h"
 #include "ui/accessibility/platform/ax_snapshot_node_android_platform.h"
 
@@ -168,8 +169,9 @@
   return HasState(ui::AX_STATE_COLLAPSED);
 }
 
+// TODO(dougt) Move to ax_role_properties?
 bool BrowserAccessibilityAndroid::IsCollection() const {
-  return (IsTableLikeRole() || GetRole() == ui::AX_ROLE_LIST ||
+  return (ui::IsTableLikeRole(GetRole()) || GetRole() == ui::AX_ROLE_LIST ||
           GetRole() == ui::AX_ROLE_LIST_BOX ||
           GetRole() == ui::AX_ROLE_DESCRIPTION_LIST ||
           GetRole() == ui::AX_ROLE_TREE);
@@ -1145,7 +1147,7 @@
 }
 
 int BrowserAccessibilityAndroid::RowCount() const {
-  if (IsTableLikeRole()) {
+  if (ui::IsTableLikeRole(GetRole())) {
     return CountChildrenWithRole(ui::AX_ROLE_ROW);
   }
 
@@ -1160,7 +1162,7 @@
 }
 
 int BrowserAccessibilityAndroid::ColumnCount() const {
-  if (IsTableLikeRole()) {
+  if (ui::IsTableLikeRole(GetRole())) {
     return CountChildrenWithRole(ui::AX_ROLE_COLUMN);
   }
   return 0;
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 2698d30..c75b854 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -27,6 +27,8 @@
 #include "content/public/common/content_client.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/accessibility/ax_range.h"
+#include "ui/accessibility/ax_role_properties.h"
+
 #import "ui/accessibility/platform/ax_platform_node_mac.h"
 
 using AXPlatformPositionInstance =
@@ -657,7 +659,7 @@
 }
 
 - (NSNumber*)ariaColumnCount {
-  if (!browserAccessibility_->IsTableLikeRole())
+  if (!ui::IsTableLikeRole(browserAccessibility_->GetRole()))
     return nil;
   int count = -1;
   if (!browserAccessibility_->GetIntAttribute(ui::AX_ATTR_ARIA_COLUMN_COUNT,
@@ -668,7 +670,7 @@
 }
 
 - (NSNumber*)ariaColumnIndex {
-  if (!browserAccessibility_->IsCellOrTableHeaderRole())
+  if (!ui::IsCellOrTableHeaderRole(browserAccessibility_->GetRole()))
     return nil;
   int index = -1;
   if (!browserAccessibility_->GetIntAttribute(
@@ -700,7 +702,7 @@
 }
 
 - (NSNumber*)ariaRowCount {
-  if (!browserAccessibility_->IsTableLikeRole())
+  if (!ui::IsTableLikeRole(browserAccessibility_->GetRole()))
     return nil;
   int count = -1;
   if (!browserAccessibility_->GetIntAttribute(ui::AX_ATTR_ARIA_ROW_COUNT,
@@ -711,7 +713,7 @@
 }
 
 - (NSNumber*)ariaRowIndex {
-  if (!browserAccessibility_->IsCellOrTableHeaderRole())
+  if (!ui::IsCellOrTableHeaderRole(browserAccessibility_->GetRole()))
     return nil;
   int index = -1;
   if (!browserAccessibility_->GetIntAttribute(ui::AX_ATTR_ARIA_CELL_ROW_INDEX,
@@ -781,7 +783,7 @@
 - (NSArray*)columnHeaders {
   if (![self instanceActive])
     return nil;
-  if (!browserAccessibility_->IsTableLikeRole()) {
+  if (!ui::IsTableLikeRole(browserAccessibility_->GetRole())) {
     return nil;
   }
 
@@ -801,7 +803,7 @@
 - (NSValue*)columnIndexRange {
   if (![self instanceActive])
     return nil;
-  if (!browserAccessibility_->IsCellOrTableHeaderRole())
+  if (!ui::IsCellOrTableHeaderRole(browserAccessibility_->GetRole()))
     return nil;
 
   int column = -1;
@@ -1009,7 +1011,7 @@
   if (![self instanceActive])
     return nil;
   int headerElementId = -1;
-  if (browserAccessibility_->IsTableLikeRole()) {
+  if (ui::IsTableLikeRole(browserAccessibility_->GetRole())) {
     browserAccessibility_->GetIntAttribute(
         ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
@@ -1507,7 +1509,7 @@
 - (NSArray*)rowHeaders {
   if (![self instanceActive])
     return nil;
-  if (!browserAccessibility_->IsTableLikeRole()) {
+  if (!ui::IsTableLikeRole(browserAccessibility_->GetRole())) {
     return nil;
   }
 
@@ -1527,7 +1529,7 @@
 - (NSValue*)rowIndexRange {
   if (![self instanceActive])
     return nil;
-  if (!browserAccessibility_->IsCellOrTableHeaderRole())
+  if (!ui::IsCellOrTableHeaderRole(browserAccessibility_->GetRole()))
     return nil;
 
   int row = -1;
@@ -2112,7 +2114,7 @@
            j < child->PlatformChildCount();
            ++j) {
         BrowserAccessibility* cell = child->PlatformGetChild(j);
-        if (!cell->IsCellOrTableHeaderRole())
+        if (!ui::IsCellOrTableHeaderRole(cell->GetRole()))
           continue;
         int colIndex;
         if (!cell->GetIntAttribute(
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 19473d8..809a6ce4 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -27,6 +27,7 @@
 #include "content/common/accessibility_mode.h"
 #include "content/public/common/content_client.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/ax_text_utils.h"
 #include "ui/base/win/accessibility_ids_win.h"
 #include "ui/base/win/accessibility_misc_utils.h"
@@ -3413,7 +3414,7 @@
       return E_NOINTERFACE;
     }
   } else if (iid == IID_IAccessibleTableCell) {
-    if (!accessibility->owner()->IsCellOrTableHeaderRole()) {
+    if (!ui::IsCellOrTableHeaderRole(accessibility->owner()->GetRole())) {
       *object = NULL;
       return E_NOINTERFACE;
     }
@@ -3628,9 +3629,9 @@
   }
 
   // Expose table cell index.
-  if (owner()->IsCellOrTableHeaderRole()) {
+  if (ui::IsCellOrTableHeaderRole(owner()->GetRole())) {
     BrowserAccessibility* table = owner()->PlatformGetParent();
-    while (table && !table->IsTableLikeRole())
+    while (table && !ui::IsTableLikeRole(table->GetRole()))
       table = table->PlatformGetParent();
     if (table) {
       const std::vector<int32_t>& unique_cell_ids =
@@ -3645,13 +3646,13 @@
   }
 
   // Expose aria-colcount and aria-rowcount in a table, grid or treegrid.
-  if (owner()->IsTableLikeRole()) {
+  if (ui::IsTableLikeRole(owner()->GetRole())) {
     IntAttributeToIA2(ui::AX_ATTR_ARIA_COLUMN_COUNT, "colcount");
     IntAttributeToIA2(ui::AX_ATTR_ARIA_ROW_COUNT, "rowcount");
   }
 
   // Expose aria-colindex and aria-rowindex in a cell or row.
-  if (owner()->IsCellOrTableHeaderRole() ||
+  if (ui::IsCellOrTableHeaderRole(owner()->GetRole()) ||
       owner()->GetRole() == ui::AX_ROLE_ROW) {
     if (owner()->GetRole() != ui::AX_ROLE_ROW)
       IntAttributeToIA2(ui::AX_ATTR_ARIA_CELL_COLUMN_INDEX, "colindex");
@@ -4845,7 +4846,7 @@
 }
 
 void BrowserAccessibilityComWin::UpdateRequiredAttributes() {
-  if (owner()->IsCellOrTableHeaderRole()) {
+  if (ui::IsCellOrTableHeaderRole(owner()->GetRole())) {
     // Expose colspan attribute.
     base::string16 colspan;
     if (owner()->GetHtmlAttribute("aria-colspan", &colspan)) {
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index c71ffbe..a625b52 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -5,11 +5,11 @@
 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
 
 #include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
 #import "base/mac/mac_util.h"
 #import "base/mac/scoped_nsobject.h"
 #import "base/mac/sdk_forward_declarations.h"
-#include "base/location.h"
-#include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -17,6 +17,7 @@
 #import "content/browser/accessibility/browser_accessibility_mac.h"
 #include "content/common/accessibility_messages.h"
 #include "content/public/browser/browser_thread.h"
+#include "ui/accessibility/ax_role_properties.h"
 
 namespace {
 
@@ -210,7 +211,7 @@
       mac_notification = NSAccessibilityInvalidStatusChangedNotification;
       break;
     case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
-      if (node->IsTableLikeRole()) {
+      if (ui::IsTableLikeRole(node->GetRole())) {
         mac_notification = NSAccessibilitySelectedRowsChangedNotification;
       } else {
         mac_notification = NSAccessibilitySelectedChildrenChangedNotification;
@@ -431,36 +432,6 @@
   }
 }
 
-bool IsContainerWithSelectableChildrenRole(ui::AXRole role) {
-  switch (role) {
-    case ui::AX_ROLE_COMBO_BOX:
-    case ui::AX_ROLE_GRID:
-    case ui::AX_ROLE_LIST_BOX:
-    case ui::AX_ROLE_MENU:
-    case ui::AX_ROLE_MENU_BAR:
-    case ui::AX_ROLE_RADIO_GROUP:
-    case ui::AX_ROLE_TAB_LIST:
-    case ui::AX_ROLE_TOOLBAR:
-    case ui::AX_ROLE_TREE:
-    case ui::AX_ROLE_TREE_GRID:
-      return true;
-    default:
-      return false;
-  }
-}
-
-bool IsRowContainer(ui::AXRole role) {
-  switch (role) {
-    case ui::AX_ROLE_TREE:
-    case ui::AX_ROLE_TREE_GRID:
-    case ui::AX_ROLE_GRID:
-    case ui::AX_ROLE_TABLE:
-      return true;
-    default:
-      return false;
-  }
-}
-
 void BrowserAccessibilityManagerMac::OnStateChanged(ui::AXTree* tree,
                                                     ui::AXNode* node,
                                                     ui::AXState state,
@@ -474,7 +445,7 @@
       else
         tree_events_[node->id()].insert(ui::AX_EVENT_ROW_COLLAPSED);
       ui::AXNode* container = node;
-      while (container && !IsRowContainer(container->data().role))
+      while (container && !ui::IsRowContainer(container->data().role))
         container = container->parent();
       if (container)
         tree_events_[container->id()].insert(ui::AX_EVENT_ROW_COUNT_CHANGED);
@@ -485,7 +456,7 @@
   if (state == ui::AX_STATE_SELECTED) {
     ui::AXNode* container = node;
     while (container &&
-           !IsContainerWithSelectableChildrenRole(container->data().role))
+           !ui::IsContainerWithSelectableChildrenRole(container->data().role))
       container = container->parent();
     if (container)
       tree_events_[container->id()].insert(
@@ -577,6 +548,9 @@
         tree_events_[live_root->id()].insert(ui::AX_EVENT_LIVE_REGION_CHANGED);
     }
   }
+
+  if (root_changed && tree->data().loaded)
+    tree_events_[tree->root()->id()].insert(ui::AX_EVENT_LOAD_COMPLETE);
 }
 
 NSDictionary* BrowserAccessibilityManagerMac::
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index dc0c5e9..b9d545a2 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -346,8 +346,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
-                       AccessibilityEventsRemoveSubtree) {
-  RunEventTest(FILE_PATH_LITERAL("remove-subtree.html"));
+                       AccessibilityEventsTableColumnHidden) {
+  RunEventTest(FILE_PATH_LITERAL("table-column-hidden.html"));
 }
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index df73588..af077e5f 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -838,6 +838,11 @@
   RunAriaTest(FILE_PATH_LITERAL("input-text-aria-placeholder.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityTableColumnHidden) {
+  RunAriaTest(FILE_PATH_LITERAL("table-column-hidden.html"));
+}
+
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityArticle) {
   RunHtmlTest(FILE_PATH_LITERAL("article.html"));
 }
diff --git a/content/browser/accessibility/one_shot_accessibility_tree_search.cc b/content/browser/accessibility/one_shot_accessibility_tree_search.cc
index 7cfbd16..45f1512 100644
--- a/content/browser/accessibility/one_shot_accessibility_tree_search.cc
+++ b/content/browser/accessibility/one_shot_accessibility_tree_search.cc
@@ -12,6 +12,7 @@
 #include "content/browser/accessibility/browser_accessibility.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "ui/accessibility/ax_enums.h"
+#include "ui/accessibility/ax_role_properties.h"
 
 namespace content {
 
@@ -409,7 +410,7 @@
 
 bool AccessibilityTablePredicate(
     BrowserAccessibility* start, BrowserAccessibility* node) {
-  return node->IsTableLikeRole();
+  return ui::IsTableLikeRole(node->GetRole());
 }
 
 bool AccessibilityTextfieldPredicate(
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index 94bade54..809d3e5 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -18,6 +18,7 @@
 #include "base/process/process_handle.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
 #include "content/browser/child_process_launcher.h"
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/browser/service_manager/common_browser_interfaces.h"
@@ -70,7 +71,9 @@
 base::LazyInstance<std::unique_ptr<service_manager::Connector>>::Leaky
     g_io_thread_connector = LAZY_INSTANCE_INITIALIZER;
 
-void DestroyConnectorOnIOThread() { g_io_thread_connector.Get().reset(); }
+void DestroyConnectorOnIOThread() {
+  g_io_thread_connector.Get().reset();
+}
 
 void StartServiceInUtilityProcess(
     const std::string& service_name,
@@ -199,9 +202,11 @@
   }
 
   void ShutDown() {
-    BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)->PostTask(
-        FROM_HERE,
-        base::Bind(&InProcessServiceManagerContext::ShutDownOnIOThread, this));
+    BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)
+        ->PostTask(
+            FROM_HERE,
+            base::Bind(&InProcessServiceManagerContext::ShutDownOnIOThread,
+                       this));
   }
 
  private:
@@ -296,8 +301,11 @@
       std::move(root_browser_service), mojo::MakeRequest(&pid_receiver));
   pid_receiver->SetPID(base::GetCurrentProcId());
 
-
   ServiceInfo device_info;
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner =
+      base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BACKGROUND});
+
 #if defined(OS_ANDROID)
   JNIEnv* env = base::android::AttachCurrentThread();
   base::android::ScopedJavaGlobalRef<jobject> java_nfc_delegate;
@@ -307,15 +315,13 @@
   // See the comments on wake_lock_context_host.h and ContentNfcDelegate.java
   // respectively for comments on those parameters.
   device_info.factory =
-      base::Bind(&device::CreateDeviceService,
-                 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
+      base::Bind(&device::CreateDeviceService, background_task_runner,
                  BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
                  base::Bind(&WakeLockContextHost::GetNativeViewForContext),
                  std::move(java_nfc_delegate));
 #else
   device_info.factory =
-      base::Bind(&device::CreateDeviceService,
-                 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
+      base::Bind(&device::CreateDeviceService, background_task_runner,
                  BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
 #endif
   device_info.task_runner = base::ThreadTaskRunnerHandle::Get();
@@ -343,9 +349,8 @@
   g_io_thread_connector.Get() = browser_connection->GetConnector()->Clone();
 
   ContentBrowserClient::OutOfProcessServiceMap sandboxed_services;
-  GetContentClient()
-      ->browser()
-      ->RegisterOutOfProcessServices(&sandboxed_services);
+  GetContentClient()->browser()->RegisterOutOfProcessServices(
+      &sandboxed_services);
   sandboxed_services.insert(
       std::make_pair(data_decoder::mojom::kServiceName,
                      base::ASCIIToUTF16("Data Decoder Service")));
@@ -356,9 +361,8 @@
   }
 
   ContentBrowserClient::OutOfProcessServiceMap unsandboxed_services;
-  GetContentClient()
-      ->browser()
-      ->RegisterUnsandboxedOutOfProcessServices(&unsandboxed_services);
+  GetContentClient()->browser()->RegisterUnsandboxedOutOfProcessServices(
+      &unsandboxed_services);
 
   bool network_service_enabled =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 71bf19c..305d39bb 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -88,16 +88,8 @@
       return IDS_AX_CALENDAR_WEEK_DESCRIPTION;
     case WebLocalizedString::kAXDayOfMonthFieldText:
       return IDS_AX_DAY_OF_MONTH_FIELD_TEXT;
-    case WebLocalizedString::kAXHeadingText:
-      return IDS_AX_ROLE_HEADING;
     case WebLocalizedString::kAXHourFieldText:
       return IDS_AX_HOUR_FIELD_TEXT;
-    case WebLocalizedString::kAXImageMapText:
-      return IDS_AX_ROLE_IMAGE_MAP;
-    case WebLocalizedString::kAXLinkText:
-      return IDS_AX_ROLE_LINK;
-    case WebLocalizedString::kAXListMarkerText:
-      return IDS_AX_ROLE_LIST_MARKER;
     case WebLocalizedString::kAXMediaDefault:
       return IDS_AX_MEDIA_DEFAULT;
     case WebLocalizedString::kAXMediaAudioElement:
@@ -112,16 +104,10 @@
       return IDS_AX_MEDIA_PLAY_BUTTON;
     case WebLocalizedString::kAXMediaPauseButton:
       return IDS_AX_MEDIA_PAUSE_BUTTON;
-    case WebLocalizedString::kAXMediaSlider:
-      return IDS_AX_MEDIA_SLIDER;
-    case WebLocalizedString::kAXMediaSliderThumb:
-      return IDS_AX_MEDIA_SLIDER_THUMB;
     case WebLocalizedString::kAXMediaCurrentTimeDisplay:
       return IDS_AX_MEDIA_CURRENT_TIME_DISPLAY;
     case WebLocalizedString::kAXMediaTimeRemainingDisplay:
       return IDS_AX_MEDIA_TIME_REMAINING_DISPLAY;
-    case WebLocalizedString::kAXMediaStatusDisplay:
-      return IDS_AX_MEDIA_STATUS_DISPLAY;
     case WebLocalizedString::kAXMediaEnterFullscreenButton:
       return IDS_AX_MEDIA_ENTER_FULL_SCREEN_BUTTON;
     case WebLocalizedString::kAXMediaExitFullscreenButton:
@@ -154,14 +140,10 @@
       return IDS_AX_MEDIA_AUDIO_SLIDER_HELP;
     case WebLocalizedString::kAXMediaVideoSliderHelp:
       return IDS_AX_MEDIA_VIDEO_SLIDER_HELP;
-    case WebLocalizedString::kAXMediaSliderThumbHelp:
-      return IDS_AX_MEDIA_SLIDER_THUMB_HELP;
     case WebLocalizedString::kAXMediaCurrentTimeDisplayHelp:
       return IDS_AX_MEDIA_CURRENT_TIME_DISPLAY_HELP;
     case WebLocalizedString::kAXMediaTimeRemainingDisplayHelp:
       return IDS_AX_MEDIA_TIME_REMAINING_DISPLAY_HELP;
-    case WebLocalizedString::kAXMediaStatusDisplayHelp:
-      return IDS_AX_MEDIA_STATUS_DISPLAY_HELP;
     case WebLocalizedString::kAXMediaEnterFullscreenButtonHelp:
       return IDS_AX_MEDIA_ENTER_FULL_SCREEN_BUTTON_HELP;
     case WebLocalizedString::kAXMediaExitFullscreenButtonHelp:
@@ -184,8 +166,6 @@
       return IDS_AX_MONTH_FIELD_TEXT;
     case WebLocalizedString::kAXSecondFieldText:
       return IDS_AX_SECOND_FIELD_TEXT;
-    case WebLocalizedString::kAXWebAreaText:
-      return IDS_AX_ROLE_WEB_AREA;
     case WebLocalizedString::kAXWeekOfYearFieldText:
       return IDS_AX_WEEK_OF_YEAR_FIELD_TEXT;
     case WebLocalizedString::kAXYearFieldText:
@@ -194,12 +174,6 @@
       return IDS_FORM_CALENDAR_CLEAR;
     case WebLocalizedString::kCalendarToday:
       return IDS_FORM_CALENDAR_TODAY;
-    case WebLocalizedString::kDateFormatDayInMonthLabel:
-      return IDS_FORM_DATE_FORMAT_DAY_IN_MONTH;
-    case WebLocalizedString::kDateFormatMonthLabel:
-      return IDS_FORM_DATE_FORMAT_MONTH;
-    case WebLocalizedString::kDateFormatYearLabel:
-      return IDS_FORM_DATE_FORMAT_YEAR;
     case WebLocalizedString::kDetailsLabel:
       return IDS_DETAILS_WITHOUT_SUMMARY_LABEL;
     case WebLocalizedString::kDownloadButtonLabel:
@@ -226,8 +200,6 @@
       return IDS_FORM_OTHER_DATE_LABEL;
     case WebLocalizedString::kOtherMonthLabel:
       return IDS_FORM_OTHER_MONTH_LABEL;
-    case WebLocalizedString::kOtherTimeLabel:
-      return IDS_FORM_OTHER_TIME_LABEL;
     case WebLocalizedString::kOtherWeekLabel:
       return IDS_FORM_OTHER_WEEK_LABEL;
     case WebLocalizedString::kOverflowMenuCaptions:
@@ -258,14 +230,6 @@
       return IDS_FORM_PLACEHOLDER_FOR_YEAR_FIELD;
     case WebLocalizedString::kResetButtonDefaultLabel:
       return IDS_FORM_RESET_LABEL;
-    case WebLocalizedString::kSearchableIndexIntroduction:
-      return IDS_SEARCHABLE_INDEX_INTRO;
-    case WebLocalizedString::kSearchMenuClearRecentSearchesText:
-      return IDS_RECENT_SEARCHES_CLEAR;
-    case WebLocalizedString::kSearchMenuNoRecentSearchesText:
-      return IDS_RECENT_SEARCHES_NONE;
-    case WebLocalizedString::kSearchMenuRecentSearchesText:
-      return IDS_RECENT_SEARCHES;
     case WebLocalizedString::kSelectMenuListText:
       return IDS_FORM_SELECT_MENU_LIST_TEXT;
     case WebLocalizedString::kSubmitButtonDefaultLabel:
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index c5613269..bd7a586 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -70,6 +70,11 @@
   WebRuntimeFeatures::EnableWebBluetooth(true);
 #endif
 
+// Web Share is shipped on Android, experimental otherwise.
+#if defined(OS_ANDROID)
+  WebRuntimeFeatures::EnableWebShare(true);
+#endif
+
 #if defined(OS_CHROMEOS)
   WebRuntimeFeatures::EnableForceTallerSelectPopup(true);
 #endif
diff --git a/content/network/network_context.cc b/content/network/network_context.cc
index ea81b5c..5862ea6 100644
--- a/content/network/network_context.cc
+++ b/content/network/network_context.cc
@@ -9,7 +9,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "content/network/cache_url_loader.h"
-#include "content/network/network_service.h"
 #include "content/network/network_service_url_loader_factory_impl.h"
 #include "content/network/url_loader_impl.h"
 #include "content/public/common/content_client.h"
@@ -87,17 +86,11 @@
 
 }  // namespace
 
-NetworkContext::NetworkContext(NetworkService* network_service,
-                               mojom::NetworkContextRequest request,
+NetworkContext::NetworkContext(mojom::NetworkContextRequest request,
                                mojom::NetworkContextParamsPtr params)
-    : network_service_(network_service),
-      url_request_context_(MakeURLRequestContext()),
+    : url_request_context_(MakeURLRequestContext()),
       params_(std::move(params)),
-      binding_(this, std::move(request)) {
-  network_service_->RegisterNetworkContext(this);
-  binding_.set_connection_error_handler(
-      base::Bind(&NetworkContext::OnConnectionError, base::Unretained(this)));
-}
+      binding_(this, std::move(request)) {}
 
 NetworkContext::~NetworkContext() {
   // Call each URLLoaderImpl and ask it to release its net::URLRequest, as the
@@ -106,10 +99,6 @@
   // so have to be careful.
   while (!url_loaders_.empty())
     (*url_loaders_.begin())->Cleanup();
-
-  // May be nullptr in tests.
-  if (network_service_)
-    network_service_->DeregisterNetworkContext(this);
 }
 
 std::unique_ptr<NetworkContext> NetworkContext::CreateForTesting() {
@@ -139,22 +128,8 @@
   StartCacheURLLoader(url, url_request_context_.get(), std::move(client));
 }
 
-void NetworkContext::Cleanup() {
-  // The NetworkService is going away, so have to destroy the
-  // net::URLRequestContext held by this NetworkContext.
-  delete this;
-}
-
 NetworkContext::NetworkContext()
-    : network_service_(nullptr),
-      url_request_context_(MakeURLRequestContext()),
+    : url_request_context_(MakeURLRequestContext()),
       binding_(this) {}
 
-void NetworkContext::OnConnectionError() {
-  // Don't delete |this| in response to connection errors when it was created by
-  // CreateForTesting.
-  if (network_service_)
-    delete this;
-}
-
 }  // namespace content
diff --git a/content/network/network_context.h b/content/network/network_context.h
index b6b5332f..a4dd42c 100644
--- a/content/network/network_context.h
+++ b/content/network/network_context.h
@@ -22,13 +22,11 @@
 }
 
 namespace content {
-class NetworkService;
 class URLLoaderImpl;
 
 class NetworkContext : public mojom::NetworkContext {
  public:
-  NetworkContext(NetworkService* network_service,
-                 mojom::NetworkContextRequest request,
+  NetworkContext(mojom::NetworkContextRequest request,
                  mojom::NetworkContextParamsPtr params);
   ~NetworkContext() override;
 
@@ -49,18 +47,9 @@
   void HandleViewCacheRequest(const GURL& url,
                               mojom::URLLoaderClientPtr client) override;
 
-  // Called when the associated NetworkService is going away. Guaranteed to
-  // destroy NetworkContext's URLRequestContext.
-  void Cleanup();
-
  private:
   NetworkContext();
 
-  // On connection errors the NetworkContext destroys itself.
-  void OnConnectionError();
-
-  NetworkService* const network_service_;
-
   std::unique_ptr<net::URLRequestContext> url_request_context_;
 
   // Put it below |url_request_context_| so that it outlives all the
diff --git a/content/network/network_service.cc b/content/network/network_service.cc
index a133884..28d947b 100644
--- a/content/network/network_service.cc
+++ b/content/network/network_service.cc
@@ -55,44 +55,11 @@
 NetworkService::NetworkService(
     std::unique_ptr<service_manager::BinderRegistry> registry)
     : net_log_(new MojoNetLog), registry_(std::move(registry)), binding_(this) {
-  // |registry_| may be nullptr in tests.
-  if (registry_) {
-    registry_->AddInterface<mojom::NetworkService>(
-        base::Bind(&NetworkService::Create, base::Unretained(this)));
-  }
+  registry_->AddInterface<mojom::NetworkService>(
+      base::Bind(&NetworkService::Create, base::Unretained(this)));
 }
 
-NetworkService::~NetworkService() {
-  // Call each Network and ask it to release its net::URLRequestContext, as they
-  // may have references to shared objects owned by the NetworkService. The
-  // NetworkContexts deregister themselves in Cleanup(), so have to be careful.
-  while (!network_contexts_.empty())
-    (*network_contexts_.begin())->Cleanup();
-}
-
-std::unique_ptr<NetworkService> NetworkService::CreateForTesting() {
-  return base::WrapUnique(new NetworkService());
-}
-
-void NetworkService::RegisterNetworkContext(NetworkContext* network_context) {
-  DCHECK_EQ(0u, network_contexts_.count(network_context));
-  network_contexts_.insert(network_context);
-}
-
-void NetworkService::DeregisterNetworkContext(NetworkContext* network_context) {
-  DCHECK_EQ(1u, network_contexts_.count(network_context));
-  network_contexts_.erase(network_context);
-}
-
-void NetworkService::CreateNetworkContext(
-    mojom::NetworkContextRequest request,
-    mojom::NetworkContextParamsPtr params) {
-  // The NetworkContext will destroy itself on connection error, or when the
-  // service is destroyed.
-  new NetworkContext(this, std::move(request), std::move(params));
-}
-
-NetworkService::NetworkService() : NetworkService(nullptr) {}
+NetworkService::~NetworkService() = default;
 
 void NetworkService::OnBindInterface(
     const service_manager::BindSourceInfo& source_info,
@@ -108,4 +75,12 @@
   binding_.Bind(std::move(request));
 }
 
+void NetworkService::CreateNetworkContext(
+    mojom::NetworkContextRequest request,
+    mojom::NetworkContextParamsPtr params) {
+  mojo::MakeStrongBinding(
+      base::MakeUnique<NetworkContext>(std::move(request), std::move(params)),
+      std::move(request));
+}
+
 }  // namespace content
diff --git a/content/network/network_service.h b/content/network/network_service.h
index c945b9ad..e139546d 100644
--- a/content/network/network_service.h
+++ b/content/network/network_service.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "content/common/content_export.h"
 #include "content/common/network_service.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
@@ -16,8 +15,6 @@
 
 namespace content {
 
-class NetworkContext;
-
 class NetworkService : public service_manager::Service,
                        public mojom::NetworkService {
  public:
@@ -25,23 +22,9 @@
       std::unique_ptr<service_manager::BinderRegistry> registry);
   ~NetworkService() override;
 
-  CONTENT_EXPORT static std::unique_ptr<NetworkService> CreateForTesting();
-
-  // These are called by NetworkContexts as they are being created and
-  // destroyed.
-  void RegisterNetworkContext(NetworkContext* network_context);
-  void DeregisterNetworkContext(NetworkContext* network_context);
-
-  // mojom::NetworkService implementation:
-  void CreateNetworkContext(mojom::NetworkContextRequest request,
-                            mojom::NetworkContextParamsPtr params) override;
-
  private:
   class MojoNetLog;
 
-  // Used for tests.
-  NetworkService();
-
   // service_manager::Service implementation.
   void OnBindInterface(const service_manager::BindSourceInfo& source_info,
                        const std::string& interface_name,
@@ -50,18 +33,16 @@
   void Create(const service_manager::BindSourceInfo& source_info,
               mojom::NetworkServiceRequest request);
 
+  // mojom::NetworkService implementation:
+  void CreateNetworkContext(mojom::NetworkContextRequest request,
+                            mojom::NetworkContextParamsPtr params) override;
+
   std::unique_ptr<MojoNetLog> net_log_;
 
   std::unique_ptr<service_manager::BinderRegistry> registry_;
 
   mojo::Binding<mojom::NetworkService> binding_;
 
-  // NetworkContexts register themselves with the NetworkService so that they
-  // can be cleaned up when the NetworkService goes away. This is needed as
-  // NetworkContexts share global state with the NetworkService, so must be
-  // destroyed first.
-  std::set<NetworkContext*> network_contexts_;
-
   DISALLOW_COPY_AND_ASSIGN(NetworkService);
 };
 
diff --git a/content/network/network_service_unittest.cc b/content/network/network_service_unittest.cc
deleted file mode 100644
index 4897e12..0000000
--- a/content/network/network_service_unittest.cc
+++ /dev/null
@@ -1,68 +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 <memory>
-
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "content/common/network_service.mojom.h"
-#include "content/network/network_context.h"
-#include "content/network/network_service.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-namespace {
-
-class NetworkServiceTest : public testing::Test {
- public:
-  NetworkServiceTest() : service_(NetworkService::CreateForTesting()) {}
-  ~NetworkServiceTest() override {}
-
-  NetworkService* service() const { return service_.get(); }
-
-  void DestroyService() { service_.reset(); }
-
- private:
-  base::MessageLoopForIO message_loop_;
-  std::unique_ptr<NetworkService> service_;
-};
-
-// Test shutdown in the case a NetworkContext is destroyed before the
-// NetworkService.
-TEST_F(NetworkServiceTest, CreateAndDestroyContext) {
-  mojom::NetworkContextPtr network_context;
-  mojom::NetworkContextParamsPtr context_params =
-      mojom::NetworkContextParams::New();
-
-  service()->CreateNetworkContext(mojo::MakeRequest(&network_context),
-                                  std::move(context_params));
-  network_context.reset();
-  // Make sure the NetworkContext is destroyed.
-  base::RunLoop().RunUntilIdle();
-}
-
-// Test shutdown in the case there is still a live NetworkContext when the
-// NetworkService is destroyed. The service should destroy the NetworkContext
-// itself.
-TEST_F(NetworkServiceTest, DestroyingServiceDestroysContext) {
-  mojom::NetworkContextPtr network_context;
-  mojom::NetworkContextParamsPtr context_params =
-      mojom::NetworkContextParams::New();
-
-  service()->CreateNetworkContext(mojo::MakeRequest(&network_context),
-                                  std::move(context_params));
-  base::RunLoop run_loop;
-  network_context.set_connection_error_handler(run_loop.QuitClosure());
-  DestroyService();
-
-  // Destroying the service should destroy the context, causing a connection
-  // error.
-  run_loop.Run();
-}
-
-}  // namespace
-
-}  // namespace content
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 071ae907..dd60b833 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -469,6 +469,7 @@
     "junit/src/org/chromium/content/browser/ChildConnectionAllocatorTest.java",
     "junit/src/org/chromium/content/browser/MenuDescriptorTest.java",
     "junit/src/org/chromium/content/browser/SpareChildConnectionTest.java",
+    "junit/src/org/chromium/content/browser/TestChildProcessConnection.java",
     "junit/src/org/chromium/content/browser/androidoverlay/DialogOverlayCoreTest.java",
     "junit/src/org/chromium/content/browser/input/RangeTest.java",
     "junit/src/org/chromium/content/browser/input/TextInputStateTest.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
index b0767b8..ce2e033 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
@@ -57,23 +57,6 @@
     // Delay between the call to freeConnection and the connection actually beeing freed.
     private static final long FREE_CONNECTION_DELAY_MILLIS = 1;
 
-    // Factory used by the SpareConnection to create the actual ChildProcessConnection.
-    private static final SpareChildConnection.ConnectionFactory SANDBOXED_CONNECTION_FACTORY =
-            new SpareChildConnection.ConnectionFactory() {
-                @Override
-                public ChildProcessConnection allocateBoundConnection(Context context,
-                        ChildProcessCreationParams creationParams,
-                        ChildProcessConnection.ServiceCallback serviceCallback) {
-                    boolean bindToCallerCheck =
-                            creationParams == null ? false : creationParams.getBindToCallerCheck();
-                    ChildConnectionAllocator allocator =
-                            getConnectionAllocator(context, creationParams, true /* sandboxed */);
-                    Bundle serviceBundle =
-                            ChildProcessLauncherHelper.createServiceBundle(bindToCallerCheck);
-                    return allocator.allocate(context, serviceBundle, serviceCallback);
-                }
-            };
-
     // A warmed-up connection to a sandboxed service.
     private static SpareChildConnection sSpareSandboxedConnection;
 
@@ -189,9 +172,16 @@
                 if (sSpareSandboxedConnection != null && !sSpareSandboxedConnection.isEmpty()) {
                     return;
                 }
+
+                ChildProcessCreationParams creationParams = ChildProcessCreationParams.getDefault();
+                boolean bindToCallerCheck =
+                        creationParams == null ? false : creationParams.getBindToCallerCheck();
+                Bundle serviceBundle =
+                        ChildProcessLauncherHelper.createServiceBundle(bindToCallerCheck);
+                ChildConnectionAllocator allocator =
+                        getConnectionAllocator(context, creationParams, true /* sandboxed */);
                 sSpareSandboxedConnection =
-                        new SpareChildConnection(context, SANDBOXED_CONNECTION_FACTORY,
-                                ChildProcessCreationParams.getDefault(), true /* sandboxed */);
+                        new SpareChildConnection(context, allocator, serviceBundle);
             }
         });
     }
@@ -417,7 +407,7 @@
             // Try to use the spare connection if there's one.
             if (sSpareSandboxedConnection != null) {
                 mConnection = sSpareSandboxedConnection.getConnection(
-                        mCreationParams, mSandboxed, serviceCallback);
+                        mConnectionAllocator, serviceCallback);
                 if (mConnection != null) {
                     Log.d(TAG, "Using warmed-up connection for service %s.",
                             mConnection.getServiceName());
diff --git a/content/public/android/java/src/org/chromium/content/browser/SpareChildConnection.java b/content/public/android/java/src/org/chromium/content/browser/SpareChildConnection.java
index 8372db4..79e20c3 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SpareChildConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SpareChildConnection.java
@@ -5,11 +5,11 @@
 package org.chromium.content.browser;
 
 import android.content.Context;
+import android.os.Bundle;
 import android.support.annotation.NonNull;
 
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.base.process_launcher.ChildProcessCreationParams;
 
 /**
  * This class is used to create a single spare ChildProcessConnection (usually early on during
@@ -18,19 +18,8 @@
 public class SpareChildConnection {
     private static final String TAG = "SpareChildConn";
 
-    // Factory interface used to create the actual connection.
-    interface ConnectionFactory {
-        ChildProcessConnection allocateBoundConnection(Context context,
-                ChildProcessCreationParams creationParams,
-                ChildProcessConnection.ServiceCallback serviceCallback);
-    }
-
-    // The parameters passed to the Connectionfactory when creating the connection.
-    // Also used to identify the connection when the connection is retrieved.
-    private final ChildProcessCreationParams mCreationParams;
-
-    // Whether the connection is sandboxed.
-    private final boolean mSandoxed;
+    // The allocator used to create the connection.
+    private final ChildConnectionAllocator mConnectionAllocator;
 
     // The actual spare connection.
     private ChildProcessConnection mConnection;
@@ -43,12 +32,11 @@
     private ChildProcessConnection.ServiceCallback mConnectionServiceCallback;
 
     /** Creates and binds a ChildProcessConnection using the specified parameters. */
-    public SpareChildConnection(Context context, ConnectionFactory connectionFactory,
-            ChildProcessCreationParams creationParams, boolean sandboxed) {
+    public SpareChildConnection(
+            Context context, ChildConnectionAllocator connectionAllocator, Bundle serviceBundle) {
         assert LauncherThread.runningOnLauncherThread();
 
-        mCreationParams = creationParams;
-        mSandoxed = sandboxed;
+        mConnectionAllocator = connectionAllocator;
 
         ChildProcessConnection.ServiceCallback serviceCallback =
                 new ChildProcessConnection.ServiceCallback() {
@@ -86,19 +74,17 @@
                     }
                 };
 
-        mConnection = connectionFactory.allocateBoundConnection(
-                context, mCreationParams, serviceCallback);
+        mConnection = mConnectionAllocator.allocate(context, serviceBundle, serviceCallback);
     }
 
     /**
-     * @return a connection that has been bound or is being bound matching the given paramters, null
-     * otherwise.
+     * @return a connection that has been bound or is being bound if one was created with the same
+     * allocator as the one provided, null otherwise.
      */
-    public ChildProcessConnection getConnection(ChildProcessCreationParams creationParams,
-            boolean sandboxed,
+    public ChildProcessConnection getConnection(ChildConnectionAllocator allocator,
             @NonNull final ChildProcessConnection.ServiceCallback serviceCallback) {
         assert LauncherThread.runningOnLauncherThread();
-        if (mConnection == null || creationParams != mCreationParams || sandboxed != mSandoxed
+        if (mConnection == null || allocator != mConnectionAllocator
                 || mConnectionServiceCallback != null) {
             return null;
         }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
index ab28c54..3a36065f 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
@@ -461,31 +461,31 @@
     @Test
     @MediumTest
     @Feature({"ProcessManagement"})
+    @ChildProcessAllocatorSettings(sandboxedServiceCount = 4)
     public void testCustomCreationParamDoesNotReuseWarmupConnection() {
         // Since warmUp only uses default params.
         final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         ChildProcessCreationParams defaultCreationParams =
                 getDefaultChildProcessCreationParams(context.getPackageName());
-        ChildProcessCreationParams otherCreationParams =
-                getDefaultChildProcessCreationParams(context.getPackageName());
         ChildProcessCreationParams.registerDefault(defaultCreationParams);
+        ChildProcessCreationParams otherCreationParams = getDefaultChildProcessCreationParams(
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageName());
 
         warmUpOnUiThreadBlocking(context);
         Assert.assertEquals(1, getConnectedSandboxedServicesCount());
 
-        // First create a connnection with different creation params then the default. It should not
-        // use the warm-up connection which uses the default creation params (check uses object
-        // identity, having the params match exactly is fine).
+        // First create a connnection with different creation params than the default, it should not
+        // use the warmup connection (note that it won't bind since we are using the wrong package,
+        // but we need to use a different package to differentiate them, and we can only have 1
+        // valid package per app).
         startSandboxedChildProcessWithCreationParams(
-                otherCreationParams, BLOCK_UNTIL_SETUP, true /* doSetupConnection */);
-        Assert.assertEquals(2, getConnectedSandboxedServicesCount());
+                otherCreationParams, DONT_BLOCK, false /* doSetupConnection */);
         Assert.assertNotNull(getWarmUpConnection());
 
         // Then start a process with the default creation params, the warmup-connection should be
         // used.
         startSandboxedChildProcessWithCreationParams(
                 defaultCreationParams, BLOCK_UNTIL_SETUP, true /* doSetupConnection */);
-        Assert.assertEquals(2, getConnectedSandboxedServicesCount());
         Assert.assertNull(getWarmUpConnection());
     }
 
diff --git a/content/public/android/junit/src/org/chromium/content/browser/BindingManagerImplTest.java b/content/public/android/junit/src/org/chromium/content/browser/BindingManagerImplTest.java
index 84ca0c3..2711464 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/BindingManagerImplTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/BindingManagerImplTest.java
@@ -37,77 +37,18 @@
 @RunWith(LocalRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BindingManagerImplTest {
-    private static class MockChildServiceConnection
-            implements ChildProcessConnection.ChildServiceConnection {
-        private boolean mBound;
-
-        @Override
-        public boolean bind() {
-            mBound = true;
-            return true;
-        }
-
-        @Override
-        public void unbind() {
-            mBound = false;
-        }
-
-        @Override
-        public boolean isBound() {
-            return mBound;
-        }
-    }
-
-    private static class TestChildProcessConnection extends ChildProcessConnection {
-        private final int mPid;
-        private boolean mConnected;
-
-        /**
-         * Creates a mock binding corresponding to real ManagedChildProcessConnection after the
-         * connection is established: with initial binding bound and no strong binding.
-         */
-        private TestChildProcessConnection(int pid) {
-            super(null /* context */, new ComponentName("org.chromium.test", "TestService"),
-                    false /* isExternalService */, null /* childProcessCommonParameters */,
-                    new ChildProcessCreationParams("org.chromium.test",
-                            false /* isExternalService */, 0 /* libraryProcessType */,
-                            false /* bindToCallerCheck */));
-            mPid = pid;
-        }
-
-        @Override
-        public int getPid() {
-            return mPid;
-        }
-
-        @Override
-        protected ChildServiceConnection createServiceConnection(int bindFlags) {
-            return new MockChildServiceConnection();
-        }
-
-        // We don't have a real service so we have to mock the connection status.
-        @Override
-        public void start(boolean useStrongBinding, ServiceCallback serviceCallback) {
-            super.start(useStrongBinding, serviceCallback);
-            mConnected = true;
-        }
-
-        @Override
-        public void stop() {
-            super.stop();
-            mConnected = false;
-        }
-
-        @Override
-        public boolean isConnected() {
-            return mConnected;
-        }
-    }
 
     // Creates a mocked ChildProcessConnection that is optionally added to a BindingManager.
     private static ChildProcessConnection createTestChildProcessConnection(
             int pid, BindingManager manager) {
-        ChildProcessConnection connection = new TestChildProcessConnection(pid);
+        String packageName = "org.chromium.test";
+        ChildProcessCreationParams creationParams =
+                new ChildProcessCreationParams(packageName, false /* isExternalService */,
+                        0 /* libraryProcessType */, false /* bindToCallerCheck */);
+        TestChildProcessConnection connection = new TestChildProcessConnection(
+                new ComponentName(packageName, "TestService"), false /* bindAsExternalService */,
+                null /* serviceBundle */, creationParams);
+        connection.setPid(pid);
         connection.start(false /* useStrongBinding */, null /* serviceCallback */);
         if (manager != null) {
             manager.addNewConnection(pid, connection);
diff --git a/content/public/android/junit/src/org/chromium/content/browser/SpareChildConnectionTest.java b/content/public/android/junit/src/org/chromium/content/browser/SpareChildConnectionTest.java
index 7d92b395..1779db5 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/SpareChildConnectionTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/SpareChildConnectionTest.java
@@ -4,12 +4,13 @@
 
 package org.chromium.content.browser;
 
+import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -33,33 +34,44 @@
     @Mock
     private ChildProcessConnection.ServiceCallback mServiceCallback;
 
-    static class TestConnectionFactory implements SpareChildConnection.ConnectionFactory {
-        private ChildProcessConnection mConnection;
-        private ChildProcessConnection.ServiceCallback mServiceCallback;
+    // A connection allocator not used to create connections.
+    private final ChildConnectionAllocator mWrongConnectionAllocator =
+            ChildConnectionAllocator.createForTest(null /* creationParams */, "org.chromium.test",
+                    "TestServiceName", 3 /* serviceCount */, false /* bindAsExternalService */,
+                    false /* useStrongBinding */);
+
+    // The allocator used to allocate the actual connection.
+    private ChildConnectionAllocator mConnectionAllocator;
+
+    private static class TestConnectionFactory
+            implements ChildConnectionAllocator.ConnectionFactory {
+        private TestChildProcessConnection mConnection;
 
         @Override
-        public ChildProcessConnection allocateBoundConnection(Context context,
-                ChildProcessCreationParams creationParams,
-                ChildProcessConnection.ServiceCallback serviceCallback) {
-            mConnection = mock(ChildProcessConnection.class);
-            mServiceCallback = serviceCallback;
+        public ChildProcessConnection createConnection(Context context, ComponentName serviceName,
+                boolean bindAsExternalService, Bundle serviceBundle,
+                ChildProcessCreationParams creationParams) {
+            // We expect to create only one connection in these tests.
+            assert mConnection == null;
+            mConnection = new TestChildProcessConnection(
+                    serviceName, bindAsExternalService, serviceBundle, creationParams);
             return mConnection;
         }
 
         public void simulateConnectionBindingSuccessfully() {
-            mServiceCallback.onChildStarted();
+            mConnection.getServiceCallback().onChildStarted();
         }
 
         public void simulateConnectionFailingToBind() {
-            mServiceCallback.onChildStartFailed();
+            mConnection.getServiceCallback().onChildStartFailed();
         }
 
         public void simulateConnectionDied() {
-            mServiceCallback.onChildProcessDied(mConnection);
+            mConnection.getServiceCallback().onChildProcessDied(mConnection);
         }
-    }
+    };
 
-    private final TestConnectionFactory mTestConnectionfactory = new TestConnectionFactory();
+    private final TestConnectionFactory mTestConnectionFactory = new TestConnectionFactory();
 
     // For some reason creating ChildProcessCreationParams from a static context makes the launcher
     // unhappy. (some Dalvik native library is not found when initializing a SparseArray)
@@ -77,8 +89,13 @@
         // asserts are not triggered.
         LauncherThread.setCurrentThreadAsLauncherThread();
 
+        mConnectionAllocator = ChildConnectionAllocator.createForTest(mCreationParams,
+                mCreationParams.getPackageNameForSandboxedService(), "TestServiceName",
+                5 /* serviceCount */, false /* bindAsExternalService */,
+                false /* useStrongBinding */);
+        mConnectionAllocator.setConnectionFactoryForTesting(mTestConnectionFactory);
         mSpareConnection = new SpareChildConnection(
-                null /* context */, mTestConnectionfactory, mCreationParams, true /* sandboxed */);
+                null /* context */, mConnectionAllocator, null /* serviceBundle */);
     }
 
     @After
@@ -90,33 +107,28 @@
     @Test
     @Feature({"ProcessManagement"})
     public void testCreateAndGet() {
-        // Tests retrieving the connection with the wrong parameters.
-        ChildProcessConnection connection = mSpareConnection.getConnection(
-                null /* creationParams */, true /* sandboxed */, mServiceCallback);
-        assertNull(connection);
-        connection = mSpareConnection.getConnection(
-                mCreationParams, false /* sandboxed */, mServiceCallback);
+        // Tests retrieving the connection with the wrong allocator.
+        ChildProcessConnection connection =
+                mSpareConnection.getConnection(mWrongConnectionAllocator, mServiceCallback);
         assertNull(connection);
 
         // And with the right one.
-        connection = mSpareConnection.getConnection(
-                mCreationParams, true /* sandboxed */, mServiceCallback);
+        connection = mSpareConnection.getConnection(mConnectionAllocator, mServiceCallback);
         assertNotNull(connection);
 
         // The connection has been used, subsequent calls should return null.
-        connection = mSpareConnection.getConnection(
-                mCreationParams, true /* sandboxed */, mServiceCallback);
+        connection = mSpareConnection.getConnection(mConnectionAllocator, mServiceCallback);
         assertNull(connection);
     }
 
     @Test
     @Feature({"ProcessManagement"})
     public void testCallbackNotCalledWhenNoConnection() {
-        mTestConnectionfactory.simulateConnectionBindingSuccessfully();
+        mTestConnectionFactory.simulateConnectionBindingSuccessfully();
 
         // Retrieve the wrong connection, no callback should be fired.
-        ChildProcessConnection connection = mSpareConnection.getConnection(
-                null /* creationParams */, true /* sandboxed */, mServiceCallback);
+        ChildProcessConnection connection =
+                mSpareConnection.getConnection(mWrongConnectionAllocator, mServiceCallback);
         assertNull(connection);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         verify(mServiceCallback, times(0)).onChildStarted();
@@ -127,11 +139,11 @@
     @Test
     @Feature({"ProcessManagement"})
     public void testCallbackCalledConnectionReady() {
-        mTestConnectionfactory.simulateConnectionBindingSuccessfully();
+        mTestConnectionFactory.simulateConnectionBindingSuccessfully();
 
         // Now retrieve the connection, the callback should be invoked.
-        ChildProcessConnection connection = mSpareConnection.getConnection(
-                mCreationParams, true /* sandboxed */, mServiceCallback);
+        ChildProcessConnection connection =
+                mSpareConnection.getConnection(mConnectionAllocator, mServiceCallback);
         assertNotNull(connection);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         verify(mServiceCallback, times(1)).onChildStarted();
@@ -142,8 +154,8 @@
     @Feature({"ProcessManagement"})
     public void testCallbackCalledConnectionNotReady() {
         // Retrieve the connection before it's bound.
-        ChildProcessConnection connection = mSpareConnection.getConnection(
-                mCreationParams, true /* sandboxed */, mServiceCallback);
+        ChildProcessConnection connection =
+                mSpareConnection.getConnection(mConnectionAllocator, mServiceCallback);
         assertNotNull(connection);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         // No callbacks are called.
@@ -151,7 +163,7 @@
         verify(mServiceCallback, times(0)).onChildStartFailed();
 
         // Simulate the connection getting bound, it should trigger the callback.
-        mTestConnectionfactory.simulateConnectionBindingSuccessfully();
+        mTestConnectionFactory.simulateConnectionBindingSuccessfully();
         verify(mServiceCallback, times(1)).onChildStarted();
         verify(mServiceCallback, times(0)).onChildStartFailed();
     }
@@ -159,11 +171,11 @@
     @Test
     @Feature({"ProcessManagement"})
     public void testUnretrievedConnectionFailsToBind() {
-        mTestConnectionfactory.simulateConnectionFailingToBind();
+        mTestConnectionFactory.simulateConnectionFailingToBind();
 
         // We should not have a spare connection.
-        ChildProcessConnection connection = mSpareConnection.getConnection(
-                mCreationParams, true /* sandboxed */, mServiceCallback);
+        ChildProcessConnection connection =
+                mSpareConnection.getConnection(mConnectionAllocator, mServiceCallback);
         assertNull(connection);
     }
 
@@ -171,11 +183,11 @@
     @Feature({"ProcessManagement"})
     public void testRetrievedConnectionFailsToBind() {
         // Retrieve the spare connection before it's bound.
-        ChildProcessConnection connection = mSpareConnection.getConnection(
-                mCreationParams, true /* sandboxed */, mServiceCallback);
+        ChildProcessConnection connection =
+                mSpareConnection.getConnection(mConnectionAllocator, mServiceCallback);
         assertNotNull(connection);
 
-        mTestConnectionfactory.simulateConnectionFailingToBind();
+        mTestConnectionFactory.simulateConnectionFailingToBind();
 
         // We should get a failure callback.
         verify(mServiceCallback, times(0)).onChildStarted();
@@ -186,11 +198,11 @@
     @Feature({"ProcessManagement"})
     public void testRetrievedConnectionStops() {
         // Retrieve the spare connection before it's bound.
-        ChildProcessConnection connection = mSpareConnection.getConnection(
-                mCreationParams, true /* sandboxed */, mServiceCallback);
+        ChildProcessConnection connection =
+                mSpareConnection.getConnection(mConnectionAllocator, mServiceCallback);
         assertNotNull(connection);
 
-        mTestConnectionfactory.simulateConnectionDied();
+        mTestConnectionFactory.simulateConnectionDied();
 
         // We should get a failure callback.
         verify(mServiceCallback, times(0)).onChildStarted();
@@ -202,11 +214,11 @@
     @Feature({"ProcessManagement"})
     public void testConnectionFreeing() {
         // Simulate the connection dying.
-        mTestConnectionfactory.simulateConnectionDied();
+        mTestConnectionFactory.simulateConnectionDied();
 
         // Connection should be gone.
-        ChildProcessConnection connection = mSpareConnection.getConnection(
-                mCreationParams, true /* sandboxed */, mServiceCallback);
+        ChildProcessConnection connection =
+                mSpareConnection.getConnection(mConnectionAllocator, mServiceCallback);
         assertNull(connection);
     }
 }
diff --git a/content/public/android/junit/src/org/chromium/content/browser/TestChildProcessConnection.java b/content/public/android/junit/src/org/chromium/content/browser/TestChildProcessConnection.java
new file mode 100644
index 0000000..94f8dd8
--- /dev/null
+++ b/content/public/android/junit/src/org/chromium/content/browser/TestChildProcessConnection.java
@@ -0,0 +1,85 @@
+// 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.content.browser;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+
+import org.chromium.base.process_launcher.ChildProcessCreationParams;
+
+/** An implementation of ChildProcessConnection that does not connect to a real service. */
+class TestChildProcessConnection extends ChildProcessConnection {
+    private static class MockChildServiceConnection
+            implements ChildProcessConnection.ChildServiceConnection {
+        private boolean mBound;
+
+        @Override
+        public boolean bind() {
+            mBound = true;
+            return true;
+        }
+
+        @Override
+        public void unbind() {
+            mBound = false;
+        }
+
+        @Override
+        public boolean isBound() {
+            return mBound;
+        }
+    }
+
+    private int mPid;
+    private boolean mConnected;
+    private ServiceCallback mServiceCallback;
+
+    /**
+     * Creates a mock binding corresponding to real ManagedChildProcessConnection after the
+     * connection is established: with initial binding bound and no strong binding.
+     */
+    TestChildProcessConnection(ComponentName serviceName, boolean bindAsExternalService,
+            Bundle serviceBundle, ChildProcessCreationParams creationParams) {
+        super(null /* context */, serviceName, bindAsExternalService, serviceBundle,
+                creationParams);
+    }
+
+    public void setPid(int pid) {
+        mPid = pid;
+    }
+
+    @Override
+    public int getPid() {
+        return mPid;
+    }
+
+    @Override
+    protected ChildServiceConnection createServiceConnection(int bindFlags) {
+        return new MockChildServiceConnection();
+    }
+
+    // We don't have a real service so we have to mock the connection status.
+    @Override
+    public void start(boolean useStrongBinding, ServiceCallback serviceCallback) {
+        super.start(useStrongBinding, serviceCallback);
+        mConnected = true;
+        mServiceCallback = serviceCallback;
+    }
+
+    @Override
+    public void stop() {
+        super.stop();
+        mConnected = false;
+    }
+
+    @Override
+    public boolean isConnected() {
+        return mConnected;
+    }
+
+    public ServiceCallback getServiceCallback() {
+        return mServiceCallback;
+    }
+}
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index 7c8f196..059bd19 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -261,6 +261,17 @@
     serializer_.DeleteClientSubtree(obj);
 #endif
 
+  // If some cell IDs have been added or removed, we need to update the whole
+  // table.
+  if (obj.Role() == blink::kWebAXRoleRow &&
+      event == ui::AX_EVENT_CHILDREN_CHANGED) {
+    WebAXObject table_like_object = obj.ParentObject();
+    if (!table_like_object.IsDetached()) {
+      serializer_.DeleteClientSubtree(table_like_object);
+      HandleAXEvent(table_like_object, ui::AX_EVENT_CHILDREN_CHANGED);
+    }
+  }
+
   // Add the accessibility object to our cache and ensure it's valid.
   AccessibilityHostMsg_EventParams acc_event;
   acc_event.id = obj.AxID();
diff --git a/content/shell/browser/layout_test/blink_test_controller.cc b/content/shell/browser/layout_test/blink_test_controller.cc
index 5b61ab4..03ed2f2 100644
--- a/content/shell/browser/layout_test/blink_test_controller.cc
+++ b/content/shell/browser/layout_test/blink_test_controller.cc
@@ -526,19 +526,6 @@
   return handled;
 }
 
-bool BlinkTestController::OnMessageReceived(
-    const IPC::Message& message,
-    RenderFrameHost* render_frame_host) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(BlinkTestController, message,
-                                   render_frame_host)
-    IPC_MESSAGE_HANDLER(ShellViewHostMsg_LayoutDumpResponse,
-                        OnLayoutDumpResponse)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
 void BlinkTestController::PluginCrashed(const base::FilePath& plugin_path,
                                         base::ProcessId plugin_pid) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -791,7 +778,9 @@
       continue;
 
     ++number_of_messages;
-    GetLayoutTestControlPtr(rfh)->LayoutDumpRequest();
+    GetLayoutTestControlPtr(rfh)->DumpFrameLayout(
+        base::Bind(&BlinkTestController::OnDumpFrameLayoutResponse,
+                   base::Unretained(this), rfh->GetFrameTreeNodeId()));
   }
 
   pending_layout_dumps_ = number_of_messages;
@@ -817,11 +806,11 @@
   }
 }
 
-void BlinkTestController::OnLayoutDumpResponse(RenderFrameHost* sender,
-                                               const std::string& dump) {
+void BlinkTestController::OnDumpFrameLayoutResponse(int frame_tree_node_id,
+                                                    const std::string& dump) {
   // Store the result.
   auto pair = frame_to_layout_dump_map_.insert(
-      std::make_pair(sender->GetFrameTreeNodeId(), dump));
+      std::make_pair(frame_tree_node_id, dump));
   bool insertion_took_place = pair.second;
   DCHECK(insertion_took_place);
 
diff --git a/content/shell/browser/layout_test/blink_test_controller.h b/content/shell/browser/layout_test/blink_test_controller.h
index e854c88..5bdc5900 100644
--- a/content/shell/browser/layout_test/blink_test_controller.h
+++ b/content/shell/browser/layout_test/blink_test_controller.h
@@ -159,8 +159,6 @@
 
   // WebContentsObserver implementation.
   bool OnMessageReceived(const IPC::Message& message) override;
-  bool OnMessageReceived(const IPC::Message& message,
-                         RenderFrameHost* render_frame_host) override;
   void PluginCrashed(const base::FilePath& plugin_path,
                      base::ProcessId plugin_pid) override;
   void RenderFrameCreated(RenderFrameHost* render_frame_host) override;
@@ -204,7 +202,8 @@
   void OnImageDump(const std::string& actual_pixel_hash, const SkBitmap& image);
   void OnTextDump(const std::string& dump);
   void OnInitiateLayoutDump();
-  void OnLayoutDumpResponse(RenderFrameHost* sender, const std::string& dump);
+  void OnDumpFrameLayoutResponse(int frame_tree_node_id,
+                                 const std::string& dump);
   void OnPrintMessageToStderr(const std::string& message);
   void OnPrintMessage(const std::string& message);
   void OnOverridePreferences(const WebPreferences& prefs);
@@ -283,7 +282,7 @@
 
   // Map from frame_tree_node_id into frame-specific dumps.
   std::map<int, std::string> frame_to_layout_dump_map_;
-  // Number of ShellViewHostMsg_LayoutDumpResponse messages we are waiting for.
+  // Number of LayoutTestControl.DumpFrameLayout responses we are waiting for.
   int pending_layout_dumps_;
 
   // Renderer processes are observed to detect crashes.
diff --git a/content/shell/common/layout_test.mojom b/content/shell/common/layout_test.mojom
index dd61553..19146dd 100644
--- a/content/shell/common/layout_test.mojom
+++ b/content/shell/common/layout_test.mojom
@@ -35,9 +35,8 @@
 };
 
 interface LayoutTestControl {
-  // Asks a frame to dump its contents into a string and send them back over
-  // IPC.
-  LayoutDumpRequest();
+  // Dumps the frame's contents into a string.
+  DumpFrameLayout() => (string frame_layout_dump);
 
   // Replicates test config (for an already started test) to a new renderer
   // that hosts parts of the main test window.
diff --git a/content/shell/common/shell_content_client.cc b/content/shell/common/shell_content_client.cc
index 7e06a28..3cc7fd0 100644
--- a/content/shell/common/shell_content_client.cc
+++ b/content/shell/common/shell_content_client.cc
@@ -43,8 +43,6 @@
         return base::ASCIIToUTF16("<<OtherDateLabel>>");
       case IDS_FORM_OTHER_MONTH_LABEL:
         return base::ASCIIToUTF16("<<OtherMonthLabel>>");
-      case IDS_FORM_OTHER_TIME_LABEL:
-        return base::ASCIIToUTF16("<<OtherTimeLabel>>");
       case IDS_FORM_OTHER_WEEK_LABEL:
         return base::ASCIIToUTF16("<<OtherWeekLabel>>");
       case IDS_FORM_CALENDAR_CLEAR:
diff --git a/content/shell/common/shell_messages.h b/content/shell/common/shell_messages.h
index 275c96f..db671cd 100644
--- a/content/shell/common/shell_messages.h
+++ b/content/shell/common/shell_messages.h
@@ -51,14 +51,11 @@
                     std::string /* dump */)
 
 // Asks the browser process to perform a layout dump spanning all the
-// (potentially cross-process) frames.  This triggers multiple
-// ShellViewMsg_LayoutDumpRequest / ShellViewHostMsg_LayoutDumpResponse messages
-// and ends with sending of ShellViewMsg_LayoutDumpCompleted.
+// (potentially cross-process) frames.  This goes through multiple
+// LayoutTestControl.DumpFrameLayout calls and ends with sending of
+// ShellViewMsg_LayoutDumpCompleted.
 IPC_MESSAGE_ROUTED0(ShellViewHostMsg_InitiateLayoutDump)
 
-// Sends a layout dump of a frame (response to ShellViewMsg_LayoutDumpRequest).
-IPC_MESSAGE_ROUTED1(ShellViewHostMsg_LayoutDumpResponse, std::string /* dump */)
-
 // Send an image dump of the WebContents to the render host.
 IPC_MESSAGE_ROUTED2(ShellViewHostMsg_ImageDump,
                     std::string /* actual pixel hash */,
diff --git a/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc b/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
index e10cca7d..b4193023 100644
--- a/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
+++ b/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
@@ -8,12 +8,10 @@
 
 #include "content/public/common/associated_interface_registry.h"
 #include "content/public/renderer/render_frame.h"
-#include "content/shell/common/shell_messages.h"
 #include "content/shell/renderer/layout_test/blink_test_runner.h"
 #include "content/shell/renderer/layout_test/layout_test_render_thread_observer.h"
 #include "content/shell/test_runner/web_test_interfaces.h"
 #include "content/shell/test_runner/web_test_runner.h"
-#include "ipc/ipc_message_macros.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 
 namespace content {
@@ -44,13 +42,14 @@
   delete this;
 }
 
-void LayoutTestRenderFrameObserver::LayoutDumpRequest() {
+void LayoutTestRenderFrameObserver::DumpFrameLayout(
+    DumpFrameLayoutCallback callback) {
   std::string dump =
       LayoutTestRenderThreadObserver::GetInstance()
           ->test_interfaces()
           ->TestRunner()
           ->DumpLayout(render_frame()->GetWebFrame());
-  Send(new ShellViewHostMsg_LayoutDumpResponse(routing_id(), dump));
+  std::move(callback).Run(dump);
 }
 
 void LayoutTestRenderFrameObserver::ReplicateTestConfiguration(
diff --git a/content/shell/renderer/layout_test/layout_test_render_frame_observer.h b/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
index bd620c08..cc86135e 100644
--- a/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
+++ b/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
@@ -22,7 +22,7 @@
   // RenderFrameObserver implementation.
   void OnDestruct() override;
 
-  void LayoutDumpRequest() override;
+  void DumpFrameLayout(DumpFrameLayoutCallback callback) override;
   void SetTestConfiguration(mojom::ShellTestConfigurationPtr config) override;
   void ReplicateTestConfiguration(
       mojom::ShellTestConfigurationPtr config) override;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 6963f2b..fe4acf6 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1420,7 +1420,6 @@
     "../common/service_worker/service_worker_utils_unittest.cc",
     "../common/throttling_url_loader_unittest.cc",
     "../common/webplugininfo_unittest.cc",
-    "../network/network_service_unittest.cc",
     "../network/url_loader_unittest.cc",
     "../public/test/referrer_unittest.cc",
     "../public/test/test_browser_thread_bundle_unittest.cc",
diff --git a/content/test/data/accessibility/aria/table-column-hidden-expected-blink.txt b/content/test/data/accessibility/aria/table-column-hidden-expected-blink.txt
new file mode 100644
index 0000000..d9de919
--- /dev/null
+++ b/content/test/data/accessibility/aria/table-column-hidden-expected-blink.txt
@@ -0,0 +1,39 @@
+rootWebArea
+++grid ariaColumnCount=4 tableRowCount=3 tableColumnCount=3 cellIds=columnHeader,columnHeader,columnHeader,cell,cell,cell,cell,cell,cell
+++++row
+++++++columnHeader name='Month' ariaCellColumnIndex=1 ariaCellRowIndex=1
+++++++++staticText name='Month'
+++++++++++inlineTextBox name='Month'
+++++++columnHeader name='Day' ariaCellColumnIndex=2 ariaCellRowIndex=1
+++++++++staticText name='Day'
+++++++++++inlineTextBox name='Day'
+++++++columnHeader name='Weather' ariaCellColumnIndex=4 ariaCellRowIndex=1
+++++++++staticText name='Weather'
+++++++++++inlineTextBox name='Weather'
+++++row
+++++++cell selectable name='January' ariaCellColumnIndex=1 ariaCellRowIndex=2
+++++++++staticText name='January'
+++++++++++inlineTextBox name='January'
+++++++cell selectable name='01' ariaCellColumnIndex=2 ariaCellRowIndex=2
+++++++++staticText name='01'
+++++++++++inlineTextBox name='01'
+++++++cell selectable name='Sunny' ariaCellColumnIndex=4 ariaCellRowIndex=2
+++++++++staticText name='Sunny'
+++++++++++inlineTextBox name='Sunny'
+++++row
+++++++cell selectable name='January' ariaCellColumnIndex=1 ariaCellRowIndex=2
+++++++++staticText name='January'
+++++++++++inlineTextBox name='January'
+++++++cell selectable name='02' ariaCellColumnIndex=2 ariaCellRowIndex=2
+++++++++staticText name='02'
+++++++++++inlineTextBox name='02'
+++++++cell selectable name='Rainy' ariaCellColumnIndex=4 ariaCellRowIndex=2
+++++++++staticText name='Rainy'
+++++++++++inlineTextBox name='Rainy'
+++++column
+++++column
+++++column
+++++tableHeaderContainer
+++paragraph
+++++staticText name='done'
+++++++inlineTextBox name='done'
diff --git a/content/test/data/accessibility/aria/table-column-hidden-expected-mac.txt b/content/test/data/accessibility/aria/table-column-hidden-expected-mac.txt
new file mode 100644
index 0000000..073f663
--- /dev/null
+++ b/content/test/data/accessibility/aria/table-column-hidden-expected-mac.txt
@@ -0,0 +1,29 @@
+AXWebArea
+++AXGrid AXARIAColumnCount='4'
+++++AXRow
+++++++AXCell AXTitle='Month' AXARIAColumnIndex='1' AXARIARowIndex='1'
+++++++++AXStaticText AXValue='Month'
+++++++AXCell AXTitle='Day' AXARIAColumnIndex='2' AXARIARowIndex='1'
+++++++++AXStaticText AXValue='Day'
+++++++AXCell AXTitle='Weather' AXARIAColumnIndex='4' AXARIARowIndex='1'
+++++++++AXStaticText AXValue='Weather'
+++++AXRow
+++++++AXCell AXTitle='January' AXARIAColumnIndex='1' AXARIARowIndex='2'
+++++++++AXStaticText AXValue='January'
+++++++AXCell AXTitle='01' AXARIAColumnIndex='2' AXARIARowIndex='2'
+++++++++AXStaticText AXValue='01'
+++++++AXCell AXTitle='Sunny' AXARIAColumnIndex='4' AXARIARowIndex='2'
+++++++++AXStaticText AXValue='Sunny'
+++++AXRow
+++++++AXCell AXTitle='January' AXARIAColumnIndex='1' AXARIARowIndex='2'
+++++++++AXStaticText AXValue='January'
+++++++AXCell AXTitle='02' AXARIAColumnIndex='2' AXARIARowIndex='2'
+++++++++AXStaticText AXValue='02'
+++++++AXCell AXTitle='Rainy' AXARIAColumnIndex='4' AXARIARowIndex='2'
+++++++++AXStaticText AXValue='Rainy'
+++++AXColumn
+++++AXColumn
+++++AXColumn
+++++AXGroup
+++AXGroup
+++++AXStaticText AXValue='done'
diff --git a/content/test/data/accessibility/aria/table-column-hidden-expected-win.txt b/content/test/data/accessibility/aria/table-column-hidden-expected-win.txt
new file mode 100644
index 0000000..6a5ec4bc
--- /dev/null
+++ b/content/test/data/accessibility/aria/table-column-hidden-expected-win.txt
@@ -0,0 +1,29 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++ROLE_SYSTEM_TABLE colcount:4
+++++ROLE_SYSTEM_ROW
+++++++ROLE_SYSTEM_COLUMNHEADER name='Month' colindex:1 rowindex:1
+++++++++ROLE_SYSTEM_STATICTEXT name='Month'
+++++++ROLE_SYSTEM_COLUMNHEADER name='Day' colindex:2 rowindex:1
+++++++++ROLE_SYSTEM_STATICTEXT name='Day'
+++++++ROLE_SYSTEM_COLUMNHEADER name='Weather' colindex:4 rowindex:1
+++++++++ROLE_SYSTEM_STATICTEXT name='Weather'
+++++ROLE_SYSTEM_ROW
+++++++ROLE_SYSTEM_CELL name='January' FOCUSABLE colindex:1 rowindex:2
+++++++++ROLE_SYSTEM_STATICTEXT name='January'
+++++++ROLE_SYSTEM_CELL name='01' FOCUSABLE colindex:2 rowindex:2
+++++++++ROLE_SYSTEM_STATICTEXT name='01'
+++++++ROLE_SYSTEM_CELL name='Sunny' FOCUSABLE colindex:4 rowindex:2
+++++++++ROLE_SYSTEM_STATICTEXT name='Sunny'
+++++ROLE_SYSTEM_ROW
+++++++ROLE_SYSTEM_CELL name='January' FOCUSABLE colindex:1 rowindex:2
+++++++++ROLE_SYSTEM_STATICTEXT name='January'
+++++++ROLE_SYSTEM_CELL name='02' FOCUSABLE colindex:2 rowindex:2
+++++++++ROLE_SYSTEM_STATICTEXT name='02'
+++++++ROLE_SYSTEM_CELL name='Rainy' FOCUSABLE colindex:4 rowindex:2
+++++++++ROLE_SYSTEM_STATICTEXT name='Rainy'
+++++ROLE_SYSTEM_COLUMN
+++++ROLE_SYSTEM_COLUMN
+++++ROLE_SYSTEM_COLUMN
+++++IA2_ROLE_SECTION
+++IA2_ROLE_PARAGRAPH
+++++ROLE_SYSTEM_STATICTEXT name='done'
diff --git a/content/test/data/accessibility/aria/table-column-hidden.html b/content/test/data/accessibility/aria/table-column-hidden.html
new file mode 100644
index 0000000..85802a1
--- /dev/null
+++ b/content/test/data/accessibility/aria/table-column-hidden.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!--
+@WAIT-FOR:done
+@WIN-ALLOW:colcount*
+@WIN-ALLOW:colindex*
+@WIN-ALLOW:rowcount*
+@WIN-ALLOW:rowindex*
+@MAC-ALLOW:AXARIAColumn*
+@MAC-ALLOW:AXARIARow*
+@BLINK-ALLOW:cellIds*
+@BLINK-ALLOW:*ColumnCount*
+@BLINK-ALLOW:ariaCellColumnIndex*
+@BLINK-ALLOW:*RowCount*
+@BLINK-ALLOW:ariaCellRowIndex*
+-->
+<table role="grid" aria-rowcount="3" aria-colcount="4">
+  <tbody><tr>
+    <th aria-rowindex="1" aria-colindex="1">Month</th>
+    <th aria-rowindex="1" aria-colindex="2">Day</th>
+    <th aria-rowindex="1" aria-colindex="3">Year</th>
+    <th aria-rowindex="1" aria-colindex="4">Weather</th>
+  </tr>
+  <tr>
+    <td role="gridcell" tabindex="0" aria-rowindex="2" aria-colindex="1">January</td>
+    <td role="gridcell" tabindex="-1" aria-rowindex="2" aria-colindex="2">01</td>
+    <td role="gridcell" tabindex="-1" aria-rowindex="2" aria-colindex="3">2017</td>
+    <td role="gridcell" tabindex="-1" aria-rowindex="2" aria-colindex="4">Sunny</td>
+  </tr>
+  <tr>
+    <td role="gridcell" tabindex="0" aria-rowindex="2" aria-colindex="1">January</td>
+    <td role="gridcell" tabindex="-1" aria-rowindex="2" aria-colindex="2">02</td>
+    <td role="gridcell" tabindex="-1" aria-rowindex="2" aria-colindex="3">2017</td>
+    <td role="gridcell" tabindex="-1" aria-rowindex="2" aria-colindex="4">Rainy</td>
+  </tr>
+  </tbody>
+</table>
+<p></p>
+
+<script>
+  // Hide the year column.
+  let cells = document.querySelectorAll('[aria-colindex="3"]');
+  for (let cell of cells) {
+    cell.style.display = 'none';
+  }
+  document.querySelector('p').textContent = 'done';
+</script>
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 636209c1..a06bce8 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -259,12 +259,6 @@
     self.Flaky('conformance/canvas/canvas-test.html',
         ['win7', 'nvidia', 'd3d9'], bug=690248)
 
-    # Win / AMD flakiness seen on new tryservers.
-    # It's unfortunate that this suppression needs to be so broad, but
-    # basically any test that uses readPixels is potentially flaky, and
-    # it's infeasible to suppress individual failures one by one.
-    self.Flaky('conformance/*', ['win', ('amd', 0x6779)], bug=491419)
-
     # Win AMD failures
     # This test is probably flaky on all AMD, but only visible on the
     # new AMD (the whole test suite is flaky on the old config).
@@ -314,12 +308,6 @@
     # Win / OpenGL / AMD failures
     self.Skip('conformance/attribs/gl-bindAttribLocation-aliasing.html',
         ['win', 'amd', 'opengl'], bug=649824)
-    self.Flaky('conformance/attribs/gl-bindAttribLocation-matrix.html',
-        ['win', ('amd', 0x6779), 'opengl'], bug=649824)
-    self.Flaky('conformance/attribs/gl-bindAttribLocation-repeated.html',
-        ['win', ('amd', 0x6779), 'opengl'], bug=649824)
-    self.Fail('conformance/extensions/webgl-draw-buffers.html',
-        ['win', ('amd', 0x6779), 'opengl', 'no_passthrough'], bug=649824)
     self.Skip('conformance/glsl/misc/shader-struct-scope.html',
         ['win', 'amd', 'opengl'], bug=1007) # angle bug ID
     self.Skip('conformance/glsl/misc/shaders-with-invariance.html',
@@ -446,30 +434,8 @@
     # AMD Radeon 6450 and/or R7 240
     self.Fail('conformance/extensions/angle-instanced-arrays.html',
         ['linux', 'amd', 'no_angle'], bug=479260)
-    self.Flaky('conformance/extensions/ext-texture-filter-anisotropic.html',
-        ['linux', ('amd', 0x6779)], bug=436212)
-    self.Flaky('conformance/glsl/misc/shader-struct-scope.html',
-        ['linux', ('amd', 0x6779), 'no_passthrough'], bug=436212)
-    self.Flaky('conformance/glsl/misc/struct-nesting-of-variable-names.html',
-        ['linux', ('amd', 0x6779), 'no_passthrough'], bug=436212)
-    self.Flaky('conformance/rendering/point-size.html',
-        ['linux', ('amd', 0x6779)], bug=436212)
-    self.Flaky('conformance/textures/misc/texture-sub-image-cube-maps.html',
-        ['linux', ('amd', 0x6779)], bug=436212)
-    self.Flaky('conformance/more/functions/uniformf.html',
-        ['linux', ('amd', 0x6779)], bug=436212)
     self.Fail('conformance/glsl/misc/shaders-with-invariance.html',
         ['linux', 'amd', 'no_passthrough'], bug=479952)
-    self.Flaky('conformance/textures/misc/texture-mips.html',
-        ['linux', ('amd', 0x6779), 'no_passthrough'], bug=479981)
-    self.Flaky('conformance/textures/misc/texture-size-cube-maps.html',
-        ['linux', ('amd', 0x6779)], bug=479983)
-    self.Flaky('conformance/uniforms/uniform-default-values.html',
-        ['linux', ('amd', 0x6779)], bug=482013)
-    self.Flaky('conformance/glsl/samplers/glsl-function-texture2dlod.html',
-        ['linux', ('amd', 0x6779)], bug=436212)
-    self.Flaky('conformance/glsl/samplers/glsl-function-texture2dprojlod.html',
-        ['linux', ('amd', 0x6779)], bug=436212)
     # Intel
     # See https://bugs.freedesktop.org/show_bug.cgi?id=94477
     self.Skip('conformance/glsl/bugs/temp-expressions-should-not-crash.html',
diff --git a/content/test/test_blink_web_unit_test_support.cc b/content/test/test_blink_web_unit_test_support.cc
index 7150daf1..c8e4a28 100644
--- a/content/test/test_blink_web_unit_test_support.cc
+++ b/content/test/test_blink_web_unit_test_support.cc
@@ -214,8 +214,6 @@
       return WebString::FromASCII("<<OtherDateLabel>>");
     case blink::WebLocalizedString::kOtherMonthLabel:
       return WebString::FromASCII("<<OtherMonthLabel>>");
-    case blink::WebLocalizedString::kOtherTimeLabel:
-      return WebString::FromASCII("<<OtherTimeLabel>>");
     case blink::WebLocalizedString::kOtherWeekLabel:
       return WebString::FromASCII("<<OtherWeekLabel>>");
     case blink::WebLocalizedString::kCalendarClear:
diff --git a/device/generic_sensor/platform_sensor_provider_base.h b/device/generic_sensor/platform_sensor_provider_base.h
index c38443b..eb5d86c0 100644
--- a/device/generic_sensor/platform_sensor_provider_base.h
+++ b/device/generic_sensor/platform_sensor_provider_base.h
@@ -38,7 +38,7 @@
   // Implementations might want to override this in order to be able
   // to read from sensor files. For example, linux does so.
   virtual void SetFileTaskRunner(
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) {}
+      scoped_refptr<base::SequencedTaskRunner> file_task_runner) {}
 
  protected:
   PlatformSensorProviderBase();
diff --git a/device/generic_sensor/platform_sensor_provider_linux.cc b/device/generic_sensor/platform_sensor_provider_linux.cc
index 4db2a4c..4e296e18 100644
--- a/device/generic_sensor/platform_sensor_provider_linux.cc
+++ b/device/generic_sensor/platform_sensor_provider_linux.cc
@@ -78,7 +78,7 @@
 }
 
 void PlatformSensorProviderLinux::SetFileTaskRunner(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) {
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!file_task_runner_)
     file_task_runner_ = file_task_runner;
@@ -109,7 +109,7 @@
 
 void PlatformSensorProviderLinux::StopPollingThread() {
   DCHECK(file_task_runner_);
-  DCHECK(file_task_runner_->BelongsToCurrentThread());
+  DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
   if (polling_thread_ && polling_thread_->IsRunning())
     polling_thread_->Stop();
 }
@@ -147,7 +147,7 @@
 }
 
 void PlatformSensorProviderLinux::SetFileTaskRunnerForTesting(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+    scoped_refptr<base::SequencedTaskRunner> task_runner) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   file_task_runner_ = std::move(task_runner);
 }
diff --git a/device/generic_sensor/platform_sensor_provider_linux.h b/device/generic_sensor/platform_sensor_provider_linux.h
index 5bde10f..e7e0a08f 100644
--- a/device/generic_sensor/platform_sensor_provider_linux.h
+++ b/device/generic_sensor/platform_sensor_provider_linux.h
@@ -15,7 +15,7 @@
 template <typename T>
 struct DefaultSingletonTraits;
 class Thread;
-}
+}  // namespace base
 
 namespace device {
 
@@ -33,7 +33,7 @@
 
   // Sets task runner for tests.
   void SetFileTaskRunnerForTesting(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
 
  protected:
   ~PlatformSensorProviderLinux() override;
@@ -45,7 +45,7 @@
   void AllSensorsRemoved() override;
 
   void SetFileTaskRunner(
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) override;
+      scoped_refptr<base::SequencedTaskRunner> file_task_runner) override;
 
  private:
   friend struct base::DefaultSingletonTraits<PlatformSensorProviderLinux>;
@@ -112,7 +112,7 @@
   // Browser's file thread task runner passed from renderer. Used by this
   // provider to stop a polling thread and passed to a manager that
   // runs a linux device monitor service on this task runner.
-  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformSensorProviderLinux);
 };
diff --git a/device/generic_sensor/sensor_provider_impl.cc b/device/generic_sensor/sensor_provider_impl.cc
index 3e78e3a1..501c67a 100644
--- a/device/generic_sensor/sensor_provider_impl.cc
+++ b/device/generic_sensor/sensor_provider_impl.cc
@@ -34,7 +34,7 @@
 
 // static
 void SensorProviderImpl::Create(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner,
     mojom::SensorProviderRequest request) {
   PlatformSensorProvider* provider = PlatformSensorProvider::GetInstance();
   if (provider) {
diff --git a/device/generic_sensor/sensor_provider_impl.h b/device/generic_sensor/sensor_provider_impl.h
index 2dd3728b..380fa7bc 100644
--- a/device/generic_sensor/sensor_provider_impl.h
+++ b/device/generic_sensor/sensor_provider_impl.h
@@ -6,7 +6,7 @@
 #define DEVICE_GENERIC_SENSOR_SENSOR_PROVIDER_IMPL_H_
 
 #include "base/macros.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
 #include "device/generic_sensor/generic_sensor_export.h"
 #include "device/generic_sensor/public/interfaces/sensor_provider.mojom.h"
 
@@ -21,9 +21,8 @@
 class DEVICE_GENERIC_SENSOR_EXPORT SensorProviderImpl final
     : public mojom::SensorProvider {
  public:
-  static void Create(
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
-      mojom::SensorProviderRequest request);
+  static void Create(scoped_refptr<base::SequencedTaskRunner> file_task_runner,
+                     mojom::SensorProviderRequest request);
 
   ~SensorProviderImpl() override;
 
diff --git a/device/power_save_blocker/power_save_blocker.h b/device/power_save_blocker/power_save_blocker.h
index 621eaef..a9544b8 100644
--- a/device/power_save_blocker/power_save_blocker.h
+++ b/device/power_save_blocker/power_save_blocker.h
@@ -58,7 +58,7 @@
       Reason reason,
       const std::string& description,
       scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner);
+      scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
   virtual ~PowerSaveBlocker();
 
 #if defined(OS_ANDROID)
diff --git a/device/power_save_blocker/power_save_blocker_android.cc b/device/power_save_blocker/power_save_blocker_android.cc
index 3aa5346..5227065 100644
--- a/device/power_save_blocker/power_save_blocker_android.cc
+++ b/device/power_save_blocker/power_save_blocker_android.cc
@@ -71,7 +71,7 @@
     Reason reason,
     const std::string& description,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
     : ui_task_runner_(ui_task_runner),
       blocking_task_runner_(blocking_task_runner) {
   // Don't support kPowerSaveBlockPreventAppSuspension
diff --git a/device/power_save_blocker/power_save_blocker_chromeos.cc b/device/power_save_blocker/power_save_blocker_chromeos.cc
index f3390147..0dd1bee 100644
--- a/device/power_save_blocker/power_save_blocker_chromeos.cc
+++ b/device/power_save_blocker/power_save_blocker_chromeos.cc
@@ -97,7 +97,7 @@
     Reason reason,
     const std::string& description,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
     : delegate_(new Delegate(type, reason, description, ui_task_runner)),
       ui_task_runner_(ui_task_runner),
       blocking_task_runner_(blocking_task_runner) {
diff --git a/device/power_save_blocker/power_save_blocker_mac.cc b/device/power_save_blocker/power_save_blocker_mac.cc
index 4ce6c18..97f6734d 100644
--- a/device/power_save_blocker/power_save_blocker_mac.cc
+++ b/device/power_save_blocker/power_save_blocker_mac.cc
@@ -84,8 +84,8 @@
         base::SysUTF8ToCFStringRef(description_));
     IOReturn result = IOPMAssertionCreateWithName(level, kIOPMAssertionLevelOn,
                                                   cf_description, &assertion_);
-    LOG_IF(ERROR, result != kIOReturnSuccess) << "IOPMAssertionCreate: "
-                                              << result;
+    LOG_IF(ERROR, result != kIOReturnSuccess)
+        << "IOPMAssertionCreate: " << result;
   }
 }
 
@@ -95,8 +95,8 @@
 
   if (assertion_ != kIOPMNullAssertionID) {
     IOReturn result = IOPMAssertionRelease(assertion_);
-    LOG_IF(ERROR, result != kIOReturnSuccess) << "IOPMAssertionRelease: "
-                                              << result;
+    LOG_IF(ERROR, result != kIOReturnSuccess)
+        << "IOPMAssertionRelease: " << result;
   }
 }
 
@@ -105,7 +105,7 @@
     Reason reason,
     const std::string& description,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
     : delegate_(new Delegate(type, description)),
       ui_task_runner_(ui_task_runner),
       blocking_task_runner_(blocking_task_runner) {
diff --git a/device/power_save_blocker/power_save_blocker_ozone.cc b/device/power_save_blocker/power_save_blocker_ozone.cc
index 981794eb..21f0f25 100644
--- a/device/power_save_blocker/power_save_blocker_ozone.cc
+++ b/device/power_save_blocker/power_save_blocker_ozone.cc
@@ -30,7 +30,7 @@
     Reason reason,
     const std::string& description,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
     : delegate_(new Delegate()),
       ui_task_runner_(ui_task_runner),
       blocking_task_runner_(blocking_task_runner) {}
diff --git a/device/power_save_blocker/power_save_blocker_win.cc b/device/power_save_blocker/power_save_blocker_win.cc
index 83830a7..ee7b92a 100644
--- a/device/power_save_blocker/power_save_blocker_win.cc
+++ b/device/power_save_blocker/power_save_blocker_win.cc
@@ -113,7 +113,7 @@
     Reason reason,
     const std::string& description,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
     : delegate_(new Delegate(type, description, ui_task_runner)),
       ui_task_runner_(ui_task_runner),
       blocking_task_runner_(blocking_task_runner) {
diff --git a/device/power_save_blocker/power_save_blocker_x11.cc b/device/power_save_blocker/power_save_blocker_x11.cc
index 56e6584b..e2a66147 100644
--- a/device/power_save_blocker/power_save_blocker_x11.cc
+++ b/device/power_save_blocker/power_save_blocker_x11.cc
@@ -81,7 +81,7 @@
            const std::string& description,
            bool freedesktop_only,
            scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-           scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner);
+           scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
 
   // Post a task to initialize the delegate on the UI thread, which will itself
   // then post a task to apply the power save block on the FILE thread.
@@ -164,7 +164,7 @@
   uint32_t inhibit_cookie_;
 
   scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
-  scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(Delegate);
 };
@@ -174,7 +174,7 @@
     const std::string& description,
     bool freedesktop_only,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
     : type_(type),
       description_(description),
       freedesktop_only_(freedesktop_only),
@@ -486,7 +486,7 @@
     Reason reason,
     const std::string& description,
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
     : delegate_(new Delegate(type,
                              description,
                              false /* freedesktop_only */,
diff --git a/device/wake_lock/wake_lock.cc b/device/wake_lock/wake_lock.cc
index 2bef9ed..d767587 100644
--- a/device/wake_lock/wake_lock.cc
+++ b/device/wake_lock/wake_lock.cc
@@ -51,7 +51,7 @@
                    const std::string& description,
                    int context_id,
                    WakeLockContextCallback native_view_getter,
-                   scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
+                   scoped_refptr<base::SequencedTaskRunner> file_task_runner)
     : num_lock_requests_(0),
       type_(type),
       reason_(reason),
diff --git a/device/wake_lock/wake_lock.h b/device/wake_lock/wake_lock.h
index 4686570..5dfee6b 100644
--- a/device/wake_lock/wake_lock.h
+++ b/device/wake_lock/wake_lock.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
 #include "device/power_save_blocker/power_save_blocker.h"
 #include "device/wake_lock/public/interfaces/wake_lock.mojom.h"
 #include "device/wake_lock/public/interfaces/wake_lock_context.mojom.h"
@@ -27,7 +27,7 @@
            const std::string& description,
            int context_id,
            WakeLockContextCallback native_view_getter,
-           scoped_refptr<base::SingleThreadTaskRunner> file_task_runner);
+           scoped_refptr<base::SequencedTaskRunner> file_task_runner);
   ~WakeLock() override;
 
   // WakeLockSevice implementation.
@@ -59,7 +59,7 @@
 #endif
 
   scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
-  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
 
   // The actual power save blocker for screen.
   std::unique_ptr<PowerSaveBlocker> wake_lock_;
diff --git a/device/wake_lock/wake_lock_context.cc b/device/wake_lock/wake_lock_context.cc
index dd11d52..ef7131b 100644
--- a/device/wake_lock/wake_lock_context.cc
+++ b/device/wake_lock/wake_lock_context.cc
@@ -14,7 +14,7 @@
 
 WakeLockContext::WakeLockContext(
     int context_id,
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner,
     const WakeLockContextCallback& native_view_getter)
     : file_task_runner_(std::move(file_task_runner)),
       context_id_(context_id),
diff --git a/device/wake_lock/wake_lock_context.h b/device/wake_lock/wake_lock_context.h
index 2e02be8..a897b4d 100644
--- a/device/wake_lock/wake_lock_context.h
+++ b/device/wake_lock/wake_lock_context.h
@@ -10,7 +10,7 @@
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
 #include "device/wake_lock/public/interfaces/wake_lock_context.mojom.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -25,7 +25,7 @@
 class WakeLockContext : public mojom::WakeLockContext {
  public:
   WakeLockContext(int context_id,
-                  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+                  scoped_refptr<base::SequencedTaskRunner> file_task_runner,
                   const WakeLockContextCallback& native_view_getter);
   ~WakeLockContext() override;
 
@@ -38,7 +38,7 @@
   static const int WakeLockInvalidContextId;
 
  private:
-  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
   int context_id_;
   WakeLockContextCallback native_view_getter_;
 
diff --git a/device/wake_lock/wake_lock_for_testing.cc b/device/wake_lock/wake_lock_for_testing.cc
index 776bd56..bc4bfac 100644
--- a/device/wake_lock/wake_lock_for_testing.cc
+++ b/device/wake_lock/wake_lock_for_testing.cc
@@ -15,7 +15,7 @@
     const std::string& description,
     int context_id,
     WakeLockContextCallback native_view_getter,
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner)
     : WakeLock(std::move(request),
                type,
                reason,
diff --git a/device/wake_lock/wake_lock_for_testing.h b/device/wake_lock/wake_lock_for_testing.h
index 3de901ca..8ab62ff5 100644
--- a/device/wake_lock/wake_lock_for_testing.h
+++ b/device/wake_lock/wake_lock_for_testing.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
 #include "device/wake_lock/public/interfaces/wake_lock.mojom.h"
 #include "device/wake_lock/public/interfaces/wake_lock_context.mojom.h"
 #include "device/wake_lock/wake_lock.h"
@@ -20,14 +20,13 @@
 
 class WakeLockForTesting : public WakeLock {
  public:
-  WakeLockForTesting(
-      mojom::WakeLockRequest request,
-      mojom::WakeLockType type,
-      mojom::WakeLockReason reason,
-      const std::string& description,
-      int context_id,
-      WakeLockContextCallback native_view_getter,
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner);
+  WakeLockForTesting(mojom::WakeLockRequest request,
+                     mojom::WakeLockType type,
+                     mojom::WakeLockReason reason,
+                     const std::string& description,
+                     int context_id,
+                     WakeLockContextCallback native_view_getter,
+                     scoped_refptr<base::SequencedTaskRunner> file_task_runner);
   ~WakeLockForTesting() override;
 
   void HasWakeLockForTests(HasWakeLockForTestsCallback callback) override;
diff --git a/device/wake_lock/wake_lock_provider.cc b/device/wake_lock/wake_lock_provider.cc
index 26e4aa1..c1062e19 100644
--- a/device/wake_lock/wake_lock_provider.cc
+++ b/device/wake_lock/wake_lock_provider.cc
@@ -17,7 +17,7 @@
 // static
 void WakeLockProvider::Create(
     mojom::WakeLockProviderRequest request,
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner,
     const WakeLockContextCallback& native_view_getter) {
   mojo::MakeStrongBinding(base::MakeUnique<WakeLockProvider>(
                               std::move(file_task_runner), native_view_getter),
@@ -25,7 +25,7 @@
 }
 
 WakeLockProvider::WakeLockProvider(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner,
     const WakeLockContextCallback& native_view_getter)
     : file_task_runner_(std::move(file_task_runner)),
       native_view_getter_(native_view_getter) {}
diff --git a/device/wake_lock/wake_lock_provider.h b/device/wake_lock/wake_lock_provider.h
index 297c0ee..72b4442f1 100644
--- a/device/wake_lock/wake_lock_provider.h
+++ b/device/wake_lock/wake_lock_provider.h
@@ -6,7 +6,6 @@
 #define DEVICE_WAKE_LOCK_WAKE_LOCK_PROVIDER_H_
 
 #include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
 #include "device/wake_lock/public/interfaces/wake_lock_context.mojom.h"
 #include "device/wake_lock/public/interfaces/wake_lock_provider.mojom.h"
 #include "device/wake_lock/wake_lock_context.h"
@@ -18,14 +17,13 @@
 // Serves requests for WakeLockContext connections.
 class WakeLockProvider : public mojom::WakeLockProvider {
  public:
-  WakeLockProvider(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+  WakeLockProvider(scoped_refptr<base::SequencedTaskRunner> file_task_runner,
                    const WakeLockContextCallback& native_view_getter);
   ~WakeLockProvider() override;
 
-  static void Create(
-      mojom::WakeLockProviderRequest request,
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
-      const WakeLockContextCallback& native_view_getter);
+  static void Create(mojom::WakeLockProviderRequest request,
+                     scoped_refptr<base::SequencedTaskRunner> file_task_runner,
+                     const WakeLockContextCallback& native_view_getter);
 
   // mojom::WakeLockProvider:
   void GetWakeLockContextForID(
@@ -40,7 +38,7 @@
   static bool is_in_unittest_;
 
  private:
-  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
   WakeLockContextCallback native_view_getter_;
 
   DISALLOW_COPY_AND_ASSIGN(WakeLockProvider);
diff --git a/extensions/browser/api/api_resource_manager.h b/extensions/browser/api/api_resource_manager.h
index 8626e21..d8ca7c33 100644
--- a/extensions/browser/api/api_resource_manager.h
+++ b/extensions/browser/api/api_resource_manager.h
@@ -36,7 +36,7 @@
 class TCPServerSocketEventDispatcher;
 class TCPSocketEventDispatcher;
 class UDPSocketEventDispatcher;
-}
+}  // namespace api
 
 template <typename T>
 struct NamedThreadTraits {
@@ -126,12 +126,12 @@
   }
 
   // BrowserContextKeyedAPI implementation.
-  static BrowserContextKeyedAPIFactory<ApiResourceManager<T> >*
-      GetFactoryInstance();
+  static BrowserContextKeyedAPIFactory<ApiResourceManager<T>>*
+  GetFactoryInstance();
 
   // Convenience method to get the ApiResourceManager for a profile.
   static ApiResourceManager<T>* Get(content::BrowserContext* context) {
-    return BrowserContextKeyedAPIFactory<ApiResourceManager<T> >::Get(context);
+    return BrowserContextKeyedAPIFactory<ApiResourceManager<T>>::Get(context);
   }
 
   // BrowserContextKeyedAPI implementation.
@@ -171,7 +171,7 @@
   friend class api::TCPServerSocketEventDispatcher;
   friend class api::TCPSocketEventDispatcher;
   friend class api::UDPSocketEventDispatcher;
-  friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T> >;
+  friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T>>;
 
   static const bool kServiceHasOwnInstanceInIncognito = true;
   static const bool kServiceIsNULLWhileTesting = true;
@@ -182,7 +182,7 @@
    public:
     typedef std::map<int, std::unique_ptr<T>> ApiResourceMap;
     // Lookup map from extension id's to allocated resource id's.
-    typedef std::map<std::string, base::hash_set<int> > ExtensionToResourceMap;
+    typedef std::map<std::string, base::hash_set<int>> ExtensionToResourceMap;
 
     ApiResourceData() : next_id_(1) { sequence_checker_.DetachFromSequence(); }
 
@@ -197,8 +197,9 @@
         ExtensionToResourceMap::iterator it =
             extension_resource_map_.find(extension_id);
         if (it == extension_resource_map_.end()) {
-          it = extension_resource_map_.insert(
-              std::make_pair(extension_id, base::hash_set<int>())).first;
+          it = extension_resource_map_
+                   .insert(std::make_pair(extension_id, base::hash_set<int>()))
+                   .first;
         }
         it->second.insert(id);
         return id;
@@ -243,24 +244,22 @@
     }
 
     void InitiateExtensionUnloadedCleanup(const std::string& extension_id) {
-        ThreadingTraits::GetSequencedTaskRunner()->PostTask(
-            FROM_HERE,
-            base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension,
-                       this,
-                       extension_id));
+      ThreadingTraits::GetSequencedTaskRunner()->PostTask(
+          FROM_HERE,
+          base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension,
+                     this, extension_id));
     }
 
     void InitiateExtensionSuspendedCleanup(const std::string& extension_id) {
-        ThreadingTraits::GetSequencedTaskRunner()->PostTask(
-            FROM_HERE,
-            base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension,
-                       this,
-                       extension_id));
+      ThreadingTraits::GetSequencedTaskRunner()->PostTask(
+          FROM_HERE,
+          base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension,
+                     this, extension_id));
     }
 
     void InititateCleanup() {
-        ThreadingTraits::GetSequencedTaskRunner()->PostTask(
-            FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
+      ThreadingTraits::GetSequencedTaskRunner()->PostTask(
+          FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
     }
 
    private:
@@ -378,65 +377,6 @@
   }
 };
 
-// With WorkerPoolThreadTraits, ApiResourceManager can be used to manage the
-// lifetime of a set of resources that live on sequenced task runner threads
-// which ApiFunctions use. Examples of such resources are temporary file
-// resources produced by certain API calls.
-//
-// Instead of kThreadId. classes used for tracking such resources should define
-// kSequenceToken and kShutdownBehavior to identify sequence task runner for
-// ApiResourceManager to work on and how pending tasks should behave on
-// shutdown.
-// The user must also define a static const char* service_name() that returns
-// the name of the service, and in order for ApiWorkerPoolResourceManager to use
-// service_name() friend this class.
-//
-// In the cc file the user must define a GetFactoryInstance() and manage their
-// own instances (typically using LazyInstance or Singleton).
-//
-// E.g.:
-//
-// class PoolResource {
-//  public:
-//   static const char kSequenceToken[] = "temp_files";
-//   static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior =
-//       base::SequencedWorkerPool::BLOCK_SHUTDOWN;
-//  private:
-//   friend class ApiResourceManager<WorkerPoolResource,
-//                                   WorkerPoolThreadTraits>;
-//   static const char* service_name() {
-//     return "TempFilesResourceManager";
-//    }
-// };
-//
-// In the cc file:
-//
-// static base::LazyInstance<BrowserContextKeyedAPIFactory<
-//     ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
-//         g_factory = LAZY_INSTANCE_INITIALIZER;
-//
-//
-// template <>
-// BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >*
-// ApiResourceManager<WorkerPoolPoolResource,
-//                    WorkerPoolThreadTraits>::GetFactoryInstance() {
-//   return g_factory.Pointer();
-// }
-template <typename T>
-struct WorkerPoolThreadTraits {
-  static bool IsMessageLoopValid() {
-    return content::BrowserThread::GetBlockingPool() != NULL;
-  }
-
-  static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
-    return content::BrowserThread::GetBlockingPool()
-        ->GetSequencedTaskRunnerWithShutdownBehavior(
-            content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
-                T::kSequenceToken),
-            T::kShutdownBehavior);
-  }
-};
-
 }  // namespace extensions
 
 #endif  // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
diff --git a/extensions/browser/api/async_api_function.cc b/extensions/browser/api/async_api_function.cc
index a4e40f6f..b17063f 100644
--- a/extensions/browser/api/async_api_function.cc
+++ b/extensions/browser/api/async_api_function.cc
@@ -5,6 +5,7 @@
 #include "extensions/browser/api/async_api_function.h"
 
 #include "base/bind.h"
+#include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_system.h"
 
 using content::BrowserThread;
@@ -12,7 +13,9 @@
 namespace extensions {
 
 // AsyncApiFunction
-AsyncApiFunction::AsyncApiFunction() : work_thread_id_(BrowserThread::IO) {}
+AsyncApiFunction::AsyncApiFunction()
+    : work_task_runner_(
+          BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)) {}
 
 AsyncApiFunction::~AsyncApiFunction() {}
 
@@ -22,15 +25,15 @@
   if (!PrePrepare() || !Prepare()) {
     return false;
   }
-  bool rv = BrowserThread::PostTask(
-      work_thread_id_,
-      FROM_HERE,
-      base::Bind(&AsyncApiFunction::WorkOnWorkThread, this));
+  bool rv = work_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AsyncApiFunction::WorkOnWorkThread, this));
   DCHECK(rv);
   return true;
 }
 
-bool AsyncApiFunction::PrePrepare() { return true; }
+bool AsyncApiFunction::PrePrepare() {
+  return true;
+}
 
 void AsyncApiFunction::Work() {}
 
@@ -42,8 +45,7 @@
 void AsyncApiFunction::AsyncWorkCompleted() {
   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
     bool rv = BrowserThread::PostTask(
-        BrowserThread::UI,
-        FROM_HERE,
+        BrowserThread::UI, FROM_HERE,
         base::Bind(&AsyncApiFunction::RespondOnUIThread, this));
     DCHECK(rv);
   } else {
@@ -52,11 +54,7 @@
 }
 
 void AsyncApiFunction::WorkOnWorkThread() {
-  DCHECK_CURRENTLY_ON(work_thread_id_);
-  DLOG_IF(ERROR, (work_thread_id_ == BrowserThread::UI))
-      << "You have specified that AsyncApiFunction::Work() should happen on "
-         "the UI thread. This nullifies the point of this class. Either "
-         "specify a different thread or derive from a different class.";
+  DCHECK(work_task_runner_->RunsTasksInCurrentSequence());
   AsyncWorkStart();
 }
 
diff --git a/extensions/browser/api/async_api_function.h b/extensions/browser/api/async_api_function.h
index e260806..ab47953 100644
--- a/extensions/browser/api/async_api_function.h
+++ b/extensions/browser/api/async_api_function.h
@@ -5,7 +5,8 @@
 #ifndef EXTENSIONS_BROWSER_API_ASYNC_API_FUNCTION_H_
 #define EXTENSIONS_BROWSER_API_ASYNC_API_FUNCTION_H_
 
-#include "content/public/browser/browser_thread.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
 #include "extensions/browser/extension_function.h"
 
 namespace extensions {
@@ -25,11 +26,11 @@
   // thread.
   virtual bool Prepare() = 0;
 
-  // Do actual work. Guaranteed to happen on the thread specified in
-  // work_thread_id_.
+  // Do actual work. Guaranteed to happen on the task runner specified in
+  // |work_task_runner_| if non-null; or on the IO thread otherwise.
   virtual void Work();
 
-  // Start the asynchronous work. Guraranteed to happen on requested thread.
+  // Start the asynchronous work. Guraranteed to happen on work thread.
   virtual void AsyncWorkStart();
 
   // Notify AsyncIOApiFunction that the work is completed
@@ -42,9 +43,12 @@
   bool RunAsync() override;
 
  protected:
-  content::BrowserThread::ID work_thread_id() const { return work_thread_id_; }
-  void set_work_thread_id(content::BrowserThread::ID work_thread_id) {
-    work_thread_id_ = work_thread_id;
+  scoped_refptr<base::SequencedTaskRunner> work_task_runner() const {
+    return work_task_runner_;
+  }
+  void set_work_task_runner(
+      scoped_refptr<base::SequencedTaskRunner> work_task_runner) {
+    work_task_runner_ = work_task_runner;
   }
 
  private:
@@ -52,8 +56,8 @@
   void RespondOnUIThread();
 
   // If you don't want your Work() method to happen on the IO thread, then set
-  // this to the thread that you do want, preferably in Prepare().
-  content::BrowserThread::ID work_thread_id_;
+  // this to the SequenceTaskRunner you do want to use, preferably in Prepare().
+  scoped_refptr<base::SequencedTaskRunner> work_task_runner_;
 };
 
 }  // namespace extensions
diff --git a/extensions/browser/api/document_scan/document_scan_api.cc b/extensions/browser/api/document_scan/document_scan_api.cc
index 317199f..54e9b95f 100644
--- a/extensions/browser/api/document_scan/document_scan_api.cc
+++ b/extensions/browser/api/document_scan/document_scan_api.cc
@@ -5,6 +5,7 @@
 #include "extensions/browser/api/document_scan/document_scan_api.h"
 
 #include "base/stl_util.h"
+#include "base/task_scheduler/post_task.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_system.h"
 
@@ -23,14 +24,13 @@
 namespace api {
 
 DocumentScanScanFunction::DocumentScanScanFunction()
-    : document_scan_interface_(DocumentScanInterface::CreateInstance()) {
-}
+    : document_scan_interface_(DocumentScanInterface::CreateInstance()) {}
 
-DocumentScanScanFunction::~DocumentScanScanFunction() {
-}
+DocumentScanScanFunction::~DocumentScanScanFunction() {}
 
 bool DocumentScanScanFunction::Prepare() {
-  set_work_thread_id(BrowserThread::FILE);
+  set_work_task_runner(base::CreateSequencedTaskRunnerWithTraits(
+      {base::MayBlock(), base::TaskPriority::BACKGROUND}));
   params_ = document_scan::Scan::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params_.get());
   return true;
diff --git a/extensions/browser/api/serial/serial_api.cc b/extensions/browser/api/serial/serial_api.cc
index d7326aa..734b6b3 100644
--- a/extensions/browser/api/serial/serial_api.cc
+++ b/extensions/browser/api/serial/serial_api.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <vector>
 
+#include "base/task_scheduler/post_task.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
@@ -50,11 +51,9 @@
 
 }  // namespace
 
-SerialAsyncApiFunction::SerialAsyncApiFunction() : manager_(NULL) {
-}
+SerialAsyncApiFunction::SerialAsyncApiFunction() : manager_(NULL) {}
 
-SerialAsyncApiFunction::~SerialAsyncApiFunction() {
-}
+SerialAsyncApiFunction::~SerialAsyncApiFunction() {}
 
 bool SerialAsyncApiFunction::PrePrepare() {
   manager_ = ApiResourceManager<SerialConnection>::Get(browser_context());
@@ -75,16 +74,16 @@
   manager_->Remove(extension_->id(), api_resource_id);
 }
 
-SerialGetDevicesFunction::SerialGetDevicesFunction() {
-}
+SerialGetDevicesFunction::SerialGetDevicesFunction() {}
 
 bool SerialGetDevicesFunction::Prepare() {
-  set_work_thread_id(BrowserThread::FILE);
+  set_work_task_runner(base::CreateSequencedTaskRunnerWithTraits(
+      {base::MayBlock(), base::TaskPriority::BACKGROUND}));
   return true;
 }
 
 void SerialGetDevicesFunction::Work() {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+  DCHECK(work_task_runner()->RunsTasksInCurrentSequence());
 
   std::unique_ptr<device::SerialDeviceEnumerator> enumerator =
       device::SerialDeviceEnumerator::Create();
@@ -93,11 +92,9 @@
       mojo::ConvertTo<std::vector<serial::DeviceInfo>>(devices));
 }
 
-SerialConnectFunction::SerialConnectFunction() {
-}
+SerialConnectFunction::SerialConnectFunction() {}
 
-SerialConnectFunction::~SerialConnectFunction() {
-}
+SerialConnectFunction::~SerialConnectFunction() {}
 
 bool SerialConnectFunction::Prepare() {
   params_ = serial::Connect::Params::Create(*args_);
@@ -144,8 +141,7 @@
   }
 
   BrowserThread::PostTask(
-      BrowserThread::IO,
-      FROM_HERE,
+      BrowserThread::IO, FROM_HERE,
       base::Bind(&SerialConnectFunction::FinishConnect, this));
 }
 
@@ -174,11 +170,9 @@
   return new SerialConnection(port, extension_id);
 }
 
-SerialUpdateFunction::SerialUpdateFunction() {
-}
+SerialUpdateFunction::SerialUpdateFunction() {}
 
-SerialUpdateFunction::~SerialUpdateFunction() {
-}
+SerialUpdateFunction::~SerialUpdateFunction() {}
 
 bool SerialUpdateFunction::Prepare() {
   params_ = serial::Update::Params::Create(*args_);
@@ -197,11 +191,9 @@
   results_ = serial::Update::Results::Create(success);
 }
 
-SerialDisconnectFunction::SerialDisconnectFunction() {
-}
+SerialDisconnectFunction::SerialDisconnectFunction() {}
 
-SerialDisconnectFunction::~SerialDisconnectFunction() {
-}
+SerialDisconnectFunction::~SerialDisconnectFunction() {}
 
 bool SerialDisconnectFunction::Prepare() {
   params_ = serial::Disconnect::Params::Create(*args_);
@@ -220,11 +212,9 @@
   results_ = serial::Disconnect::Results::Create(true);
 }
 
-SerialSendFunction::SerialSendFunction() {
-}
+SerialSendFunction::SerialSendFunction() {}
 
-SerialSendFunction::~SerialSendFunction() {
-}
+SerialSendFunction::~SerialSendFunction() {}
 
 bool SerialSendFunction::Prepare() {
   params_ = serial::Send::Params::Create(*args_);
@@ -257,11 +247,9 @@
   AsyncWorkCompleted();
 }
 
-SerialFlushFunction::SerialFlushFunction() {
-}
+SerialFlushFunction::SerialFlushFunction() {}
 
-SerialFlushFunction::~SerialFlushFunction() {
-}
+SerialFlushFunction::~SerialFlushFunction() {}
 
 bool SerialFlushFunction::Prepare() {
   params_ = serial::Flush::Params::Create(*args_);
@@ -280,11 +268,9 @@
   results_ = serial::Flush::Results::Create(success);
 }
 
-SerialSetPausedFunction::SerialSetPausedFunction() {
-}
+SerialSetPausedFunction::SerialSetPausedFunction() {}
 
-SerialSetPausedFunction::~SerialSetPausedFunction() {
-}
+SerialSetPausedFunction::~SerialSetPausedFunction() {}
 
 bool SerialSetPausedFunction::Prepare() {
   params_ = serial::SetPaused::Params::Create(*args_);
@@ -313,11 +299,9 @@
   results_ = serial::SetPaused::Results::Create();
 }
 
-SerialGetInfoFunction::SerialGetInfoFunction() {
-}
+SerialGetInfoFunction::SerialGetInfoFunction() {}
 
-SerialGetInfoFunction::~SerialGetInfoFunction() {
-}
+SerialGetInfoFunction::~SerialGetInfoFunction() {}
 
 bool SerialGetInfoFunction::Prepare() {
   params_ = serial::GetInfo::Params::Create(*args_);
@@ -339,11 +323,9 @@
   results_ = serial::GetInfo::Results::Create(info);
 }
 
-SerialGetConnectionsFunction::SerialGetConnectionsFunction() {
-}
+SerialGetConnectionsFunction::SerialGetConnectionsFunction() {}
 
-SerialGetConnectionsFunction::~SerialGetConnectionsFunction() {
-}
+SerialGetConnectionsFunction::~SerialGetConnectionsFunction() {}
 
 bool SerialGetConnectionsFunction::Prepare() {
   return true;
@@ -355,8 +337,7 @@
       manager_->GetResourceIds(extension_->id());
   if (connection_ids) {
     for (base::hash_set<int>::const_iterator it = connection_ids->begin();
-         it != connection_ids->end();
-         ++it) {
+         it != connection_ids->end(); ++it) {
       int connection_id = *it;
       SerialConnection* connection = GetSerialConnection(connection_id);
       if (connection) {
@@ -370,11 +351,9 @@
   results_ = serial::GetConnections::Results::Create(infos);
 }
 
-SerialGetControlSignalsFunction::SerialGetControlSignalsFunction() {
-}
+SerialGetControlSignalsFunction::SerialGetControlSignalsFunction() {}
 
-SerialGetControlSignalsFunction::~SerialGetControlSignalsFunction() {
-}
+SerialGetControlSignalsFunction::~SerialGetControlSignalsFunction() {}
 
 bool SerialGetControlSignalsFunction::Prepare() {
   params_ = serial::GetControlSignals::Params::Create(*args_);
@@ -399,11 +378,9 @@
   results_ = serial::GetControlSignals::Results::Create(signals);
 }
 
-SerialSetControlSignalsFunction::SerialSetControlSignalsFunction() {
-}
+SerialSetControlSignalsFunction::SerialSetControlSignalsFunction() {}
 
-SerialSetControlSignalsFunction::~SerialSetControlSignalsFunction() {
-}
+SerialSetControlSignalsFunction::~SerialSetControlSignalsFunction() {}
 
 bool SerialSetControlSignalsFunction::Prepare() {
   params_ = serial::SetControlSignals::Params::Create(*args_);
@@ -423,11 +400,9 @@
   results_ = serial::SetControlSignals::Results::Create(success);
 }
 
-SerialSetBreakFunction::SerialSetBreakFunction() {
-}
+SerialSetBreakFunction::SerialSetBreakFunction() {}
 
-SerialSetBreakFunction::~SerialSetBreakFunction() {
-}
+SerialSetBreakFunction::~SerialSetBreakFunction() {}
 
 bool SerialSetBreakFunction::Prepare() {
   params_ = serial::SetBreak::Params::Create(*args_);
@@ -446,11 +421,9 @@
   results_ = serial::SetBreak::Results::Create(success);
 }
 
-SerialClearBreakFunction::SerialClearBreakFunction() {
-}
+SerialClearBreakFunction::SerialClearBreakFunction() {}
 
-SerialClearBreakFunction::~SerialClearBreakFunction() {
-}
+SerialClearBreakFunction::~SerialClearBreakFunction() {}
 
 bool SerialClearBreakFunction::Prepare() {
   params_ = serial::ClearBreak::Params::Create(*args_);
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_api_unittest.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_api_unittest.cc
index 73930c1e..100d76d 100644
--- a/extensions/browser/api/sockets_tcp/sockets_tcp_api_unittest.cc
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp_api_unittest.cc
@@ -36,13 +36,9 @@
 };
 
 TEST_F(SocketsTcpUnitTest, Create) {
-  // Get BrowserThread
-  content::BrowserThread::ID id;
-  CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id));
-
   // Create SocketCreateFunction and put it on BrowserThread
   SocketsTcpCreateFunction* function = new SocketsTcpCreateFunction();
-  function->set_work_thread_id(id);
+  function->set_work_task_runner(base::SequencedTaskRunnerHandle::Get());
 
   // Run tests
   std::unique_ptr<base::DictionaryValue> result(RunFunctionAndReturnDictionary(
diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api_unittest.cc b/extensions/browser/api/sockets_udp/sockets_udp_api_unittest.cc
index 4d3cf42..2b651825 100644
--- a/extensions/browser/api/sockets_udp/sockets_udp_api_unittest.cc
+++ b/extensions/browser/api/sockets_udp/sockets_udp_api_unittest.cc
@@ -35,13 +35,9 @@
 };
 
 TEST_F(SocketsUdpUnitTest, Create) {
-  // Get BrowserThread
-  content::BrowserThread::ID id;
-  CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id));
-
   // Create SocketCreateFunction and put it on BrowserThread
   SocketsUdpCreateFunction* function = new SocketsUdpCreateFunction();
-  function->set_work_thread_id(id);
+  function->set_work_task_runner(base::SequencedTaskRunnerHandle::Get());
 
   // Run tests
   std::unique_ptr<base::DictionaryValue> result(RunFunctionAndReturnDictionary(
diff --git a/extensions/browser/content_hash_fetcher_unittest.cc b/extensions/browser/content_hash_fetcher_unittest.cc
index 46be54e5..01d6bdc 100644
--- a/extensions/browser/content_hash_fetcher_unittest.cc
+++ b/extensions/browser/content_hash_fetcher_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "base/version.h"
 #include "content/public/browser/browser_thread.h"
@@ -172,7 +173,8 @@
         url.scheme(), url.host(),
         content::BrowserThread::GetTaskRunnerForThread(
             content::BrowserThread::IO),
-        content::BrowserThread::GetBlockingPool());
+        base::CreateTaskRunnerWithTraits(
+            {base::MayBlock(), base::TaskPriority::BACKGROUND}));
     interceptor_->SetResponse(url, response_path);
   }
 
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc
index 8f30ca6..706fa86e 100644
--- a/gpu/ipc/service/direct_composition_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -999,7 +999,7 @@
 
 // static
 bool DirectCompositionSurfaceWin::IsHDRSupported() {
-  bool hdr_monitor_found = true;
+  bool hdr_monitor_found = false;
 #if defined(ENABLE_HDR_DETECTION)
   base::win::ScopedComPtr<ID3D11Device> d3d11_device =
       gl::QueryD3D11DeviceObjectFromANGLE();
@@ -1031,14 +1031,11 @@
 
     if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
       hdr_monitor_found = true;
-      return true;
     }
   }
   UMA_HISTOGRAM_BOOLEAN("GPU.Output.HDR", hdr_monitor_found);
-  return hdr_monitor_found;
-#else
-  return false;
 #endif
+  return hdr_monitor_found;
 }
 
 bool DirectCompositionSurfaceWin::InitializeNativeWindow() {
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index dc80bda..21ae063 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -1182,14 +1182,14 @@
 
 void GpuChannel::AddFilter(IPC::MessageFilter* filter) {
   io_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&GpuChannelMessageFilter::AddChannelFilter, filter_, filter));
+      FROM_HERE, base::Bind(&GpuChannelMessageFilter::AddChannelFilter, filter_,
+                            base::RetainedRef(filter)));
 }
 
 void GpuChannel::RemoveFilter(IPC::MessageFilter* filter) {
   io_task_runner_->PostTask(
       FROM_HERE, base::Bind(&GpuChannelMessageFilter::RemoveChannelFilter,
-                            filter_, filter));
+                            filter_, base::RetainedRef(filter)));
 }
 
 uint64_t GpuChannel::GetMemoryUsage() {
diff --git a/ios/chrome/app/resources/XCTRunnerAddition+Info.plist b/ios/chrome/app/resources/XCTRunnerAddition+Info.plist
new file mode 100644
index 0000000..a0ba439
--- /dev/null
+++ b/ios/chrome/app/resources/XCTRunnerAddition+Info.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleIdentifier</key>
+  <string>com.apple.test.${PRODUCT_NAME}-Runner</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+</dict>
+</plist>
diff --git a/ios/chrome/browser/history/top_sites_factory.cc b/ios/chrome/browser/history/top_sites_factory.cc
index d2a80b7..e19ea43 100644
--- a/ios/chrome/browser/history/top_sites_factory.cc
+++ b/ios/chrome/browser/history/top_sites_factory.cc
@@ -50,8 +50,7 @@
           browser_state, ServiceAccessType::EXPLICIT_ACCESS),
       history::PrepopulatedPageList(), base::Bind(CanAddURLToHistory)));
   top_sites->Init(
-      browser_state->GetStatePath().Append(history::kTopSitesFilename),
-      web::WebThread::GetTaskRunnerForThread(web::WebThread::DB));
+      browser_state->GetStatePath().Append(history::kTopSitesFilename));
   return top_sites;
 }
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h
index 82d6ab7..c290a783 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h
@@ -47,12 +47,6 @@
 // The delegate needs to be set before calling this method.
 - (void)setAppearanceForTab:(Tab*)tab cellSize:(CGSize)cellSize;
 
-// PLACEHOLDER: Sets the cell's appearance using information in |title| and
-// |favicon|.
-- (void)setAppearanceForTabTitle:(NSString*)title
-                         favicon:(UIImage*)favicon
-                        cellSize:(CGSize)cellSize;
-
 // Sets the cell's appearance depending on |type|.
 - (void)setSessionType:(TabSwitcherSessionType)type;
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm
index 396b374..b9ebf28 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm
@@ -102,16 +102,22 @@
 
 @end
 
+@interface TabSwitcherLocalSessionCell ()
+@property(nonatomic, strong) UILabel* titleLabel;
+@property(nonatomic, strong) UIImageView* favicon;
+@property(nonatomic, strong) TabSwitcherButton* snapshotButton;
+@end
+
 @implementation TabSwitcherLocalSessionCell {
   UIView* _topBar;
-  UILabel* _titleLabel;
-  UIImageView* _favicon;
   UIButton* _closeButton;
   UIImageView* _shadow;
   UIImageView* _snapshot;
-  TabSwitcherButton* _snapshotButton;
   PendingSnapshotRequest _currentPendingSnapshotRequest;
 }
+@synthesize titleLabel = _titleLabel;
+@synthesize favicon = _favicon;
+@synthesize snapshotButton = _snapshotButton;
 
 - (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
@@ -245,22 +251,6 @@
                           }];
 }
 
-- (void)setAppearanceForTabTitle:(NSString*)title
-                         favicon:(UIImage*)favicon
-                        cellSize:(CGSize)cellSize {
-  [_titleLabel setText:title];
-  [_snapshotButton setAccessibilityIdentifier:
-                      [NSString stringWithFormat:@"%@_button", title]];
-  [self contentView].accessibilityLabel = title;
-  if (favicon) {
-    [_favicon setImage:favicon];
-  } else {
-    [_favicon setImage:NativeImage(IDR_IOS_OMNIBOX_HTTP)];
-  }
-
-  // PLACEHOLDER: Set snapshot here.
-}
-
 - (void)setSessionType:(TabSwitcherSessionType)type {
   UIColor* topBarBackgroundColor;
   UIColor* closeButtonTintColor;
diff --git a/ios/clean/chrome/browser/ui/tab_collection/BUILD.gn b/ios/clean/chrome/browser/ui/tab_collection/BUILD.gn
index 2301668..66b56153 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/tab_collection/BUILD.gn
@@ -28,6 +28,8 @@
   ]
   deps = [
     "//base",
+    "//ios/chrome/app/theme:theme_grit",
+    "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/tab_switcher",
     "//ios/clean/chrome/browser/ui/commands",
   ]
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.h b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.h
index ca0ec15..ed919d5 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.h
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.h
@@ -9,12 +9,14 @@
 
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h"
 
+@class TabCollectionItem;
+
 // Cell represents a tab for use in the tab collection. It has a title, favicon,
 // screenshot image, and close button. Cell selection is represented by a border
 // highlight in the tintColor.
 // PLACEHOLDER: Create custom implemementation rather than subclassing.
 @interface TabCollectionTabCell : TabSwitcherLocalSessionCell
-
+@property(nonatomic, strong) TabCollectionItem* item;
 @end
 
 #endif  // IOS_CLEAN_CHROME_BROWSER_UI_TAB_COLLECTION_TAB_COLLECTION_TAB_CELL_H_
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.mm b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.mm
index ff06540a..f9c7914 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.mm
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.mm
@@ -4,6 +4,11 @@
 
 #import "ios/clean/chrome/browser/ui/tab_collection/tab_collection_tab_cell.h"
 
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_button.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_theme_resources.h"
+#import "ios/clean/chrome/browser/ui/tab_collection/tab_collection_item.h"
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -14,7 +19,17 @@
 const CGFloat kSelectedBorderWidth = 4.0f;
 }
 
+@interface TabCollectionTabCell ()
+@property(nonatomic, strong) UILabel* titleLabel;
+@property(nonatomic, strong) UIImageView* favicon;
+@property(nonatomic, strong) TabSwitcherButton* snapshotButton;
+@end
+
 @implementation TabCollectionTabCell
+@synthesize item = _item;
+@dynamic titleLabel;
+@dynamic favicon;
+@dynamic snapshotButton;
 
 - (instancetype)initWithFrame:(CGRect)frame {
   if ((self = [super initWithFrame:frame])) {
@@ -23,6 +38,20 @@
   return self;
 }
 
+#pragma mark - Properties
+
+- (void)setItem:(TabCollectionItem*)item {
+  DCHECK(item);
+  _item = item;
+  self.titleLabel.text = item.title;
+  self.snapshotButton.accessibilityIdentifier =
+      [NSString stringWithFormat:@"%@_button", item.title];
+  self.contentView.accessibilityLabel = item.title;
+  self.favicon.image = NativeImage(IDR_IOS_OMNIBOX_HTTP);
+}
+
+#pragma mark - Private
+
 - (void)setupSelectedBackgroundView {
   self.selectedBackgroundView = [[UIView alloc] init];
   self.selectedBackgroundView.backgroundColor = [UIColor blackColor];
diff --git a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.mm b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.mm
index 4fa101e..dba8b4c 100644
--- a/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.mm
+++ b/ios/clean/chrome/browser/ui/tab_collection/tab_collection_view_controller.mm
@@ -118,9 +118,7 @@
   [cell setSessionType:TabSwitcherSessionType::REGULAR_SESSION];
   DCHECK_LE(indexPath.item, INT_MAX);
   int index = static_cast<int>(indexPath.item);
-  [cell setAppearanceForTabTitle:self.items[index].title
-                         favicon:nil
-                        cellSize:CGSizeZero];
+  cell.item = self.items[index];
   return cell;
 }
 
@@ -194,9 +192,7 @@
   TabCollectionTabCell* cell = base::mac::ObjCCastStrict<TabCollectionTabCell>(
       [self.tabs cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index
                                                             inSection:0]]);
-  [cell setAppearanceForTabTitle:self.items[index].title
-                         favicon:nil
-                        cellSize:CGSizeZero];
+  cell.item = self.items[index];
 }
 
 - (void)populateItems:(NSArray<TabCollectionItem*>*)items
diff --git a/ios/web/webui/web_ui_ios_data_source_impl.mm b/ios/web/webui/web_ui_ios_data_source_impl.mm
index 189722f..80da6f8 100644
--- a/ios/web/webui/web_ui_ios_data_source_impl.mm
+++ b/ios/web/webui/web_ui_ios_data_source_impl.mm
@@ -66,7 +66,7 @@
       source_name_(source_name),
       default_resource_(-1),
       deny_xframe_options_(true),
-      load_time_data_defaults_added_(true),
+      load_time_data_defaults_added_(false),
       replace_existing_source_(true) {}
 
 WebUIIOSDataSourceImpl::~WebUIIOSDataSourceImpl() {}
@@ -142,10 +142,10 @@
 }
 
 void WebUIIOSDataSourceImpl::EnsureLoadTimeDataDefaultsAdded() {
-  if (!load_time_data_defaults_added_)
+  if (load_time_data_defaults_added_)
     return;
 
-  load_time_data_defaults_added_ = false;
+  load_time_data_defaults_added_ = true;
   base::DictionaryValue defaults;
   webui::SetLoadTimeDataDefaults(web::GetWebClient()->GetApplicationLocale(),
                                  &defaults);
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index cbd429c..2986a27c 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -350,7 +350,8 @@
 
 bool WebMediaPlayerImpl::SupportsOverlayFullscreenVideo() {
 #if defined(OS_ANDROID)
-  return !using_media_player_renderer_;
+  return !using_media_player_renderer_ &&
+         overlay_mode_ == OverlayMode::kUseContentVideoView;
 #else
   return false;
 #endif
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 2c234226e..1e5b1d0 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -182,6 +182,9 @@
     sources += [
       "android/device_info.cc",
       "android/device_info.h",
+      "android/promotion_hint_aggregator.h",
+      "android/promotion_hint_aggregator_impl.cc",
+      "android/promotion_hint_aggregator_impl.h",
       "android_video_decode_accelerator.cc",
       "android_video_decode_accelerator.h",
       "android_video_surface_chooser.h",
@@ -464,6 +467,7 @@
       "android/fake_codec_allocator.h",
       "android/mock_device_info.cc",
       "android/mock_device_info.h",
+      "android/promotion_hint_aggregator_impl_unittest.cc",
       "android_video_decode_accelerator_unittest.cc",
       "android_video_surface_chooser_impl_unittest.cc",
       "avda_codec_allocator_unittest.cc",
diff --git a/media/gpu/android/promotion_hint_aggregator.h b/media/gpu/android/promotion_hint_aggregator.h
new file mode 100644
index 0000000..44e5561
--- /dev/null
+++ b/media/gpu/android/promotion_hint_aggregator.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 MEDIA_GPU_ANDROID_PROMOTION_HINT_AGGREGATOR_H_
+#define MEDIA_GPU_ANDROID_PROMOTION_HINT_AGGREGATOR_H_
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "media/gpu/media_gpu_export.h"
+
+namespace media {
+
+// Receive lots of promotion hints, and aggregate them into a single signal.  A
+// promotion hint is feedback from the compositor about whether a quad could be
+// promoted to an overlay, or whether the compositor would refuse to do so.
+// For example, the compositor won't promote a quad that's rotated, since an
+// overlay can't do that.
+class MEDIA_GPU_EXPORT PromotionHintAggregator {
+ public:
+  struct Hint {
+    int x = 0;
+    int y = 0;
+    bool is_promotable = false;
+  };
+
+  using NotifyPromotionHintCB = base::Callback<void(const Hint& hint)>;
+
+  virtual ~PromotionHintAggregator() = default;
+
+  // Notify us that an image has / would be drawn with the given hint.
+  virtual void NotifyPromotionHint(const Hint& hint) = 0;
+
+  // Returns true if and only if it's probably okay to promote the video.
+  virtual bool IsSafeToPromote() = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_ANDROID_PROMOTION_HINT_AGGREGATOR_H_
diff --git a/media/gpu/android/promotion_hint_aggregator_impl.cc b/media/gpu/android/promotion_hint_aggregator_impl.cc
new file mode 100644
index 0000000..ec5ab788
--- /dev/null
+++ b/media/gpu/android/promotion_hint_aggregator_impl.cc
@@ -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.
+
+#include "media/gpu/android/promotion_hint_aggregator_impl.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/time/default_tick_clock.h"
+
+namespace media {
+
+// Minimum amount of time between promotable frames before we start over.  The
+// idea is to prevent promoting on paused / background rendering.  Note that
+// this time is only enforced when transitioning from unpromotable to promotable
+// frames.  We don't unpromote later because of this.
+constexpr base::TimeDelta MaximumInterFrameTime =
+    base::TimeDelta::FromMilliseconds(100);
+
+// Minimum number of consecutive promotable frames before we actually start
+// promoting frames.
+constexpr int MinimumPromotableFrames = 10;
+
+// Minimum time since the last unpromotable frame that we require before we will
+// promote new ones.
+constexpr base::TimeDelta MinimumUnpromotableFrameTime =
+    base::TimeDelta::FromMilliseconds(2000);
+
+PromotionHintAggregatorImpl::PromotionHintAggregatorImpl(
+    base::TickClock* tick_clock)
+    : weak_ptr_factory_(this) {
+  if (!tick_clock) {
+    clock_we_own_ = base::MakeUnique<base::DefaultTickClock>();
+    tick_clock = clock_we_own_.get();
+  }
+
+  tick_clock_ = tick_clock;
+}
+
+PromotionHintAggregatorImpl::~PromotionHintAggregatorImpl() {}
+
+void PromotionHintAggregatorImpl::NotifyPromotionHint(const Hint& hint) {
+  base::TimeTicks now = tick_clock_->NowTicks();
+
+  if (!hint.is_promotable) {
+    most_recent_unpromotable_ = now;
+    consecutive_promotable_frames_ = 0;
+  } else if (!IsSafeToPromote() &&
+             now - most_recent_update_ > MaximumInterFrameTime) {
+    // Promotable, but we aren't getting frames fast enough to count.  We
+    // don't want to transition to promotable unless frames are actually
+    // playing.  We check IsSafeToPromote() so that we don't transition back
+    // to unpromotable just because it's paused; that would cause the frame
+    // to become unrenderable.  We just want to delay the transition into
+    // promotable until it works.
+    consecutive_promotable_frames_ = 1;
+  } else {
+    // Promotable frame, and we're getting frames fast enough.
+    consecutive_promotable_frames_++;
+  }
+
+  most_recent_update_ = now;
+}
+
+bool PromotionHintAggregatorImpl::IsSafeToPromote() {
+  base::TimeTicks now = tick_clock_->NowTicks();
+  base::TimeDelta since_last_unpromotable = now - most_recent_unpromotable_;
+
+  return consecutive_promotable_frames_ >= MinimumPromotableFrames &&
+         since_last_unpromotable >= MinimumUnpromotableFrameTime;
+}
+
+}  // namespace media
diff --git a/media/gpu/android/promotion_hint_aggregator_impl.h b/media/gpu/android/promotion_hint_aggregator_impl.h
new file mode 100644
index 0000000..94ba04f
--- /dev/null
+++ b/media/gpu/android/promotion_hint_aggregator_impl.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 MEDIA_GPU_ANDROID_PROMOTION_HINT_AGGREGATOR_IMPL_H_
+#define MEDIA_GPU_ANDROID_PROMOTION_HINT_AGGREGATOR_IMPL_H_
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/tick_clock.h"
+#include "media/gpu/android/promotion_hint_aggregator.h"
+#include "media/gpu/media_gpu_export.h"
+
+namespace media {
+
+// Receive lots of promotion hints, and aggregate them into a single signal.
+class MEDIA_GPU_EXPORT PromotionHintAggregatorImpl
+    : public PromotionHintAggregator {
+ public:
+  // |tick_clock| may be null, in which case we will use wall clock.  If it is
+  // not null, then it must outlive |this|.  It is provided for tests.
+  PromotionHintAggregatorImpl(base::TickClock* tick_clock = nullptr);
+  ~PromotionHintAggregatorImpl() override;
+
+  void NotifyPromotionHint(const Hint& hint) override;
+  bool IsSafeToPromote() override;
+
+ private:
+  // Clock, which we might not own, that we'll use.
+  base::TickClock* tick_clock_;
+
+  // Will be non-null if we allocate our own clock.  Use |tick_clock| instead.
+  std::unique_ptr<base::TickClock> clock_we_own_;
+
+  // When did we receive the most recent "not promotable" frame?
+  base::TimeTicks most_recent_unpromotable_;
+
+  // When did we last receive an update?
+  base::TimeTicks most_recent_update_;
+
+  // Number of frames which were promotable in a row.
+  int consecutive_promotable_frames_ = 0;
+
+  base::WeakPtrFactory<PromotionHintAggregatorImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PromotionHintAggregatorImpl);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_ANDROID_PROMOTION_HINT_AGGREGATOR_IMPL_H_
diff --git a/media/gpu/android/promotion_hint_aggregator_impl_unittest.cc b/media/gpu/android/promotion_hint_aggregator_impl_unittest.cc
new file mode 100644
index 0000000..c8a16aeb
--- /dev/null
+++ b/media/gpu/android/promotion_hint_aggregator_impl_unittest.cc
@@ -0,0 +1,98 @@
+// 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 "media/gpu/android/promotion_hint_aggregator_impl.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+
+namespace {
+// Default elapsed time between frames.
+constexpr TimeDelta FrameTime = TimeDelta::FromMilliseconds(10);
+}  // namespace
+
+namespace media {
+
+// Unit tests for PromotionHintAggregatorImplTest
+class PromotionHintAggregatorImplTest : public testing::Test {
+ public:
+  ~PromotionHintAggregatorImplTest() override {}
+
+  void SetUp() override {
+    // Advance the clock so that time 0 isn't recent.
+    tick_clock_.Advance(TimeDelta::FromSeconds(10000));
+    impl_ = base::MakeUnique<PromotionHintAggregatorImpl>(&tick_clock_);
+  }
+
+  void TearDown() override {}
+
+  // Sends a new frame that's |is_promotable| or not, with |elapsed| since the
+  // previous frame.  Returns whether the video is promotable.
+  bool SendFrame(bool is_promotable, TimeDelta elapsed = FrameTime) {
+    tick_clock_.Advance(elapsed);
+    PromotionHintAggregator::Hint hint;
+    hint.is_promotable = is_promotable;
+    impl_->NotifyPromotionHint(hint);
+    return impl_->IsSafeToPromote();
+  }
+
+  base::SimpleTestTickClock tick_clock_;
+
+  std::unique_ptr<PromotionHintAggregatorImpl> impl_;
+};
+
+TEST_F(PromotionHintAggregatorImplTest, InitiallyNotPromotable) {
+  // A new aggregator shouldn't promote.
+  ASSERT_FALSE(impl_->IsSafeToPromote());
+}
+
+TEST_F(PromotionHintAggregatorImplTest, SomePromotableFramesArePromotable) {
+  // We should have to send 10 frames before promoting.
+  for (int i = 0; i < 9; i++)
+    ASSERT_FALSE(SendFrame(true));
+  ASSERT_TRUE(SendFrame(true));
+
+  // Waiting a while should't cause un-promotion.
+  ASSERT_TRUE(SendFrame(true, TimeDelta::FromMilliseconds(10000)));
+  ASSERT_TRUE(SendFrame(true, TimeDelta::FromMilliseconds(10000)));
+}
+
+TEST_F(PromotionHintAggregatorImplTest, UnpromotableFramesDelayPromotion) {
+  // Start with an unpromotable frame.
+  ASSERT_FALSE(SendFrame(false));
+  base::TimeTicks start = tick_clock_.NowTicks();
+
+  // Send more until the minimum time has elapsed.  Note that this will also be
+  // at least enough promotable frames in a row.
+  while (tick_clock_.NowTicks() - start + FrameTime < TimeDelta::FromSeconds(2))
+    ASSERT_FALSE(SendFrame(true));
+
+  // The next frame should do it.
+  ASSERT_TRUE(SendFrame(true));
+}
+
+TEST_F(PromotionHintAggregatorImplTest, PromotableFramesMustBeFastEnough) {
+  // Send some promotable frames, but not enough to promote.
+  for (int i = 0; i < 8; i++)
+    ASSERT_FALSE(SendFrame(true));
+
+  // Time passes.
+  tick_clock_.Advance(TimeDelta::FromMilliseconds(500));
+
+  // We should now start over.
+  for (int i = 0; i < 9; i++)
+    ASSERT_FALSE(SendFrame(true));
+  ASSERT_TRUE(SendFrame(true));
+}
+
+}  // namespace media
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index 2a56994..f22e5e7 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -35,6 +35,7 @@
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_decoder_config.h"
 #include "media/gpu/android/device_info.h"
+#include "media/gpu/android/promotion_hint_aggregator_impl.h"
 #include "media/gpu/android_video_surface_chooser_impl.h"
 #include "media/gpu/avda_picture_buffer_manager.h"
 #include "media/gpu/content_video_view_overlay.h"
@@ -249,6 +250,8 @@
       device_info_(device_info),
       force_defer_surface_creation_for_testing_(false),
       overlay_factory_cb_(overlay_factory_cb),
+      promotion_hint_aggregator_(
+          base::MakeUnique<PromotionHintAggregatorImpl>()),
       weak_this_factory_(this) {}
 
 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() {
@@ -889,13 +892,23 @@
   if (size_changed)
     picture_buffer.set_size(size_);
 
-  // TODO(liberato): request a hint for promotability.  crbug.com/671365 .
+  // Only ask for promotion hints if we can actually switch surfaces.
+  const bool want_promotion_hint = device_info_->IsSetOutputSurfaceSupported();
   const bool allow_overlay = picture_buffer_manager_.ArePicturesOverlayable();
   UMA_HISTOGRAM_BOOLEAN("Media.AVDA.FrameSentAsOverlay", allow_overlay);
+  // We unconditionally mark the picture as overlayable, even if
+  // |!allow_overlay|, if we want to get hints.  It's required, else we won't
+  // get hints.
   // TODO(hubbe): Insert the correct color space. http://crbug.com/647725
   Picture picture(picture_buffer_id, bitstream_id, gfx::Rect(size_),
-                  gfx::ColorSpace(), allow_overlay);
+                  gfx::ColorSpace(),
+                  want_promotion_hint ? true : allow_overlay);
   picture.set_size_changed(size_changed);
+  if (want_promotion_hint) {
+    picture.set_wants_promotion_hint(true);
+    // This will prevent it from actually being promoted if it shouldn't be.
+    picture.set_surface_texture(!allow_overlay);
+  }
 
   // Notify picture ready before calling UseCodecBufferForPictureBuffer() since
   // that process may be slow and shouldn't delay delivery of the frame to the
@@ -1539,6 +1552,23 @@
     client_->NotifyError(error);
 }
 
+PromotionHintAggregator::NotifyPromotionHintCB
+AndroidVideoDecodeAccelerator::GetPromotionHintCB() {
+  return base::Bind(&AndroidVideoDecodeAccelerator::NotifyPromotionHint,
+                    weak_this_factory_.GetWeakPtr());
+}
+
+void AndroidVideoDecodeAccelerator::NotifyPromotionHint(
+    const PromotionHintAggregator::Hint& hint) {
+  promotion_hint_aggregator_->NotifyPromotionHint(hint);
+  bool promotable = promotion_hint_aggregator_->IsSafeToPromote();
+  if (promotable != chooser_state_.is_compositor_promotable) {
+    chooser_state_.is_compositor_promotable = promotable;
+    surface_chooser_->UpdateState(base::Optional<AndroidOverlayFactoryCB>(),
+                                  chooser_state_);
+  }
+}
+
 void AndroidVideoDecodeAccelerator::ManageTimer(bool did_work) {
   bool should_be_running = true;
 
diff --git a/media/gpu/android_video_decode_accelerator.h b/media/gpu/android_video_decode_accelerator.h
index 4f74687..f01ea5ab 100644
--- a/media/gpu/android_video_decode_accelerator.h
+++ b/media/gpu/android_video_decode_accelerator.h
@@ -35,6 +35,7 @@
 
 namespace media {
 class SharedMemoryRegion;
+class PromotionHintAggregator;
 
 // A VideoDecodeAccelerator implementation for Android. This class decodes the
 // encoded input stream using Android's MediaCodec. It handles the work of
@@ -80,6 +81,7 @@
   // failure.  If deferred init is pending, then we'll fail deferred init.
   // Otherwise, we'll signal errors normally.
   void NotifyError(Error error) override;
+  PromotionHintAggregator::NotifyPromotionHintCB GetPromotionHintCB() override;
 
   // AVDACodecAllocatorClient implementation:
   void OnCodecConfigured(
@@ -254,6 +256,9 @@
   // another codec.  Normally, one doesn't.
   void ReleaseCodecAndBundle();
 
+  // Send a |hint| to |promotion_hint_aggregator_|.
+  void NotifyPromotionHint(const PromotionHintAggregator::Hint& hint);
+
   // Used to DCHECK that we are called on the correct thread.
   base::ThreadChecker thread_checker_;
 
@@ -392,6 +397,8 @@
   // Optional factory to produce mojo AndroidOverlay instances.
   AndroidOverlayMojoFactoryCB overlay_factory_cb_;
 
+  std::unique_ptr<PromotionHintAggregator> promotion_hint_aggregator_;
+
   // WeakPtrFactory for posting tasks back to |this|.
   base::WeakPtrFactory<AndroidVideoDecodeAccelerator> weak_this_factory_;
 
diff --git a/media/gpu/android_video_surface_chooser.h b/media/gpu/android_video_surface_chooser.h
index 34a40ae1..67051a3 100644
--- a/media/gpu/android_video_surface_chooser.h
+++ b/media/gpu/android_video_surface_chooser.h
@@ -24,10 +24,11 @@
     // Does playback require a secure surface?
     bool is_secure = false;
 
-    // TODO(liberato): add compositor feedback.
-
     // Is the player's frame hidden / closed?
     bool is_frame_hidden = false;
+
+    // Is the compositor willing to promote this?
+    bool is_compositor_promotable = false;
   };
 
   // Notify the client that |overlay| is ready for use.  The client may get
diff --git a/media/gpu/android_video_surface_chooser_impl.cc b/media/gpu/android_video_surface_chooser_impl.cc
index 89df36d5..8266e208 100644
--- a/media/gpu/android_video_surface_chooser_impl.cc
+++ b/media/gpu/android_video_surface_chooser_impl.cc
@@ -106,10 +106,14 @@
   if (current_state_.is_fullscreen)
     new_overlay_state = kUsingOverlay;
 
-  // TODO(liberato): add other checks for things like "safe for overlay".
+  // If the compositor won't promote, then don't.
+  if (!current_state_.is_compositor_promotable)
+    new_overlay_state = kUsingSurfaceTexture;
 
   // If we need a secure surface, then we must choose an overlay.  The only way
-  // we won't is if we don't have a factory.
+  // we won't is if we don't have a factory.  If the compositor won't promote,
+  // we still use the overlay, since hopefully it's a temporary restriction.
+  // If we drop the overlay, then playback will fail.
   if (current_state_.is_secure)
     new_overlay_state = kUsingOverlay;
 
diff --git a/media/gpu/android_video_surface_chooser_impl_unittest.cc b/media/gpu/android_video_surface_chooser_impl_unittest.cc
index 010057a..acf4bd4f 100644
--- a/media/gpu/android_video_surface_chooser_impl_unittest.cc
+++ b/media/gpu/android_video_surface_chooser_impl_unittest.cc
@@ -56,16 +56,23 @@
 enum class IsFullscreen { No, Yes };
 enum class IsSecure { No, Yes };
 enum class IsFrameHidden { No, Yes };
+enum class IsCCPromotable { No, Yes };
 
 using TestParams = std::tuple<ShouldUseOverlay,
                               AllowDynamic,
                               IsFullscreen,
                               IsSecure,
-                              IsFrameHidden>;
+                              IsFrameHidden,
+                              IsCCPromotable>;
 
 // Useful macro for instantiating tests.
 #define Either(x) Values(x::No, x::Yes)
 
+// Check if a parameter of type |type| is Yes.  |n| is the location of the
+// parameter of that type.
+// c++14 can remove |n|, and std::get() by type.
+#define IsYes(type, n) (::testing::get<n>(GetParam()) == type::Yes);
+
 }  // namespace
 
 namespace media {
@@ -82,6 +89,9 @@
     // Advance the clock just so we're not at 0.
     tick_clock_.Advance(base::TimeDelta::FromSeconds(10));
 
+    // Don't prevent promotions because of the compositor.
+    chooser_state_.is_compositor_promotable = true;
+
     // We create a destruction observer.  By default, the overlay must not be
     // destroyed until the test completes.  Of course, the test may ask the
     // observer to expect something else.
@@ -288,17 +298,16 @@
 }
 
 TEST_P(AndroidVideoSurfaceChooserImplTest, OverlayIsUsedOrNotBasedOnState) {
-// Provide a factory, and verify that it is used when the state says that it
-// should be.  If the overlay is used, then we also verify that it does not
-// switch to SurfaceTexture first, since pre-M requires it.
+  // Provide a factory, and verify that it is used when the state says that it
+  // should be.  If the overlay is used, then we also verify that it does not
+  // switch to SurfaceTexture first, since pre-M requires it.
 
-// c++14 can remove |n|, and std::get() by type.
-#define IsTrue(x, n) (::testing::get<n>(GetParam()) == x::Yes);
-  const bool should_use_overlay = IsTrue(ShouldUseOverlay, 0);
-  allow_dynamic_ = IsTrue(AllowDynamic, 1);
-  chooser_state_.is_fullscreen = IsTrue(IsFullscreen, 2);
-  chooser_state_.is_secure = IsTrue(IsSecure, 3);
-  chooser_state_.is_frame_hidden = IsTrue(IsFrameHidden, 4);
+  const bool should_use_overlay = IsYes(ShouldUseOverlay, 0);
+  allow_dynamic_ = IsYes(AllowDynamic, 1);
+  chooser_state_.is_fullscreen = IsYes(IsFullscreen, 2);
+  chooser_state_.is_secure = IsYes(IsSecure, 3);
+  chooser_state_.is_frame_hidden = IsYes(IsFrameHidden, 4);
+  chooser_state_.is_compositor_promotable = IsYes(IsCCPromotable, 5);
 
   if (should_use_overlay) {
     EXPECT_CALL(client_, UseSurfaceTexture()).Times(0);
@@ -323,27 +332,45 @@
                                 Either(AllowDynamic),
                                 Values(IsFullscreen::No),
                                 Values(IsSecure::No),
-                                Values(IsFrameHidden::No)));
+                                Values(IsFrameHidden::No),
+                                Either(IsCCPromotable)));
 INSTANTIATE_TEST_CASE_P(FullscreenUsesOverlay,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::Yes),
                                 Either(AllowDynamic),
                                 Values(IsFullscreen::Yes),
                                 Values(IsSecure::No),
-                                Values(IsFrameHidden::No)));
+                                Values(IsFrameHidden::No),
+                                Values(IsCCPromotable::Yes)));
 INSTANTIATE_TEST_CASE_P(SecureUsesOverlay,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::Yes),
                                 Either(AllowDynamic),
                                 Either(IsFullscreen),
                                 Values(IsSecure::Yes),
-                                Values(IsFrameHidden::No)));
+                                Values(IsFrameHidden::No),
+                                Values(IsCCPromotable::Yes)));
+
 INSTANTIATE_TEST_CASE_P(HiddenFramesUseSurfaceTexture,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::No),
                                 Values(AllowDynamic::Yes),
                                 Either(IsFullscreen),
                                 Either(IsSecure),
-                                Values(IsFrameHidden::Yes)));
+                                Values(IsFrameHidden::Yes),
+                                Either(IsCCPromotable)));
+// For all dynamic cases, we shouldn't use an overlay if the compositor won't
+// promote it.  For L1, it will fail either way until the CC supports "must
+// promote" overlays, so we ignore those cases.  Non-dynamic is excluded, since
+// we don't get (or use) compositor feedback before the first frame.  At that
+// point, we've already chosen the output surface and can't switch it.
+INSTANTIATE_TEST_CASE_P(NotCCPromotableNotSecureUsesSurfaceTexture,
+                        AndroidVideoSurfaceChooserImplTest,
+                        Combine(Values(ShouldUseOverlay::No),
+                                Values(AllowDynamic::Yes),
+                                Either(IsFullscreen),
+                                Values(IsSecure::No),
+                                Values(IsFrameHidden::No),
+                                Values(IsCCPromotable::No)));
 
 }  // namespace media
diff --git a/media/gpu/avda_codec_image.cc b/media/gpu/avda_codec_image.cc
index d8688d2..00db66b 100644
--- a/media/gpu/avda_codec_image.cc
+++ b/media/gpu/avda_codec_image.cc
@@ -232,6 +232,17 @@
   YInvertMatrix(matrix);
 }
 
+void AVDACodecImage::NotifyPromotionHint(bool promotion_hint,
+                                         int display_x,
+                                         int display_y) {
+  // TODO(liberato): this should just be given to us.
+  PromotionHintAggregator::Hint hint;
+  hint.x = display_x;
+  hint.y = display_y;
+  hint.is_promotable = promotion_hint;
+  shared_state_->GetPromotionHintCB().Run(hint);
+}
+
 bool AVDACodecImage::IsCodecBufferOutstanding() const {
   static_assert(kUpdateOnly < 0 && kUpdateOnly > kRendered &&
                     kRendered > kInvalidCodecBufferIndex,
diff --git a/media/gpu/avda_codec_image.h b/media/gpu/avda_codec_image.h
index 4b0bd9c..c8c8c26 100644
--- a/media/gpu/avda_codec_image.h
+++ b/media/gpu/avda_codec_image.h
@@ -48,6 +48,9 @@
                     const std::string& dump_name) override;
   // gpu::gles2::GLStreamTextureMatrix implementation
   void GetTextureMatrix(float xform[16]) override;
+  void NotifyPromotionHint(bool promotion_hint,
+                           int display_x,
+                           int display_y) override;
 
   enum class UpdateMode {
     // Discards the codec buffer, no UpdateTexImage().
diff --git a/media/gpu/avda_picture_buffer_manager.cc b/media/gpu/avda_picture_buffer_manager.cc
index 330e309..1442d3e 100644
--- a/media/gpu/avda_picture_buffer_manager.cc
+++ b/media/gpu/avda_picture_buffer_manager.cc
@@ -66,6 +66,7 @@
   // Only do this once the surface texture is filled in, since the constructor
   // assumes that it will be.
   shared_state_ = new AVDASharedState(surface_bundle);
+  shared_state_->SetPromotionHintCB(state_provider_->GetPromotionHintCB());
 
   return true;
 }
diff --git a/media/gpu/avda_shared_state.cc b/media/gpu/avda_shared_state.cc
index 3dd8cec..57330b1 100644
--- a/media/gpu/avda_shared_state.cc
+++ b/media/gpu/avda_shared_state.cc
@@ -66,4 +66,14 @@
     surface_bundle_ = nullptr;
 }
 
+void AVDASharedState::SetPromotionHintCB(
+    PromotionHintAggregator::NotifyPromotionHintCB cb) {
+  promotion_hint_cb_ = cb;
+}
+
+const PromotionHintAggregator::NotifyPromotionHintCB&
+AVDASharedState::GetPromotionHintCB() {
+  return promotion_hint_cb_;
+}
+
 }  // namespace media
diff --git a/media/gpu/avda_shared_state.h b/media/gpu/avda_shared_state.h
index aa4bfd3..e41d40a4 100644
--- a/media/gpu/avda_shared_state.h
+++ b/media/gpu/avda_shared_state.h
@@ -10,6 +10,7 @@
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "media/base/android/android_overlay.h"
 #include "media/base/android/media_codec_bridge.h"
+#include "media/gpu/android/promotion_hint_aggregator.h"
 #include "media/gpu/avda_shared_state.h"
 #include "media/gpu/avda_surface_bundle.h"
 #include "ui/gl/gl_bindings.h"
@@ -86,6 +87,9 @@
 
   void ClearOverlay(AndroidOverlay* overlay);
 
+  void SetPromotionHintCB(PromotionHintAggregator::NotifyPromotionHintCB cb);
+  const PromotionHintAggregator::NotifyPromotionHintCB& GetPromotionHintCB();
+
  protected:
   virtual ~AVDASharedState();
 
@@ -97,6 +101,8 @@
 
   scoped_refptr<AVDASurfaceBundle> surface_bundle_;
 
+  PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb_;
+
   base::WeakPtrFactory<AVDASharedState> weak_this_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AVDASharedState);
diff --git a/media/gpu/avda_state_provider.h b/media/gpu/avda_state_provider.h
index 819992d..c1c0161 100644
--- a/media/gpu/avda_state_provider.h
+++ b/media/gpu/avda_state_provider.h
@@ -8,6 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/threading/thread_checker.h"
 #include "gpu/command_buffer/service/texture_manager.h"
+#include "media/gpu/android/promotion_hint_aggregator.h"
 #include "media/video/video_decode_accelerator.h"
 
 namespace gpu {
@@ -30,6 +31,10 @@
   // error state.
   virtual void NotifyError(VideoDecodeAccelerator::Error error) = 0;
 
+  // Return a callback that may be used to signal promotion hint info.
+  virtual PromotionHintAggregator::NotifyPromotionHintCB
+  GetPromotionHintCB() = 0;
+
  protected:
   ~AVDAStateProvider() = default;
 };
diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h
index d2f8ecfd72..044b9d0 100644
--- a/mojo/public/cpp/bindings/lib/array_serialization.h
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -278,8 +278,7 @@
         BelongsTo<typename MojomType::Element,
                   MojomTypeCategory::ASSOCIATED_INTERFACE |
                       MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST |
-                      MojomTypeCategory::HANDLE |
-                      MojomTypeCategory::INTERFACE |
+                      MojomTypeCategory::HANDLE | MojomTypeCategory::INTERFACE |
                       MojomTypeCategory::INTERFACE_REQUEST>::value>::type> {
   using UserType = typename std::remove_const<MaybeConstUserType>::type;
   using Data = typename MojomTypeTraits<MojomType>::Data;
@@ -289,14 +288,10 @@
   static size_t GetSerializedSize(UserTypeIterator* input,
                                   SerializationContext* context) {
     size_t element_count = input->GetSize();
-    if (BelongsTo<Element,
-                  MojomTypeCategory::ASSOCIATED_INTERFACE |
-                      MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST>::value) {
-      for (size_t i = 0; i < element_count; ++i) {
-        typename UserTypeIterator::GetNextResult next = input->GetNext();
-        size_t size = PrepareToSerialize<Element>(next, context);
-        DCHECK_EQ(size, 0u);
-      }
+    for (size_t i = 0; i < element_count; ++i) {
+      typename UserTypeIterator::GetNextResult next = input->GetNext();
+      size_t size = PrepareToSerialize<Element>(next, context);
+      DCHECK_EQ(size, 0u);
     }
     return sizeof(Data) + Align(element_count * sizeof(typename Data::Element));
   }
@@ -509,7 +504,9 @@
 
   static size_t PrepareToSerialize(MaybeConstUserType& input,
                                    SerializationContext* context) {
-    if (CallIsNullIfExists<Traits>(input))
+    const bool is_null = CallIsNullIfExists<Traits>(input);
+    context->null_states.container().push_back(is_null);
+    if (is_null)
       return 0;
     ArrayIterator<Traits, MaybeConstUserType> iterator(input);
     return Impl::GetSerializedSize(&iterator, context);
@@ -520,7 +517,7 @@
                         Data** output,
                         const ContainerValidateParams* validate_params,
                         SerializationContext* context) {
-    if (!CallIsNullIfExists<Traits>(input)) {
+    if (!context->IsNextFieldNull()) {
       MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
           validate_params->expected_num_elements != 0 &&
               Traits::GetSize(input) != validate_params->expected_num_elements,
diff --git a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
index 1641d55..cfc2fd9f 100644
--- a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
+++ b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
@@ -108,18 +108,18 @@
 struct Serializer<InterfacePtrDataView<Base>, InterfacePtr<T>> {
   static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
 
-  static size_t PrepareToSerialize(const InterfacePtr<T>& input,
+  static size_t PrepareToSerialize(InterfacePtr<T>& input,
                                    SerializationContext* context) {
+    InterfacePtrInfo<T> info = input.PassInterface();
+    context->handles.AddInterfaceInfo(info.PassHandle(), info.version());
     return 0;
   }
 
-  static void Serialize(InterfacePtr<T>& input,
+  static void Serialize(const InterfacePtr<T>& input,
                         Interface_Data* output,
                         SerializationContext* context) {
-    InterfacePtrInfo<T> info = input.PassInterface();
-    output->handle =
-        context->handles.AddHandle(ScopedHandle::From(info.PassHandle()));
-    output->version = info.version();
+    DCHECK(!input.is_bound());
+    context->handles.ConsumeNextSerializedInterfaceInfo(output);
   }
 
   static bool Deserialize(Interface_Data* input,
@@ -136,16 +136,17 @@
 struct Serializer<InterfaceRequestDataView<Base>, InterfaceRequest<T>> {
   static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
 
-  static size_t PrepareToSerialize(const InterfaceRequest<T>& input,
+  static size_t PrepareToSerialize(InterfaceRequest<T>& input,
                                    SerializationContext* context) {
+    context->handles.AddHandle(ScopedHandle::From(input.PassMessagePipe()));
     return 0;
   }
 
-  static void Serialize(InterfaceRequest<T>& input,
+  static void Serialize(const InterfaceRequest<T>& input,
                         Handle_Data* output,
                         SerializationContext* context) {
-    *output =
-        context->handles.AddHandle(ScopedHandle::From(input.PassMessagePipe()));
+    DCHECK(!input.is_pending());
+    context->handles.ConsumeNextSerializedHandle(output);
   }
 
   static bool Deserialize(Handle_Data* input,
@@ -159,15 +160,17 @@
 
 template <typename T>
 struct Serializer<ScopedHandleBase<T>, ScopedHandleBase<T>> {
-  static size_t PrepareToSerialize(const ScopedHandleBase<T>& input,
+  static size_t PrepareToSerialize(ScopedHandleBase<T>& input,
                                    SerializationContext* context) {
+    context->handles.AddHandle(ScopedHandle::From(std::move(input)));
     return 0;
   }
 
-  static void Serialize(ScopedHandleBase<T>& input,
+  static void Serialize(const ScopedHandleBase<T>& input,
                         Handle_Data* output,
                         SerializationContext* context) {
-    *output = context->handles.AddHandle(ScopedHandle::From(std::move(input)));
+    DCHECK(!input.is_valid());
+    context->handles.ConsumeNextSerializedHandle(output);
   }
 
   static bool Deserialize(Handle_Data* input,
diff --git a/mojo/public/cpp/bindings/lib/map_serialization.h b/mojo/public/cpp/bindings/lib/map_serialization.h
index 718a763..08287608 100644
--- a/mojo/public/cpp/bindings/lib/map_serialization.h
+++ b/mojo/public/cpp/bindings/lib/map_serialization.h
@@ -97,7 +97,9 @@
 
   static size_t PrepareToSerialize(MaybeConstUserType& input,
                                    SerializationContext* context) {
-    if (CallIsNullIfExists<Traits>(input))
+    const bool is_null = CallIsNullIfExists<Traits>(input);
+    context->null_states.container().push_back(is_null);
+    if (is_null)
       return 0;
 
     size_t struct_overhead = sizeof(Data);
@@ -118,7 +120,7 @@
                         SerializationContext* context) {
     DCHECK(validate_params->key_validate_params);
     DCHECK(validate_params->element_validate_params);
-    if (CallIsNullIfExists<Traits>(input)) {
+    if (context->IsNextFieldNull()) {
       *output = nullptr;
       return;
     }
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index c543994..6658e60 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -181,9 +181,10 @@
 Message::Message(uint32_t name,
                  uint32_t flags,
                  size_t payload_size,
-                 size_t payload_interface_id_count) {
+                 size_t payload_interface_id_count,
+                 std::vector<ScopedHandle>* handles) {
   CreateSerializedMessageObject(name, flags, payload_size,
-                                payload_interface_id_count, nullptr, &handle_,
+                                payload_interface_id_count, handles, &handle_,
                                 &payload_buffer_);
   data_ = payload_buffer_.data();
   data_size_ = payload_buffer_.size();
@@ -274,45 +275,6 @@
   return array_pointer ? array_pointer->storage() : nullptr;
 }
 
-void Message::AttachHandles(std::vector<ScopedHandle>* handles) {
-  DCHECK(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_, 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.
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.cc b/mojo/public/cpp/bindings/lib/serialization_context.cc
index 8e4d86b..5748d10 100644
--- a/mojo/public/cpp/bindings/lib/serialization_context.cc
+++ b/mojo/public/cpp/bindings/lib/serialization_context.cc
@@ -17,7 +17,7 @@
 
 SerializedHandleVector::~SerializedHandleVector() = default;
 
-Handle_Data SerializedHandleVector::AddHandle(mojo::ScopedHandle handle) {
+void SerializedHandleVector::AddHandle(mojo::ScopedHandle handle) {
   Handle_Data data;
   if (!handle.is_valid()) {
     data.value = kEncodedInvalidHandleValue;
@@ -26,7 +26,29 @@
     data.value = static_cast<uint32_t>(handles_.size());
     handles_.emplace_back(std::move(handle));
   }
-  return data;
+  serialized_handles_.emplace_back(data);
+}
+
+void SerializedHandleVector::AddInterfaceInfo(
+    mojo::ScopedMessagePipeHandle handle,
+    uint32_t version) {
+  AddHandle(ScopedHandle::From(std::move(handle)));
+  serialized_interface_versions_.emplace_back(version);
+}
+
+void SerializedHandleVector::ConsumeNextSerializedHandle(
+    Handle_Data* out_data) {
+  DCHECK_LT(next_serialized_handle_index_, serialized_handles_.size());
+  *out_data = serialized_handles_[next_serialized_handle_index_++];
+}
+
+void SerializedHandleVector::ConsumeNextSerializedInterfaceInfo(
+    Interface_Data* out_data) {
+  ConsumeNextSerializedHandle(&out_data->handle);
+  DCHECK_LT(next_serialized_version_index_,
+            serialized_interface_versions_.size());
+  out_data->version =
+      serialized_interface_versions_[next_serialized_version_index_++];
 }
 
 mojo::ScopedHandle SerializedHandleVector::TakeHandle(
@@ -50,7 +72,6 @@
 void SerializationContext::AttachHandlesToMessage(Message* message) {
   associated_endpoint_handles.swap(
       *message->mutable_associated_endpoint_handles());
-  message->AttachHandles(handles.mutable_handles());
 }
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.h b/mojo/public/cpp/bindings/lib/serialization_context.h
index 6a6e5142..bc355044 100644
--- a/mojo/public/cpp/bindings/lib/serialization_context.h
+++ b/mojo/public/cpp/bindings/lib/serialization_context.h
@@ -11,6 +11,7 @@
 #include <queue>
 #include <vector>
 
+#include "base/containers/stack_container.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
@@ -31,9 +32,22 @@
 
   size_t size() const { return handles_.size(); }
   std::vector<mojo::ScopedHandle>* mutable_handles() { return &handles_; }
+  std::vector<Handle_Data>* serialized_handles() {
+    return &serialized_handles_;
+  }
 
-  // Adds a handle to the handle list and returns its index for encoding.
-  Handle_Data AddHandle(mojo::ScopedHandle handle);
+  // Adds a handle to the handle and serialized handle data lists.
+  void AddHandle(mojo::ScopedHandle handle);
+
+  // Adds an interface info to the handle and serialized handle+version data
+  // lists.
+  void AddInterfaceInfo(mojo::ScopedMessagePipeHandle handle, uint32_t version);
+
+  // Consumes the next available serialized handle data.
+  void ConsumeNextSerializedHandle(Handle_Data* out_data);
+
+  // Consumes the next available serialized handle and version data.
+  void ConsumeNextSerializedInterfaceInfo(Interface_Data* out_data);
 
   // Takes a handle from the list of serialized handle data.
   mojo::ScopedHandle TakeHandle(const Handle_Data& encoded_handle);
@@ -52,6 +66,14 @@
   // Handles are owned by this object.
   std::vector<mojo::ScopedHandle> handles_;
 
+  // Serialized handle and (optional) version data. This is accumulated during
+  // pre-serialization by AddHandle and AddInterfaceInfo calls, and later
+  // consumed during serialization by ConsumeNextSerializedHandle/InterfaceInfo.
+  size_t next_serialized_handle_index_ = 0;
+  std::vector<Handle_Data> serialized_handles_;
+  size_t next_serialized_version_index_ = 0;
+  std::vector<uint32_t> serialized_interface_versions_;
+
   DISALLOW_COPY_AND_ASSIGN(SerializedHandleVector);
 };
 
@@ -61,13 +83,23 @@
 
   ~SerializationContext();
 
-  // Transfers ownership of any accumulated handles and associated endpoint
-  // handles into |*message|.
+  bool IsNextFieldNull() {
+    DCHECK_LT(null_state_index, null_states.container().size());
+    return null_states.container()[null_state_index++];
+  }
+
+  // Transfers ownership of any accumulated associated endpoint handles into
+  // |*message|.
   void AttachHandlesToMessage(Message* message);
 
   // Opaque context pointers returned by StringTraits::SetUpContext().
   std::unique_ptr<std::queue<void*>> custom_contexts;
 
+  // A container for tracking the null-ness of every nullable field as a
+  // message's arguments are walked during serialization.
+  base::StackVector<bool, 32> null_states;
+  size_t null_state_index = 0;
+
   // Stashes handles encoded in a message by index.
   SerializedHandleVector handles;
 
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h
index 6e0c758..68165dd 100644
--- a/mojo/public/cpp/bindings/lib/string_serialization.h
+++ b/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -24,7 +24,9 @@
 
   static size_t PrepareToSerialize(MaybeConstUserType& input,
                                    SerializationContext* context) {
-    if (CallIsNullIfExists<Traits>(input))
+    const bool is_null = CallIsNullIfExists<Traits>(input);
+    context->null_states.container().push_back(is_null);
+    if (is_null)
       return 0;
 
     void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
@@ -36,7 +38,7 @@
                         Buffer* buffer,
                         String_Data** output,
                         SerializationContext* context) {
-    if (CallIsNullIfExists<Traits>(input)) {
+    if (context->IsNextFieldNull()) {
       *output = nullptr;
       return;
     }
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index 9fabda4..cfd4664 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -45,13 +45,13 @@
   // 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.
+  // If |handles| is non-null, any handles in |*handles| are attached to the
+  // newly constructed message.
   Message(uint32_t name,
           uint32_t flags,
           size_t payload_size,
-          size_t payload_interface_id_count);
+          size_t payload_interface_id_count,
+          std::vector<ScopedHandle>* handles = nullptr);
 
   // Constructs a new serialized Message object from an existing
   // ScopedMessageHandle; e.g., one read from a message pipe.
@@ -148,11 +148,6 @@
     return &associated_endpoint_handles_;
   }
 
-  // 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. Takes ownership of the contents of |*handles|.
-  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
   // ownership of its internal storage and any attached handles.
diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h
index a0c7b4f..99e13a4 100644
--- a/mojo/public/cpp/bindings/sync_call_restrictions.h
+++ b/mojo/public/cpp/bindings/sync_call_restrictions.h
@@ -15,6 +15,8 @@
 #define ENABLE_SYNC_CALL_RESTRICTIONS 0
 #endif
 
+class ChromeSelectFileDialogFactory;
+
 namespace sync_preferences {
 class PrefServiceSyncable;
 }
@@ -79,7 +81,8 @@
   friend class prefs::PersistentPrefStoreClient;
   // Incognito pref service instances are created synchronously.
   friend class sync_preferences::PrefServiceSyncable;
-
+  // For file open and save dialogs created synchronously.
+  friend class ::ChromeSelectFileDialogFactory;
   // END ALLOWED USAGE.
 
   // BEGIN USAGE THAT NEEDS TO BE FIXED.
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index 3f5b8f44..a163458d 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -91,9 +91,7 @@
  public:
   ConnectorTest() {}
 
-  void SetUp() override {
-    CreateMessagePipe(nullptr, &handle0_, &handle1_);
-  }
+  void SetUp() override { CreateMessagePipe(nullptr, &handle0_, &handle1_); }
 
   void TearDown() override {}
 
@@ -101,9 +99,8 @@
       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);
+    Message message(1, 0, size, 0, &handles);
     memcpy(message.payload_buffer()->Allocate(size), text, size);
-    message.AttachHandles(&handles);
     return message;
   }
 
@@ -418,16 +415,14 @@
   Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
                        base::ThreadTaskRunnerHandle::Get());
   bool error_handler_called0 = false;
-  connector0.set_connection_error_handler(
-      base::Bind(&ForwardErrorHandler, &error_handler_called0,
-                 run_loop.QuitClosure()));
+  connector0.set_connection_error_handler(base::Bind(
+      &ForwardErrorHandler, &error_handler_called0, run_loop.QuitClosure()));
 
   Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
                        base::ThreadTaskRunnerHandle::Get());
   bool error_handler_called1 = false;
-  connector1.set_connection_error_handler(
-      base::Bind(&ForwardErrorHandler, &error_handler_called1,
-                 run_loop2.QuitClosure()));
+  connector1.set_connection_error_handler(base::Bind(
+      &ForwardErrorHandler, &error_handler_called1, run_loop2.QuitClosure()));
 
   const char kText[] = "hello world";
   Message message = CreateMessage(kText);
@@ -489,9 +484,8 @@
   base::RunLoop run_loop;
   // Configure the accumulator such that it pauses after the first message is
   // received.
-  MessageAccumulator accumulator(
-      base::Bind(&PauseConnectorAndRunClosure, &connector1,
-                 run_loop.QuitClosure()));
+  MessageAccumulator accumulator(base::Bind(
+      &PauseConnectorAndRunClosure, &connector1, run_loop.QuitClosure()));
   connector1.set_incoming_receiver(&accumulator);
 
   run_loop.Run();
diff --git a/mojo/public/cpp/bindings/tests/struct_unittest.cc b/mojo/public/cpp/bindings/tests/struct_unittest.cc
index a687052..fa9b370 100644
--- a/mojo/public/cpp/bindings/tests/struct_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc
@@ -124,15 +124,17 @@
 TEST_F(StructTest, Serialization_Basic) {
   RectPtr rect(MakeRect());
 
-  size_t size = mojo::internal::PrepareToSerialize<RectDataView>(rect, nullptr);
+  mojo::internal::SerializationContext context;
+  size_t size =
+      mojo::internal::PrepareToSerialize<RectDataView>(rect, &context);
   EXPECT_EQ(8U + 16U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::Rect_Data* data;
-  mojo::internal::Serialize<RectDataView>(rect, &buf, &data, nullptr);
+  mojo::internal::Serialize<RectDataView>(rect, &buf, &data, &context);
 
   RectPtr rect2;
-  mojo::internal::Deserialize<RectDataView>(data, &rect2, nullptr);
+  mojo::internal::Deserialize<RectDataView>(data, &rect2, &context);
 
   CheckRect(*rect2);
 }
@@ -155,16 +157,17 @@
 TEST_F(StructTest, Serialization_StructPointers) {
   RectPairPtr pair(RectPair::New(MakeRect(), MakeRect()));
 
+  mojo::internal::SerializationContext context;
   size_t size =
-      mojo::internal::PrepareToSerialize<RectPairDataView>(pair, nullptr);
+      mojo::internal::PrepareToSerialize<RectPairDataView>(pair, &context);
   EXPECT_EQ(8U + 16U + 2 * (8U + 16U), size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::RectPair_Data* data;
-  mojo::internal::Serialize<RectPairDataView>(pair, &buf, &data, nullptr);
+  mojo::internal::Serialize<RectPairDataView>(pair, &buf, &data, &context);
 
   RectPairPtr pair2;
-  mojo::internal::Deserialize<RectPairDataView>(data, &pair2, nullptr);
+  mojo::internal::Deserialize<RectPairDataView>(data, &pair2, &context);
 
   CheckRect(*pair2->first);
   CheckRect(*pair2->second);
@@ -179,8 +182,9 @@
   NamedRegionPtr region(
       NamedRegion::New(std::string("region"), std::move(rects)));
 
+  mojo::internal::SerializationContext context;
   size_t size =
-      mojo::internal::PrepareToSerialize<NamedRegionDataView>(region, nullptr);
+      mojo::internal::PrepareToSerialize<NamedRegionDataView>(region, &context);
   EXPECT_EQ(8U +            // header
                 8U +        // name pointer
                 8U +        // rects pointer
@@ -194,10 +198,10 @@
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::NamedRegion_Data* data;
-  mojo::internal::Serialize<NamedRegionDataView>(region, &buf, &data, nullptr);
+  mojo::internal::Serialize<NamedRegionDataView>(region, &buf, &data, &context);
 
   NamedRegionPtr region2;
-  mojo::internal::Deserialize<NamedRegionDataView>(data, &region2, nullptr);
+  mojo::internal::Deserialize<NamedRegionDataView>(data, &region2, &context);
 
   EXPECT_EQ("region", *region2->name);
 
@@ -212,8 +216,9 @@
   EXPECT_FALSE(region->name);
   EXPECT_FALSE(region->rects);
 
+  mojo::internal::SerializationContext context;
   size_t size =
-      mojo::internal::PrepareToSerialize<NamedRegionDataView>(region, nullptr);
+      mojo::internal::PrepareToSerialize<NamedRegionDataView>(region, &context);
   EXPECT_EQ(8U +      // header
                 8U +  // name pointer
                 8U,   // rects pointer
@@ -221,10 +226,10 @@
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::NamedRegion_Data* data;
-  mojo::internal::Serialize<NamedRegionDataView>(region, &buf, &data, nullptr);
+  mojo::internal::Serialize<NamedRegionDataView>(region, &buf, &data, &context);
 
   NamedRegionPtr region2;
-  mojo::internal::Deserialize<NamedRegionDataView>(data, &region2, nullptr);
+  mojo::internal::Deserialize<NamedRegionDataView>(data, &region2, &context);
 
   EXPECT_FALSE(region2->name);
   EXPECT_FALSE(region2->rects);
@@ -364,40 +369,42 @@
   {
     // Serialization of a null native struct.
     NativeStructPtr native;
+    mojo::internal::SerializationContext context;
     size_t size = mojo::internal::PrepareToSerialize<NativeStructDataView>(
-        native, nullptr);
+        native, &context);
     EXPECT_EQ(0u, size);
     mojo::internal::FixedBufferForTesting buf(size);
 
     Data* data = nullptr;
     mojo::internal::Serialize<NativeStructDataView>(std::move(native), &buf,
-                                                    &data, nullptr);
+                                                    &data, &context);
 
     EXPECT_EQ(nullptr, data);
 
     NativeStructPtr output_native;
     mojo::internal::Deserialize<NativeStructDataView>(data, &output_native,
-                                                      nullptr);
+                                                      &context);
     EXPECT_TRUE(output_native.is_null());
   }
 
   {
     // Serialization of a native struct with null data.
     NativeStructPtr native(NativeStruct::New());
+    mojo::internal::SerializationContext context;
     size_t size = mojo::internal::PrepareToSerialize<NativeStructDataView>(
-        native, nullptr);
+        native, &context);
     EXPECT_EQ(0u, size);
     mojo::internal::FixedBufferForTesting buf(size);
 
     Data* data = nullptr;
     mojo::internal::Serialize<NativeStructDataView>(std::move(native), &buf,
-                                                    &data, nullptr);
+                                                    &data, &context);
 
     EXPECT_EQ(nullptr, data);
 
     NativeStructPtr output_native;
     mojo::internal::Deserialize<NativeStructDataView>(data, &output_native,
-                                                      nullptr);
+                                                      &context);
     EXPECT_TRUE(output_native.is_null());
   }
 
@@ -405,20 +412,21 @@
     NativeStructPtr native(NativeStruct::New());
     native->data = std::vector<uint8_t>{'X', 'Y'};
 
+    mojo::internal::SerializationContext context;
     size_t size = mojo::internal::PrepareToSerialize<NativeStructDataView>(
-        native, nullptr);
+        native, &context);
     EXPECT_EQ(16u, size);
     mojo::internal::FixedBufferForTesting buf(size);
 
     Data* data = nullptr;
     mojo::internal::Serialize<NativeStructDataView>(std::move(native), &buf,
-                                                    &data, nullptr);
+                                                    &data, &context);
 
     EXPECT_NE(nullptr, data);
 
     NativeStructPtr output_native;
     mojo::internal::Deserialize<NativeStructDataView>(data, &output_native,
-                                                      nullptr);
+                                                      &context);
     ASSERT_TRUE(output_native);
     ASSERT_FALSE(output_native->data->empty());
     EXPECT_EQ(2u, output_native->data->size());
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc
index 159192f..92e25828 100644
--- a/mojo/public/cpp/bindings/tests/union_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -205,14 +205,15 @@
 TEST(UnionTest, EnumSerialization) {
   PodUnionPtr pod1(PodUnion::NewFEnum(AnEnum::SECOND));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(
-      pod1, false, nullptr);
+      pod1, false, &context);
   EXPECT_EQ(16U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::PodUnion_Data* data = nullptr;
   mojo::internal::Serialize<PodUnionDataView>(pod1, &buf, &data, false,
-                                              nullptr);
+                                              &context);
 
   PodUnionPtr pod2;
   mojo::internal::Deserialize<PodUnionDataView>(data, &pod2, nullptr);
@@ -226,13 +227,15 @@
   PodUnionPtr pod(PodUnion::New());
   pod->set_f_int8(10);
 
-  size_t size =
-      mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false,
+                                                                     &context);
   EXPECT_EQ(16U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::PodUnion_Data* data = nullptr;
-  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
+  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false,
+                                              &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
@@ -245,18 +248,21 @@
 TEST(UnionTest, SerializeNotNull) {
   PodUnionPtr pod(PodUnion::New());
   pod->set_f_int8(0);
-  size_t size =
-      mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false,
+                                                                     &context);
   mojo::internal::FixedBufferForTesting buf(size);
   internal::PodUnion_Data* data = nullptr;
-  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
+  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false,
+                                              &context);
   EXPECT_FALSE(data->is_null());
 }
 
 TEST(UnionTest, SerializeIsNullInlined) {
   PodUnionPtr pod;
-  size_t size =
-      mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false,
+                                                                     &context);
   EXPECT_EQ(16U, size);
   mojo::internal::FixedBufferForTesting buf(size);
   internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
@@ -266,7 +272,7 @@
   data->tag = PodUnion::Tag::F_UINT16;
   data->data.f_f_int16 = 20;
 
-  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, true, nullptr);
+  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, true, &context);
   EXPECT_TRUE(data->is_null());
 
   PodUnionPtr pod2;
@@ -276,12 +282,14 @@
 
 TEST(UnionTest, SerializeIsNullNotInlined) {
   PodUnionPtr pod;
-  size_t size =
-      mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false,
+                                                                     &context);
   EXPECT_EQ(16U, size);
   mojo::internal::FixedBufferForTesting buf(size);
   internal::PodUnion_Data* data = nullptr;
-  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
+  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false,
+                                              &context);
   EXPECT_EQ(nullptr, data);
 }
 
@@ -336,13 +344,15 @@
 TEST(UnionTest, UnknownEnumValueValidation) {
   PodUnionPtr pod(PodUnion::NewFEnum(static_cast<AnEnum>(0xFFFF)));
 
-  size_t size =
-      mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false,
+                                                                     &context);
   EXPECT_EQ(16U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::PodUnion_Data* data = nullptr;
-  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
+  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false,
+                                              &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
@@ -356,13 +366,15 @@
   PodUnionPtr pod(
       PodUnion::NewFExtensibleEnum(static_cast<AnExtensibleEnum>(0xFFFF)));
 
-  size_t size =
-      mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false,
+                                                                     &context);
   EXPECT_EQ(16U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::PodUnion_Data* data = nullptr;
-  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
+  mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false,
+                                              &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
@@ -415,12 +427,13 @@
   std::string hello("hello world");
   ObjectUnionPtr pod1(ObjectUnion::NewFString(hello));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      pod1, false, nullptr);
+      pod1, false, &context);
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(pod1, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   ObjectUnionPtr pod2;
   mojo::internal::Deserialize<ObjectUnionDataView>(data, &pod2, nullptr);
@@ -500,16 +513,17 @@
   array[1]->set_f_int16(12);
   EXPECT_EQ(2U, array.size());
 
+  mojo::internal::SerializationContext context;
   size_t size =
       mojo::internal::PrepareToSerialize<ArrayDataView<PodUnionDataView>>(
-          array, nullptr);
+          array, &context);
   EXPECT_EQ(40U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   mojo::internal::Array_Data<internal::PodUnion_Data>* data;
   mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
   mojo::internal::Serialize<ArrayDataView<PodUnionDataView>>(
-      array, &buf, &data, &validate_params, nullptr);
+      array, &buf, &data, &validate_params, &context);
 
   std::vector<PodUnionPtr> array2;
   mojo::internal::Deserialize<ArrayDataView<PodUnionDataView>>(data, &array2,
@@ -528,16 +542,17 @@
   array[0]->set_f_int8(10);
   EXPECT_EQ(2U, array.size());
 
+  mojo::internal::SerializationContext context;
   size_t size =
       mojo::internal::PrepareToSerialize<ArrayDataView<PodUnionDataView>>(
-          array, nullptr);
+          array, &context);
   EXPECT_EQ(40U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   mojo::internal::Array_Data<internal::PodUnion_Data>* data;
   mojo::internal::ContainerValidateParams validate_params(0, true, nullptr);
   mojo::internal::Serialize<ArrayDataView<PodUnionDataView>>(
-      array, &buf, &data, &validate_params, nullptr);
+      array, &buf, &data, &validate_params, &context);
 
   std::vector<PodUnionPtr> array2;
   mojo::internal::Deserialize<ArrayDataView<PodUnionDataView>>(data, &array2,
@@ -558,9 +573,10 @@
   array[1]->set_f_string("world");
   EXPECT_EQ(2U, array.size());
 
+  mojo::internal::SerializationContext context;
   size_t size =
       mojo::internal::PrepareToSerialize<ArrayDataView<ObjectUnionDataView>>(
-          array, nullptr);
+          array, &context);
   EXPECT_EQ(72U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
@@ -568,7 +584,7 @@
   mojo::internal::Array_Data<internal::ObjectUnion_Data>* data;
   mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
   mojo::internal::Serialize<ArrayDataView<ObjectUnionDataView>>(
-      array, &buf, &data, &validate_params, nullptr);
+      array, &buf, &data, &validate_params, &context);
 
   std::vector<char> new_buf;
   new_buf.resize(size);
@@ -635,13 +651,14 @@
   std::string hello("hello world");
   obj_struct->obj_union->set_f_string(hello);
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<SmallObjStructDataView>(
-      obj_struct, nullptr);
+      obj_struct, &context);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::SmallObjStruct_Data* data = nullptr;
   mojo::internal::Serialize<SmallObjStructDataView>(obj_struct, &buf, &data,
-                                                    nullptr);
+                                                    &context);
 
   SmallObjStructPtr deserialized;
   mojo::internal::Deserialize<SmallObjStructDataView>(data, &deserialized,
@@ -702,9 +719,10 @@
   SmallStructNonNullableUnionPtr small_struct(
       SmallStructNonNullableUnion::New());
 
+  mojo::internal::SerializationContext context;
   size_t size =
       mojo::internal::PrepareToSerialize<SmallStructNonNullableUnionDataView>(
-          small_struct, nullptr);
+          small_struct, &context);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::SmallStructNonNullableUnion_Data* data =
@@ -829,14 +847,15 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_dummy(std::move(dummy));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
   EXPECT_EQ(32U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   ObjectUnionPtr obj2;
   mojo::internal::Deserialize<ObjectUnionDataView>(data, &obj2, nullptr);
@@ -850,13 +869,14 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_dummy(std::move(dummy));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
@@ -874,13 +894,14 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_dummy(std::move(dummy));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
@@ -896,13 +917,14 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_nullable(std::move(dummy));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
@@ -932,14 +954,15 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_array_int8(std::move(array));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
   EXPECT_EQ(32U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   ObjectUnionPtr obj2;
   mojo::internal::Deserialize<ObjectUnionDataView>(data, &obj2, nullptr);
@@ -956,12 +979,13 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_array_int8(std::move(array));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
@@ -1062,14 +1086,15 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_pod_union(std::move(pod));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
   EXPECT_EQ(32U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   ObjectUnionPtr obj2;
   mojo::internal::Deserialize<ObjectUnionDataView>(data, &obj2, nullptr);
@@ -1083,14 +1108,15 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_pod_union(std::move(pod));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
   EXPECT_EQ(32U, size);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
@@ -1108,13 +1134,14 @@
   ObjectUnionPtr obj(ObjectUnion::New());
   obj->set_f_pod_union(std::move(pod));
 
+  mojo::internal::SerializationContext context;
   size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
-      obj, false, nullptr);
+      obj, false, &context);
 
   mojo::internal::FixedBufferForTesting buf(size);
   internal::ObjectUnion_Data* data = nullptr;
   mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
-                                                 nullptr);
+                                                 &context);
 
   void* raw_buf = buf.Leak();
   mojo::internal::ValidationContext validation_context(
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 fcf1596..bbc1777 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -161,7 +161,8 @@
   mojo::Message message(
       {{message_name}},
       mojo::Message::kFlagIsSync | mojo::Message::kFlagExpectsResponse,
-      size, serialization_context.associated_endpoint_count);
+      size, serialization_context.associated_endpoint_count,
+      serialization_context.handles.mutable_handles());
   {{build_message(params_struct, "param_%s", params_description,
                   "&serialization_context", "&message")}}
 
@@ -188,7 +189,8 @@
   constexpr uint32_t kFlags = 0;
 {%- endif %}
   mojo::Message message({{message_name}}, kFlags, size,
-                        serialization_context.associated_endpoint_count);
+                        serialization_context.associated_endpoint_count,
+                        serialization_context.handles.mutable_handles());
   {{build_message(params_struct, "in_%s", params_description,
                   "&serialization_context", "&message")}}
 
@@ -270,7 +272,8 @@
   uint32_t flags = (is_sync_ ? mojo::Message::kFlagIsSync : 0) |
                    mojo::Message::kFlagIsResponse;
   mojo::Message message({{message_name}}, flags, size,
-                        serialization_context.associated_endpoint_count);
+                        serialization_context.associated_endpoint_count,
+                        serialization_context.handles.mutable_handles());
   {{build_message(response_params_struct, "in_%s", params_description,
                   "&serialization_context", "&message")}}
   message.set_request_id(request_id_);
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
index bb5fb9c..3e43c88a 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -20,7 +20,8 @@
                               input_may_be_temp=False) -%}
   size_t size = sizeof({{struct|get_qualified_name_for_kind(internal=True)}});
 {%-   for pf in struct.packed.packed_fields_in_ordinal_order
-          if pf.field.kind|is_object_kind or pf.field.kind|is_associated_kind %}
+          if pf.field.kind|is_object_kind or
+              pf.field.kind|is_any_handle_or_interface_kind %}
 {%-     set name = pf.field.name -%}
 {%-     set kind = pf.field.kind -%}
 {%-     set original_input_field = input_field_pattern|format(name) %}
@@ -73,7 +74,14 @@
 {%-     set input_field = "in_%s"|format(name) if input_may_be_temp
                                                else original_input_field %}
 {%-     if input_may_be_temp %}
+{%-       if kind|is_associated_kind or kind|is_object_kind %}
   decltype({{original_input_field}}) in_{{name}} = {{original_input_field}};
+{%-       else %}
+  // Dummy input value. The serialized value is already stored in the
+  // SerializationContext.
+  typename std::remove_reference<decltype({{original_input_field}})>::type
+      in_{{name}};
+{%-       endif %}
 {%-     endif %}
 {%-   endif %}
 
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
index 835178bed..02af440 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
@@ -11,7 +11,9 @@
 
   static size_t PrepareToSerialize(MaybeConstUserType& input,
                                    SerializationContext* context) {
-    if (CallIsNullIfExists<Traits>(input))
+    const bool is_null = CallIsNullIfExists<Traits>(input);
+    context->null_states.container().push_back(is_null);
+    if (is_null)
       return 0;
 
     void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
@@ -27,7 +29,7 @@
                         Buffer* buffer,
                         {{data_type}}** output,
                         SerializationContext* context) {
-    if (CallIsNullIfExists<Traits>(input)) {
+    if (context->IsNextFieldNull()) {
       *output = nullptr;
       return;
     }
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
index b589ae91..cc513c3 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
@@ -12,8 +12,9 @@
                                    bool inlined,
                                    SerializationContext* context) {
     size_t size = inlined ? 0 : sizeof({{data_type}});
-
-    if (CallIsNullIfExists<Traits>(input))
+    const bool is_null = CallIsNullIfExists<Traits>(input);
+    context->null_states.container().push_back(is_null);
+    if (is_null)
       return size;
 
     void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
@@ -23,7 +24,8 @@
 {%- for field in union.fields %}
 {%-   set name = field.name %}
       case {{data_view}}::Tag::{{name|upper}}: {
-{%-   if field.kind|is_object_kind or field.kind|is_associated_kind %}
+{%-   if field.kind|is_object_kind or
+          field.kind|is_any_handle_or_interface_kind %}
 {%-     set kind = field.kind %}
 {%-     set serializer_type = kind|unmapped_type_for_serializer %}
         decltype(CallWithContext(Traits::{{name}}, input, custom_context))
@@ -49,7 +51,7 @@
                         {{data_type}}** output,
                         bool inlined,
                         SerializationContext* context) {
-    if (CallIsNullIfExists<Traits>(input)) {
+    if (context->IsNextFieldNull()) {
       if (inlined)
         (*output)->set_null();
       else
diff --git a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
index 962456c..30d6dbc8 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
@@ -287,7 +287,7 @@
 
 void It2MeNativeMessagingHostTest::SetPolicies(
     const base::DictionaryValue& dict) {
-  DCHECK(test_message_loop_->task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(test_message_loop_->task_runner()->RunsTasksInCurrentSequence());
   // Copy |dict| into |policy_bundle|.
   policy::PolicyNamespace policy_namespace =
       policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string());
@@ -494,7 +494,7 @@
 }
 
 void It2MeNativeMessagingHostTest::StartHost() {
-  DCHECK(host_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
 
   base::File input_read_file;
   base::File output_write_file;
@@ -536,7 +536,7 @@
 }
 
 void It2MeNativeMessagingHostTest::ExitTest() {
-  if (!test_message_loop_->task_runner()->RunsTasksOnCurrentThread()) {
+  if (!test_message_loop_->task_runner()->RunsTasksInCurrentSequence()) {
     test_message_loop_->task_runner()->PostTask(
         FROM_HERE,
         base::Bind(&It2MeNativeMessagingHostTest::ExitTest,
@@ -547,7 +547,7 @@
 }
 
 void It2MeNativeMessagingHostTest::ExitPolicyRunLoop() {
-  DCHECK(test_message_loop_->task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(test_message_loop_->task_runner()->RunsTasksInCurrentSequence());
   if (policy_run_loop_) {
     policy_run_loop_->Quit();
   }
diff --git a/remoting/host/native_messaging/native_messaging_reader.cc b/remoting/host/native_messaging/native_messaging_reader.cc
index 112a5db..26940eb 100644
--- a/remoting/host/native_messaging/native_messaging_reader.cc
+++ b/remoting/host/native_messaging/native_messaging_reader.cc
@@ -88,7 +88,7 @@
 NativeMessagingReader::Core::~Core() {}
 
 void NativeMessagingReader::Core::ReadMessage() {
-  DCHECK(read_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(read_task_runner_->RunsTasksInCurrentSequence());
 
   // Keep reading messages until the stream is closed or an error occurs.
   while (true) {
@@ -136,7 +136,7 @@
 }
 
 void NativeMessagingReader::Core::NotifyEof() {
-  DCHECK(read_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(read_task_runner_->RunsTasksInCurrentSequence());
   caller_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&NativeMessagingReader::InvokeEofCallback, reader_));
diff --git a/remoting/host/security_key/security_key_message_reader.cc b/remoting/host/security_key/security_key_message_reader.cc
index bb222110..c6ba1b1c6 100644
--- a/remoting/host/security_key/security_key_message_reader.cc
+++ b/remoting/host/security_key/security_key_message_reader.cc
@@ -32,7 +32,7 @@
 }
 
 SecurityKeyMessageReader::~SecurityKeyMessageReader() {
-  DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
 
   // In order to ensure the reader thread is stopped cleanly, we close the
   // stream it is blocking on and then wait for the thread to exit.
@@ -43,7 +43,7 @@
 void SecurityKeyMessageReader::Start(
     SecurityKeyMessageCallback message_callback,
     base::Closure error_callback) {
-  DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
 
   message_callback_ = message_callback;
   error_callback_ = error_callback;
@@ -56,7 +56,7 @@
 }
 
 void SecurityKeyMessageReader::ReadMessage() {
-  DCHECK(read_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(read_task_runner_->RunsTasksInCurrentSequence());
 
   while (true) {
     if (!read_stream_.IsValid()) {
@@ -112,7 +112,7 @@
 }
 
 void SecurityKeyMessageReader::NotifyError() {
-  DCHECK(read_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(read_task_runner_->RunsTasksInCurrentSequence());
 
   main_task_runner_->PostTask(
       FROM_HERE, base::Bind(&SecurityKeyMessageReader::InvokeErrorCallback,
@@ -121,12 +121,12 @@
 
 void SecurityKeyMessageReader::InvokeMessageCallback(
     std::unique_ptr<SecurityKeyMessage> message) {
-  DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   message_callback_.Run(std::move(message));
 }
 
 void SecurityKeyMessageReader::InvokeErrorCallback() {
-  DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   error_callback_.Run();
 }
 
diff --git a/remoting/host/security_key/security_key_message_reader_impl.cc b/remoting/host/security_key/security_key_message_reader_impl.cc
index 14b8f03..ae3b07e8 100644
--- a/remoting/host/security_key/security_key_message_reader_impl.cc
+++ b/remoting/host/security_key/security_key_message_reader_impl.cc
@@ -33,7 +33,7 @@
 }
 
 SecurityKeyMessageReaderImpl::~SecurityKeyMessageReaderImpl() {
-  DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
 
   // In order to ensure the reader thread is stopped cleanly, we want to stop
   // the thread before the task runners and weak pointers are invalidated.
@@ -43,7 +43,7 @@
 void SecurityKeyMessageReaderImpl::Start(
     const SecurityKeyMessageCallback& message_callback,
     const base::Closure& error_callback) {
-  DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
 
   message_callback_ = message_callback;
   error_callback_ = error_callback;
@@ -56,7 +56,7 @@
 }
 
 void SecurityKeyMessageReaderImpl::ReadMessage() {
-  DCHECK(read_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(read_task_runner_->RunsTasksInCurrentSequence());
 
   while (true) {
     if (!read_stream_.IsValid()) {
@@ -124,7 +124,7 @@
 }
 
 void SecurityKeyMessageReaderImpl::NotifyError() {
-  DCHECK(read_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(read_task_runner_->RunsTasksInCurrentSequence());
 
   main_task_runner_->PostTask(
       FROM_HERE, base::Bind(&SecurityKeyMessageReaderImpl::InvokeErrorCallback,
@@ -133,12 +133,12 @@
 
 void SecurityKeyMessageReaderImpl::InvokeMessageCallback(
     std::unique_ptr<SecurityKeyMessage> message) {
-  DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   message_callback_.Run(std::move(message));
 }
 
 void SecurityKeyMessageReaderImpl::InvokeErrorCallback() {
-  DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   error_callback_.Run();
 }
 
diff --git a/remoting/host/setup/me2me_native_messaging_host_unittest.cc b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
index c03f2a75..2f837578 100644
--- a/remoting/host/setup/me2me_native_messaging_host_unittest.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
@@ -322,7 +322,7 @@
 }
 
 void Me2MeNativeMessagingHostTest::StartHost() {
-  DCHECK(host_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
 
   base::File input_read_file;
   base::File output_write_file;
@@ -366,7 +366,7 @@
 }
 
 void Me2MeNativeMessagingHostTest::StopHost() {
-  DCHECK(host_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
 
   native_messaging_pipe_.reset();
 
@@ -378,7 +378,7 @@
 }
 
 void Me2MeNativeMessagingHostTest::ExitTest() {
-  if (!test_message_loop_->task_runner()->RunsTasksOnCurrentThread()) {
+  if (!test_message_loop_->task_runner()->RunsTasksInCurrentSequence()) {
     test_message_loop_->task_runner()->PostTask(
         FROM_HERE,
         base::Bind(&Me2MeNativeMessagingHostTest::ExitTest,
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index fbbc6e2a..e578cb0 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -87,7 +87,7 @@
     <output filename="remoting/webapp/_locales/da/messages.json" lang="da" type="chrome_messages_json"/>
     <output filename="remoting/webapp/_locales/de/messages.json" lang="de" type="chrome_messages_json"/>
     <output filename="remoting/webapp/_locales/fa/messages.json" lang="fa" type="chrome_messages_json"/>
-    <output filename="remoting/webapp/_locales/fake_bidi/messages.json" lang="fake_bidi" type="chrome_messages_json"/>
+    <output filename="remoting/webapp/_locales/fake_bidi/messages.json" lang="fake-bidi" type="chrome_messages_json"/>
     <output filename="remoting/webapp/_locales/el/messages.json" lang="el" type="chrome_messages_json"/>
     <output filename="remoting/webapp/_locales/en/messages.json" lang="en" type="chrome_messages_json"/>
     <if expr="chromeos or is_ios">
diff --git a/services/device/device_service.cc b/services/device/device_service.cc
index ca19aa5..271878b1 100644
--- a/services/device/device_service.cc
+++ b/services/device/device_service.cc
@@ -39,7 +39,7 @@
 
 #if defined(OS_ANDROID)
 std::unique_ptr<service_manager::Service> CreateDeviceService(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     const WakeLockContextCallback& wake_lock_context_callback,
     const base::android::JavaRef<jobject>& java_nfc_delegate) {
@@ -49,25 +49,25 @@
   }
 
   return base::MakeUnique<DeviceService>(
-      std::move(file_task_runner), std::move(io_task_runner),
+      std::move(blocking_task_runner), std::move(io_task_runner),
       wake_lock_context_callback, java_nfc_delegate);
 }
 #else
 std::unique_ptr<service_manager::Service> CreateDeviceService(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-  return base::MakeUnique<DeviceService>(std::move(file_task_runner),
+  return base::MakeUnique<DeviceService>(std::move(blocking_task_runner),
                                          std::move(io_task_runner));
 }
 #endif
 
 #if defined(OS_ANDROID)
 DeviceService::DeviceService(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     const WakeLockContextCallback& wake_lock_context_callback,
     const base::android::JavaRef<jobject>& java_nfc_delegate)
-    : file_task_runner_(std::move(file_task_runner)),
+    : blocking_task_runner_(std::move(blocking_task_runner)),
       io_task_runner_(std::move(io_task_runner)),
       wake_lock_context_callback_(wake_lock_context_callback),
       java_interface_provider_initialized_(false) {
@@ -75,9 +75,9 @@
 }
 #else
 DeviceService::DeviceService(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
-    : file_task_runner_(std::move(file_task_runner)),
+    : blocking_task_runner_(std::move(blocking_task_runner)),
       io_task_runner_(std::move(io_task_runner)) {}
 #endif
 
@@ -245,7 +245,7 @@
   if (io_task_runner_) {
     io_task_runner_->PostTask(
         FROM_HERE, base::Bind(&device::SensorProviderImpl::Create,
-                              file_task_runner_, base::Passed(&request)));
+                              blocking_task_runner_, base::Passed(&request)));
   }
 }
 
@@ -253,14 +253,14 @@
     const service_manager::BindSourceInfo& source_info,
     mojom::TimeZoneMonitorRequest request) {
   if (!time_zone_monitor_)
-    time_zone_monitor_ = TimeZoneMonitor::Create(file_task_runner_);
+    time_zone_monitor_ = TimeZoneMonitor::Create(blocking_task_runner_);
   time_zone_monitor_->Bind(std::move(request));
 }
 
 void DeviceService::BindWakeLockProviderRequest(
     const service_manager::BindSourceInfo& source_info,
     mojom::WakeLockProviderRequest request) {
-  WakeLockProvider::Create(std::move(request), file_task_runner_,
+  WakeLockProvider::Create(std::move(request), blocking_task_runner_,
                            wake_lock_context_callback_);
 }
 
diff --git a/services/device/device_service.h b/services/device/device_service.h
index 69ddb654..35e8814 100644
--- a/services/device/device_service.h
+++ b/services/device/device_service.h
@@ -6,6 +6,7 @@
 #define SERVICES_DEVICE_DEVICE_SERVICE_H_
 
 #include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
 #include "device/generic_sensor/public/interfaces/sensor_provider.mojom.h"
 #include "device/screen_orientation/public/interfaces/screen_orientation.mojom.h"
 #include "device/sensors/public/interfaces/motion.mojom.h"
@@ -41,25 +42,25 @@
 // and NFCDelegate.java to understand the semantics and usage of these
 // parameters.
 std::unique_ptr<service_manager::Service> CreateDeviceService(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     const WakeLockContextCallback& wake_lock_context_callback,
     const base::android::JavaRef<jobject>& java_nfc_delegate);
 #else
 std::unique_ptr<service_manager::Service> CreateDeviceService(
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
 #endif
 
 class DeviceService : public service_manager::Service {
  public:
 #if defined(OS_ANDROID)
-  DeviceService(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+  DeviceService(scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
                 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
                 const WakeLockContextCallback& wake_lock_context_callback,
                 const base::android::JavaRef<jobject>& java_nfc_delegate);
 #else
-  DeviceService(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+  DeviceService(scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
                 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
 #endif
   ~DeviceService() override;
@@ -122,7 +123,7 @@
   std::unique_ptr<PowerMonitorMessageBroadcaster>
       power_monitor_message_broadcaster_;
   std::unique_ptr<TimeZoneMonitor> time_zone_monitor_;
-  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
   WakeLockContextCallback wake_lock_context_callback_;
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b8d5041..b8c9ede2 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1909,22 +1909,13 @@
                     "name": "SearchOrTypeUrl"
                 },
                 {
-                    "name": "SearchOrTypeWebsiteName"
+                    "name": "SearchOrTypeWebAddress"
                 },
                 {
-                    "name": "SearchTheWeb"
+                    "name": "TypeWhatYouAreLookingFor"
                 },
                 {
-                    "name": "EnterASearchOrWebsite"
-                },
-                {
-                    "name": "SearchNews"
-                },
-                {
-                    "name": "SearchRecipes"
-                },
-                {
-                    "name": "SearchWeather"
+                    "name": "FindNewsRecipesWeather"
                 },
                 {
                     "name": "Blank"
@@ -3345,6 +3336,24 @@
             ]
         }
     ],
+    "VideoCaptureService": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "win",
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "MojoVideoCapture"
+                    ]
+                }
+            ]
+        }
+    ],
     "ViewsSimplifiedFullscreenUI": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations
index ae1ecbd..5ce9785 100644
--- a/third_party/WebKit/LayoutTests/W3CImportExpectations
+++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -174,7 +174,8 @@
 external/wpt/css/css-speech-1 [ Skip ]
 external/wpt/css/css-syntax-3 [ Skip ]
 external/wpt/css/css-style-attr [ Skip ]
-external/wpt/css/css-tables-3 [ Skip ]
+## Owners: dgrogan@chromium.org,joysyu@google.com
+# external/wpt/css/css-tables-3 [ Pass ]
 external/wpt/css/css-text-3/hanging-punctuation [ Skip ]
 ## Owners: kojii@chromium.org
 # external/wpt/css/css-text-3/i18n [ Pass ]
diff --git a/third_party/WebKit/LayoutTests/fast/forms/select/listbox-no-force-layout.html b/third_party/WebKit/LayoutTests/fast/forms/select/listbox-no-force-layout.html
new file mode 100644
index 0000000..1133966
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/forms/select/listbox-no-force-layout.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<body>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<select size="4" multiple>
+<option>option 1</option>
+<option>option 2</option>
+<option>option 3</option>
+<option>option 4</option>
+<option id="o5">option 5</option>
+</select>
+<script>
+var preCount;
+var t = async_test('OPTION selection should not force layout.');
+requestAnimationFrame(t.step_func(() => {
+  assert_exists(window, 'internals');
+  preCount = internals.forceLayoutCount;
+  // Make sure layout-dirty
+  document.body.append('text');
+  document.querySelector('#o5').selected = true;
+
+  requestAnimationFrame(t.step_func_done(() => {
+    assert_equals(internals.forceLayoutCount - preCount, 0);
+  }));
+}));
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/select/select-initial-position.html b/third_party/WebKit/LayoutTests/fast/forms/select/select-initial-position.html
index 7e24851..966565a 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/select/select-initial-position.html
+++ b/third_party/WebKit/LayoutTests/fast/forms/select/select-initial-position.html
@@ -143,6 +143,26 @@
 sel.appendChild(opt);
 </script>
 
-</body>
+<p>Display 'none' to 'inline-block'</p>
+<select style="display:none;" id="select-none" size="4">
+<option>option1</option>
+<option>option2</option>
+<option>option3</option>
+<option>option4</option>
+<option>option5</option>
+<option>option6</option>
+<option selected>This should be selected and visible.</option>
+</select>
+<script>
+// Force layout with display:none.
+document.body.offsetHeight;
+if (window.testRunner)
+  testRunner.waitUntilDone();
+requestAnimationFrame(() => {
+  document.querySelector('#select-none').style.display = 'inline-block';
+  testRunner.notifyDone();
+});
+</script>
 
+</body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-nameSources-input-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-nameSources-input-expected.txt
index 964fb32..aac4641 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-nameSources-input-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-nameSources-input-expected.txt
@@ -948,7 +948,7 @@
         "type": "relatedElement",
         "value": {
           "type": "computedString",
-          "value": "label-wrapping-text10 "
+          "value": "label-wrapping-text10"
         },
         "nativeSource": "labelwrapped",
         "nativeSourceValue": {
@@ -956,7 +956,7 @@
           "relatedNodes": [
             {
               "backendDOMNodeId": "<number>",
-              "text": "label-wrapping-text10 "
+              "text": "label-wrapping-text10"
             }
           ]
         }
@@ -1037,7 +1037,7 @@
         "type": "nodeList",
         "relatedNodes": [
           {
-            "text": "label-wrapping-text10 ",
+            "text": "label-wrapping-text10",
             "nodeResult": "label"
           }
         ]
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-nameSources-input.js b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-nameSources-input.js
index 2e46958..c6d5d94f 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-nameSources-input.js
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/accessibility/accessibility-nameSources-input.js
@@ -35,8 +35,7 @@
       <label for='text9'>label-for-text9</label>
       <label>label-wrapping-text9<input data-dump id='text9' type='text' title='text9-title' aria-placeholder='text9-aria-placeholder' placeholder='text9-placeholder'></label>
 
-      <label>label-wrapping-text10<input data-dump id='text10' type='text' title='text10-title' aria-placeholder='text10-aria-placeholder' placeholder='text10-placeholder'>
-      </label>
+      <label>label-wrapping-text10<input data-dump id='text10' type='text' title='text10-title' aria-placeholder='text10-aria-placeholder' placeholder='text10-placeholder'></label>
 
       <input data-dump id='text11' type='text'>
       <label for='text11'>first-label-for-text11</label>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/dom/dom-request-document-with-child-nodes-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/dom/dom-request-document-with-child-nodes-expected.txt
index ff1d5575..3339db2 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/dom/dom-request-document-with-child-nodes-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/dom/dom-request-document-with-child-nodes-expected.txt
@@ -60,7 +60,7 @@
                                                         [0] : {
                                                             attributes : [
                                                                 [0] : src
-                                                                [1] : ../dom/resources/shadow-dom-iframe.html
+                                                                [1] : ./shadow-dom-iframe.html
                                                             ]
                                                             backendNodeId : <backendNodeId>
                                                             childNodeCount : 0
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/dom/dom-request-document-with-child-nodes.js b/third_party/WebKit/LayoutTests/inspector-protocol/dom/dom-request-document-with-child-nodes.js
index 6c807ff..f9d57a7 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/dom/dom-request-document-with-child-nodes.js
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/dom/dom-request-document-with-child-nodes.js
@@ -1,14 +1,5 @@
 (async function(testRunner) {
-  var {page, session, dp} = await testRunner.startHTML(`
-    <div id='depth-1'>
-        <div id='depth-2'>
-            <div id='depth-3'>
-                <iframe src='../dom/resources/shadow-dom-iframe.html'></iframe>
-            </div>
-        </div>
-        <div id='targetDiv'></div>
-    </div>
-  `, '');
+  var {page, session, dp} = await testRunner.startURL('./resources/dom-request-document-with-child-nodes.html', '');
   var response = await dp.DOM.getDocument({depth: -1});
   var iframeOwner = response.result.root.children[0].children[1].children[0].children[0].children[0].children[0];
   if (iframeOwner.contentDocument.children) {
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/dom/resources/dom-request-document-with-child-nodes.html b/third_party/WebKit/LayoutTests/inspector-protocol/dom/resources/dom-request-document-with-child-nodes.html
new file mode 100644
index 0000000..08ec4a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/dom/resources/dom-request-document-with-child-nodes.html
@@ -0,0 +1,8 @@
+<div id='depth-1'>
+  <div id='depth-2'>
+    <div id='depth-3'>
+      <iframe src='./shadow-dom-iframe.html'></iframe>
+    </div>
+  </div>
+  <div id='targetDiv'></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-evaluate-without-enabling.js b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-evaluate-without-enabling.js
index dd9354ef..ce2e839 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-evaluate-without-enabling.js
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-evaluate-without-enabling.js
@@ -2,7 +2,9 @@
   let {page, session, dp} = await testRunner.startBlank(`Tests that default execution context accessed without enabling Runtime domain gets properly cleaned up on reload.`);
   await session.evaluate('window.dummyObject = { a : 1 };');
   var result = await dp.Runtime.evaluate({expression: 'window.dummyObject' });
-  await dp.Page.reload();
+  dp.Page.enable();
+  dp.Page.reload();
+  await dp.Page.onceLoadEventFired();
   testRunner.logMessage(await dp.Runtime.getProperties({ objectId: result.result.result.objectId, ownProperties: true }));
   testRunner.completeTest();
 })
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/stylesheet-tracking-restart.js b/third_party/WebKit/LayoutTests/inspector-protocol/stylesheet-tracking-restart.js
index 5ca5a040..d2ed8c2 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/stylesheet-tracking-restart.js
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/stylesheet-tracking-restart.js
@@ -18,10 +18,11 @@
   await runTest(session);
   testRunner.log('Closing inspector.');
   testRunner.log('\nRemoving style sheet.\n');
-  session.evaluate('setTimeout(() => { document.head.removeChild(styleElement1); document.body.offsetWidth; }, 0)');
+  session.evaluate('Promise.resolve().then(() => { document.head.removeChild(styleElement1); document.body.offsetWidth; })');
   await session.disconnect();
   testRunner.log('Reopening inspector.');
   session = await page.createSession();
+  await session.evaluateAsync('new Promise(f => setTimeout(f, 0))');
   testRunner.log('Running test');
   testRunner.log('Opening front-end second time');
   await runTest(session);
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/select-initial-position-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/select-initial-position-expected.png
index 1357295f..d7419ab 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/select-initial-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/select-initial-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/select-initial-position-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/select-initial-position-expected.txt
index 457600a..70291bd 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/select-initial-position-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/select-initial-position-expected.txt
@@ -3,54 +3,61 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutText {#text} at (0,0) size 93x19
-        text run at (0,0) width 93: "initial selected:"
-      LayoutBR {BR} at (93,15) size 0x0
-      LayoutText {#text} at (155,71) size 4x19
-        text run at (155,71) width 4: " "
-      LayoutBR {BR} at (159,86) size 0x0
-      LayoutText {#text} at (0,91) size 161x19
-        text run at (0,91) width 161: "dynamic selected change:"
-      LayoutBR {BR} at (161,106) size 0x0
-      LayoutText {#text} at (155,162) size 4x19
-        text run at (155,162) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,182) size 211x19
-        text run at (0,182) width 211: "dynamic insert of selected option:"
-      LayoutBR {BR} at (211,197) size 0x0
-      LayoutText {#text} at (155,253) size 4x19
-        text run at (155,253) width 4: " "
-      LayoutBR {BR} at (159,268) size 0x0
-      LayoutText {#text} at (0,273) size 93x19
-        text run at (0,273) width 93: "initial selected:"
-      LayoutBR {BR} at (93,288) size 0x0
-      LayoutMenuList {SELECT} at (0,293) size 156x20 [bgcolor=#C0C0C0] [border: (1px solid #A9A9A9)]
-        LayoutBlockFlow (anonymous) at (1,1) size 154x18
-          LayoutText (anonymous) at (4,1) size 134x16
-            text run at (4,1) width 134: "this should be selected"
-      LayoutText {#text} at (156,293) size 4x19
-        text run at (156,293) width 4: " "
-      LayoutBR {BR} at (160,308) size 0x0
-      LayoutText {#text} at (0,313) size 161x19
-        text run at (0,313) width 161: "dynamic selected change:"
-      LayoutBR {BR} at (161,328) size 0x0
-      LayoutMenuList {SELECT} at (0,333) size 156x20 [bgcolor=#C0C0C0] [border: (1px solid #A9A9A9)]
-        LayoutBlockFlow (anonymous) at (1,1) size 154x18
-          LayoutText (anonymous) at (4,1) size 134x16
-            text run at (4,1) width 134: "this should be selected"
-      LayoutText {#text} at (156,333) size 4x19
-        text run at (156,333) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,353) size 211x19
-        text run at (0,353) width 211: "dynamic insert of selected option:"
-      LayoutBR {BR} at (211,368) size 0x0
-      LayoutMenuList {SELECT} at (0,373) size 156x20 [bgcolor=#C0C0C0] [border: (1px solid #A9A9A9)]
-        LayoutBlockFlow (anonymous) at (1,1) size 154x18
-          LayoutText (anonymous) at (4,1) size 134x16
-            text run at (4,1) width 134: "this should be selected"
-      LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow (anonymous) at (0,0) size 784x393
+        LayoutText {#text} at (0,0) size 93x19
+          text run at (0,0) width 93: "initial selected:"
+        LayoutBR {BR} at (93,15) size 0x0
+        LayoutText {#text} at (155,71) size 4x19
+          text run at (155,71) width 4: " "
+        LayoutBR {BR} at (159,86) size 0x0
+        LayoutText {#text} at (0,91) size 161x19
+          text run at (0,91) width 161: "dynamic selected change:"
+        LayoutBR {BR} at (161,106) size 0x0
+        LayoutText {#text} at (155,162) size 4x19
+          text run at (155,162) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,182) size 211x19
+          text run at (0,182) width 211: "dynamic insert of selected option:"
+        LayoutBR {BR} at (211,197) size 0x0
+        LayoutText {#text} at (155,253) size 4x19
+          text run at (155,253) width 4: " "
+        LayoutBR {BR} at (159,268) size 0x0
+        LayoutText {#text} at (0,273) size 93x19
+          text run at (0,273) width 93: "initial selected:"
+        LayoutBR {BR} at (93,288) size 0x0
+        LayoutMenuList {SELECT} at (0,293) size 156x20 [bgcolor=#C0C0C0] [border: (1px solid #A9A9A9)]
+          LayoutBlockFlow (anonymous) at (1,1) size 154x18
+            LayoutText (anonymous) at (4,1) size 134x16
+              text run at (4,1) width 134: "this should be selected"
+        LayoutText {#text} at (156,293) size 4x19
+          text run at (156,293) width 4: " "
+        LayoutBR {BR} at (160,308) size 0x0
+        LayoutText {#text} at (0,313) size 161x19
+          text run at (0,313) width 161: "dynamic selected change:"
+        LayoutBR {BR} at (161,328) size 0x0
+        LayoutMenuList {SELECT} at (0,333) size 156x20 [bgcolor=#C0C0C0] [border: (1px solid #A9A9A9)]
+          LayoutBlockFlow (anonymous) at (1,1) size 154x18
+            LayoutText (anonymous) at (4,1) size 134x16
+              text run at (4,1) width 134: "this should be selected"
+        LayoutText {#text} at (156,333) size 4x19
+          text run at (156,333) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,353) size 211x19
+          text run at (0,353) width 211: "dynamic insert of selected option:"
+        LayoutBR {BR} at (211,368) size 0x0
+        LayoutMenuList {SELECT} at (0,373) size 156x20 [bgcolor=#C0C0C0] [border: (1px solid #A9A9A9)]
+          LayoutBlockFlow (anonymous) at (1,1) size 154x18
+            LayoutText (anonymous) at (4,1) size 134x16
+              text run at (4,1) width 134: "this should be selected"
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow {P} at (0,409) size 784x20
+        LayoutText {#text} at (0,0) size 191x19
+          text run at (0,0) width 191: "Display 'none' to 'inline-block'"
+      LayoutBlockFlow (anonymous) at (0,445) size 784x70
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutText {#text} at (0,0) size 0x0
 layer at (8,28) size 155x70 clip at (9,29) size 138x68 scrollY 119.00 scrollHeight 238
   LayoutListBox {SELECT} at (0,20) size 155x70 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
     LayoutBlockFlow {OPTION} at (1,1) size 138x17
@@ -165,3 +172,26 @@
     LayoutBlockFlow {OPTION} at (1,120) size 138x17
       LayoutText {#text} at (2,0) size 18x16
         text run at (2,0) width 18: "opt"
+layer at (8,453) size 229x70 clip at (9,454) size 212x68 scrollY 51.00 scrollHeight 119
+  LayoutListBox {SELECT} at (0,0) size 229x70 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
+    LayoutBlockFlow {OPTION} at (1,1) size 212x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option1"
+    LayoutBlockFlow {OPTION} at (1,18) size 212x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option2"
+    LayoutBlockFlow {OPTION} at (1,35) size 212x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option3"
+    LayoutBlockFlow {OPTION} at (1,52) size 212x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option4"
+    LayoutBlockFlow {OPTION} at (1,69) size 212x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option5"
+    LayoutBlockFlow {OPTION} at (1,86) size 212x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option6"
+    LayoutBlockFlow {OPTION} at (1,103) size 212x17 [color=#FFFFFF] [bgcolor=#999999]
+      LayoutText {#text} at (2,0) size 208x16
+        text run at (2,0) width 208: "This should be selected and visible."
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/select-initial-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/select-initial-position-expected.png
index a3d3813..a7c497d 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/select-initial-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/select-initial-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/select-initial-position-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/select-initial-position-expected.txt
index 4b74084..73d65c36 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/select-initial-position-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/select-initial-position-expected.txt
@@ -3,54 +3,61 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutText {#text} at (0,0) size 98x18
-        text run at (0,0) width 98: "initial selected:"
-      LayoutBR {BR} at (97,14) size 1x0
-      LayoutText {#text} at (132,59) size 5x18
-        text run at (132,59) width 5: " "
-      LayoutBR {BR} at (136,73) size 1x0
-      LayoutText {#text} at (0,77) size 165x18
-        text run at (0,77) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,91) size 1x0
-      LayoutText {#text} at (132,136) size 5x18
-        text run at (132,136) width 5: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,154) size 217x18
-        text run at (0,154) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,168) size 1x0
-      LayoutText {#text} at (132,213) size 5x18
-        text run at (132,213) width 5: " "
-      LayoutBR {BR} at (136,227) size 1x0
-      LayoutText {#text} at (0,231) size 98x18
-        text run at (0,231) width 98: "initial selected:"
-      LayoutBR {BR} at (97,245) size 1x0
-      LayoutMenuList {SELECT} at (0,250) size 147x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 147x18
-          LayoutText (anonymous) at (8,2) size 116x13
-            text run at (8,2) width 116: "this should be selected"
-      LayoutText {#text} at (147,249) size 4x18
-        text run at (147,249) width 4: " "
-      LayoutBR {BR} at (151,263) size 0x0
-      LayoutText {#text} at (0,268) size 165x18
-        text run at (0,268) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,282) size 1x0
-      LayoutMenuList {SELECT} at (0,287) size 147x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 147x18
-          LayoutText (anonymous) at (8,2) size 116x13
-            text run at (8,2) width 116: "this should be selected"
-      LayoutText {#text} at (147,286) size 4x18
-        text run at (147,286) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,305) size 217x18
-        text run at (0,305) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,319) size 1x0
-      LayoutMenuList {SELECT} at (0,323) size 147x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 147x18
-          LayoutText (anonymous) at (8,2) size 116x13
-            text run at (8,2) width 116: "this should be selected"
-      LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow (anonymous) at (0,0) size 784x341
+        LayoutText {#text} at (0,0) size 98x18
+          text run at (0,0) width 98: "initial selected:"
+        LayoutBR {BR} at (97,14) size 1x0
+        LayoutText {#text} at (132,59) size 5x18
+          text run at (132,59) width 5: " "
+        LayoutBR {BR} at (136,73) size 1x0
+        LayoutText {#text} at (0,77) size 165x18
+          text run at (0,77) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,91) size 1x0
+        LayoutText {#text} at (132,136) size 5x18
+          text run at (132,136) width 5: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,154) size 217x18
+          text run at (0,154) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,168) size 1x0
+        LayoutText {#text} at (132,213) size 5x18
+          text run at (132,213) width 5: " "
+        LayoutBR {BR} at (136,227) size 1x0
+        LayoutText {#text} at (0,231) size 98x18
+          text run at (0,231) width 98: "initial selected:"
+        LayoutBR {BR} at (97,245) size 1x0
+        LayoutMenuList {SELECT} at (0,250) size 147x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 147x18
+            LayoutText (anonymous) at (8,2) size 116x13
+              text run at (8,2) width 116: "this should be selected"
+        LayoutText {#text} at (147,249) size 4x18
+          text run at (147,249) width 4: " "
+        LayoutBR {BR} at (151,263) size 0x0
+        LayoutText {#text} at (0,268) size 165x18
+          text run at (0,268) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,282) size 1x0
+        LayoutMenuList {SELECT} at (0,287) size 147x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 147x18
+            LayoutText (anonymous) at (8,2) size 116x13
+              text run at (8,2) width 116: "this should be selected"
+        LayoutText {#text} at (147,286) size 4x18
+          text run at (147,286) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,305) size 217x18
+          text run at (0,305) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,319) size 1x0
+        LayoutMenuList {SELECT} at (0,323) size 147x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 147x18
+            LayoutText (anonymous) at (8,2) size 116x13
+              text run at (8,2) width 116: "this should be selected"
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow {P} at (0,357) size 784x18
+        LayoutText {#text} at (0,0) size 195x18
+          text run at (0,0) width 195: "Display 'none' to 'inline-block'"
+      LayoutBlockFlow (anonymous) at (0,391) size 784x59
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutText {#text} at (0,0) size 0x0
 layer at (8,26) size 133x59 clip at (9,27) size 120x57 scrollY 99.00 scrollHeight 199
   LayoutListBox {SELECT} at (0,18.25) size 132.55x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
     LayoutBlockFlow {OPTION} at (1,1) size 119.55x14.19
@@ -165,3 +172,26 @@
     LayoutBlockFlow {OPTION} at (1,100.31) size 119.55x14.19
       LayoutText {#text} at (2,0) size 17x13
         text run at (2,0) width 17: "opt"
+layer at (8,399) size 195x59 clip at (9,400) size 182x57 scrollY 42.00 scrollHeight 100
+  LayoutListBox {SELECT} at (0,0.25) size 194.88x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
+    LayoutBlockFlow {OPTION} at (1,1) size 181.88x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option1"
+    LayoutBlockFlow {OPTION} at (1,15.19) size 181.88x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option2"
+    LayoutBlockFlow {OPTION} at (1,29.38) size 181.88x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option3"
+    LayoutBlockFlow {OPTION} at (1,43.56) size 181.88x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option4"
+    LayoutBlockFlow {OPTION} at (1,57.75) size 181.88x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option5"
+    LayoutBlockFlow {OPTION} at (1,71.94) size 181.88x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option6"
+    LayoutBlockFlow {OPTION} at (1,86.13) size 181.88x14.19 [bgcolor=#D4D4D4]
+      LayoutText {#text} at (2,0) size 178x13
+        text run at (2,0) width 178: "This should be selected and visible."
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/select/select-initial-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/select/select-initial-position-expected.png
index 0a800ca..35b041f6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/select/select-initial-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/select/select-initial-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/select/select-initial-position-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/select/select-initial-position-expected.txt
index 8909f11..569a8e6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/select/select-initial-position-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/select/select-initial-position-expected.txt
@@ -3,54 +3,61 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutText {#text} at (0,0) size 98x18
-        text run at (0,0) width 98: "initial selected:"
-      LayoutBR {BR} at (97,14) size 1x0
-      LayoutText {#text} at (137,59) size 5x18
-        text run at (137,59) width 5: " "
-      LayoutBR {BR} at (141,73) size 1x0
-      LayoutText {#text} at (0,77) size 165x18
-        text run at (0,77) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,91) size 1x0
-      LayoutText {#text} at (137,136) size 5x18
-        text run at (137,136) width 5: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,154) size 217x18
-        text run at (0,154) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,168) size 1x0
-      LayoutText {#text} at (137,213) size 5x18
-        text run at (137,213) width 5: " "
-      LayoutBR {BR} at (141,227) size 1x0
-      LayoutText {#text} at (0,231) size 98x18
-        text run at (0,231) width 98: "initial selected:"
-      LayoutBR {BR} at (97,245) size 1x0
-      LayoutMenuList {SELECT} at (0,250) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (152,249) size 4x18
-        text run at (152,249) width 4: " "
-      LayoutBR {BR} at (156,263) size 0x0
-      LayoutText {#text} at (0,268) size 165x18
-        text run at (0,268) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,282) size 1x0
-      LayoutMenuList {SELECT} at (0,287) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (152,286) size 4x18
-        text run at (152,286) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,305) size 217x18
-        text run at (0,305) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,319) size 1x0
-      LayoutMenuList {SELECT} at (0,323) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow (anonymous) at (0,0) size 784x341
+        LayoutText {#text} at (0,0) size 98x18
+          text run at (0,0) width 98: "initial selected:"
+        LayoutBR {BR} at (97,14) size 1x0
+        LayoutText {#text} at (137,59) size 5x18
+          text run at (137,59) width 5: " "
+        LayoutBR {BR} at (141,73) size 1x0
+        LayoutText {#text} at (0,77) size 165x18
+          text run at (0,77) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,91) size 1x0
+        LayoutText {#text} at (137,136) size 5x18
+          text run at (137,136) width 5: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,154) size 217x18
+          text run at (0,154) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,168) size 1x0
+        LayoutText {#text} at (137,213) size 5x18
+          text run at (137,213) width 5: " "
+        LayoutBR {BR} at (141,227) size 1x0
+        LayoutText {#text} at (0,231) size 98x18
+          text run at (0,231) width 98: "initial selected:"
+        LayoutBR {BR} at (97,245) size 1x0
+        LayoutMenuList {SELECT} at (0,250) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (152,249) size 4x18
+          text run at (152,249) width 4: " "
+        LayoutBR {BR} at (156,263) size 0x0
+        LayoutText {#text} at (0,268) size 165x18
+          text run at (0,268) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,282) size 1x0
+        LayoutMenuList {SELECT} at (0,287) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (152,286) size 4x18
+          text run at (152,286) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,305) size 217x18
+          text run at (0,305) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,319) size 1x0
+        LayoutMenuList {SELECT} at (0,323) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow {P} at (0,357) size 784x18
+        LayoutText {#text} at (0,0) size 195x18
+          text run at (0,0) width 195: "Display 'none' to 'inline-block'"
+      LayoutBlockFlow (anonymous) at (0,391) size 784x59
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutText {#text} at (0,0) size 0x0
 layer at (8,26) size 137x59 clip at (9,27) size 124x57 scrollY 99.00 scrollHeight 199
   LayoutListBox {SELECT} at (0,18.25) size 137.41x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
     LayoutBlockFlow {OPTION} at (1,1) size 124.41x14.19
@@ -165,3 +172,26 @@
     LayoutBlockFlow {OPTION} at (1,100.31) size 124.41x14.19
       LayoutText {#text} at (2,0) size 18x13
         text run at (2,0) width 18: "opt"
+layer at (8,399) size 202x59 clip at (9,400) size 189x57 scrollY 42.00 scrollHeight 100
+  LayoutListBox {SELECT} at (0,0.25) size 202.05x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
+    LayoutBlockFlow {OPTION} at (1,1) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option1"
+    LayoutBlockFlow {OPTION} at (1,15.19) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option2"
+    LayoutBlockFlow {OPTION} at (1,29.38) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option3"
+    LayoutBlockFlow {OPTION} at (1,43.56) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option4"
+    LayoutBlockFlow {OPTION} at (1,57.75) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option5"
+    LayoutBlockFlow {OPTION} at (1,71.94) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option6"
+    LayoutBlockFlow {OPTION} at (1,86.13) size 189.05x14.19 [bgcolor=#D4D4D4]
+      LayoutText {#text} at (2,0) size 186x13
+        text run at (2,0) width 186: "This should be selected and visible."
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/select-initial-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/select-initial-position-expected.png
index d7ba591..1278f1c6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/select-initial-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/select-initial-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/select-initial-position-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/select-initial-position-expected.txt
index 2c82af23..8774e74 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/select-initial-position-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/select-initial-position-expected.txt
@@ -3,54 +3,61 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutText {#text} at (0,0) size 98x18
-        text run at (0,0) width 98: "initial selected:"
-      LayoutBR {BR} at (97,14) size 1x0
-      LayoutText {#text} at (140,59) size 5x18
-        text run at (140,59) width 5: " "
-      LayoutBR {BR} at (144,73) size 1x0
-      LayoutText {#text} at (0,77) size 165x18
-        text run at (0,77) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,91) size 1x0
-      LayoutText {#text} at (140,136) size 5x18
-        text run at (140,136) width 5: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,154) size 217x18
-        text run at (0,154) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,168) size 1x0
-      LayoutText {#text} at (140,213) size 5x18
-        text run at (140,213) width 5: " "
-      LayoutBR {BR} at (144,227) size 1x0
-      LayoutText {#text} at (0,231) size 98x18
-        text run at (0,231) width 98: "initial selected:"
-      LayoutBR {BR} at (97,245) size 1x0
-      LayoutMenuList {SELECT} at (0,250) size 155x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 155x18
-          LayoutText (anonymous) at (8,2) size 124x13
-            text run at (8,2) width 124: "this should be selected"
-      LayoutText {#text} at (155,249) size 4x18
-        text run at (155,249) width 4: " "
-      LayoutBR {BR} at (159,263) size 0x0
-      LayoutText {#text} at (0,268) size 165x18
-        text run at (0,268) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,282) size 1x0
-      LayoutMenuList {SELECT} at (0,287) size 155x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 155x18
-          LayoutText (anonymous) at (8,2) size 124x13
-            text run at (8,2) width 124: "this should be selected"
-      LayoutText {#text} at (155,286) size 4x18
-        text run at (155,286) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,305) size 217x18
-        text run at (0,305) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,319) size 1x0
-      LayoutMenuList {SELECT} at (0,323) size 155x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 155x18
-          LayoutText (anonymous) at (8,2) size 124x13
-            text run at (8,2) width 124: "this should be selected"
-      LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow (anonymous) at (0,0) size 784x341
+        LayoutText {#text} at (0,0) size 98x18
+          text run at (0,0) width 98: "initial selected:"
+        LayoutBR {BR} at (97,14) size 1x0
+        LayoutText {#text} at (140,59) size 5x18
+          text run at (140,59) width 5: " "
+        LayoutBR {BR} at (144,73) size 1x0
+        LayoutText {#text} at (0,77) size 165x18
+          text run at (0,77) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,91) size 1x0
+        LayoutText {#text} at (140,136) size 5x18
+          text run at (140,136) width 5: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,154) size 217x18
+          text run at (0,154) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,168) size 1x0
+        LayoutText {#text} at (140,213) size 5x18
+          text run at (140,213) width 5: " "
+        LayoutBR {BR} at (144,227) size 1x0
+        LayoutText {#text} at (0,231) size 98x18
+          text run at (0,231) width 98: "initial selected:"
+        LayoutBR {BR} at (97,245) size 1x0
+        LayoutMenuList {SELECT} at (0,250) size 155x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 155x18
+            LayoutText (anonymous) at (8,2) size 124x13
+              text run at (8,2) width 124: "this should be selected"
+        LayoutText {#text} at (155,249) size 4x18
+          text run at (155,249) width 4: " "
+        LayoutBR {BR} at (159,263) size 0x0
+        LayoutText {#text} at (0,268) size 165x18
+          text run at (0,268) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,282) size 1x0
+        LayoutMenuList {SELECT} at (0,287) size 155x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 155x18
+            LayoutText (anonymous) at (8,2) size 124x13
+              text run at (8,2) width 124: "this should be selected"
+        LayoutText {#text} at (155,286) size 4x18
+          text run at (155,286) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,305) size 217x18
+          text run at (0,305) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,319) size 1x0
+        LayoutMenuList {SELECT} at (0,323) size 155x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 155x18
+            LayoutText (anonymous) at (8,2) size 124x13
+              text run at (8,2) width 124: "this should be selected"
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow {P} at (0,357) size 784x18
+        LayoutText {#text} at (0,0) size 195x18
+          text run at (0,0) width 195: "Display 'none' to 'inline-block'"
+      LayoutBlockFlow (anonymous) at (0,391) size 784x59
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutText {#text} at (0,0) size 0x0
 layer at (8,26) size 140x59 clip at (9,27) size 127x57 scrollY 99.00 scrollHeight 199
   LayoutListBox {SELECT} at (0,18.25) size 140.19x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
     LayoutBlockFlow {OPTION} at (1,1) size 127.19x14.19
@@ -165,3 +172,26 @@
     LayoutBlockFlow {OPTION} at (1,100.31) size 127.19x14.19
       LayoutText {#text} at (2,0) size 18x13
         text run at (2,0) width 18: "opt"
+layer at (8,399) size 207x59 clip at (9,400) size 194x57 scrollY 42.00 scrollHeight 100
+  LayoutListBox {SELECT} at (0,0.25) size 207.19x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
+    LayoutBlockFlow {OPTION} at (1,1) size 194.19x14.19
+      LayoutText {#text} at (2,0) size 42x13
+        text run at (2,0) width 42: "option1"
+    LayoutBlockFlow {OPTION} at (1,15.19) size 194.19x14.19
+      LayoutText {#text} at (2,0) size 42x13
+        text run at (2,0) width 42: "option2"
+    LayoutBlockFlow {OPTION} at (1,29.38) size 194.19x14.19
+      LayoutText {#text} at (2,0) size 42x13
+        text run at (2,0) width 42: "option3"
+    LayoutBlockFlow {OPTION} at (1,43.56) size 194.19x14.19
+      LayoutText {#text} at (2,0) size 42x13
+        text run at (2,0) width 42: "option4"
+    LayoutBlockFlow {OPTION} at (1,57.75) size 194.19x14.19
+      LayoutText {#text} at (2,0) size 42x13
+        text run at (2,0) width 42: "option5"
+    LayoutBlockFlow {OPTION} at (1,71.94) size 194.19x14.19
+      LayoutText {#text} at (2,0) size 42x13
+        text run at (2,0) width 42: "option6"
+    LayoutBlockFlow {OPTION} at (1,86.13) size 194.19x14.19 [bgcolor=#D4D4D4]
+      LayoutText {#text} at (2,0) size 191x13
+        text run at (2,0) width 191: "This should be selected and visible."
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/select/select-initial-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/select/select-initial-position-expected.png
index 0a800ca..35b041f6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/select/select-initial-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/select/select-initial-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/select/select-initial-position-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/select/select-initial-position-expected.txt
index 8909f11..569a8e6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/select/select-initial-position-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/select/select-initial-position-expected.txt
@@ -3,54 +3,61 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutText {#text} at (0,0) size 98x18
-        text run at (0,0) width 98: "initial selected:"
-      LayoutBR {BR} at (97,14) size 1x0
-      LayoutText {#text} at (137,59) size 5x18
-        text run at (137,59) width 5: " "
-      LayoutBR {BR} at (141,73) size 1x0
-      LayoutText {#text} at (0,77) size 165x18
-        text run at (0,77) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,91) size 1x0
-      LayoutText {#text} at (137,136) size 5x18
-        text run at (137,136) width 5: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,154) size 217x18
-        text run at (0,154) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,168) size 1x0
-      LayoutText {#text} at (137,213) size 5x18
-        text run at (137,213) width 5: " "
-      LayoutBR {BR} at (141,227) size 1x0
-      LayoutText {#text} at (0,231) size 98x18
-        text run at (0,231) width 98: "initial selected:"
-      LayoutBR {BR} at (97,245) size 1x0
-      LayoutMenuList {SELECT} at (0,250) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (152,249) size 4x18
-        text run at (152,249) width 4: " "
-      LayoutBR {BR} at (156,263) size 0x0
-      LayoutText {#text} at (0,268) size 165x18
-        text run at (0,268) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,282) size 1x0
-      LayoutMenuList {SELECT} at (0,287) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (152,286) size 4x18
-        text run at (152,286) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,305) size 217x18
-        text run at (0,305) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,319) size 1x0
-      LayoutMenuList {SELECT} at (0,323) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow (anonymous) at (0,0) size 784x341
+        LayoutText {#text} at (0,0) size 98x18
+          text run at (0,0) width 98: "initial selected:"
+        LayoutBR {BR} at (97,14) size 1x0
+        LayoutText {#text} at (137,59) size 5x18
+          text run at (137,59) width 5: " "
+        LayoutBR {BR} at (141,73) size 1x0
+        LayoutText {#text} at (0,77) size 165x18
+          text run at (0,77) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,91) size 1x0
+        LayoutText {#text} at (137,136) size 5x18
+          text run at (137,136) width 5: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,154) size 217x18
+          text run at (0,154) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,168) size 1x0
+        LayoutText {#text} at (137,213) size 5x18
+          text run at (137,213) width 5: " "
+        LayoutBR {BR} at (141,227) size 1x0
+        LayoutText {#text} at (0,231) size 98x18
+          text run at (0,231) width 98: "initial selected:"
+        LayoutBR {BR} at (97,245) size 1x0
+        LayoutMenuList {SELECT} at (0,250) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (152,249) size 4x18
+          text run at (152,249) width 4: " "
+        LayoutBR {BR} at (156,263) size 0x0
+        LayoutText {#text} at (0,268) size 165x18
+          text run at (0,268) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,282) size 1x0
+        LayoutMenuList {SELECT} at (0,287) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (152,286) size 4x18
+          text run at (152,286) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,305) size 217x18
+          text run at (0,305) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,319) size 1x0
+        LayoutMenuList {SELECT} at (0,323) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow {P} at (0,357) size 784x18
+        LayoutText {#text} at (0,0) size 195x18
+          text run at (0,0) width 195: "Display 'none' to 'inline-block'"
+      LayoutBlockFlow (anonymous) at (0,391) size 784x59
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutText {#text} at (0,0) size 0x0
 layer at (8,26) size 137x59 clip at (9,27) size 124x57 scrollY 99.00 scrollHeight 199
   LayoutListBox {SELECT} at (0,18.25) size 137.41x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
     LayoutBlockFlow {OPTION} at (1,1) size 124.41x14.19
@@ -165,3 +172,26 @@
     LayoutBlockFlow {OPTION} at (1,100.31) size 124.41x14.19
       LayoutText {#text} at (2,0) size 18x13
         text run at (2,0) width 18: "opt"
+layer at (8,399) size 202x59 clip at (9,400) size 189x57 scrollY 42.00 scrollHeight 100
+  LayoutListBox {SELECT} at (0,0.25) size 202.05x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
+    LayoutBlockFlow {OPTION} at (1,1) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option1"
+    LayoutBlockFlow {OPTION} at (1,15.19) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option2"
+    LayoutBlockFlow {OPTION} at (1,29.38) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option3"
+    LayoutBlockFlow {OPTION} at (1,43.56) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option4"
+    LayoutBlockFlow {OPTION} at (1,57.75) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option5"
+    LayoutBlockFlow {OPTION} at (1,71.94) size 189.05x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option6"
+    LayoutBlockFlow {OPTION} at (1,86.13) size 189.05x14.19 [bgcolor=#D4D4D4]
+      LayoutText {#text} at (2,0) size 186x13
+        text run at (2,0) width 186: "This should be selected and visible."
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/select-initial-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/select-initial-position-expected.png
index 3aec5c5..d712ea60 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/select-initial-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/select-initial-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/select-initial-position-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/select-initial-position-expected.txt
index 37e2e6c..834660a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/select-initial-position-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/select-initial-position-expected.txt
@@ -3,54 +3,61 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutText {#text} at (0,0) size 98x18
-        text run at (0,0) width 98: "initial selected:"
-      LayoutBR {BR} at (97,14) size 1x0
-      LayoutText {#text} at (137,59) size 5x18
-        text run at (137,59) width 5: " "
-      LayoutBR {BR} at (141,73) size 1x0
-      LayoutText {#text} at (0,77) size 165x18
-        text run at (0,77) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,91) size 1x0
-      LayoutText {#text} at (137,136) size 5x18
-        text run at (137,136) width 5: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,154) size 217x18
-        text run at (0,154) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,168) size 1x0
-      LayoutText {#text} at (137,213) size 5x18
-        text run at (137,213) width 5: " "
-      LayoutBR {BR} at (141,227) size 1x0
-      LayoutText {#text} at (0,231) size 98x18
-        text run at (0,231) width 98: "initial selected:"
-      LayoutBR {BR} at (97,245) size 1x0
-      LayoutMenuList {SELECT} at (0,250) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (152,249) size 4x18
-        text run at (152,249) width 4: " "
-      LayoutBR {BR} at (156,263) size 0x0
-      LayoutText {#text} at (0,268) size 165x18
-        text run at (0,268) width 165: "dynamic selected change:"
-      LayoutBR {BR} at (164,282) size 1x0
-      LayoutMenuList {SELECT} at (0,287) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (152,286) size 4x18
-        text run at (152,286) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,305) size 217x18
-        text run at (0,305) width 217: "dynamic insert of selected option:"
-      LayoutBR {BR} at (216,319) size 1x0
-      LayoutMenuList {SELECT} at (0,323) size 152x18 [bgcolor=#F8F8F8]
-        LayoutBlockFlow (anonymous) at (0,0) size 152x18
-          LayoutText (anonymous) at (8,2) size 121x13
-            text run at (8,2) width 121: "this should be selected"
-      LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow (anonymous) at (0,0) size 784x341
+        LayoutText {#text} at (0,0) size 98x18
+          text run at (0,0) width 98: "initial selected:"
+        LayoutBR {BR} at (97,14) size 1x0
+        LayoutText {#text} at (137,59) size 5x18
+          text run at (137,59) width 5: " "
+        LayoutBR {BR} at (141,73) size 1x0
+        LayoutText {#text} at (0,77) size 165x18
+          text run at (0,77) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,91) size 1x0
+        LayoutText {#text} at (137,136) size 5x18
+          text run at (137,136) width 5: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,154) size 217x18
+          text run at (0,154) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,168) size 1x0
+        LayoutText {#text} at (137,213) size 5x18
+          text run at (137,213) width 5: " "
+        LayoutBR {BR} at (141,227) size 1x0
+        LayoutText {#text} at (0,231) size 98x18
+          text run at (0,231) width 98: "initial selected:"
+        LayoutBR {BR} at (97,245) size 1x0
+        LayoutMenuList {SELECT} at (0,250) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (152,249) size 4x18
+          text run at (152,249) width 4: " "
+        LayoutBR {BR} at (156,263) size 0x0
+        LayoutText {#text} at (0,268) size 165x18
+          text run at (0,268) width 165: "dynamic selected change:"
+        LayoutBR {BR} at (164,282) size 1x0
+        LayoutMenuList {SELECT} at (0,287) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (152,286) size 4x18
+          text run at (152,286) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,305) size 217x18
+          text run at (0,305) width 217: "dynamic insert of selected option:"
+        LayoutBR {BR} at (216,319) size 1x0
+        LayoutMenuList {SELECT} at (0,323) size 152x18 [bgcolor=#F8F8F8]
+          LayoutBlockFlow (anonymous) at (0,0) size 152x18
+            LayoutText (anonymous) at (8,2) size 121x13
+              text run at (8,2) width 121: "this should be selected"
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow {P} at (0,357) size 784x18
+        LayoutText {#text} at (0,0) size 195x18
+          text run at (0,0) width 195: "Display 'none' to 'inline-block'"
+      LayoutBlockFlow (anonymous) at (0,391) size 784x59
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutText {#text} at (0,0) size 0x0
 layer at (8,26) size 137x59 clip at (9,27) size 124x57 scrollY 99.00 scrollHeight 199
   LayoutListBox {SELECT} at (0,18.25) size 137.39x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
     LayoutBlockFlow {OPTION} at (1,1) size 124.39x14.19
@@ -165,3 +172,26 @@
     LayoutBlockFlow {OPTION} at (1,100.31) size 124.39x14.19
       LayoutText {#text} at (2,0) size 18x13
         text run at (2,0) width 18: "opt"
+layer at (8,399) size 202x59 clip at (9,400) size 189x57 scrollY 42.00 scrollHeight 100
+  LayoutListBox {SELECT} at (0,0.25) size 202.03x58.75 [bgcolor=#FFFFFF] [border: (1px solid #999999)]
+    LayoutBlockFlow {OPTION} at (1,1) size 189.03x14.19
+      LayoutText {#text} at (2,0) size 38x13
+        text run at (2,0) width 38: "option1"
+    LayoutBlockFlow {OPTION} at (1,15.19) size 189.03x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option2"
+    LayoutBlockFlow {OPTION} at (1,29.38) size 189.03x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option3"
+    LayoutBlockFlow {OPTION} at (1,43.56) size 189.03x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option4"
+    LayoutBlockFlow {OPTION} at (1,57.75) size 189.03x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option5"
+    LayoutBlockFlow {OPTION} at (1,71.94) size 189.03x14.19
+      LayoutText {#text} at (2,0) size 40x13
+        text run at (2,0) width 40: "option6"
+    LayoutBlockFlow {OPTION} at (1,86.13) size 189.03x14.19 [bgcolor=#D4D4D4]
+      LayoutText {#text} at (2,0) size 186x13
+        text run at (2,0) width 186: "This should be selected and visible."
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/select-initial-position-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/select-initial-position-expected.png
index c00e1ec8..d83413b 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/select-initial-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/select-initial-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/select-initial-position-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/select-initial-position-expected.txt
index 15e784c..fec1120 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/select-initial-position-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/select-initial-position-expected.txt
@@ -3,54 +3,61 @@
 layer at (0,0) size 800x600
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutText {#text} at (0,0) size 86x19
-        text run at (0,0) width 86: "initial selected:"
-      LayoutBR {BR} at (86,15) size 0x0
-      LayoutText {#text} at (155,71) size 4x19
-        text run at (155,71) width 4: " "
-      LayoutBR {BR} at (159,86) size 0x0
-      LayoutText {#text} at (0,91) size 152x19
-        text run at (0,91) width 152: "dynamic selected change:"
-      LayoutBR {BR} at (152,106) size 0x0
-      LayoutText {#text} at (155,162) size 4x19
-        text run at (155,162) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,182) size 200x19
-        text run at (0,182) width 200: "dynamic insert of selected option:"
-      LayoutBR {BR} at (200,197) size 0x0
-      LayoutText {#text} at (155,253) size 4x19
-        text run at (155,253) width 4: " "
-      LayoutBR {BR} at (159,268) size 0x0
-      LayoutText {#text} at (0,273) size 86x19
-        text run at (0,273) width 86: "initial selected:"
-      LayoutBR {BR} at (86,288) size 0x0
-      LayoutMenuList {SELECT} at (0,293) size 156x20 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
-        LayoutBlockFlow (anonymous) at (1,1) size 154x18
-          LayoutText (anonymous) at (4,1) size 134x16
-            text run at (4,1) width 134: "this should be selected"
-      LayoutText {#text} at (156,293) size 4x19
-        text run at (156,293) width 4: " "
-      LayoutBR {BR} at (160,308) size 0x0
-      LayoutText {#text} at (0,313) size 152x19
-        text run at (0,313) width 152: "dynamic selected change:"
-      LayoutBR {BR} at (152,328) size 0x0
-      LayoutMenuList {SELECT} at (0,333) size 156x20 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
-        LayoutBlockFlow (anonymous) at (1,1) size 154x18
-          LayoutText (anonymous) at (4,1) size 134x16
-            text run at (4,1) width 134: "this should be selected"
-      LayoutText {#text} at (156,333) size 4x19
-        text run at (156,333) width 4: " "
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBR {BR} at (0,0) size 0x0
-      LayoutText {#text} at (0,353) size 200x19
-        text run at (0,353) width 200: "dynamic insert of selected option:"
-      LayoutBR {BR} at (200,368) size 0x0
-      LayoutMenuList {SELECT} at (0,373) size 156x20 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
-        LayoutBlockFlow (anonymous) at (1,1) size 154x18
-          LayoutText (anonymous) at (4,1) size 134x16
-            text run at (4,1) width 134: "this should be selected"
-      LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow (anonymous) at (0,0) size 784x393
+        LayoutText {#text} at (0,0) size 86x19
+          text run at (0,0) width 86: "initial selected:"
+        LayoutBR {BR} at (86,15) size 0x0
+        LayoutText {#text} at (155,71) size 4x19
+          text run at (155,71) width 4: " "
+        LayoutBR {BR} at (159,86) size 0x0
+        LayoutText {#text} at (0,91) size 152x19
+          text run at (0,91) width 152: "dynamic selected change:"
+        LayoutBR {BR} at (152,106) size 0x0
+        LayoutText {#text} at (155,162) size 4x19
+          text run at (155,162) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,182) size 200x19
+          text run at (0,182) width 200: "dynamic insert of selected option:"
+        LayoutBR {BR} at (200,197) size 0x0
+        LayoutText {#text} at (155,253) size 4x19
+          text run at (155,253) width 4: " "
+        LayoutBR {BR} at (159,268) size 0x0
+        LayoutText {#text} at (0,273) size 86x19
+          text run at (0,273) width 86: "initial selected:"
+        LayoutBR {BR} at (86,288) size 0x0
+        LayoutMenuList {SELECT} at (0,293) size 156x20 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
+          LayoutBlockFlow (anonymous) at (1,1) size 154x18
+            LayoutText (anonymous) at (4,1) size 134x16
+              text run at (4,1) width 134: "this should be selected"
+        LayoutText {#text} at (156,293) size 4x19
+          text run at (156,293) width 4: " "
+        LayoutBR {BR} at (160,308) size 0x0
+        LayoutText {#text} at (0,313) size 152x19
+          text run at (0,313) width 152: "dynamic selected change:"
+        LayoutBR {BR} at (152,328) size 0x0
+        LayoutMenuList {SELECT} at (0,333) size 156x20 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
+          LayoutBlockFlow (anonymous) at (1,1) size 154x18
+            LayoutText (anonymous) at (4,1) size 134x16
+              text run at (4,1) width 134: "this should be selected"
+        LayoutText {#text} at (156,333) size 4x19
+          text run at (156,333) width 4: " "
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutBR {BR} at (0,0) size 0x0
+        LayoutText {#text} at (0,353) size 200x19
+          text run at (0,353) width 200: "dynamic insert of selected option:"
+        LayoutBR {BR} at (200,368) size 0x0
+        LayoutMenuList {SELECT} at (0,373) size 156x20 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
+          LayoutBlockFlow (anonymous) at (1,1) size 154x18
+            LayoutText (anonymous) at (4,1) size 134x16
+              text run at (4,1) width 134: "this should be selected"
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow {P} at (0,409) size 784x20
+        LayoutText {#text} at (0,0) size 180x19
+          text run at (0,0) width 180: "Display 'none' to 'inline-block'"
+      LayoutBlockFlow (anonymous) at (0,445) size 784x70
+        LayoutText {#text} at (0,0) size 0x0
+        LayoutText {#text} at (0,0) size 0x0
 layer at (8,28) size 155x70 clip at (9,29) size 138x68 scrollY 119.00 scrollHeight 238
   LayoutListBox {SELECT} at (0,20) size 155x70 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
     LayoutBlockFlow {OPTION} at (1,1) size 138x17
@@ -165,3 +172,26 @@
     LayoutBlockFlow {OPTION} at (1,120) size 138x17
       LayoutText {#text} at (2,0) size 18x16
         text run at (2,0) width 18: "opt"
+layer at (8,453) size 226x70 clip at (9,454) size 209x68 scrollY 51.00 scrollHeight 119
+  LayoutListBox {SELECT} at (0,0) size 226x70 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)]
+    LayoutBlockFlow {OPTION} at (1,1) size 209x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option1"
+    LayoutBlockFlow {OPTION} at (1,18) size 209x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option2"
+    LayoutBlockFlow {OPTION} at (1,35) size 209x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option3"
+    LayoutBlockFlow {OPTION} at (1,52) size 209x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option4"
+    LayoutBlockFlow {OPTION} at (1,69) size 209x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option5"
+    LayoutBlockFlow {OPTION} at (1,86) size 209x17
+      LayoutText {#text} at (2,0) size 42x16
+        text run at (2,0) width 42: "option6"
+    LayoutBlockFlow {OPTION} at (1,103) size 209x17 [color=#FFFFFF] [bgcolor=#999999]
+      LayoutText {#text} at (2,0) size 205x16
+        text run at (2,0) width 205: "This should be selected and visible."
diff --git a/third_party/WebKit/Source/bindings/modules/v8/ConditionalFeaturesForModules.cpp b/third_party/WebKit/Source/bindings/modules/v8/ConditionalFeaturesForModules.cpp
index d107c76..88325d25 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/ConditionalFeaturesForModules.cpp
+++ b/third_party/WebKit/Source/bindings/modules/v8/ConditionalFeaturesForModules.cpp
@@ -56,11 +56,6 @@
           isolate, world, v8::Local<v8::Object>(), prototype_object,
           interface_object);
     }
-    if (OriginTrials::webShareEnabled(execution_context)) {
-      V8NavigatorPartial::installWebShare(isolate, world,
-                                          v8::Local<v8::Object>(),
-                                          prototype_object, interface_object);
-    }
     if (OriginTrials::webVREnabled(execution_context)) {
       V8NavigatorPartial::installWebVR(isolate, world, v8::Local<v8::Object>(),
                                        prototype_object, interface_object);
@@ -168,16 +163,6 @@
     }
     return;
   }
-  if (feature == "WebShare") {
-    if (context_data->GetExistingConstructorAndPrototypeForType(
-            &V8Navigator::wrapperTypeInfo, &prototype_object,
-            &interface_object)) {
-      V8NavigatorPartial::installWebShare(isolate, world,
-                                          v8::Local<v8::Object>(),
-                                          prototype_object, interface_object);
-    }
-    return;
-  }
   if (feature == "WebVR1.1") {
     global_instance_object = script_state->GetContext()->Global();
     V8WindowPartial::installGamepadExtensions(
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 49fc092..5b8dff27 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -2381,6 +2381,7 @@
 
   if (run_post_layout_tasks == kRunPostLayoutTasksSynchronously && View())
     View()->FlushAnyPendingPostLayoutTasks();
+  ++force_layout_count_;
 }
 
 PassRefPtr<ComputedStyle> Document::StyleForElementIgnoringPendingStylesheets(
@@ -2575,6 +2576,8 @@
 
   lifecycle_.AdvanceTo(DocumentLifecycle::kStopping);
   View()->Dispose();
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  CHECK(!View()->IsAttached());
 
   // If the EmbeddedContentView of the document's frame owner doesn't match
   // view() then LocalFrameView::Dispose() didn't clear the owner's
@@ -2635,6 +2638,8 @@
 
   layout_view_ = nullptr;
   ContainerNode::DetachLayoutTree();
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  CHECK(!View()->IsAttached());
 
   if (this != &AxObjectCacheOwner()) {
     if (AXObjectCache* cache = ExistingAXObjectCache()) {
@@ -2678,6 +2683,8 @@
     media_query_matcher_->DocumentDetached();
 
   lifecycle_.AdvanceTo(DocumentLifecycle::kStopped);
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  CHECK(!View()->IsAttached());
 
   // TODO(haraken): Call contextDestroyed() before we start any disruptive
   // operations.
@@ -2688,6 +2695,8 @@
   // a contextDestroyed() notification. This can happen for a document
   // created by DOMImplementation::createDocument().
   ExecutionContext::NotifyContextDestroyed();
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  CHECK(!View()->IsAttached());
 
   // This is required, as our LocalFrame might delete itself as soon as it
   // detaches us. However, this violates Node::detachLayoutTree() semantics, as
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index 731c536..31b2524a 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -967,6 +967,7 @@
   uint64_t DomTreeVersion() const { return dom_tree_version_; }
 
   uint64_t StyleVersion() const { return style_version_; }
+  unsigned ForceLayoutCountForTesting() const { return force_layout_count_; }
 
   enum PendingSheetLayout {
     kNoLayoutWithPendingSheets,
@@ -1511,6 +1512,7 @@
   static uint64_t global_tree_version_;
 
   uint64_t style_version_;
+  unsigned force_layout_count_ = 0;
 
   HeapHashSet<WeakMember<NodeIterator>> node_iterators_;
   using AttachedRangeSet = HeapHashSet<WeakMember<Range>>;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index 46f5a7d..c2975ee 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -299,6 +299,16 @@
   loader_.StopAllLoaders();
   loader_.Detach();
   GetDocument()->Shutdown();
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  // It seems to crash because Frame is detached before LocalFrameView.
+  // Verify here that any LocalFrameView has been detached by now.
+  if (view_ && view_->IsAttached()) {
+    CHECK(DeprecatedLocalOwner());
+    CHECK(DeprecatedLocalOwner()->OwnedEmbeddedContentView());
+    CHECK_EQ(view_, DeprecatedLocalOwner()->OwnedEmbeddedContentView());
+  }
+  CHECK(!view_ || !view_->IsAttached());
+
   // This is the earliest that scripting can be disabled:
   // - FrameLoader::Detach() can fire XHR abort events
   // - Document::Shutdown() can dispose plugins which can run script.
@@ -306,21 +316,15 @@
   if (!Client())
     return;
 
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  CHECK(!view_->IsAttached());
   Client()->WillBeDetached();
   // Notify ScriptController that the frame is closing, since its cleanup ends
   // up calling back to LocalFrameClient via WindowProxy.
   GetScriptController().ClearForClose();
 
   // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
-  // It seems to crash because Frame is detached before LocalFrameView.
-  // Verify here that any LocalFrameView has been detached by now.
-  if (view_->IsAttached()) {
-    CHECK(DeprecatedLocalOwner());
-    CHECK(DeprecatedLocalOwner()->OwnedEmbeddedContentView());
-    CHECK_EQ(view_, DeprecatedLocalOwner()->OwnedEmbeddedContentView());
-  }
   CHECK(!view_->IsAttached());
-
   SetView(nullptr);
 
   page_->GetEventHandlerRegistry().DidRemoveAllEventHandlers(*DomWindow());
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index a816e48d..77dd70be 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -3907,7 +3907,10 @@
 }
 
 void LocalFrameView::AttachToLayout() {
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
   CHECK(!is_attached_);
+  if (frame_->GetDocument())
+    CHECK_NE(Lifecycle().GetState(), DocumentLifecycle::kStopping);
   is_attached_ = true;
   parent_ = ParentFrameView();
   if (!parent_) {
diff --git a/third_party/WebKit/Source/core/frame/VisualViewport.cpp b/third_party/WebKit/Source/core/frame/VisualViewport.cpp
index ae72a44..6548714f 100644
--- a/third_party/WebKit/Source/core/frame/VisualViewport.cpp
+++ b/third_party/WebKit/Source/core/frame/VisualViewport.cpp
@@ -114,6 +114,7 @@
 
   if (inner_viewport_container_layer_) {
     inner_viewport_container_layer_->SetSize(FloatSize(size_));
+    inner_viewport_scroll_layer_->PlatformLayer()->SetScrollable(size_);
 
     // Need to re-compute sizes for the overlay scrollbars.
     InitializeScrollbars();
@@ -369,8 +370,7 @@
       GetPage().GetSettings().GetMainFrameClipsContent());
   inner_viewport_container_layer_->SetSize(FloatSize(size_));
 
-  inner_viewport_scroll_layer_->PlatformLayer()->SetScrollClipLayer(
-      inner_viewport_container_layer_->PlatformLayer());
+  inner_viewport_scroll_layer_->PlatformLayer()->SetScrollable(size_);
   if (MainFrame()) {
     if (Document* document = MainFrame()->GetDocument()) {
       inner_viewport_scroll_layer_->SetElementId(
diff --git a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
index 0b1bc9b1..31bc4f73 100644
--- a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
@@ -44,7 +44,6 @@
 #include "core/dom/NodeComputedStyle.h"
 #include "core/dom/NodeListsNodeData.h"
 #include "core/dom/NodeTraversal.h"
-#include "core/dom/TaskRunnerHelper.h"
 #include "core/events/GestureEvent.h"
 #include "core/events/KeyboardEvent.h"
 #include "core/events/MouseEvent.h"
@@ -70,6 +69,7 @@
 #include "core/page/ChromeClient.h"
 #include "core/page/Page.h"
 #include "core/page/SpatialNavigation.h"
+#include "core/paint/PaintLayerScrollableArea.h"
 #include "platform/instrumentation/tracing/TraceEvent.h"
 #include "platform/text/PlatformLocale.h"
 
@@ -891,30 +891,43 @@
     return;
   if (UsesMenuList())
     return;
-  bool has_pending_task = option_to_scroll_to_;
-  // We'd like to keep an HTMLOptionElement reference rather than the index of
-  // the option because the task should work even if unselected option is
-  // inserted before executing scrollToOptionTask().
+  if (GetLayoutObject()) {
+    if (GetDocument().Lifecycle().GetState() >=
+        DocumentLifecycle::kLayoutClean) {
+      ToLayoutListBox(GetLayoutObject())->ScrollToRect(option->BoundingBox());
+      return;
+    }
+    // Make sure the LayoutObject will be laid out.
+    GetLayoutObject()->SetNeedsLayout(
+        LayoutInvalidationReason::kMenuOptionsChanged);
+  }
   option_to_scroll_to_ = option;
-  if (!has_pending_task)
-    TaskRunnerHelper::Get(TaskType::kUserInteraction, &GetDocument())
-        ->PostTask(BLINK_FROM_HERE,
-                   WTF::Bind(&HTMLSelectElement::ScrollToOptionTask,
-                             WrapPersistent(this)));
+  // ScrollToOptionAfterLayout() should be called if this element is rendered.
 }
 
-void HTMLSelectElement::ScrollToOptionTask() {
+void HTMLSelectElement::ScrollToOptionAfterLayout(
+    PaintLayerScrollableArea& scrollable_area) {
   HTMLOptionElement* option = option_to_scroll_to_.Release();
-  if (!option || !isConnected())
+  if (!option || UsesMenuList())
     return;
-  // optionRemoved() makes sure m_optionToScrollTo doesn't have an option with
-  // another owner.
-  DCHECK_EQ(option->OwnerSelectElement(), this);
-  GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
-  if (!GetLayoutObject() || !GetLayoutObject()->IsListBox())
+  LayoutBox* option_box = option->GetLayoutBox();
+  if (!option_box)
     return;
-  LayoutRect bounds = option->BoundingBox();
-  ToLayoutListBox(GetLayoutObject())->ScrollToRect(bounds);
+
+  // We can't use PaintLayerScrollableArea::ScrollIntoView(), which needs
+  // absolute coordinate. We are unable to compute absolute positions because
+  // ancestors' layout aren't fixed yet.
+  LayoutObject* container = option_box->Container();
+  LayoutSize option_offset = option_box->OffsetFromContainer(container);
+  for (; container && container != GetLayoutObject();
+       container = container->Container())
+    option_offset += container->OffsetFromContainer(container->Container());
+  if (!container)
+    return;
+  scrollable_area.ScrollLocalRectIntoView(
+      LayoutRect(LayoutPoint() + option_offset, option_box->Size()),
+      ScrollAlignment::kAlignToEdgeIfNeeded,
+      ScrollAlignment::kAlignToEdgeIfNeeded, false);
 }
 
 void HTMLSelectElement::OptionSelectionStateChanged(HTMLOptionElement* option,
diff --git a/third_party/WebKit/Source/core/html/HTMLSelectElement.h b/third_party/WebKit/Source/core/html/HTMLSelectElement.h
index 8814387..59f94d0e 100644
--- a/third_party/WebKit/Source/core/html/HTMLSelectElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLSelectElement.h
@@ -45,6 +45,7 @@
 class HTMLOptionElement;
 class HTMLOptionElementOrHTMLOptGroupElement;
 class HTMLElementOrLong;
+class PaintLayerScrollableArea;
 class PopupMenu;
 
 class CORE_EXPORT HTMLSelectElement final
@@ -118,6 +119,7 @@
 
   void ScrollToSelection();
   void ScrollToOption(HTMLOptionElement*);
+  void ScrollToOptionAfterLayout(PaintLayerScrollableArea&);
 
   bool CanSelectAll() const;
   void SelectAll();
@@ -267,7 +269,6 @@
                                                   SkipDirection) const;
   HTMLOptionElement* EventTargetOption(const Event&);
   AutoscrollController* GetAutoscrollController() const;
-  void ScrollToOptionTask();
 
   bool AreAuthorShadowsAllowed() const override { return false; }
   void FinishParsingChildren() override;
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
index 977dbc96..de26df74 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
@@ -487,7 +487,7 @@
   WebLayer* web_layer = toWebLayer(scrollable_area->LayerForScrolling());
   WebLayer* container_layer = toWebLayer(scrollable_area->LayerForContainer());
   if (web_layer) {
-    web_layer->SetScrollClipLayer(container_layer);
+    web_layer->SetScrollable(container_layer->Bounds());
     FloatPoint scroll_position(scrollable_area->ScrollOrigin() +
                                scrollable_area->GetScrollOffset());
     web_layer->SetScrollPosition(scroll_position);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index 6f8b3ff9..6f3abea9 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -60,6 +60,7 @@
 #include "core/frame/Settings.h"
 #include "core/frame/VisualViewport.h"
 #include "core/html/HTMLFrameOwnerElement.h"
+#include "core/html/HTMLSelectElement.h"
 #include "core/input/EventHandler.h"
 #include "core/layout/LayoutEmbeddedContent.h"
 #include "core/layout/LayoutFlexibleBox.h"
@@ -916,6 +917,10 @@
 
   DisableCompositingQueryAsserts disabler;
   PositionOverflowControls();
+
+  Node* node = Box().GetNode();
+  if (isHTMLSelectElement(node))
+    toHTMLSelectElement(node)->ScrollToOptionAfterLayout(*this);
 }
 
 void PaintLayerScrollableArea::ClampScrollOffsetAfterOverflowChange() {
@@ -1775,16 +1780,13 @@
   // keep the point under the cursor in view.
 }
 
-LayoutRect PaintLayerScrollableArea::ScrollIntoView(
+LayoutRect PaintLayerScrollableArea::ScrollLocalRectIntoView(
     const LayoutRect& rect,
     const ScrollAlignment& align_x,
     const ScrollAlignment& align_y,
     bool is_smooth,
     ScrollType scroll_type) {
-  LayoutRect local_expose_rect(
-      Box()
-          .AbsoluteToLocalQuad(FloatQuad(FloatRect(rect)), kUseTransforms)
-          .BoundingBox());
+  LayoutRect local_expose_rect(rect);
   local_expose_rect.Move(-Box().BorderLeft(), -Box().BorderTop());
   LayoutRect visible_rect(LayoutPoint(), ClientSize());
   LayoutRect r = ScrollAlignment::GetRectToExpose(
@@ -1802,7 +1804,22 @@
   ScrollOffset scroll_offset_difference =
       ClampScrollOffset(new_scroll_offset) - old_scroll_offset;
   local_expose_rect.Move(-LayoutSize(scroll_offset_difference));
+  return local_expose_rect;
+}
 
+LayoutRect PaintLayerScrollableArea::ScrollIntoView(
+    const LayoutRect& rect,
+    const ScrollAlignment& align_x,
+    const ScrollAlignment& align_y,
+    bool is_smooth,
+    ScrollType scroll_type) {
+  LayoutRect local_expose_rect(
+      Box()
+          .AbsoluteToLocalQuad(FloatQuad(FloatRect(rect)), kUseTransforms)
+          .BoundingBox());
+  local_expose_rect = ScrollLocalRectIntoView(local_expose_rect, align_x,
+                                              align_y, is_smooth, scroll_type);
+  LayoutRect visible_rect(LayoutPoint(), ClientSize());
   LayoutRect intersect =
       LocalToAbsolute(Box(), Intersection(visible_rect, local_expose_rect));
   if (intersect.IsEmpty() && !visible_rect.IsEmpty() &&
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
index 10c6f8b7..74f24dc 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
@@ -386,6 +386,13 @@
   bool HitTestResizerInFragments(const PaintLayerFragments&,
                                  const HitTestLocation&) const;
 
+  // Returns the new offset, after scrolling, of the given rect in parents
+  // coordinates.
+  LayoutRect ScrollLocalRectIntoView(const LayoutRect&,
+                                     const ScrollAlignment& align_x,
+                                     const ScrollAlignment& align_y,
+                                     bool is_smooth,
+                                     ScrollType = kProgrammaticScroll);
   // Returns the new offset, after scrolling, of the given rect in absolute
   // coordinates, clipped by the parent's client rect.
   LayoutRect ScrollIntoView(const LayoutRect&,
diff --git a/third_party/WebKit/Source/core/testing/Internals.cpp b/third_party/WebKit/Source/core/testing/Internals.cpp
index a05e0b2..4ad93814 100644
--- a/third_party/WebKit/Source/core/testing/Internals.cpp
+++ b/third_party/WebKit/Source/core/testing/Internals.cpp
@@ -335,6 +335,14 @@
   return needs_layout_objects;
 }
 
+unsigned Internals::forceLayoutCount(ExceptionState& exception_state) const {
+  if (document_)
+    return document_->ForceLayoutCountForTesting();
+  exception_state.ThrowDOMException(kInvalidAccessError,
+                                    "No context document is available.");
+  return 0;
+}
+
 unsigned Internals::hitTestCount(Document* doc,
                                  ExceptionState& exception_state) const {
   if (!doc) {
diff --git a/third_party/WebKit/Source/core/testing/Internals.h b/third_party/WebKit/Source/core/testing/Internals.h
index d31c67a..fddaaf23c 100644
--- a/third_party/WebKit/Source/core/testing/Internals.h
+++ b/third_party/WebKit/Source/core/testing/Internals.h
@@ -151,6 +151,7 @@
 
   unsigned updateStyleAndReturnAffectedElementCount(ExceptionState&) const;
   unsigned needsLayoutCount(ExceptionState&) const;
+  unsigned forceLayoutCount(ExceptionState&) const;
   unsigned hitTestCount(Document*, ExceptionState&) const;
   unsigned hitTestCacheHits(Document*, ExceptionState&) const;
   Element* elementFromPoint(Document*,
diff --git a/third_party/WebKit/Source/core/testing/Internals.idl b/third_party/WebKit/Source/core/testing/Internals.idl
index bacd7d84..597afb4 100644
--- a/third_party/WebKit/Source/core/testing/Internals.idl
+++ b/third_party/WebKit/Source/core/testing/Internals.idl
@@ -65,7 +65,10 @@
     Node parentTreeScope(Node node);
     [RaisesException] unsigned short compareTreeScopePosition(Node treeScope1, Node treeScope2);
     [RaisesException] unsigned long updateStyleAndReturnAffectedElementCount();
+    // Returns the number of LayoutObjects with needs-layout flag.
     [RaisesException] unsigned long needsLayoutCount();
+    // The number of force layout since Document creation.
+    [RaisesException] readonly attribute unsigned long forceLayoutCount;
     [RaisesException] unsigned long hitTestCount(Document document);
     [RaisesException] unsigned long hitTestCacheHits(Document document);
     [RaisesException] Element? elementFromPoint(Document document, double x, double y, boolean ignoreClipping, boolean allowChildFrameContent);
diff --git a/third_party/WebKit/Source/modules/webshare/NavigatorShare.idl b/third_party/WebKit/Source/modules/webshare/NavigatorShare.idl
index a5c4b308..7cf20fe9 100644
--- a/third_party/WebKit/Source/modules/webshare/NavigatorShare.idl
+++ b/third_party/WebKit/Source/modules/webshare/NavigatorShare.idl
@@ -5,7 +5,7 @@
 // https://wicg.github.io/web-share/
 
 [
-  OriginTrialEnabled=WebShare
+  RuntimeEnabled=WebShare
 ] partial interface Navigator {
   [SecureContext, CallWith=ScriptState, MeasureAs=WebShareShare]
   Promise<void> share(ShareData data);
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 4c7dbb6..34cb29c 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -1103,10 +1103,9 @@
       name: "WebNFC",
       status: "experimental",
     },
+    // WebShare is enabled by default on Android.
     {
       name: "WebShare",
-      origin_trial_feature_name: "WebShare",
-      origin_trial_os: ["android"],
       status: "experimental",
     },
     {
diff --git a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
index f618395..2eedb74 100644
--- a/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebRuntimeFeatures.cpp
@@ -308,6 +308,10 @@
   RuntimeEnabledFeatures::SetPushMessagingEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableWebShare(bool enable) {
+  RuntimeEnabledFeatures::SetWebShareEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableWebVR(bool enable) {
   RuntimeEnabledFeatures::SetWebVREnabled(enable);
 }
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
index 846be66..2e354e6 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
@@ -63,8 +63,8 @@
     clip_layer_->AddChild(scroll_elasticity_layer_.get());
     scroll_elasticity_layer_->AddChild(page_scale_layer_.get());
     page_scale_layer_->AddChild(graphics_layer_.get());
-    graphics_layer_->PlatformLayer()->SetScrollClipLayer(
-        clip_layer_->PlatformLayer());
+    graphics_layer_->PlatformLayer()->SetScrollable(
+        clip_layer_->PlatformLayer()->Bounds());
     platform_layer_ = graphics_layer_->PlatformLayer();
     layer_tree_view_ = WTF::WrapUnique(new WebLayerTreeViewImplForTesting);
     DCHECK(layer_tree_view_);
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
index b3e8164..153cb71a 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -780,9 +780,9 @@
   EXPECT_EQ(transform_node_index, transform_node.id);
 
   EXPECT_EQ(0u, scroll_client.did_scroll_count);
-  // TODO(pdr): The PaintArtifactCompositor should set the scroll clip layer id
-  // so the Layer is scrollable. This call should be removed.
-  layer->SetScrollClipLayerId(layer->id());
+  // TODO(pdr): The PaintArtifactCompositor should set the scrolling content
+  // bounds so the Layer is scrollable. This call should be removed.
+  layer->SetScrollable(gfx::Size(1, 1));
   layer->SetScrollOffsetFromImplSide(gfx::ScrollOffset(1, 2));
   EXPECT_EQ(1u, scroll_client.did_scroll_count);
   EXPECT_EQ(gfx::ScrollOffset(1, 2), scroll_client.last_scroll_offset);
diff --git a/third_party/WebKit/public/platform/WebLayer.h b/third_party/WebKit/public/platform/WebLayer.h
index 12a7329..07ef4a6b 100644
--- a/third_party/WebKit/public/platform/WebLayer.h
+++ b/third_party/WebKit/public/platform/WebLayer.h
@@ -169,9 +169,9 @@
   virtual void SetScrollPosition(WebFloatPoint) = 0;
   virtual WebFloatPoint ScrollPosition() const = 0;
 
-  // To set a WebLayer as scrollable we must specify the corresponding clip
-  // layer.
-  virtual void SetScrollClipLayer(WebLayer*) = 0;
+  // To set a WebLayer as scrollable we must specify the scrolling container
+  // bounds.
+  virtual void SetScrollable(const WebSize& scroll_container_bounds) = 0;
   virtual bool Scrollable() const = 0;
   virtual void SetUserScrollable(bool horizontal, bool vertical) = 0;
   virtual bool UserScrollableHorizontal() const = 0;
diff --git a/third_party/WebKit/public/platform/WebLocalizedString.h b/third_party/WebKit/public/platform/WebLocalizedString.h
index 9ae231a..ac53b8a 100644
--- a/third_party/WebKit/public/platform/WebLocalizedString.h
+++ b/third_party/WebKit/public/platform/WebLocalizedString.h
@@ -41,11 +41,7 @@
     kAXCalendarShowPreviousMonth,
     kAXCalendarWeekDescription,
     kAXDayOfMonthFieldText,
-    kAXHeadingText,  // Deprecated.
     kAXHourFieldText,
-    kAXImageMapText,    // Deprecated.
-    kAXLinkText,        // Deprecated.
-    kAXListMarkerText,  // Deprecated.
     kAXMediaAudioElement,
     kAXMediaAudioElementHelp,
     kAXMediaAudioSliderHelp,
@@ -73,11 +69,6 @@
     kAXMediaPlayButtonHelp,
     kAXMediaShowClosedCaptionsButton,
     kAXMediaShowClosedCaptionsButtonHelp,
-    kAXMediaSlider,           // Deprecated.
-    kAXMediaSliderThumb,      // Deprecated.
-    kAXMediaSliderThumbHelp,  // Deprecated.
-    kAXMediaStatusDisplay,
-    kAXMediaStatusDisplayHelp,
     kAXMediaTimeRemainingDisplay,
     kAXMediaTimeRemainingDisplayHelp,
     kAXMediaUnMuteButton,
@@ -89,15 +80,11 @@
     kAXMinuteFieldText,
     kAXMonthFieldText,
     kAXSecondFieldText,
-    kAXWebAreaText,  // Deprecated.
     kAXWeekOfYearFieldText,
     kAXYearFieldText,
     kBlockedPluginText,
     kCalendarClear,
     kCalendarToday,
-    kDateFormatDayInMonthLabel,
-    kDateFormatMonthLabel,
-    kDateFormatYearLabel,
     kDetailsLabel,
     kDownloadButtonLabel,
     kFileButtonChooseFileLabel,
@@ -111,7 +98,6 @@
     kOtherColorLabel,
     kOtherDateLabel,
     kOtherMonthLabel,
-    kOtherTimeLabel,
     kOtherWeekLabel,
     kOverflowMenuCaptions,
     kOverflowMenuCast,
@@ -136,10 +122,6 @@
     // "datetime-local" input UI instead of "----".
     kPlaceholderForYearField,
     kResetButtonDefaultLabel,
-    kSearchableIndexIntroduction,
-    kSearchMenuClearRecentSearchesText,  // Deprecated.
-    kSearchMenuNoRecentSearchesText,     // Deprecated.
-    kSearchMenuRecentSearchesText,       // Deprecated.
     kSelectMenuListText,
     kSubmitButtonDefaultLabel,
     kTextTracksNoLabel,
diff --git a/third_party/WebKit/public/platform/WebRuntimeFeatures.h b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
index c78c7a1a..e67bf35 100644
--- a/third_party/WebKit/public/platform/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/platform/WebRuntimeFeatures.h
@@ -135,6 +135,7 @@
   BLINK_PLATFORM_EXPORT static void EnableWebGLDraftExtensions(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebGLImageChromium(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebNfc(bool);
+  BLINK_PLATFORM_EXPORT static void EnableWebShare(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebUsb(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebVR(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebVRExperimentalRendering(bool);
diff --git a/tools/grit/grit/format/chrome_messages_json.py b/tools/grit/grit/format/chrome_messages_json.py
old mode 100755
new mode 100644
index 27ac361..61d718e
--- a/tools/grit/grit/format/chrome_messages_json.py
+++ b/tools/grit/grit/format/chrome_messages_json.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # 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.
@@ -31,6 +30,12 @@
       if id.startswith('IDR_') or id.startswith('IDS_'):
         id = id[4:]
 
+      translation_missing = child.GetCliques()[0].clique.get(lang) is None;
+      if child.ShouldFallbackToEnglish() and translation_missing:
+          # Skip the string if it's not translated. Chrome will fallback
+          # to English automatically.
+          continue
+
       loc_message = encoder.encode(child.ws_at_start + child.Translate(lang) +
                                    child.ws_at_end)
 
diff --git a/tools/grit/grit/format/chrome_messages_json_unittest.py b/tools/grit/grit/format/chrome_messages_json_unittest.py
index ec188cc..0a3ae2e 100755
--- a/tools/grit/grit/format/chrome_messages_json_unittest.py
+++ b/tools/grit/grit/format/chrome_messages_json_unittest.py
@@ -108,7 +108,8 @@
     """)
 
     buf = StringIO.StringIO()
-    build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'), buf)
+    build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'),
+                                buf)
     output = buf.getvalue()
     test = u"""
 {
@@ -122,6 +123,31 @@
 """
     self.assertEqual(test.strip(), output.strip())
 
+  def testSkipMissingTranslations(self):
+    grd = """<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="2" current_release="3" source_lang_id="en"
+    base_dir="%s">
+  <outputs>
+  </outputs>
+  <release seq="3" allow_pseudo="False">
+    <messages fallback_to_english="true">
+      <message name="ID_HELLO_NO_TRANSLATION">Hello not translated</message>
+    </messages>
+  </release>
+</grit>"""
+    root = grd_reader.Parse(StringIO.StringIO(grd), dir=".")
+
+    buf = StringIO.StringIO()
+    build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'),
+                                buf)
+    output = buf.getvalue()
+    test = u"""
+{
+
+}
+"""
+    self.assertEqual(test.strip(), output.strip())
+
 
 class DummyOutput(object):
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index c6d8e48..b7ba56a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -36497,6 +36497,8 @@
   <int value="11" label="Disabled by Key"/>
   <int value="12" label="Language in ULP"/>
   <int value="13" label="Aborted by translate ranker"/>
+  <int value="14" label="Aborted by too often denied rule"/>
+  <int value="15" label="Aborted by matches previous language"/>
 </enum>
 
 <enum name="TranslateLanguage">
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc
index aa712fa..0d7d03c 100644
--- a/ui/accessibility/ax_role_properties.cc
+++ b/ui/accessibility/ax_role_properties.cc
@@ -32,4 +32,56 @@
   }
 }
 
+bool IsCellOrTableHeaderRole(ui::AXRole role) {
+  switch (role) {
+    case ui::AX_ROLE_CELL:
+    case ui::AX_ROLE_COLUMN_HEADER:
+    case ui::AX_ROLE_ROW_HEADER:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool IsTableLikeRole(ui::AXRole role) {
+  switch (role) {
+    case ui::AX_ROLE_TABLE:
+    case ui::AX_ROLE_GRID:
+    case ui::AX_ROLE_TREE_GRID:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool IsContainerWithSelectableChildrenRole(ui::AXRole role) {
+  switch (role) {
+    case ui::AX_ROLE_COMBO_BOX:
+    case ui::AX_ROLE_GRID:
+    case ui::AX_ROLE_LIST_BOX:
+    case ui::AX_ROLE_MENU:
+    case ui::AX_ROLE_MENU_BAR:
+    case ui::AX_ROLE_RADIO_GROUP:
+    case ui::AX_ROLE_TAB_LIST:
+    case ui::AX_ROLE_TOOLBAR:
+    case ui::AX_ROLE_TREE:
+    case ui::AX_ROLE_TREE_GRID:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool IsRowContainer(ui::AXRole role) {
+  switch (role) {
+    case ui::AX_ROLE_TREE:
+    case ui::AX_ROLE_TREE_GRID:
+    case ui::AX_ROLE_GRID:
+    case ui::AX_ROLE_TABLE:
+      return true;
+    default:
+      return false;
+  }
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_role_properties.h b/ui/accessibility/ax_role_properties.h
index 7d9ab796..6ee8234 100644
--- a/ui/accessibility/ax_role_properties.h
+++ b/ui/accessibility/ax_role_properties.h
@@ -14,6 +14,18 @@
 // clicks.
 AX_EXPORT bool IsRoleClickable(AXRole role);
 
+// Returns true if this node is a cell or a table header.
+AX_EXPORT bool IsCellOrTableHeaderRole(AXRole role);
+
+// Returns true if this node is a table, a grid or a treegrid.
+AX_EXPORT bool IsTableLikeRole(AXRole role);
+
+// Returns true if this node is a container with selectable children.
+AX_EXPORT bool IsContainerWithSelectableChildrenRole(ui::AXRole role);
+
+// Returns true if this node is a row container.
+AX_EXPORT bool IsRowContainer(ui::AXRole role);
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_ROLE_PROPERTIES_H_
diff --git a/ui/android/java/src/org/chromium/ui/UiUtils.java b/ui/android/java/src/org/chromium/ui/UiUtils.java
index 861320d..3a55ab9 100644
--- a/ui/android/java/src/org/chromium/ui/UiUtils.java
+++ b/ui/android/java/src/org/chromium/ui/UiUtils.java
@@ -8,6 +8,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.graphics.Typeface;
 import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
@@ -395,4 +396,19 @@
         if (parent == null) return;
         parent.removeView(view);
     }
+
+    /**
+     * Creates a {@link Typeface} that represents medium-weighted text.  This function returns
+     * Roboto Medium when it is available (Lollipop and up) and Roboto Bold where it isn't.
+     *
+     * @return Typeface that can be applied to a View.
+     */
+    public static Typeface createRobotoMediumTypeface() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            // Roboto Medium, regular.
+            return Typeface.create("sans-serif-medium", Typeface.NORMAL);
+        } else {
+            return Typeface.create("sans-serif", Typeface.BOLD);
+        }
+    }
 }
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index 330928c..979cde4 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -124,6 +124,8 @@
       "views/contents_view.h",
       "views/custom_launcher_page_view.cc",
       "views/custom_launcher_page_view.h",
+      "views/expand_arrow_view.cc",
+      "views/expand_arrow_view.h",
       "views/folder_background_view.cc",
       "views/folder_background_view.h",
       "views/folder_header_view.cc",
diff --git a/ui/app_list/vector_icons/BUILD.gn b/ui/app_list/vector_icons/BUILD.gn
index 3c49259..3a1fd24 100644
--- a/ui/app_list/vector_icons/BUILD.gn
+++ b/ui/app_list/vector_icons/BUILD.gn
@@ -8,6 +8,8 @@
   icon_directory = "."
 
   icons = [
+    "ic_arrow_up.1x.icon",
+    "ic_arrow_up.icon",
     "ic_badge_instant.1x.icon",
     "ic_badge_instant.icon",
     "ic_badge_play.1x.icon",
diff --git a/ui/app_list/vector_icons/ic_arrow_up.1x.icon b/ui/app_list/vector_icons/ic_arrow_up.1x.icon
new file mode 100644
index 0000000..d28822f
--- /dev/null
+++ b/ui/app_list/vector_icons/ic_arrow_up.1x.icon
@@ -0,0 +1,13 @@
+// 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.
+
+CANVAS_DIMENSIONS, 12,
+MOVE_TO, 10.59f, 9,
+LINE_TO, 6, 4.67f,
+LINE_TO, 1.42f, 9,
+LINE_TO, 0, 7.66f,
+LINE_TO, 6, 2,
+R_LINE_TO, 6, 5.66f,
+CLOSE,
+END
diff --git a/ui/app_list/vector_icons/ic_arrow_up.icon b/ui/app_list/vector_icons/ic_arrow_up.icon
new file mode 100644
index 0000000..5f8b186
--- /dev/null
+++ b/ui/app_list/vector_icons/ic_arrow_up.icon
@@ -0,0 +1,13 @@
+// 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 21.17f, 19,
+LINE_TO, 12, 10.34f,
+LINE_TO, 2.83f, 19,
+LINE_TO, 0, 16.33f,
+LINE_TO, 12, 5,
+R_LINE_TO, 12, 11.33f,
+CLOSE,
+END
diff --git a/ui/app_list/views/expand_arrow_view.cc b/ui/app_list/views/expand_arrow_view.cc
new file mode 100644
index 0000000..632d3e18
--- /dev/null
+++ b/ui/app_list/views/expand_arrow_view.cc
@@ -0,0 +1,108 @@
+// 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 "ui/app_list/views/expand_arrow_view.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "ui/app_list/app_list_constants.h"
+#include "ui/app_list/vector_icons/vector_icons.h"
+#include "ui/app_list/views/app_list_view.h"
+#include "ui/app_list/views/contents_view.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
+#include "ui/views/animation/ink_drop_highlight.h"
+#include "ui/views/animation/ink_drop_impl.h"
+#include "ui/views/animation/ink_drop_mask.h"
+#include "ui/views/animation/ink_drop_painted_layer_delegates.h"
+#include "ui/views/controls/image_view.h"
+
+namespace app_list {
+
+namespace {
+
+constexpr int kExpandArrowTileSize = 36;
+constexpr int kExpandArrowIconSize = 12;
+constexpr int kInkDropRadius = 18;
+
+constexpr SkColor kExpandArrowColor = SK_ColorWHITE;
+constexpr SkColor kInkDropRippleColor =
+    SkColorSetARGBMacro(0x14, 0xFF, 0xFF, 0xFF);
+constexpr SkColor kInkDropHighlightColor =
+    SkColorSetARGBMacro(0xF, 0xFF, 0xFF, 0xFF);
+
+}  // namespace
+
+ExpandArrowView::ExpandArrowView(ContentsView* contents_view,
+                                 AppListView* app_list_view)
+    : views::CustomButton(this),
+      contents_view_(contents_view),
+      app_list_view_(app_list_view) {
+  icon_ = new views::ImageView;
+  icon_->SetVerticalAlignment(views::ImageView::CENTER);
+  icon_->SetImage(gfx::CreateVectorIcon(kIcArrowUpIcon, kExpandArrowIconSize,
+                                        kExpandArrowColor));
+  AddChildView(icon_);
+
+  SetInkDropMode(InkDropHostView::InkDropMode::ON);
+}
+
+ExpandArrowView::~ExpandArrowView() {}
+
+void ExpandArrowView::ButtonPressed(views::Button* sender,
+                                    const ui::Event& event) {
+  UMA_HISTOGRAM_ENUMERATION(kPageOpenedHistogram, AppListModel::STATE_APPS,
+                            AppListModel::STATE_LAST);
+
+  contents_view_->SetActiveState(AppListModel::STATE_APPS);
+  app_list_view_->SetState(AppListView::FULLSCREEN_ALL_APPS);
+  GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
+}
+
+gfx::Size ExpandArrowView::CalculatePreferredSize() const {
+  return gfx::Size(kExpandArrowTileSize, kExpandArrowTileSize);
+}
+
+void ExpandArrowView::Layout() {
+  gfx::Rect rect(GetContentsBounds());
+  gfx::Point center = rect.CenterPoint();
+  rect.SetRect(center.x() - kExpandArrowIconSize / 2,
+               center.y() - kExpandArrowIconSize / 2, kExpandArrowIconSize,
+               kExpandArrowIconSize);
+  icon_->SetBoundsRect(rect);
+}
+
+std::unique_ptr<views::InkDrop> ExpandArrowView::CreateInkDrop() {
+  std::unique_ptr<views::InkDropImpl> ink_drop =
+      CustomButton::CreateDefaultInkDropImpl();
+  ink_drop->SetShowHighlightOnHover(false);
+  ink_drop->SetShowHighlightOnFocus(true);
+  ink_drop->SetAutoHighlightMode(
+      views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE);
+  return std::move(ink_drop);
+}
+
+std::unique_ptr<views::InkDropMask> ExpandArrowView::CreateInkDropMask() const {
+  return base::MakeUnique<views::CircleInkDropMask>(
+      size(), GetLocalBounds().CenterPoint(), kInkDropRadius);
+}
+
+std::unique_ptr<views::InkDropRipple> ExpandArrowView::CreateInkDropRipple()
+    const {
+  gfx::Point center = GetLocalBounds().CenterPoint();
+  gfx::Rect bounds(center.x() - kInkDropRadius, center.y() - kInkDropRadius,
+                   2 * kInkDropRadius, 2 * kInkDropRadius);
+  return base::MakeUnique<views::FloodFillInkDropRipple>(
+      size(), GetLocalBounds().InsetsFrom(bounds),
+      GetInkDropCenterBasedOnLastEvent(), kInkDropRippleColor, 1.0f);
+}
+
+std::unique_ptr<views::InkDropHighlight>
+ExpandArrowView::CreateInkDropHighlight() const {
+  return base::MakeUnique<views::InkDropHighlight>(
+      gfx::PointF(GetLocalBounds().CenterPoint()),
+      base::MakeUnique<views::CircleLayerDelegate>(kInkDropHighlightColor,
+                                                   kInkDropRadius));
+}
+
+}  // namespace app_list
diff --git a/ui/app_list/views/expand_arrow_view.h b/ui/app_list/views/expand_arrow_view.h
new file mode 100644
index 0000000..b1064ad4
--- /dev/null
+++ b/ui/app_list/views/expand_arrow_view.h
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_APP_LIST_VIEWS_EXPAND_ARROW_VIEW_H_
+#define UI_APP_LIST_VIEWS_EXPAND_ARROW_VIEW_H_
+
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/custom_button.h"
+
+namespace views {
+class ImageView;
+class InkDrop;
+class InkDropMask;
+class InkDropRipple;
+class InkDropHighlight;
+}  // namespace views
+
+namespace app_list {
+
+class AppListView;
+class ContentsView;
+
+// A tile item for the expand arrow on the start page.
+class ExpandArrowView : public views::CustomButton,
+                        public views::ButtonListener {
+ public:
+  ExpandArrowView(ContentsView* contents_view, AppListView* app_list_view);
+  ~ExpandArrowView() override;
+
+  // Overridden from views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  // Overridden from views::View:
+  gfx::Size CalculatePreferredSize() const override;
+  void Layout() override;
+
+  // Overridden from views::InkDropHost:
+  std::unique_ptr<views::InkDrop> CreateInkDrop() override;
+  std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
+  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
+  std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
+      const override;
+
+ private:
+  ContentsView* const contents_view_;
+  AppListView* const app_list_view_;  // Owned by the views hierarchy.
+
+  views::ImageView* icon_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExpandArrowView);
+};
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_VIEWS_EXPAND_ARROW_VIEW_H_
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index ad3b9b8..fb4e24f 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -23,6 +23,7 @@
 #include "ui/app_list/views/app_list_main_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/custom_launcher_page_view.h"
+#include "ui/app_list/views/expand_arrow_view.h"
 #include "ui/app_list/views/indicator_chip_view.h"
 #include "ui/app_list/views/search_box_view.h"
 #include "ui/app_list/views/search_result_container_view.h"
@@ -56,6 +57,7 @@
 constexpr int kWebViewWidth = 700;
 constexpr int kWebViewHeight = 224;
 
+constexpr int kExpandArrowTopPadding = 28;
 constexpr int kLauncherPageBackgroundWidth = 400;
 
 }  // namespace
@@ -125,6 +127,11 @@
 
   // The view containing the start page tiles.
   AddChildView(suggestions_container_);
+  if (is_fullscreen_app_list_enabled_) {
+    expand_arrow_view_ = new ExpandArrowView(
+        app_list_main_view_->contents_view(), app_list_view);
+    AddChildView(expand_arrow_view_);
+  }
   AddChildView(custom_launcher_page_background_);
 
   suggestions_container_->SetResults(view_delegate_->GetModel()->results());
@@ -245,6 +252,18 @@
   }
   suggestions_container_->SetBoundsRect(bounds);
 
+  if (expand_arrow_view_) {
+    gfx::Rect expand_arrow_rect(GetContentsBounds());
+    int left_right_padding =
+        (bounds.width() - expand_arrow_view_->GetPreferredSize().width()) / 2;
+
+    expand_arrow_rect.Inset(left_right_padding, 0, left_right_padding, 0);
+    expand_arrow_rect.set_y(bounds.bottom() + kExpandArrowTopPadding);
+    expand_arrow_rect.set_height(
+        expand_arrow_view_->GetPreferredSize().height());
+    expand_arrow_view_->SetBoundsRect(expand_arrow_rect);
+  }
+
   CustomLauncherPageView* custom_launcher_page_view =
       app_list_main_view_->contents_view()->custom_page_view();
   if (!custom_launcher_page_view)
diff --git a/ui/app_list/views/start_page_view.h b/ui/app_list/views/start_page_view.h
index 0c4aef3c..e1df339 100644
--- a/ui/app_list/views/start_page_view.h
+++ b/ui/app_list/views/start_page_view.h
@@ -19,6 +19,7 @@
 class AppListView;
 class AppListViewDelegate;
 class CustomLauncherPageBackgroundView;
+class ExpandArrowView;
 class IndicatorChipView;
 class SearchResultTileItemView;
 class SuggestionsContainerView;
@@ -79,6 +80,7 @@
   IndicatorChipView* indicator_ = nullptr;  // Owned by views hierarchy.
   SuggestionsContainerView*
       suggestions_container_;  // Owned by views hierarchy.
+  ExpandArrowView* expand_arrow_view_ = nullptr;  // Owned by views hierarchy.
 
   const bool is_fullscreen_app_list_enabled_;
 
diff --git a/ui/app_list/views/suggestions_container_view.cc b/ui/app_list/views/suggestions_container_view.cc
index b3d2ca6..fef2d86 100644
--- a/ui/app_list/views/suggestions_container_view.cc
+++ b/ui/app_list/views/suggestions_container_view.cc
@@ -20,7 +20,6 @@
 constexpr int kTileSpacing = 7;
 constexpr int kNumTilesCols = 5;
 constexpr int kTilesHorizontalMarginLeft = 145;
-constexpr int kCenterColumnOfStartPageAppGrid = 3;
 
 }  // namespace
 
@@ -148,21 +147,14 @@
     search_result_tile_views_.emplace_back(tile_item);
   }
 
-  if (all_apps_button_)
+  if (all_apps_button_ && !is_fullscreen_app_list_enabled_) {
     all_apps_button_->UpdateIcon();
-  if (is_fullscreen_app_list_enabled_) {
-    // Also add a special "all apps" button to the middle of the next row of the
-    // container.
-    tiles_layout_manager->StartRow(0, 0);
-    tiles_layout_manager->SkipColumns(kCenterColumnOfStartPageAppGrid);
-  } else {
+
     // Also add a special "all apps" button to the end of the next row of the
     // container.
     if (i % kNumTilesCols == 0)
       tiles_layout_manager->StartRow(0, 0);
-  }
 
-  if (all_apps_button_) {
     tiles_layout_manager->AddView(all_apps_button_);
     AddChildView(all_apps_button_);
   }
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 3a2aa4fc..b48c8a3 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -872,11 +872,13 @@
     delegate_->OnDelegatedFrameDamage(damage_rect_in_dip);
 }
 
-void Layer::SetScrollable(
-    Layer* parent_clip_layer,
-    const base::Callback<void(const gfx::ScrollOffset&)>& on_scroll) {
-  cc_layer_->SetScrollClipLayerId(parent_clip_layer->cc_layer_->id());
-  cc_layer_->set_did_scroll_callback(on_scroll);
+void Layer::SetDidScrollCallback(
+    base::Callback<void(const gfx::ScrollOffset&)> callback) {
+  cc_layer_->set_did_scroll_callback(std::move(callback));
+}
+
+void Layer::SetScrollable(const gfx::Size& container_bounds) {
+  cc_layer_->SetScrollable(container_bounds);
   cc_layer_->SetUserScrollable(true, true);
 }
 
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index a439bbdf1..0c2a0e8 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -366,11 +366,15 @@
   // Requets a copy of the layer's output as a texture or bitmap.
   void RequestCopyOfOutput(std::unique_ptr<cc::CopyOutputRequest> request);
 
-  // Makes this Layer scrollable, clipping to |parent_clip_layer|. |on_scroll|
-  // is invoked when scrolling performed by the cc::InputHandler is committed.
-  void SetScrollable(
-      Layer* parent_clip_layer,
-      const base::Callback<void(const gfx::ScrollOffset&)>& on_scroll);
+  // Invoked when scrolling performed by the cc::InputHandler is committed. This
+  // will only occur if the Layer has set scroll container bounds.
+  void SetDidScrollCallback(
+      base::Callback<void(const gfx::ScrollOffset&)> callback);
+
+  // Marks this layer as scrollable inside the provided bounds. This size only
+  // affects scrolling so if clipping is desired, a separate clipping layer
+  // needs to be created.
+  void SetScrollable(const gfx::Size& container_bounds);
 
   // Gets and sets the current scroll offset of the layer.
   gfx::ScrollOffset CurrentScrollOffset() const;
diff --git a/ui/ozone/platform/drm/host/drm_overlay_manager.h b/ui/ozone/platform/drm/host/drm_overlay_manager.h
index 89f8b41..6e5a0ac 100644
--- a/ui/ozone/platform/drm/host/drm_overlay_manager.h
+++ b/ui/ozone/platform/drm/host/drm_overlay_manager.h
@@ -28,6 +28,8 @@
   std::unique_ptr<OverlayCandidatesOzone> CreateOverlayCandidates(
       gfx::AcceleratedWidget w) override;
 
+  // Invoked on changes to the window (aka display) that require re-populating
+  // the cache from the DRM thread.
   void ResetCache();
 
   // Communication-free implementations of actions performed in response to
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
index 539d23e..61f853fe 100644
--- a/ui/views/controls/scroll_view.cc
+++ b/ui/views/controls/scroll_view.cc
@@ -237,9 +237,9 @@
       a_view->SetBackground(CreateSolidBackground(GetBackgroundColor()));
     }
     a_view->SetPaintToLayer();
-    a_view->layer()->SetScrollable(
-        contents_viewport_->layer(),
+    a_view->layer()->SetDidScrollCallback(
         base::Bind(&ScrollView::OnLayerScrolled, base::Unretained(this)));
+    a_view->layer()->SetScrollable(contents_viewport_->bounds().size());
   }
   SetHeaderOrContents(contents_viewport_, a_view, &contents_);
 }
@@ -474,6 +474,7 @@
     gfx::Size container_size = contents_ ? contents_->size() : gfx::Size();
     container_size.SetToMax(viewport_bounds.size());
     contents_->SetBoundsRect(gfx::Rect(container_size));
+    contents_->layer()->SetScrollable(viewport_bounds.size());
   }
 
   header_viewport_->SetBounds(contents_x, contents_y,