diff --git a/DEPS b/DEPS
index edfa50a..36b6b9c7 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '0bd4a23ef40d74ef051eb7f457c133d5febc377d',
+  'skia_revision': '587e08f361ee3e775a6bbc6dca761dbba82e422c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'b481bccc84783445e123b8b4e3ff9ecfdac2bd90',
+  'v8_revision': '6512d451afd862b8172384fbd94feaa3500fe994',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'd532036fbb0efa4687f89598ff37518e3825c7b9',
+  'pdfium_revision': '6438c4f36da162f72e0d53e8fff45cd6687b7f5c',
   # 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': '57b62a2ef4f17b18b83ea6700865eb0c2459f8fe',
+  'catapult_revision': '3c9b30e0cc0d45d6728dec4440585ffd3ac89b2a',
   # 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/net/aw_cookie_store_wrapper.cc b/android_webview/browser/net/aw_cookie_store_wrapper.cc
index 466081b0..811e775c 100644
--- a/android_webview/browser/net/aw_cookie_store_wrapper.cc
+++ b/android_webview/browser/net/aw_cookie_store_wrapper.cc
@@ -121,13 +121,11 @@
     bool secure,
     bool http_only,
     net::CookieSameSite same_site,
-    bool enforce_strict_secure,
     net::CookiePriority priority,
     const net::CookieStore::SetCookiesCallback& callback) {
   GetCookieStore()->SetCookieWithDetailsAsync(
       url, name, value, domain, path, creation_time, expiration_time,
-      last_access_time, secure, http_only, same_site, enforce_strict_secure,
-      priority, callback);
+      last_access_time, secure, http_only, same_site, priority, callback);
 }
 
 void GetCookiesWithOptionsAsyncOnCookieThread(
@@ -222,14 +220,13 @@
     bool secure,
     bool http_only,
     net::CookieSameSite same_site,
-    bool enforce_strict_secure,
     net::CookiePriority priority,
     const SetCookiesCallback& callback) {
   DCHECK(client_task_runner_->RunsTasksOnCurrentThread());
   PostTaskToCookieStoreTaskRunner(
       base::Bind(&SetCookieWithDetailsAsyncOnCookieThread, url, name, value,
                  domain, path, creation_time, expiration_time, last_access_time,
-                 secure, http_only, same_site, enforce_strict_secure, priority,
+                 secure, http_only, same_site, priority,
                  CreateWrappedCallback<bool>(callback)));
 }
 
diff --git a/android_webview/browser/net/aw_cookie_store_wrapper.h b/android_webview/browser/net/aw_cookie_store_wrapper.h
index 61d61b2..eb90d7d5 100644
--- a/android_webview/browser/net/aw_cookie_store_wrapper.h
+++ b/android_webview/browser/net/aw_cookie_store_wrapper.h
@@ -53,7 +53,6 @@
                                  bool secure,
                                  bool http_only,
                                  net::CookieSameSite same_site,
-                                 bool enforce_strict_secure,
                                  net::CookiePriority priority,
                                  const SetCookiesCallback& callback) override;
   void GetCookiesWithOptionsAsync(const GURL& url,
diff --git a/android_webview/browser/net/aw_cookie_store_wrapper_unittest.cc b/android_webview/browser/net/aw_cookie_store_wrapper_unittest.cc
index a4bbd28..171bfb7 100644
--- a/android_webview/browser/net/aw_cookie_store_wrapper_unittest.cc
+++ b/android_webview/browser/net/aw_cookie_store_wrapper_unittest.cc
@@ -35,7 +35,6 @@
   static const bool filters_schemes = true;
   static const bool has_path_prefix_bug = false;
   static const int creation_time_granularity_in_ms = 0;
-  static const bool enforce_strict_secure = false;
 };
 
 }  // namespace android_webview
diff --git a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
index 77f83f1..d96fefb 100644
--- a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
+++ b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
@@ -113,7 +113,6 @@
         }
     }
 
-    @DisabledTest(message = "crbug.com/683153")
     @MediumTest
     public void testWebViewExcludedInterfaces() throws Exception {
         ensureJsTestCopied();
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
index 1ade4e3..9c59a8d 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
@@ -72,7 +72,7 @@
     attribute internals
     attribute accessibilityController
     attribute textInputController
-    attribute mojo
+    attribute gin
     attribute gamepadController
     attribute GCController
     getter speechSynthesis
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h
index fead4bea..a76dd632 100644
--- a/base/metrics/histogram.h
+++ b/base/metrics/histogram.h
@@ -79,11 +79,6 @@
 #include "base/macros.h"
 #include "base/metrics/bucket_ranges.h"
 #include "base/metrics/histogram_base.h"
-#if defined(OS_IOS)
-// TODO(asvitkine): Migrate callers to to include this directly and remove this.
-// Note: Incrementally migrating platforms as they become clean.
-#include "base/metrics/histogram_macros.h"
-#endif
 #include "base/metrics/histogram_samples.h"
 #include "base/time/time.h"
 
diff --git a/cc/surfaces/BUILD.gn b/cc/surfaces/BUILD.gn
index e83d5817..5b2b022 100644
--- a/cc/surfaces/BUILD.gn
+++ b/cc/surfaces/BUILD.gn
@@ -62,7 +62,6 @@
     "surface_id_allocator.h",
     "surface_manager.cc",
     "surface_manager.h",
-    "surface_reference_manager.h",
     "surface_resource_holder.cc",
     "surface_resource_holder.h",
     "surfaces_export.h",
diff --git a/cc/surfaces/surface_manager.cc b/cc/surfaces/surface_manager.cc
index cfdc7a1..dd75af7 100644
--- a/cc/surfaces/surface_manager.cc
+++ b/cc/surfaces/surface_manager.cc
@@ -216,7 +216,6 @@
   }
 
   RemoveSurfaceReferenceImpl(parent_id, child_id);
-  GarbageCollectSurfaces();
 }
 
 void SurfaceManager::AddSurfaceReferences(
@@ -231,26 +230,10 @@
     const std::vector<SurfaceReference>& references) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  // TODO(kylechar): Each remove reference can trigger GC, it would be better if
-  // we GC only once if removing multiple references.
   for (const auto& reference : references)
     RemoveSurfaceReference(reference.parent_id(), reference.child_id());
-}
 
-size_t SurfaceManager::GetSurfaceReferenceCount(
-    const SurfaceId& surface_id) const {
-  auto iter = child_to_parent_refs_.find(surface_id);
-  if (iter == child_to_parent_refs_.end())
-    return 0;
-  return iter->second.size();
-}
-
-size_t SurfaceManager::GetReferencedSurfaceCount(
-    const SurfaceId& surface_id) const {
-  auto iter = parent_to_child_refs_.find(surface_id);
-  if (iter == parent_to_child_refs_.end())
-    return 0;
-  return iter->second.size();
+  GarbageCollectSurfaces();
 }
 
 void SurfaceManager::GarbageCollectSurfaces() {
diff --git a/cc/surfaces/surface_manager.h b/cc/surfaces/surface_manager.h
index e8f53b8f..a3c4f22b 100644
--- a/cc/surfaces/surface_manager.h
+++ b/cc/surfaces/surface_manager.h
@@ -13,7 +13,6 @@
 #include <unordered_set>
 #include <vector>
 
-#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -22,8 +21,8 @@
 #include "cc/surfaces/frame_sink_id.h"
 #include "cc/surfaces/surface_id.h"
 #include "cc/surfaces/surface_observer.h"
+#include "cc/surfaces/surface_reference.h"
 #include "cc/surfaces/surface_reference_factory.h"
-#include "cc/surfaces/surface_reference_manager.h"
 #include "cc/surfaces/surface_sequence.h"
 #include "cc/surfaces/surfaces_export.h"
 
@@ -38,8 +37,7 @@
 class Surface;
 class SurfaceFactoryClient;
 
-class CC_SURFACES_EXPORT SurfaceManager
-    : public NON_EXPORTED_BASE(SurfaceReferenceManager) {
+class CC_SURFACES_EXPORT SurfaceManager {
  public:
   enum class LifetimeType {
     REFERENCES,
@@ -47,7 +45,7 @@
   };
 
   explicit SurfaceManager(LifetimeType lifetime_type = LifetimeType::SEQUENCES);
-  ~SurfaceManager() override;
+  ~SurfaceManager();
 
 #if DCHECK_IS_ON()
   // Returns a string representation of all reachable surface references.
@@ -123,18 +121,27 @@
   void UnregisterFrameSinkHierarchy(const FrameSinkId& parent_frame_sink_id,
                                     const FrameSinkId& child_frame_sink_id);
 
-  // SurfaceReferenceManager:
-  const SurfaceId& GetRootSurfaceId() const override;
+  // Returns the top level root SurfaceId. Surfaces that are not reachable
+  // from the top level root may be garbage collected. It will not be a valid
+  // SurfaceId and will never correspond to a surface.
+  const SurfaceId& GetRootSurfaceId() const;
+
+  // Adds a reference from |parent_id| to |child_id|. If there is a temporary
+  // references for |child_id| then it will be removed.
   void AddSurfaceReference(const SurfaceId& parent_id,
-                           const SurfaceId& child_id) override;
+                           const SurfaceId& child_id);
+
+  // Removes a reference from |parent_id| to |child_id|.
   void RemoveSurfaceReference(const SurfaceId& parent_id,
-                              const SurfaceId& child_id) override;
-  void AddSurfaceReferences(
-      const std::vector<SurfaceReference>& references) override;
-  void RemoveSurfaceReferences(
-      const std::vector<SurfaceReference>& references) override;
-  size_t GetSurfaceReferenceCount(const SurfaceId& surface_id) const override;
-  size_t GetReferencedSurfaceCount(const SurfaceId& surface_id) const override;
+                              const SurfaceId& child_id);
+
+  // Adds all surface references in |references|. This will remove any temporary
+  // references for child surface in a surface reference.
+  void AddSurfaceReferences(const std::vector<SurfaceReference>& references);
+
+  // Removes all surface references in |references| then runs garbage
+  // collection to delete unreachable surfaces.
+  void RemoveSurfaceReferences(const std::vector<SurfaceReference>& references);
 
   scoped_refptr<SurfaceReferenceFactory> reference_factory() {
     return reference_factory_;
diff --git a/cc/surfaces/surface_manager_ref_unittest.cc b/cc/surfaces/surface_manager_ref_unittest.cc
index 348ee8d..beec317 100644
--- a/cc/surfaces/surface_manager_ref_unittest.cc
+++ b/cc/surfaces/surface_manager_ref_unittest.cc
@@ -58,7 +58,6 @@
     GetFactory(surface_id.frame_sink_id()).EvictSurface();
   }
 
- protected:
   SurfaceFactory& GetFactory(const FrameSinkId& frame_sink_id) {
     auto& factory_ptr = factories_[frame_sink_id];
     if (!factory_ptr)
@@ -67,17 +66,9 @@
     return *factory_ptr;
   }
 
-  // testing::Test:
-  void SetUp() override {
-    // Start each test with a fresh SurfaceManager instance.
-    manager_ = base::MakeUnique<SurfaceManager>(
-        SurfaceManager::LifetimeType::REFERENCES);
-  }
-  void TearDown() override {
-    for (auto& factory : factories_)
-      factory.second->EvictSurface();
-    factories_.clear();
-    manager_.reset();
+  void RemoveSurfaceReference(const SurfaceId& parent_id,
+                              const SurfaceId& child_id) {
+    manager_->RemoveSurfaceReferences({SurfaceReference(parent_id, child_id)});
   }
 
   // Returns all the references where |surface_id| is the parent.
@@ -113,6 +104,20 @@
     return temp_references;
   }
 
+ protected:
+  // testing::Test:
+  void SetUp() override {
+    // Start each test with a fresh SurfaceManager instance.
+    manager_ = base::MakeUnique<SurfaceManager>(
+        SurfaceManager::LifetimeType::REFERENCES);
+  }
+  void TearDown() override {
+    for (auto& factory : factories_)
+      factory.second->EvictSurface();
+    factories_.clear();
+    manager_.reset();
+  }
+
   std::unordered_map<FrameSinkId,
                      std::unique_ptr<SurfaceFactory>,
                      FrameSinkIdHash>
@@ -142,7 +147,7 @@
   EXPECT_THAT(GetReferencesFrom(id1), UnorderedElementsAre(id2));
   EXPECT_THAT(GetReferencesFrom(id2), IsEmpty());
 
-  manager().RemoveSurfaceReference(id1, id2);
+  RemoveSurfaceReference(id1, id2);
   EXPECT_THAT(GetReferencesFor(id1), SizeIs(1));
   EXPECT_THAT(GetReferencesFor(id2), IsEmpty());
   EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
@@ -171,7 +176,7 @@
   EXPECT_THAT(GetReferencesFor(id2_next), UnorderedElementsAre(id1));
   EXPECT_THAT(GetReferencesFor(id3), UnorderedElementsAre(id2, id2_next));
 
-  manager().RemoveSurfaceReference(id1, id2);
+  RemoveSurfaceReference(id1, id2);
   EXPECT_THAT(GetReferencesFor(id2), IsEmpty());
   EXPECT_THAT(GetReferencesFor(id2_next), UnorderedElementsAre(id1));
   EXPECT_THAT(GetReferencesFor(id3), UnorderedElementsAre(id2_next));
@@ -198,7 +203,7 @@
   DestroySurface(id2);
   DestroySurface(id1);
 
-  manager().RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
+  RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
 
   // Removing the reference from the root to id1 should allow all three surfaces
   // to be deleted during GC even with a cycle between 2 and 3.
@@ -225,11 +230,11 @@
   EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
 
   // Should delete |id2| when the only reference to it is removed.
-  manager().RemoveSurfaceReference(id1, id2);
+  RemoveSurfaceReference(id1, id2);
   EXPECT_EQ(nullptr, manager().GetSurfaceForId(id2));
 
   // Should delete |id1| when the only reference to it is removed.
-  manager().RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
+  RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
   EXPECT_EQ(nullptr, manager().GetSurfaceForId(id1));
 }
 
@@ -252,7 +257,7 @@
   EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
   EXPECT_NE(nullptr, manager().GetSurfaceForId(id1));
 
-  manager().RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
+  RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
 
   // Removing the reference from the root to id1 should allow all three surfaces
   // to be deleted during GC.
@@ -309,7 +314,7 @@
 
   // Removing non-existent reference should be ignored.
   manager().AddSurfaceReference(id1, id2);
-  manager().RemoveSurfaceReference(id2, id1);
+  RemoveSurfaceReference(id2, id1);
   EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
   EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
 }
diff --git a/cc/surfaces/surface_reference_manager.h b/cc/surfaces/surface_reference_manager.h
deleted file mode 100644
index 7eec022..0000000
--- a/cc/surfaces/surface_reference_manager.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_SURFACES_SURFACE_REFERENCE_MANAGER_H_
-#define CC_SURFACES_SURFACE_REFERENCE_MANAGER_H_
-
-#include "cc/surfaces/surface_reference.h"
-
-namespace cc {
-
-class SurfaceId;
-
-// Interface to manage surface references.
-class SurfaceReferenceManager {
- public:
-  // Returns the top level root SurfaceId. Surfaces that are not reachable
-  // from the top level root may be garbage collected. It will not be a valid
-  // SurfaceId and will never correspond to a surface.
-  virtual const SurfaceId& GetRootSurfaceId() const = 0;
-
-  // Adds a reference from a parent surface to a child surface. Any surface
-  // embedding a child surface should have a reference added so that the child
-  // surface is not garbage collected until after the parent surface.
-  virtual void AddSurfaceReference(const SurfaceId& parent_id,
-                                   const SurfaceId& child_id) = 0;
-
-  // Removes a reference from a parent surface to a child surface.
-  virtual void RemoveSurfaceReference(const SurfaceId& parent_id,
-                                      const SurfaceId& child_id) = 0;
-
-  // Adds surface references. For each reference added, this will remove the
-  // temporary reference to the child surface if one exists.
-  virtual void AddSurfaceReferences(
-      const std::vector<SurfaceReference>& references) = 0;
-
-  // Removes surface references.
-  virtual void RemoveSurfaceReferences(
-      const std::vector<SurfaceReference>& references) = 0;
-
-  // Returns the number of surfaces that have references to |surface_id|. When
-  // the count is zero nothing is referencing the surface and it may be garbage
-  // collected.
-  virtual size_t GetSurfaceReferenceCount(
-      const SurfaceId& surface_id) const = 0;
-
-  // Returns the number of surfaces that |surface_id| has references to.
-  virtual size_t GetReferencedSurfaceCount(
-      const SurfaceId& surface_id) const = 0;
-
- protected:
-  virtual ~SurfaceReferenceManager() {}
-};
-
-}  // namespace cc
-
-#endif  // CC_SURFACES_SURFACE_REFERENCE_MANAGER_H_
diff --git a/chrome/android/java/res/layout/new_tab_page_layout.xml b/chrome/android/java/res/layout/new_tab_page_layout.xml
index d68172e..0af0820 100644
--- a/chrome/android/java/res/layout/new_tab_page_layout.xml
+++ b/chrome/android/java/res/layout/new_tab_page_layout.xml
@@ -138,11 +138,4 @@
         android:layout_height="0dp"
         android:layout_weight="1"
         android:visibility="gone" />
-
-    <!-- Spacer to make the page scrollable when needed. -->
-    <View
-        android:id="@+id/ntp_scroll_spacer"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:visibility="gone" />
 </org.chromium.chrome.browser.ntp.NewTabPageLayout>
diff --git a/chrome/android/java/res/layout/new_tab_page_scroll_view.xml b/chrome/android/java/res/layout/new_tab_page_scroll_view.xml
deleted file mode 100644
index 42fa6436..0000000
--- a/chrome/android/java/res/layout/new_tab_page_scroll_view.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2016 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<org.chromium.chrome.browser.ntp.NewTabPageScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:background="@color/ntp_bg"
-    android:fillViewport="true"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:focusable="true"
-    android:focusableInTouchMode="true"
-    android:contentDescription="@string/accessibility_new_tab_page" >
-
-    <include layout="@layout/new_tab_page_layout" />
-
-</org.chromium.chrome.browser.ntp.NewTabPageScrollView>
diff --git a/chrome/android/java/res/layout/new_tab_page_view.xml b/chrome/android/java/res/layout/new_tab_page_view.xml
index 71359c6..0073c45 100644
--- a/chrome/android/java/res/layout/new_tab_page_view.xml
+++ b/chrome/android/java/res/layout/new_tab_page_view.xml
@@ -9,64 +9,7 @@
     android:layout_height="match_parent"
     android:paddingTop="@dimen/tab_strip_height" >
 
-    <ViewStub
-        android:id="@+id/new_tab_page_layout_stub"
-        android:inflatedId="@+id/ntp_scrollview"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginBottom="48dp" />
-
-    <!-- Bookmarks and Recent tabs buttons -->
-    <org.chromium.chrome.browser.ntp.NewTabPageToolbar
-        android:id="@+id/ntp_toolbar"
-        android:background="@color/ntp_bg"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_gravity="bottom"
-        android:orientation="horizontal" >
-        <!-- Each button is a FrameLayout. Why? The TextView contains both text and a compound
-             drawable icon. If the TextView is half the screen width, the icon will be at the left
-             edge of the TextView even while the text is centered. To center the icon along with the
-             text, we need to set the TextView's width to wrap_content; but since the button needs
-             to be half the screen width, we wrap the TextView in a FrameLayout, whose width can
-             expand as needed. -->
-        <FrameLayout
-            android:id="@+id/bookmarks_button"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:background="?attr/listChoiceBackgroundIndicator"
-            android:contentDescription="@null"
-            android:paddingTop="12dp"
-            android:paddingBottom="12dp" >
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:layout_gravity="center_horizontal"
-                android:drawablePadding="8dp"
-                android:gravity="center_vertical"
-                android:text="@null"
-                android:textSize="12sp"
-                android:textColor="#5a5a5a" />
-        </FrameLayout>
-        <FrameLayout
-            android:id="@+id/recent_tabs_button"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:background="?attr/listChoiceBackgroundIndicator"
-            android:contentDescription="@string/accessibility_ntp_toolbar_btn_recent_tabs"
-            android:paddingTop="12dp"
-            android:paddingBottom="12dp" >
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:layout_gravity="center_horizontal"
-                android:drawablePadding="8dp"
-                android:gravity="center_vertical"
-                android:text="@string/recent_tabs"
-                android:textSize="12sp"
-                android:textColor="#5a5a5a" />
-        </FrameLayout>
-    </org.chromium.chrome.browser.ntp.NewTabPageToolbar>
+    <include
+        android:id="@+id/new_tab_page_recycler_view"
+        layout="@layout/new_tab_page_recycler_view" />
 </org.chromium.chrome.browser.ntp.NewTabPageView>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index c2f3fec5..4325a36 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -66,7 +66,6 @@
     public static final String NTP_FAKE_OMNIBOX_TEXT = "NTPFakeOmniboxText";
     public static final String NTP_FOREIGN_SESSIONS_SUGGESTIONS = "NTPForeignSessionsSuggestions";
     public static final String NTP_OFFLINE_PAGES_FEATURE_NAME = "NTPOfflinePages";
-    public static final String NTP_SNIPPETS = "NTPSnippets";
     public static final String NTP_SNIPPETS_INCREASED_VISIBILITY = "NTPSnippetsIncreasedVisibility";
     public static final String NTP_SNIPPETS_SAVE_TO_OFFLINE = "NTPSaveToOffline";
     public static final String NTP_SNIPPETS_OFFLINE_BADGE = "NTPOfflineBadge";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 04586583..6107bf2e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -84,7 +84,6 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
-import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig;
 import org.chromium.chrome.browser.omaha.OmahaClient;
 import org.chromium.chrome.browser.omnibox.AutocompleteController;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
@@ -496,12 +495,10 @@
         mLocaleManager.setSnackbarManager(getSnackbarManager());
         mLocaleManager.startObservingPhoneChanges();
 
-        if (SnippetsConfig.isEnabled()) {
-            if (isWarmOnResume()) {
-                SnippetsBridge.notifySchedulerAboutWarmResume();
-            } else {
-                SnippetsBridge.notifySchedulerAboutColdStart();
-            }
+        if (isWarmOnResume()) {
+            SnippetsBridge.notifySchedulerAboutWarmResume();
+        } else {
+            SnippetsBridge.notifySchedulerAboutColdStart();
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PostMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PostMessageHandler.java
index 9329a451..43995da 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PostMessageHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PostMessageHandler.java
@@ -123,7 +123,7 @@
                     return;
                 }
                 webContents.postMessageToFrame(
-                        frameName, message, targetOrigin, sentPortIds);
+                        frameName, message, mOrigin.toString(), targetOrigin, sentPortIds);
             }
 
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
index 3f78989..2a28c690 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java
@@ -70,6 +70,7 @@
      * Opens the content suggestion when notification is tapped.
      */
     public static final class OpenUrlReceiver extends BroadcastReceiver {
+        @Override
         public void onReceive(Context context, Intent intent) {
             int category = intent.getIntExtra(NOTIFICATION_CATEGORY_EXTRA, -1);
             String idWithinCategory = intent.getStringExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA);
@@ -83,6 +84,7 @@
      * Records dismissal when notification is swiped away.
      */
     public static final class DeleteReceiver extends BroadcastReceiver {
+        @Override
         public void onReceive(Context context, Intent intent) {
             int category = intent.getIntExtra(NOTIFICATION_CATEGORY_EXTRA, -1);
             String idWithinCategory = intent.getStringExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA);
@@ -96,6 +98,7 @@
      * Removes the notification after a timeout period.
      */
     public static final class TimeoutReceiver extends BroadcastReceiver {
+        @Override
         public void onReceive(Context context, Intent intent) {
             int category = intent.getIntExtra(NOTIFICATION_CATEGORY_EXTRA, -1);
             String idWithinCategory = intent.getStringExtra(NOTIFICATION_ID_WITHIN_CATEGORY_EXTRA);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 75c3f19..f1987f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -36,7 +36,6 @@
 import org.chromium.chrome.browser.ntp.LogoBridge.LogoObserver;
 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
-import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.profiles.MostVisitedSites;
 import org.chromium.chrome.browser.profiles.MostVisitedSites.MostVisitedURLsObserver;
@@ -409,7 +408,7 @@
             assert !mIsDestroyed;
             return mNewTabPageView.getContextMenuManager();
         }
-    };
+    }
 
     /**
      * Constructs a NewTabPage.
@@ -424,9 +423,7 @@
         mTabModelSelector = tabModelSelector;
         Profile profile = tab.getProfile();
 
-        if (SnippetsConfig.isEnabled()) {
-            mSnippetsBridge = new SnippetsBridge(profile);
-        }
+        mSnippetsBridge = new SnippetsBridge(profile);
 
         SuggestionsNavigationDelegateImpl navigationDelegate =
                 new SuggestionsNavigationDelegateImpl(activity, profile, tab, tabModelSelector);
@@ -534,11 +531,6 @@
         return mNewTabPageView;
     }
 
-    /** @return whether the NTP is using the cards UI. */
-    public boolean isCardsUiEnabled() {
-        return SnippetsConfig.isEnabled();
-    }
-
     /**
      * Updates whether the NewTabPage should animate on URL focus changes.
      * @param disable Whether to disable the animations.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
index 1ff8770..184f52fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -43,7 +43,6 @@
 
     private int mParentViewportHeight;
 
-    private boolean mCardsUiEnabled;
     private View mTopSpacer; // Spacer above search logo.
     private View mMiddleSpacer; // Spacer between toolbar and Most Likely.
     private View mBottomSpacer; // Spacer below Most Likely.
@@ -51,10 +50,6 @@
     private View mLogoSpacer; // Spacer above the logo.
     private View mSearchBoxSpacer; // Spacer above the search box.
 
-    // Separate spacer below Most Likely to add enough space so the user can scroll with Most Likely
-    // at the top of the screen.
-    private View mScrollCompensationSpacer;
-
     private LogoView mSearchProviderLogoView;
     private View mSearchBoxView;
     private MostVisitedLayout mMostVisitedLayout;
@@ -90,7 +85,6 @@
         mBottomSpacer = findViewById(R.id.ntp_bottom_spacer);
         mLogoSpacer = findViewById(R.id.search_provider_logo_spacer);
         mSearchBoxSpacer = findViewById(R.id.search_box_spacer);
-        mScrollCompensationSpacer = findViewById(R.id.ntp_scroll_spacer);
         mSearchProviderLogoView = (LogoView) findViewById(R.id.search_provider_logo);
         mSearchBoxView = findViewById(R.id.search_box);
         mMostVisitedLayout = (MostVisitedLayout) findViewById(R.id.most_visited_layout);
@@ -108,30 +102,20 @@
         mParentViewportHeight = height;
     }
 
-    /**
-     * Sets whether the cards UI is enabled.
-     */
-    public void setUseCardsUiEnabled(boolean useCardsUi) {
-        mCardsUiEnabled = useCardsUi;
-    }
-
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mCardsUiEnabled) {
-            measureWithCardsUiEnabled(widthMeasureSpec, heightMeasureSpec);
-        } else {
-            measureWithCardsUiDisabled(widthMeasureSpec, heightMeasureSpec);
-        }
-
-        measureCommonParts();
+        calculateVerticalSpacing(widthMeasureSpec, heightMeasureSpec);
+        unifyElementWidths();
     }
 
     /**
-     * Performs layout measurements for when the cards ui is enabled.
+     * Uses the total vertical space to determine and configure the layout. This can be one of:
+     * - If our contents cannot fit on the screen, increase the spacing to push the Most Likely
+     *   partially off the screen, suggesting to users they can scroll.
+     * - If our contents can fit on the screen, increase the spacing to fill the space (minus space
+     *   for the CardsUI Peeking card).
      */
-    private void measureWithCardsUiEnabled(int widthMeasureSpec, int heightMeasureSpec) {
-        assert mCardsUiEnabled;
-
+    private void calculateVerticalSpacing(int widthMeasureSpec, int heightMeasureSpec) {
         mLogoSpacer.setVisibility(View.GONE);
         mSearchBoxSpacer.setVisibility(View.GONE);
 
@@ -198,7 +182,6 @@
             distributeExtraSpace(mTopSpacer.getMeasuredHeight());
         }
 
-        assert getParent() instanceof NewTabPageRecyclerView;
         NewTabPageRecyclerView recyclerView = (NewTabPageRecyclerView) getParent();
         recyclerView.setHasSpaceForPeekingCard(hasSpaceForPeekingCard);
 
@@ -211,79 +194,19 @@
     }
 
     /**
-     * Performs layout measurements for when the cards ui is disabled.
+     * Makes the Search Box and Logo as wide as Most Visited.
      */
-    private void measureWithCardsUiDisabled(int widthMeasureSpec, int heightMeasureSpec) {
-        assert !mCardsUiEnabled;
-
-        // Remove the scroll spacer from the layout so the weighted children can be measured
-        // correctly.
-        mScrollCompensationSpacer.setVisibility(View.GONE);
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        if (getMeasuredHeight() > mParentViewportHeight) {
-            // This layout is bigger than its parent's viewport, so the user will need to scroll
-            // to see all of it. Extra spacing should be added at the bottom so the user can
-            // scroll until Most Visited is at the top.
-
-            // The top, middle, and bottom spacers should have a measured height of 0 at this
-            // point since they use weights to set height, and there was no extra space.
-            assert mTopSpacer.getMeasuredHeight() == 0;
-            assert mMiddleSpacer.getMeasuredHeight() == 0;
-            assert mBottomSpacer.getMeasuredHeight() == 0;
-
-            final int topOfMostVisited = calculateTopOfMostVisited();
-            final int belowTheFoldHeight = getMeasuredHeight() - mParentViewportHeight;
-            if (belowTheFoldHeight < topOfMostVisited) {
-                // Include the scroll spacer in the layout and call super.onMeasure again so it
-                // is measured.
-                mScrollCompensationSpacer.getLayoutParams().height =
-                        topOfMostVisited - belowTheFoldHeight;
-
-                mScrollCompensationSpacer.setVisibility(View.INVISIBLE);
-                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            }
-        } else {
-            distributeExtraSpace(mTopSpacer.getMeasuredHeight());
-        }
-    }
-
-    /**
-     * Performs measurements that should be done whether the cards ui is enabled or not.
-     */
-    private void measureCommonParts() {
-        // Make the search box and logo as wide as the most visited items.
+    private void unifyElementWidths() {
         if (mMostVisitedLayout.getVisibility() != GONE) {
             final int width = mMostVisitedLayout.getMeasuredWidth() - mMostVisitedLayoutBleed;
-            measureExactly(mSearchBoxView, width + mSearchboxShadowWidth,
-                    mSearchBoxView.getMeasuredHeight());
-            measureExactly(
-                    mSearchProviderLogoView, width, mSearchProviderLogoView.getMeasuredHeight());
+            measureExactly(mSearchBoxView,
+                    width + mSearchboxShadowWidth, mSearchBoxView.getMeasuredHeight());
+            measureExactly(mSearchProviderLogoView,
+                    width, mSearchProviderLogoView.getMeasuredHeight());
         }
     }
 
     /**
-     * Calculate the vertical position of Most Visited.
-     * This method does not use mMostVisitedLayout.getTop(), so can be called in onMeasure.
-     */
-    private int calculateTopOfMostVisited() {
-        // Manually add the heights (and margins) of all children above Most Visited.
-        int top = 0;
-        int mostVisitedIndex = indexOfChild(mMostVisitedLayout);
-        for (int i = 0; i < mostVisitedIndex; i++) {
-            View child = getChildAt(i);
-
-            if (child.getVisibility() == View.GONE) continue;
-
-            MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
-            top += params.topMargin + child.getMeasuredHeight() + params.bottomMargin;
-        }
-        top += ((MarginLayoutParams) mMostVisitedLayout.getLayoutParams()).topMargin;
-        return top;
-    }
-
-    /**
      * Distribute extra vertical space between the three spacer views. Doing this here allows for
      * more sophisticated constraints than in xml.
      * @param extraHeight The amount of extra space, in pixels.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java
index ef89def8..1da0d0b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java
@@ -24,7 +24,7 @@
 
 /**
  * Simple wrapper on top of a ScrollView that will acquire focus when tapped.  Ensures the
- * New Tab page receives focus when clicked.
+ * New Tab page receives focus when clicked. This is only used in the Incognito NTP.
  */
 public class NewTabPageScrollView extends ScrollView implements TouchDisableableView {
     private static final String TAG = "NewTabPageScrollView";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageToolbar.java
deleted file mode 100644
index be10f768..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageToolbar.java
+++ /dev/null
@@ -1,63 +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.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.TintedDrawable;
-
-/**
- * The toolbar at the bottom of the new tab page. Contains buttons to open the bookmarks and
- * recent tabs pages.
- */
-public class NewTabPageToolbar extends LinearLayout {
-
-    private ViewGroup mBookmarksButton, mRecentTabsButton;
-
-    /**
-     * Constructor for inflating from xml.
-     */
-    public NewTabPageToolbar(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public View getBookmarksButton() {
-        return mBookmarksButton;
-    }
-
-    public View getRecentTabsButton() {
-        return mRecentTabsButton;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mBookmarksButton = initButton(R.id.bookmarks_button, R.drawable.btn_star);
-        mRecentTabsButton = initButton(R.id.recent_tabs_button, R.drawable.btn_recents);
-        ((TextView) mBookmarksButton.getChildAt(0)).setText(R.string.ntp_bookmarks);
-        ((TextView) mBookmarksButton.getChildAt(0)).setContentDescription(getResources().getString(
-                R.string.accessibility_ntp_toolbar_btn_bookmarks));
-
-        setBackgroundColor(ApiCompatibilityUtils.getColor(getResources(), R.color.ntp_bg));
-    }
-
-    private ViewGroup initButton(int buttonId, int drawableId) {
-        ViewGroup button = (ViewGroup) findViewById(buttonId);
-        TextView textView = (TextView) button.getChildAt(0);
-
-        TintedDrawable icon = TintedDrawable.constructTintedDrawable(getResources(), drawableId);
-        ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
-                textView, icon, null, null, null);
-
-        return button;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index 5718ad0..e4597ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -51,7 +51,6 @@
 import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView;
-import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.profiles.MostVisitedSites.MostVisitedURLsObserver;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -75,19 +74,9 @@
 public class NewTabPageView extends FrameLayout
         implements MostVisitedURLsObserver, OnLayoutChangeListener {
 
-    private static final int SHADOW_COLOR = 0x11000000;
     private static final long SNAP_SCROLL_DELAY_MS = 30;
     private static final String TAG = "Ntp";
 
-    /**
-     * Indicates which UI mode we are using. Should be checked when manipulating some members, as
-     * they may be unused or {@code null} depending on the mode.
-     */
-    private boolean mUseCardsUi;
-
-    // Note: Only one of these will be valid at a time, depending on if we are using the old NTP
-    // (NewTabPageScrollView) or the new NTP with cards (NewTabPageRecyclerView).
-    private NewTabPageScrollView mScrollView;
     private NewTabPageRecyclerView mRecyclerView;
 
     private NewTabPageLayout mNewTabPageLayout;
@@ -211,45 +200,34 @@
         mActivity = tab.getActivity();
         mManager = manager;
         mUiConfig = new UiConfig(this);
-        ViewStub stub = (ViewStub) findViewById(R.id.new_tab_page_layout_stub);
 
-        mUseCardsUi = manager.getSuggestionsSource() != null;
-        if (mUseCardsUi) {
-            stub.setLayoutResource(R.layout.new_tab_page_recycler_view);
-            mRecyclerView = (NewTabPageRecyclerView) stub.inflate();
+        assert manager.getSuggestionsSource() != null;
 
-            // Don't attach now, the recyclerView itself will determine when to do it.
-            mNewTabPageLayout =
-                    (NewTabPageLayout) LayoutInflater.from(getContext())
-                            .inflate(R.layout.new_tab_page_layout, mRecyclerView, false);
-            mNewTabPageLayout.setUseCardsUiEnabled(mUseCardsUi);
-            mRecyclerView.setAboveTheFoldView(mNewTabPageLayout);
+        mRecyclerView = (NewTabPageRecyclerView) findViewById(R.id.new_tab_page_recycler_view);
+        // Don't attach now, the recyclerView itself will determine when to do it.
+        mNewTabPageLayout =
+                (NewTabPageLayout) LayoutInflater.from(getContext())
+                        .inflate(R.layout.new_tab_page_layout, mRecyclerView, false);
+        mRecyclerView.setAboveTheFoldView(mNewTabPageLayout);
 
-            // Tailor the LayoutParams for the snippets UI, as the configuration in the XML is
-            // made for the ScrollView UI.
-            ViewGroup.LayoutParams params = mNewTabPageLayout.getLayoutParams();
-            params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+        // Tailor the LayoutParams for the snippets UI, as the configuration in the XML is
+        // made for the ScrollView UI.
+        ViewGroup.LayoutParams params = mNewTabPageLayout.getLayoutParams();
+        params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
 
-            mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
-                @Override
-                public void onAnimationFinished(ViewHolder viewHolder) {
-                    super.onAnimationFinished(viewHolder);
-                    // When removing sections, because the animations are all translations, the
-                    // scroll events don't fire and we can get in the situation where the toolbar
-                    // buttons disappear.
-                    updateSearchBoxOnScroll();
-                }
-            });
-        } else {
-            stub.setLayoutResource(R.layout.new_tab_page_scroll_view);
-            mScrollView = (NewTabPageScrollView) stub.inflate();
-            mScrollView.setBackgroundColor(
-                    ApiCompatibilityUtils.getColor(getResources(), R.color.ntp_bg));
-            mScrollView.enableBottomShadow(SHADOW_COLOR);
-            mNewTabPageLayout = (NewTabPageLayout) findViewById(R.id.ntp_content);
-        }
-        mContextMenuManager = new ContextMenuManager(mActivity, mManager.getNavigationDelegate(),
-                mUseCardsUi ? mRecyclerView : mScrollView);
+        mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
+            @Override
+            public void onAnimationFinished(ViewHolder viewHolder) {
+                super.onAnimationFinished(viewHolder);
+                // When removing sections, because the animations are all translations, the
+                // scroll events don't fire and we can get in the situation where the toolbar
+                // buttons disappear.
+                updateSearchBoxOnScroll();
+            }
+        });
+
+        mContextMenuManager =
+                new ContextMenuManager(mActivity, mManager.getNavigationDelegate(), mRecyclerView);
         mActivity.getWindowAndroid().addContextMenuCloseListener(mContextMenuManager);
         manager.addDestructionObserver(new DestructionObserver() {
             @Override
@@ -270,7 +248,6 @@
 
         initializeSearchBoxTextView();
         initializeVoiceSearchButton();
-        initializeBottomToolbar();
 
         mNewTabPageLayout.addOnLayoutChangeListener(this);
         setSearchProviderHasLogo(searchProviderHasLogo);
@@ -280,57 +257,54 @@
                 this, mMostVisitedDesign.getNumberOfTiles(searchProviderHasLogo));
 
         // Set up snippets
-        if (mUseCardsUi) {
-            NewTabPageAdapter newTabPageAdapter = new NewTabPageAdapter(mManager, mNewTabPageLayout,
-                    mUiConfig, OfflinePageBridge.getForProfile(Profile.getLastUsedProfile()),
-                    mContextMenuManager);
-            mRecyclerView.setAdapter(newTabPageAdapter);
+        NewTabPageAdapter newTabPageAdapter = new NewTabPageAdapter(mManager, mNewTabPageLayout,
+                mUiConfig, OfflinePageBridge.getForProfile(Profile.getLastUsedProfile()),
+                mContextMenuManager);
+        mRecyclerView.setAdapter(newTabPageAdapter);
 
-            int scrollOffset;
-            if (CardsVariationParameters.isScrollBelowTheFoldEnabled()) {
-                scrollPosition = newTabPageAdapter.getFirstHeaderPosition();
-                scrollOffset = getResources().getDimensionPixelSize(R.dimen.ntp_search_box_height);
-            } else {
-                scrollOffset = 0;
-            }
-            mRecyclerView.getLinearLayoutManager().scrollToPositionWithOffset(
-                    scrollPosition, scrollOffset);
-
-            mRecyclerView.setUpSwipeToDismiss();
-
-            initializeSearchBoxRecyclerViewScrollHandling();
-
-            // When the NewTabPageAdapter's data changes we need to invalidate any previous
-            // screen captures of the NewTabPageView.
-            newTabPageAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
-                @Override
-                public void onChanged() {
-                    mNewTabPageRecyclerViewChanged = true;
-                }
-
-                @Override
-                public void onItemRangeChanged(int positionStart, int itemCount) {
-                    onChanged();
-                }
-
-                @Override
-                public void onItemRangeInserted(int positionStart, int itemCount) {
-                    onChanged();
-                }
-
-                @Override
-                public void onItemRangeRemoved(int positionStart, int itemCount) {
-                    onChanged();
-                }
-
-                @Override
-                public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-                    onChanged();
-                }
-            });
+        int scrollOffset;
+        if (CardsVariationParameters.isScrollBelowTheFoldEnabled()) {
+            scrollPosition = newTabPageAdapter.getFirstHeaderPosition();
+            scrollOffset = getResources().getDimensionPixelSize(R.dimen.ntp_search_box_height);
         } else {
-            initializeSearchBoxScrollHandling();
+            scrollOffset = 0;
         }
+        mRecyclerView.getLinearLayoutManager().scrollToPositionWithOffset(
+                scrollPosition, scrollOffset);
+
+        mRecyclerView.setUpSwipeToDismiss();
+
+        setupScrollHandling();
+
+        // When the NewTabPageAdapter's data changes we need to invalidate any previous
+        // screen captures of the NewTabPageView.
+        newTabPageAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
+            @Override
+            public void onChanged() {
+                mNewTabPageRecyclerViewChanged = true;
+            }
+
+            @Override
+            public void onItemRangeChanged(int positionStart, int itemCount) {
+                onChanged();
+            }
+
+            @Override
+            public void onItemRangeInserted(int positionStart, int itemCount) {
+                onChanged();
+            }
+
+            @Override
+            public void onItemRangeRemoved(int positionStart, int itemCount) {
+                onChanged();
+            }
+
+            @Override
+            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+                onChanged();
+            }
+        });
+
     }
 
     /**
@@ -380,34 +354,6 @@
         });
     }
 
-    /**
-     * Sets up event listeners for the bottom toolbar if it is enabled. Removes the bottom toolbar
-     * if it is disabled.
-     */
-    private void initializeBottomToolbar() {
-        NewTabPageToolbar toolbar = (NewTabPageToolbar) findViewById(R.id.ntp_toolbar);
-        if (SnippetsConfig.isEnabled()) {
-            ((ViewGroup) toolbar.getParent()).removeView(toolbar);
-            MarginLayoutParams params = (MarginLayoutParams) getWrapperView().getLayoutParams();
-            params.bottomMargin = 0;
-        } else {
-            toolbar.getRecentTabsButton().setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_RECENT_TABS_MANAGER);
-                    mManager.getNavigationDelegate().navigateToRecentTabs();
-                }
-            });
-            toolbar.getBookmarksButton().setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_BOOKMARKS_MANAGER);
-                    mManager.getNavigationDelegate().navigateToBookmarks();
-                }
-            });
-        }
-    }
-
     private void updateSearchBoxOnScroll() {
         if (mDisableUrlFocusChangeAnimations) return;
 
@@ -433,9 +379,9 @@
         // During startup the view may not be fully initialized, so we only calculate the current
         // percentage if some basic view properties (height of the containing view, position of the
         // search box) are sane.
-        if (getWrapperView().getHeight() == 0) return 0f;
+        if (getRecyclerView().getHeight() == 0) return 0f;
 
-        if (mUseCardsUi && !mRecyclerView.isFirstItemVisible()) {
+        if (!mRecyclerView.isFirstItemVisible()) {
             // getVerticalScroll is valid only for the RecyclerView if the first item is visible.
             // If the first item is not visible, we must have scrolled quite far and we know the
             // toolbar transition should be 100%. This might be the initial scroll position due to
@@ -450,11 +396,7 @@
         // visible "border" of the search box is.
         searchBoxTop += mSearchBoxView.getPaddingTop();
 
-        if (!mUseCardsUi) {
-            return MathUtils.clamp(getVerticalScroll() / (float) searchBoxTop, 0f, 1f);
-        }
-
-        final int scrollY = getVerticalScroll();
+        final int scrollY = mRecyclerView.computeVerticalScrollOffset();
         final float transitionLength =
                 getResources().getDimension(R.dimen.ntp_search_box_transition_length);
         // Tab strip height is zero on phones, nonzero on tablets.
@@ -467,22 +409,23 @@
     }
 
     @VisibleForTesting
-    public ViewGroup getWrapperView() {
-        return mUseCardsUi ? mRecyclerView : mScrollView;
+    public NewTabPageRecyclerView getRecyclerView() {
+        return mRecyclerView;
     }
 
     /**
-     * Sets up scrolling when snippets are enabled. It adds scroll listeners and touch listeners to
-     * the RecyclerView.
+     * Adds listeners to scrolling to take care of snap scrolling and updating the search box on
+     * scroll.
      */
-    private void initializeSearchBoxRecyclerViewScrollHandling() {
+    private void setupScrollHandling() {
         final Runnable mSnapScrollRunnable = new Runnable() {
             @Override
             public void run() {
                 assert mPendingSnapScroll;
                 mPendingSnapScroll = false;
 
-                mRecyclerView.snapScroll(mSearchBoxView, getVerticalScroll(), getHeight());
+                mRecyclerView.snapScroll(mSearchBoxView,
+                        mRecyclerView.computeVerticalScrollOffset(), getHeight());
             }
         };
 
@@ -517,51 +460,6 @@
     }
 
     /**
-     * Sets up scrolling when snippets are disabled. It adds scroll and touch listeners to the
-     * scroll view.
-     */
-    private void initializeSearchBoxScrollHandling() {
-        final Runnable mSnapScrollRunnable = new Runnable() {
-            @Override
-            public void run() {
-                if (!mPendingSnapScroll) return;
-                int scrollY = mScrollView.getScrollY();
-                int dividerTop = mMostVisitedLayout.getTop() - mNewTabPageLayout.getPaddingTop();
-                if (scrollY > 0 && scrollY < dividerTop) {
-                    mScrollView.smoothScrollTo(0, scrollY < (dividerTop / 2) ? 0 : dividerTop);
-                }
-                mPendingSnapScroll = false;
-            }
-        };
-        mScrollView.setOnScrollListener(new NewTabPageScrollView.OnScrollListener() {
-            @Override
-            public void onScrollChanged(int l, int t, int oldl, int oldt) {
-                if (mPendingSnapScroll) {
-                    mScrollView.removeCallbacks(mSnapScrollRunnable);
-                    mScrollView.postDelayed(mSnapScrollRunnable, SNAP_SCROLL_DELAY_MS);
-                }
-                updateSearchBoxOnScroll();
-            }
-        });
-        mScrollView.setOnTouchListener(new OnTouchListener() {
-            @Override
-            @SuppressLint("ClickableViewAccessibility")
-            public boolean onTouch(View v, MotionEvent event) {
-                mScrollView.removeCallbacks(mSnapScrollRunnable);
-
-                if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
-                        || event.getActionMasked() == MotionEvent.ACTION_UP) {
-                    mPendingSnapScroll = true;
-                    mScrollView.postDelayed(mSnapScrollRunnable, SNAP_SCROLL_DELAY_MS);
-                } else {
-                    mPendingSnapScroll = false;
-                }
-                return false;
-            }
-        });
-    }
-
-    /**
      * Decrements the count of pending load tasks and notifies the manager when the page load
      * is complete.
      */
@@ -678,18 +576,13 @@
     private void onUrlFocusAnimationChanged() {
         if (mDisableUrlFocusChangeAnimations) return;
 
+        // Translate so that the search box is at the top, but only upwards.
         float percent = mSearchProviderHasLogo ? mUrlFocusChangePercent : 0;
-
-        int basePosition = getVerticalScroll() + mNewTabPageLayout.getPaddingTop();
-        int target;
-        if (mUseCardsUi) {
-            // Cards UI: translate so that the search box is at the top, but only upwards.
-            target = Math.max(basePosition,
+        int basePosition = mRecyclerView.computeVerticalScrollOffset()
+                + mNewTabPageLayout.getPaddingTop();
+        int target = Math.max(basePosition,
                     mSearchBoxView.getBottom() - mSearchBoxView.getPaddingBottom());
-        } else {
-            // Otherwise: translate so that Most Visited is right below the omnibox.
-            target = mMostVisitedLayout.getTop();
-        }
+
         mNewTabPageLayout.setTranslationY(percent * (basePosition - target));
     }
 
@@ -807,7 +700,7 @@
 
         return mNewTabPageRecyclerViewChanged || mSnapshotMostVisitedChanged
                 || getWidth() != mSnapshotWidth || getHeight() != mSnapshotHeight
-                || getVerticalScroll() != mSnapshotScrollY;
+                || mRecyclerView.computeVerticalScrollOffset() != mSnapshotScrollY;
     }
 
     /**
@@ -819,7 +712,7 @@
         ViewUtils.captureBitmap(this, canvas);
         mSnapshotWidth = getWidth();
         mSnapshotHeight = getHeight();
-        mSnapshotScrollY = getVerticalScroll();
+        mSnapshotScrollY = mRecyclerView.computeVerticalScrollOffset();
         mSnapshotMostVisitedChanged = false;
         mNewTabPageRecyclerViewChanged = false;
     }
@@ -840,12 +733,11 @@
         onUrlFocusAnimationChanged();
         updateSearchBoxOnScroll();
 
-        if (mUseCardsUi) {
-            mRecyclerView.updatePeekingCardAndHeader();
-            // The positioning of elements may have been changed (since the elements expand to fill
-            // the available vertical space), so adjust the scroll.
-            mRecyclerView.snapScroll(mSearchBoxView, getVerticalScroll(), getHeight());
-        }
+        mRecyclerView.updatePeekingCardAndHeader();
+        // The positioning of elements may have been changed (since the elements expand to fill
+        // the available vertical space), so adjust the scroll.
+        mRecyclerView.snapScroll(mSearchBoxView,
+                mRecyclerView.computeVerticalScrollOffset(), getHeight());
     }
 
     // MostVisitedURLsObserver implementation
@@ -1107,7 +999,7 @@
         }
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        if (mUseCardsUi) mRecyclerView.updatePeekingCardAndHeader();
+        mRecyclerView.updatePeekingCardAndHeader();
     }
 
     @Override
@@ -1125,20 +1017,11 @@
         mContextMenuManager.closeContextMenu();
     }
 
-    private int getVerticalScroll() {
-        if (mUseCardsUi) {
-            return mRecyclerView.computeVerticalScrollOffset();
-        } else {
-            return mScrollView.getScrollY();
-        }
-    }
-
     /**
      * @return The adapter position the user has scrolled to.
      */
     public int getScrollPosition() {
-        if (mUseCardsUi) return mRecyclerView.getScrollPosition();
-        return RecyclerView.NO_POSITION;
+        return mRecyclerView.getScrollPosition();
     }
 
     /** @return the context menu manager. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
index 9ba6879..bc9c9b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
@@ -220,18 +220,18 @@
             hasCardBelow = isCard(belowViewType) && belowViewType != ItemViewType.PROMO;
         }
 
+        @DrawableRes
+        int selectedBackground = selectBackground(hasCardAbove, hasCardBelow);
+        if (mBackground == selectedBackground) return;
+
+        mBackground = selectedBackground;
+        ViewUtils.setNinePatchBackgroundResource(itemView, selectedBackground);
+
         // By default the apparent distance between two cards is the sum of the bottom and top
         // height of their shadows. We want |mCardGap| instead, so we set the bottom margin to
         // the difference.
         getParams().bottomMargin =
                 hasCardBelow ? (mCardGap - (mCardShadow.top + mCardShadow.bottom)) : 0;
-
-        @DrawableRes
-        int selectedBackground = selectBackground(hasCardAbove, hasCardBelow);
-        if (mBackground != selectedBackground) {
-            mBackground = selectedBackground;
-            ViewUtils.setNinePatchBackgroundResource(itemView, selectedBackground);
-        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index 16a1db1..311be1c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -156,6 +156,9 @@
                     assert holder instanceof SnippetArticleViewHolder;
                     ((SnippetArticleViewHolder) holder).refreshOfflineBadgeVisibility();
                     break;
+                case PartialUpdateId.CARD_BACKGROUND:
+                    holder.updateLayoutParams();
+                    break;
                 default:
                     assert false; // Unknown payload
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
index c9a636a..b34a0e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -267,6 +267,27 @@
     }
 
     @Override
+    protected void notifyItemRangeInserted(int index, int count) {
+        super.notifyItemRangeInserted(index, count);
+        notifyNeighboursModified(index - 1, index + count);
+    }
+
+    @Override
+    protected void notifyItemRangeRemoved(int index, int count) {
+        super.notifyItemRangeRemoved(index, count);
+        notifyNeighboursModified(index - 1, index);
+    }
+
+    /** Sends a notification to the items at the provided indices to refresh their background. */
+    private void notifyNeighboursModified(int aboveNeighbour, int belowNeighbour) {
+        assert aboveNeighbour < belowNeighbour;
+        if (aboveNeighbour >= 0) notifyItemChanged(aboveNeighbour, PartialUpdateId.CARD_BACKGROUND);
+        if (belowNeighbour < getItemCount()) {
+            notifyItemChanged(belowNeighbour, PartialUpdateId.CARD_BACKGROUND);
+        }
+    }
+
+    @Override
     public void onBindViewHolder(NewTabPageViewHolder holder, int position) {
         super.onBindViewHolder(holder, position);
         childSeen(position);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java
index ff7aef1..00a3ffd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java
@@ -47,7 +47,7 @@
      * Display the data at {@code position} under this subtree.
      * @param holder The view holder that should be updated.
      * @param position The position of the item under this subtree.
-     * @see android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder(ViewHolder, int)
+     * @see android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder
      */
     void onBindViewHolder(NewTabPageViewHolder holder, int position);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsConfig.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsConfig.java
index 9b7557a6..6ea61fe0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsConfig.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsConfig.java
@@ -12,10 +12,6 @@
 public final class SnippetsConfig {
     private SnippetsConfig() {}
 
-    public static boolean isEnabled() {
-        return ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_SNIPPETS);
-    }
-
     public static boolean isSaveToOfflineEnabled() {
         return ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_SNIPPETS_SAVE_TO_OFFLINE);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/PartialUpdateId.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/PartialUpdateId.java
index cde816d..def5a2a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/PartialUpdateId.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/PartialUpdateId.java
@@ -14,9 +14,16 @@
  * holders
  * @see org.chromium.chrome.browser.ntp.cards.InnerNode#notifyItemChanged(int, Object)
  */
-@IntDef({PartialUpdateId.OFFLINE_BADGE})
+@IntDef({PartialUpdateId.OFFLINE_BADGE, PartialUpdateId.CARD_BACKGROUND})
 @Retention(RetentionPolicy.SOURCE)
 public @interface PartialUpdateId {
-    /** Marks a request to update a suggestion's offline badge */
+    /** Marks a request to update a suggestion's offline badge. */
     int OFFLINE_BADGE = 1;
+
+    /**
+     * Marks a request to update a card's background. Will no-op if received by a non card
+     * view holder.
+     * @see org.chromium.chrome.browser.ntp.cards.CardViewHolder#updateLayoutParams()
+     */
+    int CARD_BACKGROUND = 2;
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
index 0bcee98e4..2f80c7f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -726,18 +726,6 @@
     // NewTabPage.OnSearchBoxScrollListener
     @Override
     public void onNtpScrollChanged(float scrollPercentage) {
-        // TODO(peconn): Clear up the animation transition calculations so that the parts that
-        // depend on the absolute scroll value (such as the Toolbar location) are separate from the
-        // parts that depend on the fakebox transition percentage (such as the omnibox width and
-        // opacity).
-        // At the moment, we disable the check below because these two concepts are not
-        // separate and we want to still update the parts that depend on scroll value when the
-        // transition percentage is not changed.
-        if (scrollPercentage == mNtpSearchBoxScrollPercent
-                && !getToolbarDataProvider().getNewTabPageForCurrentTab().isCardsUiEnabled()) {
-            return;
-        }
-
         mNtpSearchBoxScrollPercent = scrollPercentage;
         updateUrlExpansionPercent();
         updateUrlExpansionAnimation();
@@ -920,15 +908,8 @@
         // Linearly interpolate between the bounds of the search box on the NTP and the omnibox
         // background bounds. |shrinkage| is the scaling factor for the offset -- if it's 1, we are
         // shrinking the omnibox down to the size of the search box.
-        float shrinkage;
-        if (ntp.isCardsUiEnabled()) {
-            shrinkage = 1f
-                    - NTP_SEARCH_BOX_EXPANSION_INTERPOLATOR.getInterpolation(mUrlExpansionPercent);
-        } else {
-            // During the transition from middle of the NTP to the top, keep the omnibox drawing
-            // at the same size of the search box for first 40% of the scroll transition.
-            shrinkage = Math.min(1f, (1f - mUrlExpansionPercent) * 1.66667f);
-        }
+        float shrinkage =
+                1f - NTP_SEARCH_BOX_EXPANSION_INTERPOLATOR.getInterpolation(mUrlExpansionPercent);
 
         int leftBoundDifference = mNtpSearchBoxBounds.left - mLocationBarBackgroundBounds.left;
         int rightBoundDifference = mNtpSearchBoxBounds.right - mLocationBarBackgroundBounds.right;
@@ -953,10 +934,6 @@
 
         // The search box on the NTP is visible if our omnibox is invisible, and vice-versa.
         ntp.setSearchBoxAlpha(1f - relativeAlpha);
-
-        if (!ntp.isCardsUiEnabled()) {
-            ntp.setSearchProviderLogoAlpha(Math.max(1f - mUrlExpansionPercent * 2.5f, 0f));
-        }
     }
 
     /**
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 2394fd3..e0a921bf 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2364,12 +2364,6 @@
       <message name="IDS_ACCESSIBILITY_TABSTRIP_INCOGNITO_IDENTIFIER_SELECTED" desc="Content description for the tab that is selected in incognito mode">
         Selected Incognito Tab
       </message>
-      <message name="IDS_ACCESSIBILITY_NTP_TOOLBAR_BTN_BOOKMARKS" desc="Content description for the bookmarks tab of the new tab page.">
-        Bookmarks
-      </message>
-      <message name="IDS_ACCESSIBILITY_NTP_TOOLBAR_BTN_RECENT_TABS" desc="Content description for the recent tabs section of the new tab page.">
-        Recent tabs
-      </message>
       <message name="IDS_ACCESSIBILITY_OMNIBOX_BTN_REFINE" desc="Content description for the omnibox refine button.">
         Refine
       </message>
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index c628f52..07a5358 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -570,7 +570,6 @@
   "java/src/org/chromium/chrome/browser/ntp/NewTabPage.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java",
-  "java/src/org/chromium/chrome/browser/ntp/NewTabPageToolbar.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java",
   "java/src/org/chromium/chrome/browser/ntp/RecentTabsExpandableListView.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index c6a62968..0689c38 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -122,8 +122,7 @@
         viewRenderer.renderAndCompare(mNtp.getView().getRootView(), "new_tab_page");
 
         // Scroll to search bar
-        final NewTabPageRecyclerView recyclerView =
-                (NewTabPageRecyclerView) mNtp.getNewTabPageView().getWrapperView();
+        final NewTabPageRecyclerView recyclerView = mNtp.getNewTabPageView().getRecyclerView();
 
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
@@ -151,8 +150,7 @@
         assertFalse(mNtp.shouldCaptureThumbnail());
 
         // Check that we invalidate the thumbnail when the Recycler View is updated.
-        NewTabPageRecyclerView recyclerView =
-                (NewTabPageRecyclerView) mNtp.getNewTabPageView().getWrapperView();
+        NewTabPageRecyclerView recyclerView = mNtp.getNewTabPageView().getRecyclerView();
 
         recyclerView.getAdapter().notifyDataSetChanged();
         assertThumbnailInvalidAndRecapture();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
index 5cd10e94..0c3c0d0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
@@ -228,7 +228,7 @@
     }
 
     private NewTabPageRecyclerView getRecyclerView() {
-        return (NewTabPageRecyclerView) getNtpView().getWrapperView();
+        return getNtpView().getRecyclerView();
     }
 
     private NewTabPageAdapter getAdapter() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/EnableFeatures.java b/chrome/android/junit/src/org/chromium/chrome/browser/EnableFeatures.java
index 35126942..a1344931 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/EnableFeatures.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/EnableFeatures.java
@@ -26,7 +26,7 @@
  *    &#64;Rule
  *    public EnableFeatures.Processor processor = new EnableFeatures.Processor();
  *
- *    &#64;EnableFeatures(ChromeFeatureList.NTP_SNIPPETS)
+ *    &#64;EnableFeatures(ChromeFeatureList.NTP_SNIPPETS_OFFLINE_BADGE)
  *    public void testFoo() { ... }
  * }
  * </pre>
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsTestUtils.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsTestUtils.java
index b1fbb183..7ca06c3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsTestUtils.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsTestUtils.java
@@ -18,6 +18,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 /** Utilities to make testing content suggestions code easier. */
 public final class ContentSuggestionsTestUtils {
@@ -191,4 +192,41 @@
                 return mock(NewTabPageViewHolder.class);
         }
     }
+
+    /** Helper method to print the current state of a node. */
+    public static String stringify(TreeNode root) {
+        return explainFailedExpectation(root, -1, ItemViewType.ALL_DISMISSED);
+    }
+
+    /**
+     * Helper method to print the current state of a node, highlighting something that went wrong.
+     * @param root node to print information about.
+     * @param errorIndex index where an unexpected item was found.
+     * @param expectedType item type that was expected at {@code errorIndex}.
+     */
+    public static String explainFailedExpectation(
+            TreeNode root, int errorIndex, @ItemViewType int expectedType) {
+        StringBuilder stringBuilder = new StringBuilder();
+
+        stringBuilder.append("explainFailedExpectation -- START -- \n");
+        for (int i = 0; i < root.getItemCount(); ++i) {
+            if (errorIndex == i) {
+                addLine(stringBuilder, "%d - %s <= expected: %s", i,
+                        viewTypeToString(root.getItemViewType(i)), viewTypeToString(expectedType));
+            } else {
+                addLine(stringBuilder, "%d - %s", i, viewTypeToString(root.getItemViewType(i)));
+            }
+        }
+        if (errorIndex >= root.getItemCount()) {
+            addLine(stringBuilder, "<end of list>");
+            addLine(stringBuilder, "%d - <NONE> <= expected: %s", errorIndex,
+                    viewTypeToString(expectedType));
+        }
+        addLine(stringBuilder, "explainFailedExpectation -- END --");
+        return stringBuilder.toString();
+    }
+
+    private static void addLine(StringBuilder stringBuilder, String template, Object... args) {
+        stringBuilder.append(String.format(Locale.US, template + "\n", args));
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index 5241c2c..52125c7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -18,12 +18,12 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import static org.chromium.base.test.util.Matchers.greaterThanOrEqualTo;
 import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.bindViewHolders;
 import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.createDummySuggestions;
+import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.explainFailedExpectation;
 import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.registerCategory;
 import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.viewTypeToString;
 
@@ -75,7 +75,6 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
 
 /**
  * Unit tests for {@link NewTabPageAdapter}.
@@ -696,7 +695,6 @@
         verify(itemDismissedCallback, times(2)).onResult(anyString());
         verify(dataObserver, times(2)).onItemRangeRemoved(3, 1);
         verify(dataObserver).onItemRangeChanged(4, 1, null);
-        verifyNoMoreInteractions(dataObserver);
 
         // Dismiss the last suggestion in the section. We should now show the status card.
         reset(dataObserver);
@@ -708,7 +706,6 @@
         verify(dataObserver).onItemRangeChanged(4, 1, null); // Spacer refresh
         verify(dataObserver).onItemRangeInserted(3, 1); // Action item added
         verify(dataObserver).onItemRangeChanged(5, 1, null); // Spacer refresh
-        verifyNoMoreInteractions(dataObserver);
 
         // Adapter content:
         // Idx | Item
@@ -740,7 +737,6 @@
         // 9   | Footer
         // 10  | Spacer
 
-        verifyNoMoreInteractions(dataObserver);
         reset(dataObserver);
         suggestionsSource.setSuggestionsForCategory(
                 KnownCategories.ARTICLES, createDummySuggestions(0, KnownCategories.ARTICLES));
@@ -752,7 +748,6 @@
         verify(dataObserver).onItemRangeChanged(4, 1, null); // Spacer refresh
         verify(dataObserver).onItemRangeInserted(3, 1); // Action item added
         verify(dataObserver).onItemRangeChanged(5, 1, null); // Spacer refresh
-        verifyNoMoreInteractions(dataObserver);
     }
 
     @Test
@@ -1028,30 +1023,4 @@
     private int getCategory(TreeNode item) {
         return ((SuggestionsSection) item).getCategory();
     }
-
-    private static String explainFailedExpectation(
-            TreeNode root, int errorIndex, @ItemViewType int expectedType) {
-        StringBuilder stringBuilder = new StringBuilder();
-
-        stringBuilder.append("explainFailedExpectation -- START -- \n");
-        for (int i = 0; i < root.getItemCount(); ++i) {
-            if (errorIndex == i) {
-                addLine(stringBuilder, "%d - %s <= expected: %s", i,
-                        viewTypeToString(root.getItemViewType(i)), viewTypeToString(expectedType));
-            } else {
-                addLine(stringBuilder, "%d - %s", i, viewTypeToString(root.getItemViewType(i)));
-            }
-        }
-        if (errorIndex >= root.getItemCount()) {
-            addLine(stringBuilder, "<end of list>");
-            addLine(stringBuilder, "%d - <NONE> <= expected: %s", errorIndex,
-                    viewTypeToString(expectedType));
-        }
-        addLine(stringBuilder, "explainFailedExpectation -- END --");
-        return stringBuilder.toString();
-    }
-
-    private static void addLine(StringBuilder stringBuilder, String template, Object... args) {
-        stringBuilder.append(String.format(Locale.US, template + "\n", args));
-    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
index 7b13c4a..f07229d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -42,6 +42,7 @@
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
+import org.chromium.chrome.browser.suggestions.PartialUpdateId;
 import org.chromium.chrome.browser.suggestions.SuggestionsMetricsReporter;
 import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate;
 import org.chromium.chrome.browser.suggestions.SuggestionsRanker;
@@ -185,7 +186,6 @@
 
         section.setStatus(CategoryStatus.AVAILABLE);
         verify(mParent).onItemRangeRemoved(section, 2, 1);
-        verifyNoMoreInteractions(mParent);
     }
 
     @Test
@@ -674,6 +674,59 @@
         verifySnippets(section, snippets);
     }
 
+    @Test
+    @Feature({"Ntp"})
+    public void testCardIsNotifiedWhenBecomingFirst() {
+        List<SnippetArticle> suggestions = createDummySuggestions(5, /* categoryId = */ 42);
+        SuggestionsSection section = createSectionWithReloadAction(false);
+        section.setSuggestions(suggestions, CategoryStatus.AVAILABLE, /* replaceExisting = */ true);
+        reset(mParent);
+
+        // Remove the first card. The second one should get the update.
+        section.removeSuggestionById(suggestions.get(0).mIdWithinCategory);
+        verify(mParent).onItemRangeChanged(section, 1, 1, PartialUpdateId.CARD_BACKGROUND);
+    }
+
+    @Test
+    @Feature({"Ntp"})
+    public void testCardIsNotifiedWhenBecomingLast() {
+        List<SnippetArticle> suggestions = createDummySuggestions(5, /* categoryId = */ 42);
+        SuggestionsSection section = createSectionWithReloadAction(false);
+        section.setSuggestions(suggestions, CategoryStatus.AVAILABLE, /* replaceExisting = */ true);
+        reset(mParent);
+
+        // Remove the last card. The penultimate one should get the update.
+        section.removeSuggestionById(suggestions.get(4).mIdWithinCategory);
+        verify(mParent).onItemRangeChanged(section, 4, 1, PartialUpdateId.CARD_BACKGROUND);
+    }
+
+    @Test
+    @Feature({"Ntp"})
+    public void testCardIsNotifiedWhenBecomingSoleCard() {
+        List<SnippetArticle> suggestions = createDummySuggestions(2, /* categoryId = */ 42);
+        SuggestionsSection section = createSectionWithReloadAction(false);
+        section.setSuggestions(suggestions, CategoryStatus.AVAILABLE, /* replaceExisting = */ true);
+        reset(mParent);
+
+        // Remove the last card. The penultimate one should get the update.
+        section.removeSuggestionById(suggestions.get(1).mIdWithinCategory);
+        verify(mParent).onItemRangeChanged(section, 1, 1, PartialUpdateId.CARD_BACKGROUND);
+    }
+
+    @Test
+    @Feature({"Ntp"})
+    public void testCardIsNotifiedWhenNotTheLastAnymore() {
+        List<SnippetArticle> suggestions = createDummySuggestions(5, /* categoryId = */ 42);
+        SuggestionsSection section = createSectionWithReloadAction(false);
+
+        section.setSuggestions(suggestions, CategoryStatus.AVAILABLE, /* replaceExisting = */ true);
+        reset(mParent);
+
+        section.setSuggestions(createDummySuggestions(2, /* categoryId = */ 42, "new"),
+                CategoryStatus.AVAILABLE, /* replaceExisting = */ false);
+        verify(mParent).onItemRangeChanged(section, 5, 1, PartialUpdateId.CARD_BACKGROUND);
+    }
+
     private SuggestionsSection createSectionWithSuggestions(List<SnippetArticle> snippets) {
         SuggestionsSection section = createSectionWithReloadAction(true);
         section.setStatus(CategoryStatus.AVAILABLE);
diff --git a/chrome/app/framework.order b/chrome/app/framework.order
index 8dc2eec..ecf1d08 100644
--- a/chrome/app/framework.order
+++ b/chrome/app/framework.order
@@ -15,9 +15,6 @@
 # release modes.  It is not an error to list symbols in this file that will not
 # be present in each output variant.
 
-__ZnwmPv
-__ZdlPvS_
-
 # Provided by build/sanitizers/sanitizer_options.cc in ASan builds.
 ___asan_default_options
 
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 41c1e21..990baffd 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -15131,11 +15131,11 @@
       <message name="IDS_FLAGS_CONTENT_SUGGESTIONS_CATEGORY_RANKER_DESCRIPTION" desc="Description for the flag to choose a category ranker for content suggestions, e.g. on the New Tab page." translateable="false">
         Set category ranker to order sections of content suggestions (e.g. on the NTP).
       </message>
-      <message name="IDS_FLAGS_ENABLE_NTP_SNIPPETS_NAME" desc="Name for the flag to enable snippets on the New Tab page." translateable="false">
-        Show content snippets on the New Tab page
+      <message name="IDS_FLAGS_OVERRIDE_SNIPPETS_SOURCE_NAME" desc="Name for flag to override the NTP snippets source" translateable="false">
+        Override New Tab Page suggestions snippets source
       </message>
-      <message name="IDS_FLAGS_ENABLE_NTP_SNIPPETS_DESCRIPTION" desc="Description for the flag to enable snippets on the New Tab page." translateable="false">
-        If enabled, the New Tab page will show a list of content snippets
+      <message name="IDS_FLAGS_OVERRIDE_SNIPPETS_SOURCE_DESCRIPTION" desc="Description for the flag to override the NTP snippets source." translateable="false">
+        If set to something other than enabled, default or disabled, this will override the source used to retrieve content suggestions on the New Tab Page.
       </message>
       <message name="IDS_FLAGS_ENABLE_NTP_SNIPPETS_VISIBILITY_NAME" desc="Name for the flag to enable increased visibilty for snippets on the New Tab Page." translateable="false">
         Make New Tab Page Snippets more visible.
diff --git a/chrome/app/theme/default_100_percent/common/profile_avatar_awesome.png b/chrome/app/theme/default_100_percent/common/profile_avatar_awesome.png
index 554d167..113e8302 100644
--- a/chrome/app/theme/default_100_percent/common/profile_avatar_awesome.png
+++ b/chrome/app/theme/default_100_percent/common/profile_avatar_awesome.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/profile_avatar_awesome.png b/chrome/app/theme/default_200_percent/common/profile_avatar_awesome.png
index 4d1f7f5..319aac9 100644
--- a/chrome/app/theme/default_200_percent/common/profile_avatar_awesome.png
+++ b/chrome/app/theme/default_200_percent/common/profile_avatar_awesome.png
Binary files differ
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index feb95051..e4b6fa6 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1867,10 +1867,10 @@
          ntp_snippets::kCategoryRanker,
          kContentSuggestionsCategoryRankerFeatureVariations,
          ntp_snippets::kStudyName)},
-    {"enable-ntp-snippets", IDS_FLAGS_ENABLE_NTP_SNIPPETS_NAME,
-     IDS_FLAGS_ENABLE_NTP_SNIPPETS_DESCRIPTION, kOsAndroid,
+    {"override-ntp-suggestions-source", IDS_FLAGS_OVERRIDE_SNIPPETS_SOURCE_NAME,
+     IDS_FLAGS_OVERRIDE_SNIPPETS_SOURCE_DESCRIPTION, kOsAndroid,
      FEATURE_WITH_VARIATIONS_VALUE_TYPE(
-         ntp_snippets::kContentSuggestionsFeature,
+         ntp_snippets::kContentSuggestionsSource,
          kNTPSnippetsFeatureVariations,
          ntp_snippets::kStudyName)},
     {"enable-ntp-snippets-increased-visibility",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index d2d8bc2..d237ad8 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -60,7 +60,6 @@
     &kTabReparenting,
     &kWebPaymentsModifiers,
     &kWebPaymentsSingleAppUiSkip,
-    &ntp_snippets::kContentSuggestionsFeature,
     &ntp_snippets::kIncreasedVisibility,
     &ntp_snippets::kForeignSessionsSuggestionsFeature,
     &ntp_snippets::kOfflineBadgeFeature,
diff --git a/chrome/browser/android/cookies/cookies_fetcher.cc b/chrome/browser/android/cookies/cookies_fetcher.cc
index 6443c14..f94ed4c 100644
--- a/chrome/browser/android/cookies/cookies_fetcher.cc
+++ b/chrome/browser/android/cookies/cookies_fetcher.cc
@@ -125,7 +125,7 @@
   store->SetCookieWithDetailsAsync(
       url, cookie.Name(), cookie.Value(), effective_domain, cookie.Path(),
       base::Time(), cookie.ExpiryDate(), cookie.LastAccessDate(),
-      cookie.IsSecure(), cookie.IsHttpOnly(), cookie.SameSite(), false,
+      cookie.IsSecure(), cookie.IsHttpOnly(), cookie.SameSite(),
       cookie.Priority(), cb);
 }
 
diff --git a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc
index a13bc7e8..8a095d2d 100644
--- a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc
+++ b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h"
 
+#include "base/callback_helpers.h"
 #include "chrome/browser/android/vr_shell/vr_shell.h"
 
 namespace vr_shell {
@@ -64,9 +65,9 @@
 
 void NonPresentingGvrDelegate::StopVSyncLoop() {
   vsync_task_.Cancel();
-  if (!callback_.is_null())
-    callback_.Run(nullptr, base::TimeDelta());
-  callback_.Reset();
+  if (!callback_.is_null()) {
+    base::ResetAndReturn(&callback_).Run(nullptr, base::TimeDelta(), -1);
+  }
   gvr_api_->PauseTracking();
   // If the loop is stopped, it's not considered to be paused.
   vsync_paused_ = false;
@@ -102,8 +103,7 @@
 
   base::TimeDelta time = intervals * vsync_interval_;
   if (!callback_.is_null()) {
-    callback_.Run(GetPose(), time);
-    callback_.Reset();
+    SendVSync(time, base::ResetAndReturn(&callback_));
   } else {
     pending_vsync_ = true;
     pending_time_ = time;
@@ -121,7 +121,7 @@
     return;
   }
   pending_vsync_ = false;
-  callback.Run(GetPose(), pending_time_);
+  SendVSync(pending_time_, callback);
 }
 
 void NonPresentingGvrDelegate::UpdateVSyncInterval(long timebase_nanos,
@@ -132,17 +132,14 @@
   StartVSyncLoop();
 }
 
-device::mojom::VRPosePtr NonPresentingGvrDelegate::GetPose() {
+void NonPresentingGvrDelegate::SendVSync(base::TimeDelta time,
+                                         const GetVSyncCallback& callback) {
   gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
   target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
 
-  gvr::Mat4f head_mat =
-      gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time);
-  head_mat = gvr_api_->ApplyNeckModel(head_mat, 1.0f);
-
-  uint32_t pose_index = pose_index_++;
-
-  return VrShell::VRPosePtrFromGvrPose(head_mat, pose_index);
+  gvr::Mat4f head_mat = gvr_api_->ApplyNeckModel(
+      gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time), 1.0f);
+  callback.Run(VrShell::VRPosePtrFromGvrPose(head_mat), time, -1);
 }
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h
index 7e40607..bf6d4d10 100644
--- a/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h
+++ b/chrome/browser/android/vr_shell/non_presenting_gvr_delegate.h
@@ -26,7 +26,8 @@
   // GvrDelegate implementation
   void SetWebVRSecureOrigin(bool secure_origin) override {}
   void SubmitWebVRFrame() override {}
-  void UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
+  void UpdateWebVRTextureBounds(int16_t frame_index,
+                                const gvr::Rectf& left_bounds,
                                 const gvr::Rectf& right_bounds) override {}
   void SetWebVRRenderSurfaceSize(int width, int height) override {}
   gvr::Sizei GetWebVRCompositorSurfaceSize() override;
@@ -44,9 +45,9 @@
   void StopVSyncLoop();
   void StartVSyncLoop();
   void OnVSync();
-  device::mojom::VRPosePtr GetPose();
+  void SendVSync(base::TimeDelta time, const GetVSyncCallback& callback);
 
-  // VRVSyncProvider
+  // VRVSyncProvider implementation
   void GetVSync(const GetVSyncCallback& callback) override;
 
   std::unique_ptr<gvr::GvrApi> gvr_api_;
@@ -60,7 +61,6 @@
   base::TimeDelta pending_time_;
   bool pending_vsync_ = false;
   GetVSyncCallback callback_;
-  uint32_t pose_index_ = 1;
   mojo::Binding<device::mojom::VRVSyncProvider> binding_;
   base::WeakPtrFactory<NonPresentingGvrDelegate> weak_ptr_factory_;
 
diff --git a/chrome/browser/android/vr_shell/vr_controller.cc b/chrome/browser/android/vr_shell/vr_controller.cc
index 5df13277c..b278c3e 100644
--- a/chrome/browser/android/vr_shell/vr_controller.cc
+++ b/chrome/browser/android/vr_shell/vr_controller.cc
@@ -25,7 +25,7 @@
 constexpr float kSlopVertical = 0.165f;
 
 // Horizontal distance from the border to the center of slop.
-constexpr float kSlopHorizontal = 0.125f;
+constexpr float kSlopHorizontal = 0.15f;
 
 // Minimum distance needed in at least one direction to call two vectors
 // not equal. Also, minimum time distance needed to call two timestamps
@@ -36,6 +36,8 @@
 constexpr float kRC = static_cast<float>(1.0 / (2.0 * M_PI * kCutoffHz));
 constexpr float kNanoSecondsPerSecond = 1.0e9f;
 
+constexpr int kMaxNumOfExtrapolations = 2;
+
 class Vector {
  public:
   static inline void ClampTouchpadPosition(gvr::Vec2f* position) {
@@ -146,27 +148,27 @@
 
 void VrController::UpdateTouchInfo() {
   CHECK(touch_info_ != nullptr) << "touch_info_ not initialized properly.";
-  gvr::Vec2f position;
-  position.x = TouchPosX();
-  position.y = TouchPosY();
-  touch_info_->touch_up = TouchUpHappened();
-  touch_info_->touch_down = TouchDownHappened();
-  touch_info_->is_touching = IsTouching();
-  touch_info_->touch_point.position = position;
-  Vector::ClampTouchpadPosition(&touch_info_->touch_point.position);
-  touch_info_->touch_point.timestamp =
-      gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos;
-  if (controller_state_->GetLastTouchTimestamp() == last_touch_timestamp_) {
+  if (IsTouching() && state_ == SCROLLING &&
+      (controller_state_->GetLastTouchTimestamp() == last_touch_timestamp_ ||
+       (Vector::Equal(cur_touch_point_->position,
+                      prev_touch_point_->position) &&
+        extrapolated_touch_ < kMaxNumOfExtrapolations))) {
+    extrapolated_touch_++;
+    touch_position_changed_ = true;
     // Fill the touch_info
     float duration =
         (gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos -
          last_timestamp_nanos_) /
         kNanoSecondsPerSecond;
-
-    position.x += overall_velocity_.x * duration;
-    position.y += overall_velocity_.y * duration;
-    touch_info_->touch_point.position.x = position.x;
-    touch_info_->touch_point.position.y = position.y;
+    touch_info_->touch_point.position.x =
+        cur_touch_point_->position.x + overall_velocity_.x * duration;
+    touch_info_->touch_point.position.y =
+        cur_touch_point_->position.y + overall_velocity_.y * duration;
+  } else {
+    if (extrapolated_touch_ == kMaxNumOfExtrapolations) {
+      Vector::SetZero(&overall_velocity_);
+    }
+    extrapolated_touch_ = 0;
   }
   last_touch_timestamp_ = controller_state_->GetLastTouchTimestamp();
   last_timestamp_nanos_ =
@@ -193,7 +195,12 @@
     gesture_list.push_back(std::move(gesture));
     return gesture_list;
   }
+
+  touch_position_changed_ = UpdateCurrentTouchpoint();
   UpdateTouchInfo();
+  if (touch_position_changed_)
+    UpdateOverallVelocity();
+
   UpdateGestureFromTouchInfo(gesture.get());
 
   if (gesture->type() == WebInputEvent::Undefined &&
@@ -206,17 +213,18 @@
   gesture_list.push_back(std::move(gesture));
 
   if (gesture_list.back()->type() == WebInputEvent::GestureScrollEnd) {
-    if (!ButtonDownHappened(gvr::kControllerButtonClick)) {
+    if (!ButtonDownHappened(gvr::kControllerButtonClick) &&
+        (last_velocity_.x != 0.0 || last_velocity_.y != 0.0)) {
       std::unique_ptr<WebGestureEvent> fling(new WebGestureEvent(
           WebInputEvent::GestureFlingStart, WebInputEvent::NoModifiers,
           gesture_list.back()->timeStampSeconds()));
       fling->sourceDevice = blink::WebGestureDeviceTouchpad;
       if (IsHorizontalGesture()) {
         fling->data.flingStart.velocityX =
-            overall_velocity_.x * kDisplacementScaleFactor;
+            last_velocity_.x * kDisplacementScaleFactor;
       } else {
         fling->data.flingStart.velocityY =
-            overall_velocity_.y * kDisplacementScaleFactor;
+            last_velocity_.y * kDisplacementScaleFactor;
       }
       gesture_list.push_back(std::move(fling));
     }
@@ -272,7 +280,7 @@
 
   // Touch position is changed, the touch point moves outside of slop,
   // and the Controller's button is not down.
-  if (UpdateCurrentTouchpoint() && touch_info_->is_touching &&
+  if (touch_position_changed_ && touch_info_->is_touching &&
       !InSlop(touch_info_->touch_point.position) &&
       !ButtonDownHappened(gvr::kControllerButtonClick)) {
     state_ = SCROLLING;
@@ -286,14 +294,12 @@
 }
 
 void VrController::HandleScrollingState(WebGestureEvent* gesture) {
-  // Update current touch point.
-  bool touch_position_changed = UpdateCurrentTouchpoint();
   if (touch_info_->touch_up || !(touch_info_->is_touching) ||
       ButtonDownHappened(gvr::kControllerButtonClick)) {
     // Gesture ends.
     gesture->setType(WebInputEvent::GestureScrollEnd);
     UpdateGesture(gesture);
-  } else if (touch_position_changed) {
+  } else if (touch_position_changed_) {
     // User continues scrolling and there is a change in touch position.
     gesture->setType(WebInputEvent::GestureScrollUpdate);
     UpdateGesture(gesture);
@@ -304,11 +310,12 @@
       gesture->data.scrollUpdate.deltaY =
           displacement_.y * kDisplacementScaleFactor;
     }
+    last_velocity_ = overall_velocity_;
   }
 }
 
 bool VrController::IsHorizontalGesture() {
-  return std::abs(overall_velocity_.x) > std::abs(overall_velocity_.y);
+  return std::abs(last_velocity_.x) > std::abs(last_velocity_.y);
 }
 
 bool VrController::InSlop(const gvr::Vec2f touch_position) {
@@ -328,23 +335,34 @@
   init_touch_point_.reset(new TouchPoint);
   touch_info_.reset(new TouchInfo);
   Vector::SetZero(&overall_velocity_);
+  Vector::SetZero(&last_velocity_);
 }
 
 void VrController::UpdateGesture(WebGestureEvent* gesture) {
   if (!gesture)
     LOG(ERROR) << "The gesture pointer is not initiated properly.";
-  displacement_ =
-      Vector::Subtract(cur_touch_point_->position, prev_touch_point_->position);
+  displacement_ = Vector::Subtract(touch_info_->touch_point.position,
+                                   prev_touch_point_->position);
 }
 
 bool VrController::UpdateCurrentTouchpoint() {
-  if (touch_info_->is_touching || touch_info_->touch_up) {
+  touch_info_->touch_up = TouchUpHappened();
+  touch_info_->touch_down = TouchDownHappened();
+  touch_info_->is_touching = IsTouching();
+  touch_info_->touch_point.position.x = TouchPosX();
+  touch_info_->touch_point.position.y = TouchPosY();
+  Vector::ClampTouchpadPosition(&touch_info_->touch_point.position);
+  touch_info_->touch_point.timestamp =
+      gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos;
+
+  if (IsTouching() || TouchUpHappened()) {
     // Update the touch point when the touch position has changed.
     if (!Vector::Equal(cur_touch_point_->position,
                        touch_info_->touch_point.position)) {
       prev_touch_point_.swap(cur_touch_point_);
-      *cur_touch_point_ = touch_info_->touch_point;
-      UpdateOverallVelocity();
+      cur_touch_point_.reset(new TouchPoint);
+      cur_touch_point_->position = touch_info_->touch_point.position;
+      cur_touch_point_->timestamp = touch_info_->touch_point.timestamp;
       return true;
     }
   }
@@ -353,15 +371,15 @@
 
 void VrController::UpdateOverallVelocity() {
   float duration =
-      (cur_touch_point_->timestamp - prev_touch_point_->timestamp) /
+      (touch_info_->touch_point.timestamp - prev_touch_point_->timestamp) /
       kNanoSecondsPerSecond;
 
   // If the timestamp does not change, do not update velocity.
   if (duration < kDelta)
     return;
 
-  gvr::Vec2f displacement =
-      Vector::Subtract(cur_touch_point_->position, prev_touch_point_->position);
+  gvr::Vec2f displacement = Vector::Subtract(touch_info_->touch_point.position,
+                                             prev_touch_point_->position);
 
   gvr::Vec2f velocity = Vector::ScalarMult(displacement, 1 / duration);
 
diff --git a/chrome/browser/android/vr_shell/vr_controller.h b/chrome/browser/android/vr_shell/vr_controller.h
index 7580500..3d4de03 100644
--- a/chrome/browser/android/vr_shell/vr_controller.h
+++ b/chrome/browser/android/vr_shell/vr_controller.h
@@ -129,7 +129,9 @@
   float last_qx_;
   bool pinch_started_;
   bool zoom_in_progress_ = false;
+  bool touch_position_changed_ = false;
 
+  // Current touch info after the extrapolation.
   std::unique_ptr<TouchInfo> touch_info_;
 
   // A pointer storing the touch point from previous frame.
@@ -144,12 +146,18 @@
   // Overall velocity
   gvr::Vec2f overall_velocity_;
 
+  // Last velocity that is used for fling and direction detection
+  gvr::Vec2f last_velocity_;
+
   // Displacement of the touch point from the previews to the current touch
   gvr::Vec2f displacement_;
 
   int64_t last_touch_timestamp_ = 0;
   int64_t last_timestamp_nanos_ = 0;
 
+  // Number of consecutively extrapolated touch points
+  int extrapolated_touch_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(VrController);
 };
 
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
index 515ca6c3..8a0c52a 100644
--- a/chrome/browser/android/vr_shell/vr_shell.cc
+++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -254,11 +254,12 @@
 
 void VrShell::SubmitWebVRFrame() {}
 
-void VrShell::UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
+void VrShell::UpdateWebVRTextureBounds(int16_t frame_index,
+                                       const gvr::Rectf& left_bounds,
                                        const gvr::Rectf& right_bounds) {
   PostToGlThreadWhenReady(base::Bind(&VrShellGl::UpdateWebVRTextureBounds,
-                                     gl_thread_->GetVrShellGl(), left_bounds,
-                                     right_bounds));
+                                     gl_thread_->GetVrShellGl(), frame_index,
+                                     left_bounds, right_bounds));
 }
 
 // TODO(mthiesse): Do not expose GVR API outside of GL thread.
@@ -448,13 +449,10 @@
   }
 }
 
-device::mojom::VRPosePtr VrShell::VRPosePtrFromGvrPose(gvr::Mat4f head_mat,
-                                                       uint32_t pose_index) {
+device::mojom::VRPosePtr VrShell::VRPosePtrFromGvrPose(gvr::Mat4f head_mat) {
   device::mojom::VRPosePtr pose = device::mojom::VRPose::New();
 
   pose->timestamp = base::Time::Now().ToJsTime();
-
-  pose->poseIndex = pose_index;
   pose->orientation.emplace(4);
 
   gfx::Transform inv_transform(
diff --git a/chrome/browser/android/vr_shell/vr_shell.h b/chrome/browser/android/vr_shell/vr_shell.h
index 66b0f53a..d979adf 100644
--- a/chrome/browser/android/vr_shell/vr_shell.h
+++ b/chrome/browser/android/vr_shell/vr_shell.h
@@ -134,8 +134,7 @@
   void ProcessUIGesture(std::unique_ptr<blink::WebInputEvent> event);
   void ProcessContentGesture(std::unique_ptr<blink::WebInputEvent> event);
 
-  static device::mojom::VRPosePtr VRPosePtrFromGvrPose(gvr::Mat4f head_mat,
-                                                       uint32_t pose_index);
+  static device::mojom::VRPosePtr VRPosePtrFromGvrPose(gvr::Mat4f head_mat);
 
  private:
   ~VrShell() override;
@@ -150,7 +149,8 @@
   // device::GvrDelegate implementation
   void SetWebVRSecureOrigin(bool secure_origin) override;
   void SubmitWebVRFrame() override;
-  void UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
+  void UpdateWebVRTextureBounds(int16_t frame_index,
+                                const gvr::Rectf& left_bounds,
                                 const gvr::Rectf& right_bounds) override;
   gvr::GvrApi* gvr_api() override;
   void SetWebVRRenderSurfaceSize(int width, int height) override;
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 501a811..78bc7f8 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/android/jni_android.h"
+#include "base/callback_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -86,6 +87,10 @@
 static constexpr int kViewportListPrimaryOffset = 0;
 static constexpr int kViewportListHeadlockedOffset = 2;
 
+// Buffer size large enough to handle the current backlog of poses which is
+// 2-3 frames.
+static constexpr unsigned kPoseRingBufferSize = 8;
+
 // Magic numbers used to mark valid pose index values encoded in frame
 // data. Must match the magic numbers used in blink's VRDisplay.cpp.
 static constexpr std::array<uint8_t, 2> kWebVrPosePixelMagicNumbers{{42, 142}};
@@ -171,8 +176,9 @@
 
 VrShellGl::~VrShellGl() {
   vsync_task_.Cancel();
-  if (!callback_.is_null())
-    callback_.Run(nullptr, base::TimeDelta());
+  if (!callback_.is_null()) {
+    base::ResetAndReturn(&callback_).Run(nullptr, base::TimeDelta(), -1);
+  }
   if (binding_.is_bound()) {
     main_thread_task_runner_->PostTask(FROM_HERE, base::Bind(
         &VrShellDelegate::OnVRVsyncProviderRequest, delegate_provider_,
@@ -264,10 +270,12 @@
   received_frame_ = true;
 }
 
-bool VrShellGl::GetPixelEncodedPoseIndexByte(int* pose_index) {
-  TRACE_EVENT0("gpu", "VrShellGl::GetPixelEncodedPoseIndex");
+bool VrShellGl::GetPixelEncodedFrameIndex(uint16_t* frame_index) {
+  TRACE_EVENT0("gpu", "VrShellGl::GetPixelEncodedFrameIndex");
   if (!received_frame_) {
-    *pose_index = last_pose_;
+    if (last_frame_index_ == (uint16_t) -1)
+      return false;
+    *frame_index = last_frame_index_;
     return true;
   }
   received_frame_ = false;
@@ -290,8 +298,8 @@
   if (pixels[1] == kWebVrPosePixelMagicNumbers[0] &&
       pixels[2] == kWebVrPosePixelMagicNumbers[1]) {
     // Pose is good.
-    *pose_index = pixels[0];
-    last_pose_ = pixels[0];
+    *frame_index = pixels[0];
+    last_frame_index_ = pixels[0];
     return true;
   }
   VLOG(1) << "WebVR: reject decoded pose index " << (int)pixels[0]
@@ -606,7 +614,7 @@
     DrawWebVr();
   }
 
-  int pose_index;
+  uint16_t frame_index;
   gvr::Mat4f head_pose;
 
   // When using async reprojection, we need to know which pose was used in
@@ -617,8 +625,38 @@
   // pixels is an expensive operation. TODO(klausw,crbug.com/655722): stop
   // doing this once we have working no-compositor rendering for WebVR.
   if (web_vr_mode_ && gvr_api_->GetAsyncReprojectionEnabled() &&
-      GetPixelEncodedPoseIndexByte(&pose_index)) {
-    head_pose = webvr_head_pose_[pose_index % kPoseRingBufferSize];
+      GetPixelEncodedFrameIndex(&frame_index)) {
+    static_assert(!((kPoseRingBufferSize - 1) & kPoseRingBufferSize),
+                  "kPoseRingBufferSize must be a power of 2");
+    head_pose = webvr_head_pose_[frame_index % kPoseRingBufferSize];
+    // Process all pending_bounds_ changes targeted for before this frame, being
+    // careful of wrapping frame indices.
+    static constexpr unsigned max =
+        std::numeric_limits<decltype(frame_index_)>::max();
+    static_assert(max > kPoseRingBufferSize * 2,
+                  "To detect wrapping, kPoseRingBufferSize must be smaller "
+                  "than half of frame_index_ range.");
+    while (!pending_bounds_.empty()) {
+      uint16_t index = pending_bounds_.front().first;
+      // If index is less than the frame_index it's possible we've wrapped, so
+      // we extend the range and 'un-wrap' to account for this.
+      if (index < frame_index) index += max;
+      // If the pending bounds change is for an upcoming frame within our buffer
+      // size, wait to apply it. Otherwise, apply it immediately. This
+      // guarantees that even if we miss many frames, the queue can't fill up
+      // with stale bounds.
+      if (index > frame_index && index <= frame_index + kPoseRingBufferSize)
+        break;
+
+      const BoundsPair& bounds = pending_bounds_.front().second;
+      webvr_left_viewport_->SetSourceUv(bounds.first);
+      webvr_right_viewport_->SetSourceUv(bounds.second);
+      pending_bounds_.pop();
+    }
+    buffer_viewport_list_->SetBufferViewport(GVR_LEFT_EYE,
+                                             *webvr_left_viewport_);
+    buffer_viewport_list_->SetBufferViewport(GVR_RIGHT_EYE,
+                                             *webvr_right_viewport_);
   } else {
     gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
     target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
@@ -865,11 +903,6 @@
 
   glViewport(0, 0, render_size_primary_.width, render_size_primary_.height);
   vr_shell_renderer_->GetWebVrRenderer()->Draw(webvr_texture_id_);
-
-  buffer_viewport_list_->SetBufferViewport(GVR_LEFT_EYE,
-                                           *webvr_left_viewport_);
-  buffer_viewport_list_->SetBufferViewport(GVR_RIGHT_EYE,
-                                           *webvr_right_viewport_);
 }
 
 void VrShellGl::DrawBackground(const gvr::Mat4f& render_matrix) {
@@ -901,10 +934,16 @@
   web_vr_mode_ = enabled;
 }
 
-void VrShellGl::UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
+void VrShellGl::UpdateWebVRTextureBounds(int16_t frame_index,
+                                         const gvr::Rectf& left_bounds,
                                          const gvr::Rectf& right_bounds) {
-  webvr_left_viewport_->SetSourceUv(left_bounds);
-  webvr_right_viewport_->SetSourceUv(right_bounds);
+  if (frame_index < 0) {
+    webvr_left_viewport_->SetSourceUv(left_bounds);
+    webvr_right_viewport_->SetSourceUv(right_bounds);
+  } else {
+    pending_bounds_.emplace(
+        std::make_pair(frame_index, std::make_pair(left_bounds, right_bounds)));
+  }
 }
 
 gvr::GvrApi* VrShellGl::gvr_api() {
@@ -955,8 +994,7 @@
 
   base::TimeDelta time = intervals * vsync_interval_;
   if (!callback_.is_null()) {
-    callback_.Run(GetPose(), time);
-    callback_.Reset();
+    SendVSync(time, base::ResetAndReturn(&callback_));
   } else {
     pending_vsync_ = true;
     pending_time_ = time;
@@ -980,7 +1018,7 @@
     return;
   }
   pending_vsync_ = false;
-  callback.Run(GetPose(), pending_time_);
+  SendVSync(pending_time_, callback);
 }
 
 void VrShellGl::UpdateVSyncInterval(long timebase_nanos,
@@ -1001,8 +1039,11 @@
   scene_->HandleCommands(std::move(commands), TimeInMicroseconds());
 }
 
-device::mojom::VRPosePtr VrShellGl::GetPose() {
-  TRACE_EVENT0("input", "VrShellGl::GetPose");
+void VrShellGl::SendVSync(base::TimeDelta time,
+                          const GetVSyncCallback& callback) {
+  TRACE_EVENT0("input", "VrShellGl::SendVSync");
+
+  uint8_t frame_index = frame_index_++;
 
   gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
   target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
@@ -1011,10 +1052,9 @@
       gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time);
   head_mat = gvr_api_->ApplyNeckModel(head_mat, 1.0f);
 
-  uint32_t pose_index = pose_index_++;
-  webvr_head_pose_[pose_index % kPoseRingBufferSize] = head_mat;
+  webvr_head_pose_[frame_index % kPoseRingBufferSize] = head_mat;
 
-  return VrShell::VRPosePtrFromGvrPose(head_mat, pose_index);
+  callback.Run(VrShell::VRPosePtrFromGvrPose(head_mat), time, frame_index);
 }
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 25151e99..cc9a2e72 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ANDROID_VR_SHELL_VR_SHELL_GL_H_
 
 #include <memory>
+#include <queue>
 
 #include "base/cancelable_callback.h"
 #include "base/macros.h"
@@ -76,7 +77,8 @@
   void UIPhysicalBoundsChanged(int width, int height);
   base::WeakPtr<VrShellGl> GetWeakPtr();
 
-  void UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
+  void UpdateWebVRTextureBounds(int16_t frame_index,
+                                const gvr::Rectf& left_bounds,
                                 const gvr::Rectf& right_bounds);
   gvr::GvrApi* gvr_api();
   void SetGvrPoseForWebVr(const gvr::Mat4f& pose, uint32_t pose_num);
@@ -111,7 +113,7 @@
 
   void OnUIFrameAvailable();
   void OnContentFrameAvailable();
-  bool GetPixelEncodedPoseIndexByte(int* pose_index);
+  bool GetPixelEncodedFrameIndex(uint16_t* frame_index);
 
   void OnVSync();
 
@@ -120,7 +122,7 @@
 
   void ForceExitVr();
 
-  device::mojom::VRPosePtr GetPose();
+  void SendVSync(base::TimeDelta time, const GetVSyncCallback& callback);
 
   // samplerExternalOES texture data for UI content image.
   int ui_texture_id_ = 0;
@@ -145,6 +147,8 @@
   std::unique_ptr<gvr::BufferViewport> webvr_left_viewport_;
   std::unique_ptr<gvr::BufferViewport> webvr_right_viewport_;
   std::unique_ptr<gvr::SwapChain> swap_chain_;
+  using BoundsPair = std::pair<gvr::Rectf, gvr::Rectf>;
+  std::queue<std::pair<uint8_t, BoundsPair>> pending_bounds_;
 
   // Current sizes for the render buffers.
   gvr::Sizei render_size_primary_;
@@ -165,10 +169,6 @@
   gvr::Sizei content_tex_physical_size_ = {0, 0};
   gvr::Sizei ui_tex_physical_size_ = {0, 0};
 
-  // The pose ring buffer size must be a power of two to avoid glitches when
-  // the pose index wraps around. It should be large enough to handle the
-  // current backlog of poses which is 2-3 frames.
-  static constexpr int kPoseRingBufferSize = 8;
   std::vector<gvr::Mat4f> webvr_head_pose_;
   int webvr_texture_id_ = 0;
   bool web_vr_mode_;
@@ -192,8 +192,9 @@
   base::WeakPtr<VrShellDelegate> delegate_provider_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
 
-  uint32_t pose_index_ = 1;
-  int last_pose_ = 0;
+  uint8_t frame_index_ = 0;
+  // larger than frame_index_ so it can be initialized out-of-band.
+  uint16_t last_frame_index_ = -1;
 
   base::WeakPtrFactory<VrShellGl> weak_ptr_factory_;
 
diff --git a/chrome/browser/browsing_data/browsing_data_remover_impl_unittest.cc b/chrome/browser/browsing_data/browsing_data_remover_impl_unittest.cc
index 5059421..caedff9c 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_impl_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_impl_unittest.cc
@@ -129,7 +129,7 @@
 net::CanonicalCookie CreateCookieWithHost(const GURL& source) {
   std::unique_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create(
       source, "A", "1", std::string(), "/", base::Time::Now(),
-      base::Time::Now(), false, false, net::CookieSameSite::DEFAULT_MODE, false,
+      base::Time::Now(), false, false, net::CookieSameSite::DEFAULT_MODE,
       net::COOKIE_PRIORITY_MEDIUM));
   EXPECT_TRUE(cookie);
   return *cookie;
diff --git a/chrome/browser/chrome_find_request_manager_browsertest.cc b/chrome/browser/chrome_find_request_manager_browsertest.cc
new file mode 100644
index 0000000..73bfe940
--- /dev/null
+++ b/chrome/browser/chrome_find_request_manager_browsertest.cc
@@ -0,0 +1,101 @@
+// 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/strings/utf_string_conversions.h"
+#include "chrome/browser/pdf/pdf_extension_test_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/find_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
+#include "third_party/WebKit/public/web/WebFindOptions.h"
+
+namespace content {
+
+class ChromeFindRequestManagerTest : public InProcessBrowserTest {
+ public:
+  ChromeFindRequestManagerTest()
+      : normal_delegate_(nullptr),
+        last_request_id_(0) {}
+  ~ChromeFindRequestManagerTest() override {}
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    // Swap the WebContents's delegate for our test delegate.
+    normal_delegate_ = contents()->GetDelegate();
+    contents()->SetDelegate(&test_delegate_);
+  }
+
+  void TearDownOnMainThread() override {
+    // Swap the WebContents's delegate back to its usual delegate.
+    contents()->SetDelegate(normal_delegate_);
+  }
+
+ protected:
+  // Navigates to |url| and waits for it to finish loading.
+  void LoadAndWait(const std::string& url) {
+    TestNavigationObserver navigation_observer(contents());
+    ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+        browser(), embedded_test_server()->GetURL("a.com", url), 1);
+    ASSERT_TRUE(navigation_observer.last_navigation_succeeded());
+  }
+
+  void Find(const std::string& search_text,
+            const blink::WebFindOptions& options) {
+    delegate()->UpdateLastRequest(++last_request_id_);
+    contents()->Find(last_request_id_,
+                     base::UTF8ToUTF16(search_text),
+                     options);
+  }
+
+  WebContents* contents() const {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  FindTestWebContentsDelegate* delegate() const {
+    return static_cast<FindTestWebContentsDelegate*>(contents()->GetDelegate());
+  }
+
+  int last_request_id() const {
+    return last_request_id_;
+  }
+
+ private:
+  FindTestWebContentsDelegate test_delegate_;
+  WebContentsDelegate* normal_delegate_;
+
+  // The ID of the last find request requested.
+  int last_request_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeFindRequestManagerTest);
+};
+
+
+// Tests searching in a full-page PDF.
+IN_PROC_BROWSER_TEST_F(ChromeFindRequestManagerTest, FindInPDF) {
+  LoadAndWait("/find_in_pdf_page.pdf");
+  pdf_extension_test_util::EnsurePDFHasLoaded(contents());
+
+  blink::WebFindOptions options;
+  Find("result", options);
+  options.findNext = true;
+  Find("result", options);
+  Find("result", options);
+  delegate()->WaitForFinalReply();
+
+  FindResults results = delegate()->GetFindResults();
+  EXPECT_EQ(last_request_id(), results.request_id);
+  EXPECT_EQ(5, results.number_of_matches);
+  EXPECT_EQ(3, results.active_match_ordinal);
+}
+
+}  // namespace content
diff --git a/chrome/browser/chromeos/extensions/external_cache.cc b/chrome/browser/chromeos/extensions/external_cache.cc
index ec39c237..dabb3b6 100644
--- a/chrome/browser/chromeos/extensions/external_cache.cc
+++ b/chrome/browser/chromeos/extensions/external_cache.cc
@@ -229,7 +229,7 @@
     return;
 
   // If request_context_ is missing we can't download anything.
-  if (!downloader_ && request_context_.get()) {
+  if (request_context_.get()) {
     downloader_ = ChromeExtensionDownloaderFactory::CreateForRequestContext(
         request_context_.get(), this);
   }
diff --git a/chrome/browser/chromeos/login/profile_auth_data.cc b/chrome/browser/chromeos/login/profile_auth_data.cc
index 400d6f8..08968f5 100644
--- a/chrome/browser/chromeos/login/profile_auth_data.cc
+++ b/chrome/browser/chromeos/login/profile_auth_data.cc
@@ -62,11 +62,7 @@
         url, cookie.Name(), cookie.Value(), effective_domain, cookie.Path(),
         cookie.CreationDate(), cookie.ExpiryDate(), cookie.LastAccessDate(),
         cookie.IsSecure(), cookie.IsHttpOnly(), cookie.SameSite(),
-        // enforce_strict_secure should have been applied on the original
-        // cookie, prior to import. This allows URL to be treated as an HTTPS
-        // URL, whether the cookie was set by an HTTP or HTTPS domain (Something
-        // that can't be determined by just looking at the CanonicalCookie).
-        false, cookie.Priority(), net::CookieStore::SetCookiesCallback());
+        cookie.Priority(), net::CookieStore::SetCookiesCallback());
   }
 }
 
diff --git a/chrome/browser/chromeos/login/profile_auth_data_unittest.cc b/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
index b54b83b..d2a1470 100644
--- a/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
+++ b/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
@@ -39,14 +39,14 @@
 
 namespace {
 
-const char kProxyAuthURL[] = "http://example.com/";
+const char kProxyAuthURL[] = "https://example.com/";
 const char kProxyAuthRealm[] = "realm";
 const char kProxyAuthChallenge[] = "challenge";
 const char kProxyAuthPassword1[] = "password 1";
 const char kProxyAuthPassword2[] = "password 2";
 
-const char kGAIACookieURL[] = "http://google.com/";
-const char kSAMLIdPCookieURL[] = "http://example.com/";
+const char kGAIACookieURL[] = "https://google.com/";
+const char kSAMLIdPCookieURL[] = "https://example.com/";
 const char kCookieName[] = "cookie";
 const char kCookieValue1[] = "value 1";
 const char kCookieValue2[] = "value 2";
@@ -228,17 +228,17 @@
       GURL(kSAMLIdPCookieURL), kCookieName, cookie_value,
       kSAMLIdPCookieDomainWithWildcard, std::string(), base::Time(),
       base::Time(), base::Time(), true, false,
-      net::CookieSameSite::DEFAULT_MODE, false, net::COOKIE_PRIORITY_DEFAULT,
+      net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT,
       net::CookieStore::SetCookiesCallback());
   cookies->SetCookieWithDetailsAsync(
       GURL(kSAMLIdPCookieURL), kCookieName, cookie_value, std::string(),
       std::string(), base::Time(), base::Time(), base::Time(), true, false,
-      net::CookieSameSite::DEFAULT_MODE, false, net::COOKIE_PRIORITY_DEFAULT,
+      net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT,
       net::CookieStore::SetCookiesCallback());
   cookies->SetCookieWithDetailsAsync(
       GURL(kGAIACookieURL), kCookieName, cookie_value, std::string(),
       std::string(), base::Time(), base::Time(), base::Time(), true, false,
-      net::CookieSameSite::DEFAULT_MODE, false, net::COOKIE_PRIORITY_DEFAULT,
+      net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT,
       net::CookieStore::SetCookiesCallback());
 
   GetChannelIDs(browser_context)
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_storage.cc b/chrome/browser/chromeos/login/quick_unlock/pin_storage.cc
index f245ad8..86058df 100644
--- a/chrome/browser/chromeos/login/quick_unlock/pin_storage.cc
+++ b/chrome/browser/chromeos/login/quick_unlock/pin_storage.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/login/auth/key.h"
-#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "crypto/random.h"
 
@@ -56,8 +56,7 @@
 }  // namespace
 
 // static
-void PinStorage::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
+void PinStorage::RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterStringPref(prefs::kQuickUnlockPinSalt, "");
   registry->RegisterStringPref(prefs::kQuickUnlockPinSecret, "");
 }
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_storage.h b/chrome/browser/chromeos/login/quick_unlock/pin_storage.h
index 83ecc90..f3c88c4 100644
--- a/chrome/browser/chromeos/login/quick_unlock/pin_storage.h
+++ b/chrome/browser/chromeos/login/quick_unlock/pin_storage.h
@@ -11,12 +11,9 @@
 #include "base/time/time.h"
 #include "components/keyed_service/core/keyed_service.h"
 
+class PrefRegistrySimple;
 class PrefService;
 
-namespace user_prefs {
-class PrefRegistrySyncable;
-}  // namespace user_prefs
-
 class PinStorageTestApi;
 
 namespace chromeos {
@@ -27,7 +24,7 @@
   static const int kMaximumUnlockAttempts = 3;
 
   // Registers profile prefs.
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
   explicit PinStorage(PrefService* pref_service);
   ~PinStorage() override;
diff --git a/chrome/browser/extensions/api/cookies/cookies_api.cc b/chrome/browser/extensions/api/cookies/cookies_api.cc
index 5c472f2..38e7d96 100644
--- a/chrome/browser/extensions/api/cookies/cookies_api.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_api.cc
@@ -395,11 +395,6 @@
     break;
   }
 
-  bool are_experimental_cookie_features_enabled =
-      store_browser_context_->GetURLRequestContext()
-          ->network_delegate()
-          ->AreExperimentalCookieFeaturesEnabled();
-
   // clang-format off
   cookie_store->SetCookieWithDetailsAsync(
       url_, parsed_args_->details.name.get() ? *parsed_args_->details.name
@@ -418,7 +413,6 @@
       parsed_args_->details.http_only.get() ? *parsed_args_->details.http_only
                                             : false,
       same_site,
-      are_experimental_cookie_features_enabled,
       net::COOKIE_PRIORITY_DEFAULT,
       base::Bind(&CookiesSetFunction::PullCookie, this));
   // clang-format on
diff --git a/chrome/browser/extensions/api/cookies/cookies_unittest.cc b/chrome/browser/extensions/api/cookies/cookies_unittest.cc
index 724b2fe..8eab2626 100644
--- a/chrome/browser/extensions/api/cookies/cookies_unittest.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_unittest.cc
@@ -85,11 +85,10 @@
 
 TEST_F(ExtensionCookiesTest, ExtensionTypeCreation) {
   std::unique_ptr<net::CanonicalCookie> canonical_cookie1(
-      net::CanonicalCookie::Create(GURL("http://www.example.com"), "ABC", "DEF",
-                                   std::string(), "/", base::Time(),
-                                   base::Time(), false, false,
-                                   net::CookieSameSite::DEFAULT_MODE, false,
-                                   net::COOKIE_PRIORITY_DEFAULT));
+      net::CanonicalCookie::Create(
+          GURL("http://www.example.com"), "ABC", "DEF", std::string(), "/",
+          base::Time(), base::Time(), false, false,
+          net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT));
   ASSERT_NE(nullptr, canonical_cookie1.get());
   Cookie cookie1 =
       cookies_helpers::CreateCookie(*canonical_cookie1, "some cookie store");
@@ -106,11 +105,10 @@
   EXPECT_EQ("some cookie store", cookie1.store_id);
 
   std::unique_ptr<net::CanonicalCookie> canonical_cookie2(
-      net::CanonicalCookie::Create(GURL("http://example.com"), "ABC", "DEF",
-                                   ".example.com", "/", base::Time(),
-                                   base::Time::FromDoubleT(10000), false, false,
-                                   net::CookieSameSite::STRICT_MODE, false,
-                                   net::COOKIE_PRIORITY_DEFAULT));
+      net::CanonicalCookie::Create(
+          GURL("http://example.com"), "ABC", "DEF", ".example.com", "/",
+          base::Time(), base::Time::FromDoubleT(10000), false, false,
+          net::CookieSameSite::STRICT_MODE, net::COOKIE_PRIORITY_DEFAULT));
   ASSERT_NE(nullptr, canonical_cookie2.get());
   Cookie cookie2 =
       cookies_helpers::CreateCookie(*canonical_cookie2, "some cookie store");
@@ -133,15 +131,15 @@
   std::unique_ptr<net::CanonicalCookie> cookie1(net::CanonicalCookie::Create(
       GURL("http://example.com"), "ABC", "DEF", "example.com", "/",
       base::Time(), base::Time(), false, false,
-      net::CookieSameSite::DEFAULT_MODE, false, net::COOKIE_PRIORITY_DEFAULT));
+      net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT));
   ASSERT_NE(nullptr, cookie1.get());
   EXPECT_EQ("http://example.com/",
             cookies_helpers::GetURLFromCanonicalCookie(*cookie1).spec());
 
   std::unique_ptr<net::CanonicalCookie> cookie2(net::CanonicalCookie::Create(
-      GURL("http://helloworld.com"), "ABC", "DEF", ".helloworld.com", "/",
+      GURL("https://helloworld.com"), "ABC", "DEF", ".helloworld.com", "/",
       base::Time(), base::Time(), true, false,
-      net::CookieSameSite::DEFAULT_MODE, false, net::COOKIE_PRIORITY_DEFAULT));
+      net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT));
   ASSERT_NE(nullptr, cookie2.get());
   EXPECT_EQ("https://helloworld.com/",
             cookies_helpers::GetURLFromCanonicalCookie(*cookie2).spec());
@@ -179,8 +177,7 @@
     std::unique_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create(
         GURL(tests[i].url), std::string(), std::string(), tests[i].domain,
         std::string(), base::Time(), base::Time(), false, false,
-        net::CookieSameSite::DEFAULT_MODE, false,
-        net::COOKIE_PRIORITY_DEFAULT));
+        net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT));
     ASSERT_NE(nullptr, cookie.get());
     EXPECT_EQ(tests[i].matches, filter.MatchesCookie(*cookie));
   }
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index 644fbd5..878fe38e 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -16,7 +16,6 @@
 #include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/user_metrics.h"
@@ -542,14 +541,6 @@
   return experimental_web_platform_features_enabled_;
 }
 
-bool ChromeNetworkDelegate::OnAreStrictSecureCookiesEnabled() const {
-  const std::string enforce_strict_secure_group =
-      base::FieldTrialList::FindFullName("StrictSecureCookies");
-  return experimental_web_platform_features_enabled_ ||
-         base::StartsWith(enforce_strict_secure_group, "Enabled",
-                          base::CompareCase::INSENSITIVE_ASCII);
-}
-
 bool ChromeNetworkDelegate::OnCancelURLRequestWithPolicyViolatingReferrerHeader(
     const net::URLRequest& request,
     const GURL& target_url,
diff --git a/chrome/browser/net/chrome_network_delegate.h b/chrome/browser/net/chrome_network_delegate.h
index e3636c07..fc0d59a 100644
--- a/chrome/browser/net/chrome_network_delegate.h
+++ b/chrome/browser/net/chrome_network_delegate.h
@@ -178,7 +178,6 @@
       const GURL& url,
       const GURL& first_party_for_cookies) const override;
   bool OnAreExperimentalCookieFeaturesEnabled() const override;
-  bool OnAreStrictSecureCookiesEnabled() const override;
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const net::URLRequest& request,
       const GURL& target_url,
diff --git a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
index 127a76c..b3befc0 100644
--- a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
+++ b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
@@ -97,13 +97,6 @@
 
 namespace {
 
-// Clear the tasks that can be scheduled by running services.
-void ClearScheduledTasks() {
-#if defined(OS_ANDROID)
-  NTPSnippetsLauncher::Get()->Unschedule();
-#endif  // OS_ANDROID
-}
-
 #if defined(OS_ANDROID)
 
 bool IsRecentTabProviderEnabled() {
@@ -263,10 +256,6 @@
   DCHECK(!profile->IsOffTheRecord());
 
   // Create the ContentSuggestionsService.
-  State state =
-      base::FeatureList::IsEnabled(ntp_snippets::kContentSuggestionsFeature)
-          ? State::ENABLED
-          : State::DISABLED;
   SigninManagerBase* signin_manager =
       SigninManagerFactory::GetForProfile(profile);
   HistoryService* history_service = HistoryServiceFactory::GetForProfile(
@@ -276,14 +265,8 @@
       ntp_snippets::BuildSelectedCategoryRanker(
           pref_service, base::MakeUnique<base::DefaultClock>());
   auto* service =
-      new ContentSuggestionsService(state, signin_manager, history_service,
-                                    pref_service, std::move(category_ranker));
-  if (state == State::DISABLED) {
-    // Since we won't initialise the services, they won't get a chance to
-    // unschedule their tasks. We do it explicitly here instead.
-    ClearScheduledTasks();
-    return service;
-  }
+      new ContentSuggestionsService(State::ENABLED, signin_manager,
+          history_service, pref_service, std::move(category_ranker));
 
 #if defined(OS_ANDROID)
   OfflinePageModel* offline_page_model =
diff --git a/chrome/browser/profiles/profile_avatar_icon_util.cc b/chrome/browser/profiles/profile_avatar_icon_util.cc
index 146bd75..2d5c1ab 100644
--- a/chrome/browser/profiles/profile_avatar_icon_util.cc
+++ b/chrome/browser/profiles/profile_avatar_icon_util.cc
@@ -414,7 +414,7 @@
        "avatar_alien.png",
        IDS_DEFAULT_AVATAR_LABEL_13},
       {IDR_PROFILE_AVATAR_14,
-       "avatar_smiley.png",
+       "avatar_awesome.png",
        IDS_DEFAULT_AVATAR_LABEL_14},
       {IDR_PROFILE_AVATAR_15,
        "avatar_flower.png",
diff --git a/chrome/browser/resources/snippets_internals.html b/chrome/browser/resources/snippets_internals.html
index 7d93f26..118b8b3e 100644
--- a/chrome/browser/resources/snippets_internals.html
+++ b/chrome/browser/resources/snippets_internals.html
@@ -21,9 +21,6 @@
     <h2>Properties</h2>
     <table class="section-details">
       <tr>
-        <td class="name">Snippets enabled
-        <td id="flag-snippets" class="value">
-      <tr>
         <td class="name">Article Suggestions enabled
         <td id="flag-article-suggestions" class="value">
       <tr>
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 9e6ae1f..b16ec09 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1801,6 +1801,7 @@
         "views/payments/payment_method_view_controller.h",
         "views/payments/payment_request_dialog_view.cc",
         "views/payments/payment_request_dialog_view.h",
+        "views/payments/payment_request_dialog_view_ids.h",
         "views/payments/payment_request_sheet_controller.h",
         "views/payments/payment_request_views_util.cc",
         "views/payments/payment_request_views_util.h",
diff --git a/chrome/browser/ui/views/payments/order_summary_view_controller.cc b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
index 20fed67..b0f0cc7 100644
--- a/chrome/browser/ui/views/payments/order_summary_view_controller.cc
+++ b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/payments/currency_formatter.h"
@@ -34,10 +35,12 @@
 // |label| is the text in the left-aligned label and |amount| is the text of the
 // right-aliged label in the row. The |amount| text is bold if |bold_amount| is
 // true, which is only the case for the last row containing the total of the
-// order.
+// order. |amount_label_id| is specified to recall the view later, e.g. in
+// tests.
 std::unique_ptr<views::View> CreateLineItemView(const base::string16& label,
                                                 const base::string16& amount,
-                                                bool bold_amount) {
+                                                bool bold_amount,
+                                                DialogViewID amount_label_id) {
   std::unique_ptr<views::View> row = base::MakeUnique<views::View>();
 
   row->SetBorder(views::CreateSolidSidedBorder(0, 0, 1, 0, SK_ColorLTGRAY));
@@ -63,6 +66,7 @@
 
   std::unique_ptr<views::StyledLabel> amount_label =
       base::MakeUnique<views::StyledLabel>(amount, nullptr);
+  amount_label->set_id(static_cast<int>(amount_label_id));
   amount_label->SetDefaultStyle(style_info);
   amount_label->SizeToFit(0);
   layout->AddView(amount_label.release());
@@ -94,11 +98,21 @@
       request()->details()->total->amount->currency_system,
       g_browser_process->GetApplicationLocale());
 
-  for (const auto& item: request()->details()->display_items) {
+  // Set the ID for the first few line items labels, for testing.
+  const std::vector<DialogViewID> line_items{
+      DialogViewID::ORDER_SUMMARY_LINE_ITEM_1,
+      DialogViewID::ORDER_SUMMARY_LINE_ITEM_2,
+      DialogViewID::ORDER_SUMMARY_LINE_ITEM_3};
+  for (size_t i = 0; i < request()->details()->display_items.size(); i++) {
+    DialogViewID view_id =
+        i < line_items.size() ? line_items[i] : DialogViewID::VIEW_ID_NONE;
     content_view->AddChildView(
-        CreateLineItemView(base::UTF8ToUTF16(item->label),
-                           formatter->Format(item->amount->value),
-                           false).release());
+        CreateLineItemView(
+            base::UTF8ToUTF16(request()->details()->display_items[i]->label),
+            formatter->Format(
+                request()->details()->display_items[i]->amount->value),
+            false, view_id)
+            .release());
   }
 
   base::string16 total_label_value = l10n_util::GetStringFUTF16(
@@ -108,8 +122,9 @@
 
   content_view->AddChildView(
       CreateLineItemView(base::UTF8ToUTF16(request()->details()->total->label),
-                         total_label_value,
-                         true).release());
+                         total_label_value, true,
+                         DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL)
+          .release());
 
   return payments::CreatePaymentView(
       CreateSheetHeaderView(
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
index 8abd360..e9ca82b 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
@@ -102,6 +102,9 @@
   view_stack_.Push(CreateViewAndInstallController<OrderSummaryViewController>(
                        &controller_map_, request_, this),
                    /* animate = */ true);
+
+  if (observer_for_testing_)
+    observer_for_testing_->OnOrderSummaryOpened();
 }
 
 void PaymentRequestDialogView::ShowPaymentMethodSheet() {
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.h b/chrome/browser/ui/views/payments/payment_request_dialog_view.h
index 1d35d2f..f6751de 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view.h
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.h
@@ -33,6 +33,8 @@
   class ObserverForTest {
    public:
     virtual void OnDialogOpened() = 0;
+
+    virtual void OnOrderSummaryOpened() = 0;
   };
 
   // Build a Dialog around the PaymentRequest object. |observer| is used to
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h b/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h
new file mode 100644
index 0000000..68fff4f9
--- /dev/null
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_REQUEST_DIALOG_VIEW_IDS_H_
+#define CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_REQUEST_DIALOG_VIEW_IDS_H_
+
+// This defines an enumeration of IDs that can uniquely identify a view within
+// the scope of the Payment Request Dialog.
+
+namespace payments {
+
+enum class DialogViewID : int {
+  VIEW_ID_NONE,
+
+  // The following are views::Button (clickable).
+  PAYMENT_SHEET_CONTACT_INFO_SECTION,
+  PAYMENT_SHEET_PAYMENT_METHOD_SECTION,
+  PAYMENT_SHEET_SHIPPING_SECTION,
+  PAYMENT_SHEET_SUMMARY_SECTION,
+
+  // The following are StyledLabel objects.
+  ORDER_SUMMARY_TOTAL_AMOUNT_LABEL,
+  ORDER_SUMMARY_LINE_ITEM_1,
+  ORDER_SUMMARY_LINE_ITEM_2,
+  ORDER_SUMMARY_LINE_ITEM_3,
+};
+
+}  // namespace payments
+
+#endif  // CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_REQUEST_DIALOG_VIEW_IDS_H_
diff --git a/chrome/browser/ui/views/payments/payment_request_interactive_uitest.cc b/chrome/browser/ui/views/payments/payment_request_interactive_uitest.cc
index 26109d2..8858da27 100644
--- a/chrome/browser/ui/views/payments/payment_request_interactive_uitest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_interactive_uitest.cc
@@ -4,7 +4,9 @@
 
 #include <vector>
 
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
 #include "chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/payments/payment_request.h"
@@ -36,8 +38,19 @@
             "/payment_request_no_shipping_test.html") {}
 };
 
-IN_PROC_BROWSER_TEST_F(PaymentRequestNoShippingTest, OpenPaymentRequestSheet) {
+IN_PROC_BROWSER_TEST_F(PaymentRequestNoShippingTest,
+                       OpenAndNavigateToOrderSummary) {
   InvokePaymentRequestUI();
+
+  OpenOrderSummaryScreen();
+
+  // Verify the expected amounts are shown.
+  EXPECT_EQ(base::ASCIIToUTF16("USD $5.00"),
+            GetStyledLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  EXPECT_EQ(base::ASCIIToUTF16("$4.50"),
+            GetStyledLabelText(DialogViewID::ORDER_SUMMARY_LINE_ITEM_1));
+  EXPECT_EQ(base::ASCIIToUTF16("$0.50"),
+            GetStyledLabelText(DialogViewID::ORDER_SUMMARY_LINE_ITEM_2));
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestNoShippingTest, OpenAndNavigateTo404) {
diff --git a/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.cc b/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.cc
index c88f0a33..10bfb9bb 100644
--- a/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.cc
+++ b/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.cc
@@ -9,9 +9,12 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
+#include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/payments/payment_request.h"
 #include "components/payments/payment_request_web_contents_manager.h"
@@ -22,12 +25,15 @@
 #include "content/public/test/browser_test_utils.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/test/ui_controls.h"
+#include "ui/views/controls/styled_label.h"
 
 namespace payments {
 
 PaymentRequestInteractiveTestBase::PaymentRequestInteractiveTestBase(
     const std::string& test_file_path)
-    : test_file_path_(test_file_path) {}
+    : test_file_path_(test_file_path),
+      delegate_(nullptr) {}
 PaymentRequestInteractiveTestBase::~PaymentRequestInteractiveTestBase() {}
 
 void PaymentRequestInteractiveTestBase::SetUpCommandLine(
@@ -63,6 +69,11 @@
     event_observer_->Observe(DialogEvent::DIALOG_OPENED);
 }
 
+void PaymentRequestInteractiveTestBase::OnOrderSummaryOpened() {
+  if (event_observer_)
+    event_observer_->Observe(DialogEvent::ORDER_SUMMARY_OPENED);
+}
+
 void PaymentRequestInteractiveTestBase::OnWidgetDestroyed(
     views::Widget* widget) {
   if (event_observer_)
@@ -70,14 +81,14 @@
 }
 
 void PaymentRequestInteractiveTestBase::InvokePaymentRequestUI() {
-  event_observer_.reset(new DialogEventObserver(DialogEvent::DIALOG_OPENED));
+  ResetEventObserver(DialogEvent::DIALOG_OPENED);
 
   content::WebContents* web_contents = GetActiveWebContents();
   const std::string click_buy_button_js =
       "(function() { document.getElementById('buy').click(); })();";
   ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
 
-  event_observer_->Wait();
+  WaitForObservedEvent();
 
   // The web-modal dialog should be open.
   web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
@@ -85,6 +96,14 @@
   EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
 }
 
+void PaymentRequestInteractiveTestBase::OpenOrderSummaryScreen() {
+  ResetEventObserver(DialogEvent::ORDER_SUMMARY_OPENED);
+
+  ClickOnDialogView(DialogViewID::PAYMENT_SHEET_SUMMARY_SECTION);
+
+  WaitForObservedEvent();
+}
+
 content::WebContents*
 PaymentRequestInteractiveTestBase::GetActiveWebContents() {
   return browser()->tab_strip_model()->GetActiveWebContents();
@@ -108,12 +127,32 @@
     content::WebContents* web_contents,
     mojo::InterfaceRequest<payments::mojom::PaymentRequest> request) {
   DCHECK(web_contents);
+  std::unique_ptr<TestChromePaymentRequestDelegate> delegate =
+      base::MakeUnique<TestChromePaymentRequestDelegate>(
+          web_contents, this /* observer */, this /* widget_observer */);
+  delegate_ = delegate.get();
   PaymentRequestWebContentsManager::GetOrCreateForWebContents(web_contents)
-      ->CreatePaymentRequest(
-          web_contents,
-          base::MakeUnique<TestChromePaymentRequestDelegate>(
-              web_contents, this /* observer */, this /* widget_observer */),
-          std::move(request));
+      ->CreatePaymentRequest(web_contents, std::move(delegate),
+                             std::move(request));
+}
+
+void PaymentRequestInteractiveTestBase::ClickOnDialogView(
+    DialogViewID view_id) {
+  views::View* view =
+      delegate_->dialog_view()->GetViewByID(static_cast<int>(view_id));
+  DCHECK(view);
+  base::RunLoop run_loop;
+  ui_test_utils::MoveMouseToCenterAndPress(
+      view, ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP,
+      run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+const base::string16& PaymentRequestInteractiveTestBase::GetStyledLabelText(
+    DialogViewID view_id) {
+  views::View* view = dialog_view()->GetViewByID(static_cast<int>(view_id));
+  DCHECK(view);
+  return static_cast<views::StyledLabel*>(view)->text();
 }
 
 PaymentRequestInteractiveTestBase::DialogEventObserver::DialogEventObserver(
@@ -142,7 +181,7 @@
 }
 
 void PaymentRequestInteractiveTestBase::ResetEventObserver(DialogEvent event) {
-  event_observer_.reset(new DialogEventObserver(event));
+  event_observer_ = base::MakeUnique<DialogEventObserver>(event);
 }
 
 void PaymentRequestInteractiveTestBase::WaitForObservedEvent() {
diff --git a/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.h b/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.h
index 3554843b..23dccc4 100644
--- a/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.h
+++ b/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
+#include "chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/payments/payment_request.mojom.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -26,6 +27,7 @@
 
 namespace payments {
 
+enum class DialogViewID;
 class PaymentRequest;
 
 // Base class for any interactive PaymentRequest test that will need to open
@@ -45,6 +47,7 @@
 
   // PaymentRequestDialogView::ObserverForTest
   void OnDialogOpened() override;
+  void OnOrderSummaryOpened() override;
 
   // views::WidgetObserver
   // Effective way to be warned of all dialog closures.
@@ -54,6 +57,8 @@
   // it's open.
   void InvokePaymentRequestUI();
 
+  void OpenOrderSummaryScreen();
+
   // Convenience method to get a list of PaymentRequest associated with
   // |web_contents|.
   const std::vector<PaymentRequest*> GetPaymentRequests(
@@ -65,12 +70,22 @@
       content::WebContents* web_contents,
       mojo::InterfaceRequest<payments::mojom::PaymentRequest> request);
 
+  // Click on a view from within the dialog.
+  void ClickOnDialogView(DialogViewID view_id);
+
+  // Returns the text of the StyledLabel with the specific |view_id| that is a
+  // child of the Payment Request dialog view.
+  const base::string16& GetStyledLabelText(DialogViewID view_id);
+
   net::EmbeddedTestServer* https_server() { return https_server_.get(); }
 
+  PaymentRequestDialogView* dialog_view() { return delegate_->dialog_view(); }
+
   // Various events that can be waited on by the DialogEventObserver.
   enum DialogEvent {
     DIALOG_OPENED,
     DIALOG_CLOSED,
+    ORDER_SUMMARY_OPENED,
   };
 
   // DialogEventObserver is used to wait on specific events that may have
@@ -113,6 +128,8 @@
   std::unique_ptr<DialogEventObserver> event_observer_;
   const std::string test_file_path_;
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
+  // Weak, owned by the PaymentRequest object.
+  TestChromePaymentRequestDelegate* delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestInteractiveTestBase);
 };
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
index 061d75fd..50b3295 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
@@ -261,6 +262,8 @@
       widest_name_column_view_width_);
   section->set_tag(static_cast<int>(
       PaymentSheetViewControllerTags::SHOW_ORDER_SUMMARY_BUTTON));
+  section->set_id(
+      static_cast<int>(DialogViewID::PAYMENT_SHEET_SUMMARY_SECTION));
   return section;
 }
 
@@ -290,6 +293,8 @@
       widest_name_column_view_width_);
   section->set_tag(
       static_cast<int>(PaymentSheetViewControllerTags::SHOW_SHIPPING_BUTTON));
+  section->set_id(
+      static_cast<int>(DialogViewID::PAYMENT_SHEET_SHIPPING_SECTION));
   return section;
 }
 
@@ -346,6 +351,8 @@
       widest_name_column_view_width_);
   section->set_tag(static_cast<int>(
       PaymentSheetViewControllerTags::SHOW_PAYMENT_METHOD_BUTTON));
+  section->set_id(
+      static_cast<int>(DialogViewID::PAYMENT_SHEET_PAYMENT_METHOD_SECTION));
   return section;
 }
 
@@ -375,6 +382,8 @@
       widest_name_column_view_width_);
   section->set_tag(static_cast<int>(
       PaymentSheetViewControllerTags::SHOW_CONTACT_INFO_BUTTON));
+  section->set_id(
+      static_cast<int>(DialogViewID::PAYMENT_SHEET_CONTACT_INFO_SECTION));
   return section;
 }
 
diff --git a/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h b/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h
index 9c1218b..e336e7b 100644
--- a/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h
+++ b/chrome/browser/ui/views/payments/test_chrome_payment_request_delegate.h
@@ -31,6 +31,10 @@
 
   void ShowDialog(PaymentRequest* request) override;
 
+  PaymentRequestDialogView* dialog_view() {
+    return static_cast<PaymentRequestDialogView*>(dialog_);
+  }
+
  private:
   PaymentRequestDialogView::ObserverForTest* observer_;
   views::WidgetObserver* widget_observer_;
diff --git a/chrome/browser/ui/webui/snippets_internals_message_handler.cc b/chrome/browser/ui/webui/snippets_internals_message_handler.cc
index 4207a04..b40f49d 100644
--- a/chrome/browser/ui/webui/snippets_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/snippets_internals_message_handler.cc
@@ -274,8 +274,6 @@
 }
 
 void SnippetsInternalsMessageHandler::SendAllContent() {
-  SendBoolean("flag-snippets", base::FeatureList::IsEnabled(
-                                   ntp_snippets::kContentSuggestionsFeature));
   SendBoolean(
       "flag-article-suggestions",
       base::FeatureList::IsEnabled(ntp_snippets::kArticleSuggestionsFeature));
diff --git a/chrome/installer/mini_installer/BUILD.gn b/chrome/installer/mini_installer/BUILD.gn
index 84be262..5d2fe25 100644
--- a/chrome/installer/mini_installer/BUILD.gn
+++ b/chrome/installer/mini_installer/BUILD.gn
@@ -25,6 +25,8 @@
     "configuration.h",
     "decompress.cc",
     "decompress.h",
+    "mini_installer.cc",
+    "mini_installer.h",
     "mini_installer.ico",
     "mini_installer.rc",
     "mini_installer_constants.cc",
@@ -61,6 +63,7 @@
   sources = [
     "configuration_test.cc",
     "decompress_test.cc",
+    "mini_installer_unittest.cc",
     "mini_string_test.cc",
   ]
 
@@ -218,7 +221,7 @@
   executable(target_name) {
     output_name = "mini_installer"
     sources = [
-      "mini_installer.cc",
+      "mini_installer_exe_main.cc",
       packed_files_rc_file,
     ]
 
diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc
index be29092fd..50b62520 100644
--- a/chrome/installer/mini_installer/mini_installer.cc
+++ b/chrome/installer/mini_installer/mini_installer.cc
@@ -21,6 +21,8 @@
 // have the linker merge the sections, saving us ~500 bytes.
 #pragma comment(linker, "/MERGE:.rdata=.text")
 
+#include "chrome/installer/mini_installer/mini_installer.h"
+
 #include <windows.h>
 
 // #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036.  See the
@@ -38,30 +40,13 @@
 #include "chrome/installer/mini_installer/appid.h"
 #include "chrome/installer/mini_installer/configuration.h"
 #include "chrome/installer/mini_installer/decompress.h"
-#include "chrome/installer/mini_installer/exit_code.h"
 #include "chrome/installer/mini_installer/mini_installer_constants.h"
-#include "chrome/installer/mini_installer/mini_string.h"
 #include "chrome/installer/mini_installer/pe_resource.h"
 #include "chrome/installer/mini_installer/regkey.h"
 
 namespace mini_installer {
 
 typedef StackString<MAX_PATH> PathString;
-typedef StackString<MAX_PATH * 4> CommandString;
-
-struct ProcessExitResult {
-  DWORD exit_code;
-  DWORD windows_error;
-
-  explicit ProcessExitResult(DWORD exit) : exit_code(exit), windows_error(0) {}
-  ProcessExitResult(DWORD exit, DWORD win)
-      : exit_code(exit), windows_error(win) {
-  }
-
-  bool IsSuccess() {
-    return exit_code == SUCCESS_EXIT_CODE;
-  }
-};
 
 // This structure passes data back and forth for the processing
 // of resource callbacks.
@@ -232,56 +217,47 @@
   return ProcessExitResult(exit_code);
 }
 
-// Appends any command line params passed to mini_installer to the given buffer
-// so that they can be passed on to setup.exe.
-// |buffer| is unchanged in case of error.
-void AppendCommandLineFlags(const Configuration& configuration,
+void AppendCommandLineFlags(const wchar_t* command_line,
                             CommandString* buffer) {
-  PathString full_exe_path;
-  size_t len = ::GetModuleFileName(
-      NULL, full_exe_path.get(), static_cast<DWORD>(full_exe_path.capacity()));
-  if (!len || len >= full_exe_path.capacity())
-    return;
-
-  const wchar_t* exe_name =
-      GetNameFromPathExt(full_exe_path.get(), static_cast<DWORD>(len));
-
-  // - configuration.program() returns the first command line argument
-  //   passed into the program (that the user probably typed in this case).
-  //       "mini_installer.exe"
-  //       "mini_installer"
-  //       "out\Release\mini_installer"
-  // - |exe_name| is the executable file of the current process.
-  //       "mini_installer.exe"
-  //
-  // Note that there are three possibilities to handle here.
-  // Receive a cmdline containing:
-  // 1) executable name WITH extension
-  // 2) executable name with NO extension
-  // 3) NO executable name as part of cmdline
-  const wchar_t* cmd_to_append = L"";
-  const wchar_t* arg0 = configuration.program();
-  if (!arg0)
-    return;
-  const wchar_t* arg0_base_name = GetNameFromPathExt(arg0, ::lstrlen(arg0));
-  if (!StrStartsWith(exe_name, arg0_base_name)) {
-    // State 3: NO executable name as part of cmdline.
-    buffer->append(L" ");
-    cmd_to_append = configuration.command_line();
-  } else if (configuration.argument_count() > 1) {
-    // State 1 or 2: Executable name is in cmdline.
-    // - Append everything AFTER the executable name.
-    //   (Using arg0_base_name here to make sure to match with or without
-    //   extension.  Then move to the space following the token.)
-    const wchar_t* tmp = SearchStringI(configuration.command_line(),
-                                       arg0_base_name);
-    tmp = SearchStringI(tmp, L" ");
-    cmd_to_append = tmp;
+  // The program name (the first argument parsed by CommandLineToArgvW) is
+  // delimited by whitespace or a double quote based on the first character of
+  // the full command line string. Use the same logic here to scan past the
+  // program name in the program's command line (obtained during startup from
+  // GetCommandLine). See
+  // http://www.windowsinspired.com/how-a-windows-programs-splits-its-command-line-into-individual-arguments/
+  // for gory details regarding how CommandLineToArgvW works.
+  wchar_t a_char = 0;
+  if (*command_line == L'"') {
+    // Scan forward past the closing double quote.
+    ++command_line;
+    while (true) {
+      a_char = *command_line;
+      if (!a_char)
+        break;
+      ++command_line;
+      if (a_char == L'"') {
+        a_char = *command_line;
+        break;
+      }
+    }  // postcondition: |a_char| contains the character at *command_line.
+  } else {
+    // Scan forward for the first space or tab character.
+    while (true) {
+      a_char = *command_line;
+      if (!a_char || a_char == L' ' || a_char == L'\t')
+        break;
+      ++command_line;
+    }  // postcondition: |a_char| contains the character at *command_line.
   }
 
-  buffer->append(cmd_to_append);
-}
+  if (!a_char)
+    return;
 
+  // Append a space if |command_line| doesn't begin with one.
+  if (a_char != ' ' && a_char != '\t' && !buffer->append(L" "))
+    return;
+  buffer->append(command_line);
+}
 
 // Windows defined callback used in the EnumResourceNames call. For each
 // matching resource found, the callback is invoked and at this point we write
@@ -407,7 +383,7 @@
 
     // Get any command line option specified for mini_installer and pass them
     // on to setup.exe.
-    AppendCommandLineFlags(configuration, &cmd_line);
+    AppendCommandLineFlags(configuration.command_line(), &cmd_line);
 
     if (exit_code.IsSuccess())
       exit_code = RunProcessAndWait(exe_path.get(), cmd_line.get());
@@ -506,7 +482,7 @@
 
   // Get any command line option specified for mini_installer and pass them
   // on to setup.exe
-  AppendCommandLineFlags(configuration, &cmd_line);
+  AppendCommandLineFlags(configuration.command_line(), &cmd_line);
 
   return RunProcessAndWait(NULL, cmd_line.get());
 }
@@ -823,8 +799,6 @@
   return true;
 }
 
-// Main function. First gets a working dir, unpacks the resources and finally
-// executes setup.exe to do the install/upgrade.
 ProcessExitResult WMain(HMODULE module) {
   // Always start with deleting potential leftovers from previous installations.
   // This can make the difference between success and failure.  We've seen
@@ -887,44 +861,3 @@
 }
 
 }  // namespace mini_installer
-
-int MainEntryPoint() {
-  mini_installer::ProcessExitResult result =
-      mini_installer::WMain(::GetModuleHandle(NULL));
-
-  ::ExitProcess(result.exit_code);
-}
-
-#if defined(ADDRESS_SANITIZER)
-// Executables instrumented with ASAN need CRT functions. We do not use
-// the /ENTRY switch for ASAN instrumented executable and a "main" function
-// is required.
-int WINAPI WinMain(HINSTANCE hInstance,
-                   HINSTANCE hPrevInstance,
-                   LPSTR lpCmdLine,
-                   int nCmdShow) {
-  MainEntryPoint();
-  return 0;
-}
-#endif
-
-// VC Express editions don't come with the memset CRT obj file and linking to
-// the obj files between versions becomes a bit problematic. Therefore,
-// simply implement memset.
-//
-// This also avoids having to explicitly set the __sse2_available hack when
-// linking with both the x64 and x86 obj files which is required when not
-// linking with the std C lib in certain instances (including Chromium) with
-// MSVC.  __sse2_available determines whether to use SSE2 intructions with
-// std C lib routines, and is set by MSVC's std C lib implementation normally.
-extern "C" {
-#pragma function(memset)
-void* memset(void* dest, int c, size_t count) {
-  void* start = dest;
-  while (count--) {
-    *reinterpret_cast<char*>(dest) = static_cast<char>(c);
-    dest = reinterpret_cast<char*>(dest) + 1;
-  }
-  return start;
-}
-}  // extern "C"
diff --git a/chrome/installer/mini_installer/mini_installer.h b/chrome/installer/mini_installer/mini_installer.h
new file mode 100644
index 0000000..cf73c309
--- /dev/null
+++ b/chrome/installer/mini_installer/mini_installer.h
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_INSTALLER_MINI_INSTALLER_MINI_INSTALLER_H_
+#define CHROME_INSTALLER_MINI_INSTALLER_MINI_INSTALLER_H_
+
+#include <windows.h>
+
+#include "chrome/installer/mini_installer/exit_code.h"
+#include "chrome/installer/mini_installer/mini_string.h"
+
+namespace mini_installer {
+
+// A container of a process exit code (eventually passed to ExitProcess) and
+// a Windows error code for cases where the exit code is non-zero.
+struct ProcessExitResult {
+  DWORD exit_code;
+  DWORD windows_error;
+
+  explicit ProcessExitResult(DWORD exit) : exit_code(exit), windows_error(0) {}
+  ProcessExitResult(DWORD exit, DWORD win)
+      : exit_code(exit), windows_error(win) {}
+
+  bool IsSuccess() const { return exit_code == SUCCESS_EXIT_CODE; }
+};
+
+// A stack-based string large enough to hold an executable to run
+// (which is a path), two additional path arguments, plus a few extra
+// arguments. Figure that MAX_PATH (260) is sufficient breathing room for the
+// extra arguments.
+using CommandString = StackString<MAX_PATH * 4>;
+
+// Appends everything following the path to the executable in |command_line|
+// verbatim to |buffer|, including all whitespace, quoted arguments,
+// etc. |buffer| is unchanged in case of error.
+void AppendCommandLineFlags(const wchar_t* command_line, CommandString* buffer);
+
+// Main function for Chrome's mini_installer. First gets a working dir, unpacks
+// the resources, and finally executes setup.exe to do the install/update. Also
+// handles invoking a previous version's setup.exe to patch itself in the case
+// of differential updates.
+ProcessExitResult WMain(HMODULE module);
+
+}  // namespace mini_installer
+
+#endif  // CHROME_INSTALLER_MINI_INSTALLER_MINI_INSTALLER_H_
diff --git a/chrome/installer/mini_installer/mini_installer_exe_main.cc b/chrome/installer/mini_installer/mini_installer_exe_main.cc
new file mode 100644
index 0000000..b592cd23e
--- /dev/null
+++ b/chrome/installer/mini_installer/mini_installer_exe_main.cc
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "chrome/installer/mini_installer/mini_installer.h"
+
+// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+extern "C" int __stdcall MainEntryPoint() {
+  mini_installer::ProcessExitResult result =
+      mini_installer::WMain(reinterpret_cast<HMODULE>(&__ImageBase));
+  ::ExitProcess(result.exit_code);
+}
+
+#if defined(ADDRESS_SANITIZER)
+// Executables instrumented with ASAN need CRT functions. We do not use
+// the /ENTRY switch for ASAN instrumented executable and a "main" function
+// is required.
+extern "C" int WINAPI wWinMain(HINSTANCE /* instance */,
+                               HINSTANCE /* previous_instance */,
+                               LPWSTR /* command_line */,
+                               int /* command_show */) {
+  return MainEntryPoint();
+}
+#endif
+
+// VC Express editions don't come with the memset CRT obj file and linking to
+// the obj files between versions becomes a bit problematic. Therefore,
+// simply implement memset.
+//
+// This also avoids having to explicitly set the __sse2_available hack when
+// linking with both the x64 and x86 obj files which is required when not
+// linking with the std C lib in certain instances (including Chromium) with
+// MSVC.  __sse2_available determines whether to use SSE2 intructions with
+// std C lib routines, and is set by MSVC's std C lib implementation normally.
+extern "C" {
+#pragma function(memset)
+void* memset(void* dest, int c, size_t count) {
+  uint8_t* scan = reinterpret_cast<uint8_t*>(dest);
+  while (count--)
+    *scan++ = static_cast<uint8_t>(c);
+  return dest;
+}
+}  // extern "C"
diff --git a/chrome/installer/mini_installer/mini_installer_unittest.cc b/chrome/installer/mini_installer/mini_installer_unittest.cc
new file mode 100644
index 0000000..e0b355b
--- /dev/null
+++ b/chrome/installer/mini_installer/mini_installer_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/mini_installer/mini_installer.h"
+
+#include "chrome/installer/mini_installer/configuration.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mini_installer {
+
+TEST(MiniInstallerTest, AppendCommandLineFlags) {
+  static constexpr struct {
+    const wchar_t* command_line;
+    const wchar_t* args;
+  } kData[] = {
+      {L"", L"foo.exe"},
+      {L"mini_installer.exe", L"foo.exe"},
+      {L"mini_installer.exe --verbose-logging", L"foo.exe --verbose-logging"},
+      {L"C:\\Temp\\mini_installer.exe --verbose-logging",
+       L"foo.exe --verbose-logging"},
+      {L"C:\\Temp\\mini_installer --verbose-logging",
+       L"foo.exe --verbose-logging"},
+      {L"\"C:\\Temp\\mini_installer (1).exe\" --verbose-logging",
+       L"foo.exe --verbose-logging"},
+      {L"\"mini_installer.exe\"--verbose-logging",
+       L"foo.exe --verbose-logging"},
+  };
+
+  CommandString buffer;
+
+  for (const auto& data : kData) {
+    buffer.assign(L"foo.exe");
+    AppendCommandLineFlags(data.command_line, &buffer);
+    EXPECT_STREQ(data.args, buffer.get()) << data.command_line;
+  }
+}
+
+}  // namespace mini_installer
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cc813b6..1938a8b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1347,6 +1347,7 @@
       "../browser/browsing_data/passwords_counter_browsertest.cc",
       "../browser/budget_service/budget_manager_browsertest.cc",
       "../browser/chrome_content_browser_client_browsertest.cc",
+      "../browser/chrome_find_request_manager_browsertest.cc",
       "../browser/chrome_main_browsertest.cc",
       "../browser/chrome_navigation_browsertest.cc",
       "../browser/chrome_plugin_browsertest.cc",
diff --git a/chrome/test/data/extensions/api_test/cookies/api/tab.js b/chrome/test/data/extensions/api_test/cookies/api/tab.js
index cb5caf2..3bb210e6 100644
--- a/chrome/test/data/extensions/api_test/cookies/api/tab.js
+++ b/chrome/test/data/extensions/api_test/cookies/api/tab.js
@@ -29,7 +29,7 @@
   expirationDate: TEST_EXPIRATION_DATE
 };
 var TEST_SECURE_COOKIE = {
-  url: TEST_URL5,
+  url: TEST_URL4,
   name: 'SECRETCOOKIE',
   value: 'foobar_password',
   secure: true,
@@ -163,9 +163,9 @@
   function getSecureCookie() {
     removeTestCookies();
     chrome.cookies.set(TEST_SECURE_COOKIE, pass(function () {
-      // Original URL doesn't work because scheme isn't secure.
+      // URL doesn't work because scheme isn't secure.
       chrome.cookies.get(
-          {url: TEST_SECURE_COOKIE.url, name: TEST_SECURE_COOKIE.name},
+          {url: TEST_URL5, name: TEST_SECURE_COOKIE.name},
           pass(expectNullCookie));
       // Path doesn't match.
       chrome.cookies.get(
diff --git a/chrome/test/data/payments/no_shipping.js b/chrome/test/data/payments/no_shipping.js
index 354fc9a..27981b45 100644
--- a/chrome/test/data/payments/no_shipping.js
+++ b/chrome/test/data/payments/no_shipping.js
@@ -11,9 +11,17 @@
  */
 function buy() {  // eslint-disable-line no-unused-vars
   try {
-    new PaymentRequest(
-        [{supportedMethods: ['visa', 'mastercard']}],
-        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}})
+    new PaymentRequest([{supportedMethods: ['visa', 'mastercard']}], {
+      total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
+      displayItems: [
+        {
+          label: 'Subtotal',
+          amount: {currency: 'USD', value: '4.50'},
+          pending: true
+        },
+        {label: 'Taxes', amount: {currency: 'USD', value: '0.50'}}
+      ]
+    })
         .show()
         .then(function(resp) {
           resp.complete('success')
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 2d8f6c91..9bbdd135 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -1399,7 +1399,7 @@
       render_frame()->GetWebFrame()->document().focusedElement();
   if (!element.isNull() && element.hasHTMLTagName("input")) {
     blink::WebInputElement input = element.to<blink::WebInputElement>();
-    if (input.isPasswordField() && !input.form().isNull()) {
+    if (input.isPasswordField()) {
       if (!input.form().isNull()) {
         password_form = CreatePasswordFormFromWebForm(
             input.form(), &field_value_and_properties_map_, &form_predictions_);
diff --git a/components/ntp_snippets/features.cc b/components/ntp_snippets/features.cc
index 25935a6df..b680661 100644
--- a/components/ntp_snippets/features.cc
+++ b/components/ntp_snippets/features.cc
@@ -33,7 +33,7 @@
 const base::Feature kPhysicalWebPageSuggestionsFeature{
     "NTPPhysicalWebPageSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kContentSuggestionsFeature{
+const base::Feature kContentSuggestionsSource{
     "NTPSnippets", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kSectionDismissalFeature{
diff --git a/components/ntp_snippets/features.h b/components/ntp_snippets/features.h
index 6ae8eab..bf9b944 100644
--- a/components/ntp_snippets/features.h
+++ b/components/ntp_snippets/features.h
@@ -35,9 +35,10 @@
 // Feature to allow dismissing sections.
 extern const base::Feature kSectionDismissalFeature;
 
-// Global toggle for the whole content suggestions feature. If this is set to
-// false, all the per-provider features are ignored.
-extern const base::Feature kContentSuggestionsFeature;
+// Feature to allow specification of content suggestions source.
+// TODO(peconn): Figure out how to remove this, it is useful to specify the
+// source, but you shouldn't be able to disable it.
+extern const base::Feature kContentSuggestionsSource;
 
 // Feature to allow UI as specified here: https://crbug.com/660837.
 extern const base::Feature kIncreasedVisibility;
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index 798d314..c06173b2 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -10,6 +10,8 @@
   sources = [
     "about_signin_internals.cc",
     "about_signin_internals.h",
+    "access_token_fetcher.cc",
+    "access_token_fetcher.h",
     "account_fetcher_service.cc",
     "account_fetcher_service.h",
     "account_info.cc",
@@ -147,6 +149,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "access_token_fetcher_unittest.cc",
     "account_info_unittest.cc",
     "account_investigator_unittest.cc",
     "account_tracker_service_unittest.cc",
@@ -161,6 +164,7 @@
 
   deps = [
     ":test_support",
+    "//base/test:test_support",
     "//components/content_settings/core/browser",
     "//components/os_crypt:test_support",
     "//components/pref_registry:pref_registry",
diff --git a/components/signin/core/browser/access_token_fetcher.cc b/components/signin/core/browser/access_token_fetcher.cc
new file mode 100644
index 0000000..d301d37
--- /dev/null
+++ b/components/signin/core/browser/access_token_fetcher.cc
@@ -0,0 +1,169 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/core/browser/access_token_fetcher.h"
+
+#include <utility>
+
+#include "base/logging.h"
+
+AccessTokenFetcher::AccessTokenFetcher(
+    const std::string& oauth_consumer_name,
+    SigninManagerBase* signin_manager,
+    OAuth2TokenService* token_service,
+    const OAuth2TokenService::ScopeSet& scopes,
+    TokenCallback callback)
+    : OAuth2TokenService::Consumer(oauth_consumer_name),
+      signin_manager_(signin_manager),
+      token_service_(token_service),
+      scopes_(scopes),
+      callback_(std::move(callback)),
+      waiting_for_sign_in_(false),
+      waiting_for_refresh_token_(false),
+      access_token_retried_(false) {
+  Start();
+}
+
+AccessTokenFetcher::~AccessTokenFetcher() {
+  if (waiting_for_sign_in_) {
+    signin_manager_->RemoveObserver(this);
+  }
+  if (waiting_for_refresh_token_) {
+    token_service_->RemoveObserver(this);
+  }
+}
+
+void AccessTokenFetcher::Start() {
+  if (signin_manager_->IsAuthenticated()) {
+    // Already signed in: Make sure we have a refresh token, then get the access
+    // token.
+    WaitForRefreshToken();
+    return;
+  }
+
+  // Not signed in: Wait for a sign-in to complete (to get the refresh token),
+  // then get the access token.
+  DCHECK(!waiting_for_sign_in_);
+  waiting_for_sign_in_ = true;
+  signin_manager_->AddObserver(this);
+}
+
+void AccessTokenFetcher::WaitForRefreshToken() {
+  DCHECK(signin_manager_->IsAuthenticated());
+  DCHECK(!waiting_for_refresh_token_);
+
+  if (token_service_->RefreshTokenIsAvailable(
+          signin_manager_->GetAuthenticatedAccountId())) {
+    // Already have refresh token: Get the access token directly.
+    StartAccessTokenRequest();
+    return;
+  }
+
+  // Signed in, but refresh token isn't there yet: Wait for the refresh
+  // token to be loaded, then get the access token.
+  waiting_for_refresh_token_ = true;
+  token_service_->AddObserver(this);
+}
+
+void AccessTokenFetcher::StartAccessTokenRequest() {
+  // Note: We might get here even in cases where we know that there's no refresh
+  // token. We're requesting an access token anyway, so that the token service
+  // will generate an appropriate error code that we can return to the client.
+  DCHECK(!access_token_request_);
+  access_token_request_ = token_service_->StartRequest(
+      signin_manager_->GetAuthenticatedAccountId(), scopes_, this);
+}
+
+void AccessTokenFetcher::GoogleSigninSucceeded(const std::string& account_id,
+                                               const std::string& username,
+                                               const std::string& password) {
+  DCHECK(waiting_for_sign_in_);
+  DCHECK(!waiting_for_refresh_token_);
+  DCHECK(signin_manager_->IsAuthenticated());
+  waiting_for_sign_in_ = false;
+  signin_manager_->RemoveObserver(this);
+
+  WaitForRefreshToken();
+}
+
+void AccessTokenFetcher::GoogleSigninFailed(
+    const GoogleServiceAuthError& error) {
+  DCHECK(waiting_for_sign_in_);
+  DCHECK(!waiting_for_refresh_token_);
+  waiting_for_sign_in_ = false;
+  signin_manager_->RemoveObserver(this);
+
+  std::move(callback_).Run(error, std::string());
+}
+
+void AccessTokenFetcher::OnRefreshTokenAvailable(
+    const std::string& account_id) {
+  DCHECK(waiting_for_refresh_token_);
+  DCHECK(!waiting_for_sign_in_);
+
+  // Only react on tokens for the account the user has signed in with.
+  if (account_id != signin_manager_->GetAuthenticatedAccountId()) {
+    return;
+  }
+
+  waiting_for_refresh_token_ = false;
+  token_service_->RemoveObserver(this);
+  StartAccessTokenRequest();
+}
+
+void AccessTokenFetcher::OnRefreshTokensLoaded() {
+  DCHECK(waiting_for_refresh_token_);
+  DCHECK(!waiting_for_sign_in_);
+  DCHECK(!access_token_request_);
+
+  // All refresh tokens were loaded, but we didn't get one for the account we
+  // care about. We probably won't get one any time soon.
+  // Attempt to fetch an access token anyway, so that the token service will
+  // provide us with an appropriate error code.
+  waiting_for_refresh_token_ = false;
+  token_service_->RemoveObserver(this);
+  StartAccessTokenRequest();
+}
+
+void AccessTokenFetcher::OnGetTokenSuccess(
+    const OAuth2TokenService::Request* request,
+    const std::string& access_token,
+    const base::Time& expiration_time) {
+  DCHECK_EQ(request, access_token_request_.get());
+  std::unique_ptr<OAuth2TokenService::Request> request_deleter(
+      std::move(access_token_request_));
+
+  std::move(callback_).Run(GoogleServiceAuthError::AuthErrorNone(),
+                           access_token);
+}
+
+void AccessTokenFetcher::OnGetTokenFailure(
+    const OAuth2TokenService::Request* request,
+    const GoogleServiceAuthError& error) {
+  DCHECK_EQ(request, access_token_request_.get());
+  std::unique_ptr<OAuth2TokenService::Request> request_deleter(
+      std::move(access_token_request_));
+
+  // There is a special case for Android that RefreshTokenIsAvailable and
+  // StartRequest are called to pre-fetch the account image and name before
+  // sign-in. In that case, our ongoing access token request gets cancelled.
+  // Moreover, OnRefreshTokenAvailable might happen after startup when the
+  // credentials are changed/updated.
+  // To handle these cases, we retry a canceled request once.
+  // However, a request may also get cancelled for legitimate reasons, e.g.
+  // because the user signed out. In those cases, there's no point in retrying,
+  // so only retry if there (still) is a valid refresh token.
+  // NOTE: Maybe we should retry for all transient errors here, so that clients
+  // don't have to.
+  if (!access_token_retried_ &&
+      error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED &&
+      token_service_->RefreshTokenIsAvailable(
+          signin_manager_->GetAuthenticatedAccountId())) {
+    access_token_retried_ = true;
+    StartAccessTokenRequest();
+    return;
+  }
+
+  std::move(callback_).Run(error, std::string());
+}
diff --git a/components/signin/core/browser/access_token_fetcher.h b/components/signin/core/browser/access_token_fetcher.h
new file mode 100644
index 0000000..2963ab8b
--- /dev/null
+++ b/components/signin/core/browser/access_token_fetcher.h
@@ -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.
+
+#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_ACCESS_TOKEN_FETCHER_H_
+#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCESS_TOKEN_FETCHER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/signin/core/browser/signin_manager_base.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+
+// Helper class to ease the task of obtaining an OAuth2 access token for the
+// authenticated account. This handles various special cases, e.g. when the
+// refresh token isn't loaded yet (during startup), or when there is some
+// transient error.
+// May only be used on the UI thread.
+class AccessTokenFetcher : public SigninManagerBase::Observer,
+                           public OAuth2TokenService::Observer,
+                           public OAuth2TokenService::Consumer {
+ public:
+  // Callback for when a request completes (successful or not). On successful
+  // requests, |error| is NONE and |access_token| contains the obtained OAuth2
+  // access token. On failed requests, |error| contains the actual error and
+  // |access_token| is empty.
+  using TokenCallback =
+      base::OnceCallback<void(const GoogleServiceAuthError& error,
+                              const std::string& access_token)>;
+
+  // Instantiates a fetcher and immediately starts the process of obtaining an
+  // OAuth2 access token for the given |scopes|. The |callback| is called once
+  // the request completes (successful or not). If the AccessTokenFetcher is
+  // destroyed before the process completes, the callback is not called.
+  AccessTokenFetcher(const std::string& oauth_consumer_name,
+                     SigninManagerBase* signin_manager,
+                     OAuth2TokenService* token_service,
+                     const OAuth2TokenService::ScopeSet& scopes,
+                     TokenCallback callback);
+
+  ~AccessTokenFetcher() override;
+
+ private:
+  void Start();
+
+  void WaitForRefreshToken();
+  void StartAccessTokenRequest();
+
+  // SigninManagerBase::Observer implementation.
+  void GoogleSigninSucceeded(const std::string& account_id,
+                             const std::string& username,
+                             const std::string& password) override;
+  void GoogleSigninFailed(const GoogleServiceAuthError& error) override;
+
+  // OAuth2TokenService::Observer implementation.
+  void OnRefreshTokenAvailable(const std::string& account_id) override;
+  void OnRefreshTokensLoaded() override;
+
+  // OAuth2TokenService::Consumer implementation.
+  void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+                         const std::string& access_token,
+                         const base::Time& expiration_time) override;
+  void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+                         const GoogleServiceAuthError& error) override;
+
+  SigninManagerBase* signin_manager_;
+  OAuth2TokenService* token_service_;
+  OAuth2TokenService::ScopeSet scopes_;
+  TokenCallback callback_;
+
+  bool waiting_for_sign_in_;
+  bool waiting_for_refresh_token_;
+
+  std::unique_ptr<OAuth2TokenService::Request> access_token_request_;
+
+  // When a token request gets canceled, we want to retry once.
+  bool access_token_retried_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessTokenFetcher);
+};
+
+#endif  // COMPONENTS_SIGNIN_CORE_BROWSER_ACCESS_TOKEN_FETCHER_H_
diff --git a/components/signin/core/browser/access_token_fetcher_unittest.cc b/components/signin/core/browser/access_token_fetcher_unittest.cc
new file mode 100644
index 0000000..331468b0
--- /dev/null
+++ b/components/signin/core/browser/access_token_fetcher_unittest.cc
@@ -0,0 +1,390 @@
+// 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/signin/core/browser/access_token_fetcher.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/fake_signin_manager.h"
+#include "components/signin/core/browser/test_signin_client.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::MockCallback;
+using sync_preferences::TestingPrefServiceSyncable;
+using testing::CallbackToFunctor;
+using testing::InvokeWithoutArgs;
+using testing::StrictMock;
+
+#if defined(OS_CHROMEOS)
+// ChromeOS doesn't have SigninManager.
+using SigninManagerForTest = FakeSigninManagerBase;
+#else
+using SigninManagerForTest = FakeSigninManager;
+#endif  // OS_CHROMEOS
+
+class AccessTokenFetcherTest : public testing::Test {
+ public:
+  using TestTokenCallback =
+      StrictMock<MockCallback<AccessTokenFetcher::TokenCallback>>;
+
+  AccessTokenFetcherTest() : signin_client_(&pref_service_) {
+    AccountTrackerService::RegisterPrefs(pref_service_.registry());
+#if defined(OS_CHROMEOS)
+    SigninManagerBase::RegisterProfilePrefs(pref_service_.registry());
+    SigninManagerBase::RegisterPrefs(pref_service_.registry());
+#else
+    SigninManager::RegisterProfilePrefs(pref_service_.registry());
+    SigninManager::RegisterPrefs(pref_service_.registry());
+#endif  // OS_CHROMEOS
+
+    account_tracker_ = base::MakeUnique<AccountTrackerService>();
+    account_tracker_->Initialize(&signin_client_);
+
+#if defined(OS_CHROMEOS)
+    signin_manager_ = base::MakeUnique<FakeSigninManagerBase>(
+        &signin_client_, account_tracker_.get());
+#else
+    signin_manager_ = base::MakeUnique<FakeSigninManager>(
+        &signin_client_, &token_service_, account_tracker_.get(),
+        /*cookie_manager_service=*/nullptr);
+#endif  // OS_CHROMEOS
+  }
+
+  ~AccessTokenFetcherTest() override {}
+
+  std::unique_ptr<AccessTokenFetcher> CreateFetcher(
+      AccessTokenFetcher::TokenCallback callback) {
+    std::set<std::string> scopes{"scope"};
+    return base::MakeUnique<AccessTokenFetcher>(
+        "test_consumer", signin_manager_.get(), &token_service_, scopes,
+        std::move(callback));
+  }
+
+  FakeProfileOAuth2TokenService* token_service() { return &token_service_; }
+  SigninManagerForTest* signin_manager() { return signin_manager_.get(); }
+
+  void SignIn(const std::string& account) {
+#if defined(OS_CHROMEOS)
+    signin_manager_->SignIn(account);
+#else
+    signin_manager_->SignIn(account, "user", "pass");
+#endif  // OS_CHROMEOS
+  }
+
+ private:
+  TestingPrefServiceSyncable pref_service_;
+  TestSigninClient signin_client_;
+  FakeProfileOAuth2TokenService token_service_;
+
+  std::unique_ptr<AccountTrackerService> account_tracker_;
+  std::unique_ptr<SigninManagerForTest> signin_manager_;
+};
+
+TEST_F(AccessTokenFetcherTest, ShouldReturnAccessToken) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Signed in and refresh token already exists, so this should result in a
+  // request for an access token.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // Once the access token request is fulfilled, we should get called back with
+  // the access token.
+  EXPECT_CALL(callback,
+              Run(GoogleServiceAuthError::AuthErrorNone(), "access token"));
+  token_service()->IssueAllTokensForAccount(
+      "account", "access token",
+      base::Time::Now() + base::TimeDelta::FromHours(1));
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldNotReplyIfDestroyed) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Signed in and refresh token already exists, so this should result in a
+  // request for an access token.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // Destroy the fetcher before the access token request is fulfilled.
+  fetcher.reset();
+
+  // Now fulfilling the access token request should have no effect.
+  token_service()->IssueAllTokensForAccount(
+      "account", "access token",
+      base::Time::Now() + base::TimeDelta::FromHours(1));
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldNotReturnWhenSignedOut) {
+  TestTokenCallback callback;
+
+  // Signed out -> the fetcher should wait for a sign-in which never happens
+  // in this test, so we shouldn't get called back.
+  auto fetcher = CreateFetcher(callback.Get());
+}
+
+// Tests related to waiting for sign-in don't apply on ChromeOS (it doesn't have
+// that concept).
+#if !defined(OS_CHROMEOS)
+
+TEST_F(AccessTokenFetcherTest, ShouldWaitForSignIn) {
+  TestTokenCallback callback;
+
+  // Not signed in, so this should wait for a sign-in to complete.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Once the access token request is fulfilled, we should get called back with
+  // the access token.
+  EXPECT_CALL(callback,
+              Run(GoogleServiceAuthError::AuthErrorNone(), "access token"));
+  token_service()->IssueAllTokensForAccount(
+      "account", "access token",
+      base::Time::Now() + base::TimeDelta::FromHours(1));
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldWaitForSignInInProgress) {
+  TestTokenCallback callback;
+
+  signin_manager()->set_auth_in_progress("account");
+
+  // A sign-in is currently in progress, so this should wait for the sign-in to
+  // complete.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Once the access token request is fulfilled, we should get called back with
+  // the access token.
+  EXPECT_CALL(callback,
+              Run(GoogleServiceAuthError::AuthErrorNone(), "access token"));
+  token_service()->IssueAllTokensForAccount(
+      "account", "access token",
+      base::Time::Now() + base::TimeDelta::FromHours(1));
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldWaitForFailedSignIn) {
+  TestTokenCallback callback;
+
+  signin_manager()->set_auth_in_progress("account");
+
+  // A sign-in is currently in progress, so this should wait for the sign-in to
+  // complete.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // The fetcher should detect the failed sign-in and call us with an empty
+  // access token.
+  EXPECT_CALL(
+      callback,
+      Run(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
+          std::string()));
+
+  signin_manager()->FailSignin(
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+}
+
+#endif  // !OS_CHROMEOS
+
+TEST_F(AccessTokenFetcherTest, ShouldWaitForRefreshToken) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+
+  // Signed in, but there is no refresh token -> we should not get called back
+  // (yet).
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // Getting a refresh token should result in a request for an access token.
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Once the access token request is fulfilled, we should get called back with
+  // the access token.
+  EXPECT_CALL(callback,
+              Run(GoogleServiceAuthError::AuthErrorNone(), "access token"));
+  token_service()->IssueAllTokensForAccount(
+      "account", "access token",
+      base::Time::Now() + base::TimeDelta::FromHours(1));
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldIgnoreRefreshTokensForOtherAccounts) {
+  TestTokenCallback callback;
+
+  // Signed-in to "account", but there's only a refresh token for a different
+  // account.
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account 2", "refresh");
+
+  // The fetcher should wait for the correct refresh token.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // A refresh token for yet another account shouldn't matter either.
+  token_service()->GetDelegate()->UpdateCredentials("account 3", "refresh");
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldReturnWhenNoRefreshTokenAvailable) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+
+  // Signed in, but there is no refresh token -> we should not get called back
+  // (yet).
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // Getting a refresh token for some other account should have no effect.
+  token_service()->GetDelegate()->UpdateCredentials("different account",
+                                                    "refresh token");
+
+  // The OAuth2TokenService posts a task to the current thread when we try to
+  // get an access token for an account without a refresh token, so we need a
+  // MessageLoop in this test to not crash.
+  base::MessageLoop message_loop;
+
+  // When all refresh tokens have been loaded by the token service, but the one
+  // for our account wasn't among them, we should get called back with an empty
+  // access token.
+  EXPECT_CALL(callback, Run(testing::_, std::string()));
+  token_service()->GetDelegate()->LoadCredentials("account doesn't matter");
+
+  // Wait for the task posted by OAuth2TokenService to run.
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequest) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Signed in and refresh token already exists, so this should result in a
+  // request for an access token.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // A canceled access token request should get retried once.
+  token_service()->IssueErrorForAllPendingRequestsForAccount(
+      "account",
+      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+
+  // Once the access token request is fulfilled, we should get called back with
+  // the access token.
+  EXPECT_CALL(callback,
+              Run(GoogleServiceAuthError::AuthErrorNone(), "access token"));
+  token_service()->IssueAllTokensForAccount(
+      "account", "access token",
+      base::Time::Now() + base::TimeDelta::FromHours(1));
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequestOnlyOnce) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Signed in and refresh token already exists, so this should result in a
+  // request for an access token.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // A canceled access token request should get retried once.
+  token_service()->IssueErrorForAllPendingRequestsForAccount(
+      "account",
+      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+
+  // On the second failure, we should get called back with an empty access
+  // token.
+  EXPECT_CALL(
+      callback,
+      Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED),
+          std::string()));
+  token_service()->IssueErrorForAllPendingRequestsForAccount(
+      "account",
+      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+}
+
+#if !defined(OS_CHROMEOS)
+
+TEST_F(AccessTokenFetcherTest,
+       ShouldNotRetryCanceledAccessTokenRequestIfSignedOut) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Signed in and refresh token already exists, so this should result in a
+  // request for an access token.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // Simulate the user signing out while the access token request is pending.
+  // In this case, the pending request gets canceled, and the fetcher should
+  // *not* retry.
+  signin_manager()->ForceSignOut();
+  EXPECT_CALL(
+      callback,
+      Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED),
+          std::string()));
+  token_service()->IssueErrorForAllPendingRequestsForAccount(
+      "account",
+      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+}
+
+#endif  // !OS_CHROMEOS
+
+TEST_F(AccessTokenFetcherTest,
+       ShouldNotRetryCanceledAccessTokenRequestIfRefreshTokenRevoked) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Signed in and refresh token already exists, so this should result in a
+  // request for an access token.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // Simulate the refresh token getting invalidated. In this case, pending
+  // access token requests get canceled, and the fetcher should *not* retry.
+  token_service()->GetDelegate()->RevokeCredentials("account");
+  EXPECT_CALL(
+      callback,
+      Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED),
+          std::string()));
+  token_service()->IssueErrorForAllPendingRequestsForAccount(
+      "account",
+      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+}
+
+TEST_F(AccessTokenFetcherTest, ShouldNotRetryFailedAccessTokenRequest) {
+  TestTokenCallback callback;
+
+  SignIn("account");
+  token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
+
+  // Signed in and refresh token already exists, so this should result in a
+  // request for an access token.
+  auto fetcher = CreateFetcher(callback.Get());
+
+  // An access token failure other than "canceled" should not be retried; we
+  // should immediately get called back with an empty access token.
+  EXPECT_CALL(
+      callback,
+      Run(GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE),
+          std::string()));
+  token_service()->IssueErrorForAllPendingRequestsForAccount(
+      "account",
+      GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
+}
diff --git a/components/signin/core/browser/fake_signin_manager.h b/components/signin/core/browser/fake_signin_manager.h
index 83b2728..5ba63b8 100644
--- a/components/signin/core/browser/fake_signin_manager.h
+++ b/components/signin/core/browser/fake_signin_manager.h
@@ -17,7 +17,6 @@
 // SigninManagerForTesting to ensure that the right type for their platform is
 // used.
 
-// Overrides InitTokenService to do-nothing in tests.
 class FakeSigninManagerBase : public SigninManagerBase {
  public:
   FakeSigninManagerBase(SigninClient* client,
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index 569c233b..28f30f1 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -397,7 +397,7 @@
   std::unique_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create(
       google_url, kGaiaCookieName, std::string(), "." + google_url.host(),
       std::string(), base::Time(), base::Time(), false, false,
-      net::CookieSameSite::DEFAULT_MODE, false, net::COOKIE_PRIORITY_DEFAULT));
+      net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT));
   OnCookieChanged(*cookie, net::CookieStore::ChangeCause::UNKNOWN_DELETION);
 }
 
diff --git a/components/suggestions/suggestions_service_impl.cc b/components/suggestions/suggestions_service_impl.cc
index 5f30527c..c6fa238 100644
--- a/components/suggestions/suggestions_service_impl.cc
+++ b/components/suggestions/suggestions_service_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/feature_list.h"
 #include "base/location.h"
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/strings/string_number_conversions.h"
@@ -136,73 +137,23 @@
 
 }  // namespace
 
-// Helper class for fetching OAuth2 access tokens.
-// To get a token, call |GetAccessToken|. Does not support multiple concurrent
-// token requests, i.e. check |HasPendingRequest| first.
-class SuggestionsServiceImpl::AccessTokenFetcher
-    : public OAuth2TokenService::Consumer {
- public:
-  using TokenCallback = base::Callback<void(const std::string&)>;
-
-  AccessTokenFetcher(const SigninManagerBase* signin_manager,
-                     OAuth2TokenService* token_service)
-      : OAuth2TokenService::Consumer("suggestions_service"),
-        signin_manager_(signin_manager),
-        token_service_(token_service) {}
-
-  void GetAccessToken(const TokenCallback& callback) {
-    callback_ = callback;
-    std::string account_id;
-    // |signin_manager_| can be null in unit tests.
-    if (signin_manager_)
-      account_id = signin_manager_->GetAuthenticatedAccountId();
-    OAuth2TokenService::ScopeSet scopes;
-    scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
-    token_request_ = token_service_->StartRequest(account_id, scopes, this);
-  }
-
-  bool HasPendingRequest() const { return !!token_request_.get(); }
-
- private:
-  void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
-                         const std::string& access_token,
-                         const base::Time& expiration_time) override {
-    DCHECK_EQ(request, token_request_.get());
-    callback_.Run(access_token);
-    token_request_.reset(nullptr);
-  }
-
-  void OnGetTokenFailure(const OAuth2TokenService::Request* request,
-                         const GoogleServiceAuthError& error) override {
-    DCHECK_EQ(request, token_request_.get());
-    LOG(WARNING) << "Token error: " << error.ToString();
-    callback_.Run(std::string());
-    token_request_.reset(nullptr);
-  }
-
-  const SigninManagerBase* signin_manager_;
-  OAuth2TokenService* token_service_;
-
-  TokenCallback callback_;
-  std::unique_ptr<OAuth2TokenService::Request> token_request_;
-};
-
 SuggestionsServiceImpl::SuggestionsServiceImpl(
-    const SigninManagerBase* signin_manager,
+    SigninManagerBase* signin_manager,
     OAuth2TokenService* token_service,
     syncer::SyncService* sync_service,
     net::URLRequestContextGetter* url_request_context,
     std::unique_ptr<SuggestionsStore> suggestions_store,
     std::unique_ptr<ImageManager> thumbnail_manager,
     std::unique_ptr<BlacklistStore> blacklist_store)
-    : sync_service_(sync_service),
+    : signin_manager_(signin_manager),
+      token_service_(token_service),
+      sync_service_(sync_service),
       sync_service_observer_(this),
       url_request_context_(url_request_context),
       suggestions_store_(std::move(suggestions_store)),
       thumbnail_manager_(std::move(thumbnail_manager)),
       blacklist_store_(std::move(blacklist_store)),
       scheduling_delay_(TimeDelta::FromSeconds(kDefaultSchedulingDelaySec)),
-      token_fetcher_(new AccessTokenFetcher(signin_manager, token_service)),
       weak_ptr_factory_(this) {
   // |sync_service_| is null if switches::kDisableSync is set (tests use that).
   if (sync_service_)
@@ -387,22 +338,40 @@
     return;
   }
   // If there is an ongoing token request, also wait for that.
-  if (token_fetcher_->HasPendingRequest()) {
+  if (token_fetcher_) {
     return;
   }
-  token_fetcher_->GetAccessToken(
-      base::Bind(&SuggestionsServiceImpl::IssueSuggestionsRequest,
-                 base::Unretained(this), url));
+
+  OAuth2TokenService::ScopeSet scopes{GaiaConstants::kChromeSyncOAuth2Scope};
+  token_fetcher_ = base::MakeUnique<AccessTokenFetcher>(
+      "suggestions_service", signin_manager_, token_service_, scopes,
+      base::BindOnce(&SuggestionsServiceImpl::AccessTokenAvailable,
+                     base::Unretained(this), url));
+}
+
+void SuggestionsServiceImpl::AccessTokenAvailable(
+    const GURL& url,
+    const GoogleServiceAuthError& error,
+    const std::string& access_token) {
+  DCHECK(token_fetcher_);
+  std::unique_ptr<AccessTokenFetcher> token_fetcher_deleter(
+      std::move(token_fetcher_));
+
+  if (error.state() != GoogleServiceAuthError::NONE) {
+    UpdateBlacklistDelay(false);
+    ScheduleBlacklistUpload();
+    return;
+  }
+
+  DCHECK(!access_token.empty());
+
+  IssueSuggestionsRequest(url, access_token);
 }
 
 void SuggestionsServiceImpl::IssueSuggestionsRequest(
     const GURL& url,
     const std::string& access_token) {
-  if (access_token.empty()) {
-    UpdateBlacklistDelay(false);
-    ScheduleBlacklistUpload();
-    return;
-  }
+  DCHECK(!access_token.empty());
   pending_request_ = CreateSuggestionsRequest(url, access_token);
   pending_request_->Start();
   last_request_started_time_ = TimeTicks::Now();
diff --git a/components/suggestions/suggestions_service_impl.h b/components/suggestions/suggestions_service_impl.h
index fe304719..740bb8f 100644
--- a/components/suggestions/suggestions_service_impl.h
+++ b/components/suggestions/suggestions_service_impl.h
@@ -19,9 +19,11 @@
 #include "base/scoped_observer.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
+#include "components/signin/core/browser/access_token_fetcher.h"
 #include "components/suggestions/proto/suggestions.pb.h"
 #include "components/suggestions/suggestions_service.h"
 #include "components/sync/driver/sync_service_observer.h"
+#include "google_apis/gaia/google_service_auth_error.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "url/gurl.h"
 
@@ -51,7 +53,7 @@
                                public net::URLFetcherDelegate,
                                public syncer::SyncServiceObserver {
  public:
-  SuggestionsServiceImpl(const SigninManagerBase* signin_manager,
+  SuggestionsServiceImpl(SigninManagerBase* signin_manager,
                          OAuth2TokenService* token_service,
                          syncer::SyncService* sync_service,
                          net::URLRequestContextGetter* url_request_context,
@@ -119,9 +121,13 @@
   // Issues a network request if there isn't already one happening.
   void IssueRequestIfNoneOngoing(const GURL& url);
 
+  // Called when an access token request completes (successfully or not).
+  void AccessTokenAvailable(const GURL& url,
+                            const GoogleServiceAuthError& error,
+                            const std::string& access_token);
+
   // Issues a network request for suggestions (fetch, blacklist, or clear
-  // blacklist, depending on |url|). |access_token| is used only if OAuth2
-  // authentication is enabled.
+  // blacklist, depending on |url|).
   void IssueSuggestionsRequest(const GURL& url,
                                const std::string& access_token);
 
@@ -159,6 +165,9 @@
 
   base::ThreadChecker thread_checker_;
 
+  SigninManagerBase* signin_manager_;
+  OAuth2TokenService* token_service_;
+
   syncer::SyncService* sync_service_;
   ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
       sync_service_observer_;
@@ -177,8 +186,8 @@
   // Delay used when scheduling a blacklisting task.
   base::TimeDelta scheduling_delay_;
 
-  // Helper for fetching OAuth2 access tokens.
-  class AccessTokenFetcher;
+  // Helper for fetching OAuth2 access tokens. This is non-null iff an access
+  // token request is currently in progress.
   std::unique_ptr<AccessTokenFetcher> token_fetcher_;
 
   // Contains the current suggestions fetch request. Will only have a value
diff --git a/components/suggestions/suggestions_service_impl_unittest.cc b/components/suggestions/suggestions_service_impl_unittest.cc
index 538ff3d..bc3aafc 100644
--- a/components/suggestions/suggestions_service_impl_unittest.cc
+++ b/components/suggestions/suggestions_service_impl_unittest.cc
@@ -14,13 +14,17 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
+#include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/fake_signin_manager.h"
+#include "components/signin/core/browser/test_signin_client.h"
 #include "components/suggestions/blacklist_store.h"
 #include "components/suggestions/image_manager.h"
 #include "components/suggestions/proto/suggestions.pb.h"
 #include "components/suggestions/suggestions_store.h"
 #include "components/sync/driver/fake_sync_service.h"
 #include "components/sync/driver/sync_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "net/base/escape.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
@@ -31,20 +35,19 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/image/image.h"
 
+using sync_preferences::TestingPrefServiceSyncable;
+using testing::_;
+using testing::AnyNumber;
 using testing::DoAll;
-using ::testing::AnyNumber;
-using ::testing::Eq;
-using ::testing::Return;
+using testing::Eq;
+using testing::NiceMock;
+using testing::Return;
 using testing::SetArgPointee;
-using ::testing::NiceMock;
-using ::testing::StrictMock;
-using ::testing::_;
+using testing::StrictMock;
 
 namespace {
 
-// SuggestionsService::AccessTokenFetcher provides an empty account ID if its
-// SigninManager is null.
-const char kAccountId[] = "";
+const char kAccountId[] = "account";
 const char kTestTitle[] = "a title";
 const char kTestUrl[] = "http://go.com";
 const char kTestFaviconUrl[] =
@@ -195,11 +198,17 @@
         suggestions_empty_data_count_(0),
         blacklisting_failed_(false),
         undo_blacklisting_failed_(false),
+        signin_client_(&pref_service_),
+        signin_manager_(&signin_client_, &account_tracker_),
         factory_(nullptr, base::Bind(&CreateURLFetcher)),
         mock_sync_service_(nullptr),
         mock_thumbnail_manager_(nullptr),
         mock_blacklist_store_(nullptr),
         test_suggestions_store_(nullptr) {
+    SigninManagerBase::RegisterProfilePrefs(pref_service_.registry());
+    SigninManagerBase::RegisterPrefs(pref_service_.registry());
+
+    signin_manager_.SignIn(kAccountId);
     token_service_.UpdateCredentials(kAccountId, "refresh_token");
     token_service_.set_auto_post_fetch_response_on_message_loop(true);
   }
@@ -213,12 +222,18 @@
 
   std::unique_ptr<SuggestionsServiceImpl> CreateSuggestionsServiceWithMocks() {
     mock_sync_service_.reset(new MockSyncService);
-    ON_CALL(*mock_sync_service_, CanSyncStart()).WillByDefault(Return(true));
-    ON_CALL(*mock_sync_service_, IsSyncActive()).WillByDefault(Return(true));
-    ON_CALL(*mock_sync_service_, ConfigurationDone())
-        .WillByDefault(Return(true));
-    ON_CALL(*mock_sync_service_, GetActiveDataTypes())
-        .WillByDefault(
+    EXPECT_CALL(*mock_sync_service_, CanSyncStart())
+        .Times(AnyNumber())
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_sync_service_, IsSyncActive())
+        .Times(AnyNumber())
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_sync_service_, ConfigurationDone())
+        .Times(AnyNumber())
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_sync_service_, GetActiveDataTypes())
+        .Times(AnyNumber())
+        .WillRepeatedly(
             Return(syncer::ModelTypeSet(syncer::HISTORY_DELETE_DIRECTIVES)));
 
     // These objects are owned by the returned SuggestionsService, but we keep
@@ -227,7 +242,7 @@
     mock_thumbnail_manager_ = new StrictMock<MockImageManager>();
     mock_blacklist_store_ = new StrictMock<MockBlacklistStore>();
     return base::MakeUnique<SuggestionsServiceImpl>(
-        nullptr /* signin_manager */, &token_service_, mock_sync_service_.get(),
+        &signin_manager_, &token_service_, mock_sync_service_.get(),
         request_context_.get(), base::WrapUnique(test_suggestions_store_),
         base::WrapUnique(mock_thumbnail_manager_),
         base::WrapUnique(mock_blacklist_store_));
@@ -295,6 +310,10 @@
 
  protected:
   base::MessageLoopForIO io_message_loop_;
+  TestingPrefServiceSyncable pref_service_;
+  AccountTrackerService account_tracker_;
+  TestSigninClient signin_client_;
+  FakeSigninManagerBase signin_manager_;
   net::FakeURLFetcherFactory factory_;
   FakeProfileOAuth2TokenService token_service_;
   std::unique_ptr<MockSyncService> mock_sync_service_;
@@ -397,7 +416,7 @@
 }
 
 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoAccessToken) {
-  token_service_.RevokeCredentials(kAccountId);
+  token_service_.set_auto_post_fetch_response_on_message_loop(false);
 
   std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
       CreateSuggestionsServiceWithMocks());
@@ -411,6 +430,9 @@
 
   suggestions_service->FetchSuggestionsData();
 
+  token_service_.IssueErrorForAllPendingRequests(GoogleServiceAuthError(
+      GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS));
+
   // No network request should be sent.
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(HasPendingSuggestionsRequest(suggestions_service.get()));
diff --git a/components/tracing/test/trace_event_perftest.cc b/components/tracing/test/trace_event_perftest.cc
index f893cce..721c3aa 100644
--- a/components/tracing/test/trace_event_perftest.cc
+++ b/components/tracing/test/trace_event_perftest.cc
@@ -5,6 +5,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/pending_task.h"
 #include "base/run_loop.h"
 #include "base/threading/thread.h"
 #include "base/trace_event/trace_event.h"
@@ -16,6 +17,8 @@
 namespace {
 
 using base::Bind;
+using base::Closure;
+using base::RunLoop;
 using base::Thread;
 using base::Unretained;
 using base::WaitableEvent;
@@ -44,7 +47,7 @@
   }
 
   static void OnTraceDataCollected(
-      base::Closure quit_closure,
+      Closure quit_closure,
       const scoped_refptr<base::RefCountedString>& events_str,
       bool has_more_events) {
 
@@ -77,6 +80,12 @@
     complete_event->Signal();
   }
 
+  static void SubmitTraceEvents(int count) {
+    for (int i = 0; i < count; i++) {
+      TRACE_EVENT0("test_category", "some call");
+    }
+  }
+
  private:
   base::MessageLoop _message_loop;
 };
@@ -85,9 +94,7 @@
   BeginTrace();
   IterableStopwatch stopwatch("events");
   for (int lap = 0; lap < kNumRuns; lap++) {
-    for (int i = 0; i < 10000; i++) {
-      TRACE_EVENT0("test_category", "TRACE_EVENT0 call");
-    }
+    SubmitTraceEvents(10000);
     stopwatch.NextLap();
   }
   EndTraceAndFlush();
@@ -98,9 +105,7 @@
   IterableStopwatch stopwatch("long_event");
   for (int lap = 0; lap < kNumRuns; lap++) {
     TRACE_EVENT0("test_category", "Outer event");
-    for (int i = 0; i < 10000; i++) {
-      TRACE_EVENT0("test_category", "TRACE_EVENT0 call");
-    }
+    SubmitTraceEvents(10000);
     stopwatch.NextLap();
   }
   EndTraceAndFlush();
@@ -165,5 +170,22 @@
   }
 }
 
+TEST_F(TraceEventPerfTest, Submit_10000_TRACE_EVENT0_in_traceable_tasks) {
+  BeginTrace();
+  IterableStopwatch task_sw("events_in_task");
+  for (int i = 0; i < 100; i++) {
+    base::PendingTask pending_task(FROM_HERE, Bind(&SubmitTraceEvents, 10000));
+    TRACE_TASK_EXECUTION("TraceEventPerfTest::PendingTask", pending_task);
+    std::move(pending_task.task).Run();
+    task_sw.NextLap();
+  }
+  EndTraceAndFlush();
+}
+
+TEST_F(TraceEventPerfTest, Submit_10000_TRACE_EVENT0_with_tracing_disabled) {
+  ScopedStopwatch stopwatch("events");
+  SubmitTraceEvents(10000);
+}
+
 }  // namespace
 }  // namespace tracing
diff --git a/components/translate/ios/browser/language_detection_controller.mm b/components/translate/ios/browser/language_detection_controller.mm
index 04f387c..4292261 100644
--- a/components/translate/ios/browser/language_detection_controller.mm
+++ b/components/translate/ios/browser/language_detection_controller.mm
@@ -8,7 +8,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
 #include "components/prefs/pref_member.h"
 #include "components/translate/core/common/translate_pref_names.h"
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 69a67af..665a572 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1035,6 +1035,8 @@
     "renderer_host/input/input_router_config_helper.h",
     "renderer_host/input/input_router_impl.cc",
     "renderer_host/input/input_router_impl.h",
+    "renderer_host/input/legacy_touch_event_queue.cc",
+    "renderer_host/input/legacy_touch_event_queue.h",
     "renderer_host/input/motion_event_web.cc",
     "renderer_host/input/motion_event_web.h",
     "renderer_host/input/mouse_wheel_event_queue.cc",
@@ -1090,7 +1092,6 @@
     "renderer_host/input/touch_emulator.cc",
     "renderer_host/input/touch_emulator.h",
     "renderer_host/input/touch_emulator_client.h",
-    "renderer_host/input/touch_event_queue.cc",
     "renderer_host/input/touch_event_queue.h",
     "renderer_host/input/touch_selection_controller_client_aura.cc",
     "renderer_host/input/touch_selection_controller_client_aura.h",
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 8a0995a4..0fdb7636 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -690,6 +690,11 @@
 }
 
 cc::FrameSinkId GpuProcessTransportFactory::AllocateFrameSinkId() {
+  // The FrameSinkId generated here must be unique with
+  // RenderWidgetHostViewAura's
+  // and RenderWidgetHostViewMac's FrameSinkId allocation.
+  // TODO(crbug.com/685777): Centralize allocation in one place for easier
+  // maintenance.
   return cc::FrameSinkId(0, next_sink_id_++);
 }
 
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 56e37ac..0136097 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -230,10 +230,6 @@
   net::URLRequestContext* request_context =
       GetRequestContextOnIO(resource_context, context_getter, url);
 
-  bool are_experimental_cookie_features_enabled =
-      request_context->network_delegate()
-        ->AreExperimentalCookieFeaturesEnabled();
-
   request_context->cookie_store()->SetCookieWithDetailsAsync(
       url, name, value, domain, path,
       base::Time(),
@@ -242,7 +238,6 @@
       secure,
       http_only,
       same_site,
-      are_experimental_cookie_features_enabled,
       net::COOKIE_PRIORITY_DEFAULT,
       base::Bind(&CookieSetOnIO, base::Passed(std::move(callback))));
 }
diff --git a/content/browser/find_request_manager_browsertest.cc b/content/browser/find_request_manager_browsertest.cc
index 68027ab..3e34167e9 100644
--- a/content/browser/find_request_manager_browsertest.cc
+++ b/content/browser/find_request_manager_browsertest.cc
@@ -10,6 +10,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/find_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
@@ -23,214 +24,8 @@
 
 const int kInvalidId = -1;
 
-// The results of a find request.
-struct FindResults {
-  FindResults(int request_id, int number_of_matches, int active_match_ordinal)
-      : request_id(request_id),
-        number_of_matches(number_of_matches),
-        active_match_ordinal(active_match_ordinal) {}
-  FindResults() : FindResults(kInvalidId, 0, 0) {}
-
-  int request_id;
-  int number_of_matches;
-  int active_match_ordinal;
-};
-
 }  // namespace
 
-class TestWebContentsDelegate : public WebContentsDelegate {
- public:
-  TestWebContentsDelegate()
-      : last_request_id_(kInvalidId),
-        last_finished_request_id_(kInvalidId),
-        next_reply_received_(false),
-        record_replies_(false),
-        waiting_for_(NOTHING) {}
-  ~TestWebContentsDelegate() override {}
-
-  // Returns the current find results.
-  const FindResults& GetFindResults() const {
-    return current_results_;
-  }
-
-  // Waits for all pending replies to be received.
-  void WaitForFinalReply() {
-    if (last_finished_request_id_ >= last_request_id_)
-      return;
-
-    WaitFor(FINAL_REPLY);
-  }
-
-  // Waits for the next find reply. This is useful for waiting for a single
-  // match to be activated, or for a new frame to be searched.
-  void WaitForNextReply() {
-    if (next_reply_received_)
-      return;
-
-    WaitFor(NEXT_REPLY);
-  }
-
-  // Indicates that the next find reply from this point will be the one to wait
-  // for when WaitForNextReply() is called. It may be the case that the reply
-  // comes before the call to WaitForNextReply(), in which case it will return
-  // immediately.
-  void MarkNextReply() {
-    next_reply_received_ = false;
-  }
-
-  // Called when a new find request is issued, so the delegate knows the last
-  // request ID.
-  void UpdateLastRequest(int request_id) {
-    last_request_id_ = request_id;
-  }
-
-  // From when this function is called, all replies coming in via FindReply()
-  // will be recorded. These replies can be retrieved via GetReplyRecord().
-  void StartReplyRecord() {
-    reply_record_.clear();
-    record_replies_ = true;
-  }
-
-  // Retreives the results from the find replies recorded since the last call to
-  // StartReplyRecord(). Calling this function also stops the recording new find
-  // replies.
-  const std::vector<FindResults>& GetReplyRecord() {
-    record_replies_ = false;
-    return reply_record_;
-  }
-
-#if defined(OS_ANDROID)
-  // Waits for all of the find match rects to be received.
-  void WaitForMatchRects() {
-    WaitFor(MATCH_RECTS);
-  }
-
-  const std::vector<gfx::RectF>& find_match_rects() const {
-    return find_match_rects_;
-  }
-
-  const gfx::RectF& active_match_rect() const {
-    return active_match_rect_;
-  }
-#endif
-
- private:
-  enum WaitingFor {
-    NOTHING,
-    FINAL_REPLY,
-    NEXT_REPLY,
-#if defined(OS_ANDROID)
-    MATCH_RECTS
-#endif
-  };
-
-  // WebContentsDelegate override.
-  void FindReply(WebContents* web_contents,
-                 int request_id,
-                 int number_of_matches,
-                 const gfx::Rect& selection_rect,
-                 int active_match_ordinal,
-                 bool final_update) override {
-    if (record_replies_) {
-      reply_record_.emplace_back(
-          request_id, number_of_matches, active_match_ordinal);
-    }
-
-    // Update the current results.
-    if (request_id > current_results_.request_id)
-      current_results_.request_id = request_id;
-    if (number_of_matches != -1)
-      current_results_.number_of_matches = number_of_matches;
-    if (active_match_ordinal != -1)
-      current_results_.active_match_ordinal = active_match_ordinal;
-
-    if (!final_update)
-      return;
-
-    if (request_id > last_finished_request_id_)
-      last_finished_request_id_ = request_id;
-    next_reply_received_ = true;
-
-    // If we are waiting for this find reply, stop waiting.
-    if (waiting_for_ == NEXT_REPLY ||
-        (waiting_for_ == FINAL_REPLY &&
-         last_finished_request_id_ >= last_request_id_)) {
-      StopWaiting();
-    }
-  }
-
-  // Uses |message_loop_runner_| to wait for various things.
-  void WaitFor(WaitingFor wait_for) {
-    ASSERT_EQ(NOTHING, waiting_for_);
-    ASSERT_NE(NOTHING, wait_for);
-
-    // Wait for |wait_for|.
-    waiting_for_ = wait_for;
-    message_loop_runner_ = new content::MessageLoopRunner;
-    message_loop_runner_->Run();
-
-    // Done waiting.
-    waiting_for_ = NOTHING;
-    message_loop_runner_ = nullptr;
-  }
-
-  // Stop waiting for |waiting_for_|.
-  void StopWaiting() {
-    if (!message_loop_runner_.get())
-      return;
-
-    ASSERT_NE(NOTHING, waiting_for_);
-    message_loop_runner_->Quit();
-  }
-
-#if defined(OS_ANDROID)
-  // WebContentsDelegate override.
-  void FindMatchRectsReply(WebContents* web_contents,
-                           int version,
-                           const std::vector<gfx::RectF>& rects,
-                           const gfx::RectF& active_rect) override {
-    // Update the current rects.
-    find_match_rects_ = rects;
-    active_match_rect_ = active_rect;
-
-    // If we are waiting for match rects, stop waiting.
-    if (waiting_for_ == MATCH_RECTS)
-      StopWaiting();
-  }
-
-  std::vector<gfx::RectF> find_match_rects_;
-
-  gfx::RectF active_match_rect_;
-#endif
-
-  // The latest known results from the current find request.
-  FindResults current_results_;
-
-  // The ID of the last find request issued.
-  int last_request_id_;
-
-  // The ID of the last find request to finish (all replies received).
-  int last_finished_request_id_;
-
-  // Indicates whether the next reply after MarkNextReply() has been received.
-  bool next_reply_received_;
-
-  // Indicates whether the find results from incoming find replies are currently
-  // being recorded.
-  bool record_replies_;
-
-  // A record of all find replies that have come in via FindReply() since
-  // StartReplyRecor() was last called.
-  std::vector<FindResults> reply_record_;
-
-  // Indicates what |message_loop_runner_| is waiting for, if anything.
-  WaitingFor waiting_for_;
-
-  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestWebContentsDelegate);
-};
-
 class FindRequestManagerTest : public ContentBrowserTest,
                                public testing::WithParamInterface<bool> {
  public:
@@ -264,7 +59,7 @@
   void LoadAndWait(const std::string& url) {
     TestNavigationObserver navigation_observer(contents());
     NavigateToURL(shell(), embedded_test_server()->GetURL("a.com", url));
-    EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
+    ASSERT_TRUE(navigation_observer.last_navigation_succeeded());
   }
 
   // Loads a multi-frame page. The page will have a full binary frame tree of
@@ -301,8 +96,8 @@
     return static_cast<WebContentsImpl*>(shell()->web_contents());
   }
 
-  TestWebContentsDelegate* delegate() const {
-    return static_cast<TestWebContentsDelegate*>(contents()->GetDelegate());
+  FindTestWebContentsDelegate* delegate() const {
+    return static_cast<FindTestWebContentsDelegate*>(contents()->GetDelegate());
   }
 
   int last_request_id() const {
@@ -337,7 +132,7 @@
     LoadMultiFramePageChildFrames(height - 1, cross_process, child);
   }
 
-  TestWebContentsDelegate test_delegate_;
+  FindTestWebContentsDelegate test_delegate_;
   WebContentsDelegate* normal_delegate_;
 
   // The ID of the last find request requested.
diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc
index 93a1da4d..0dd8d8b 100644
--- a/content/browser/frame_host/render_frame_message_filter.cc
+++ b/content/browser/frame_host/render_frame_message_filter.cc
@@ -6,7 +6,6 @@
 
 #include "base/command_line.h"
 #include "base/macros.h"
-#include "base/metrics/field_trial.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "content/browser/bad_message.h"
@@ -26,7 +25,6 @@
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/download_url_parameters.h"
 #include "content/public/common/content_constants.h"
-#include "content/public/common/content_switches.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/cookies/cookie_options.h"
@@ -57,8 +55,6 @@
 const int kPluginsRefreshThresholdInSeconds = 3;
 #endif
 
-const char kEnforceStrictSecureExperiment[] = "StrictSecureCookies";
-
 void CreateChildFrameOnUI(int process_id,
                           int parent_routing_id,
                           blink::WebTreeScopeType scope,
@@ -396,16 +392,6 @@
   }
 
   net::CookieOptions options;
-  bool experimental_web_platform_features_enabled =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableExperimentalWebPlatformFeatures);
-  const std::string enforce_strict_secure_group =
-      base::FieldTrialList::FindFullName(kEnforceStrictSecureExperiment);
-  if (experimental_web_platform_features_enabled ||
-      base::StartsWith(enforce_strict_secure_group, "Enabled",
-                       base::CompareCase::INSENSITIVE_ASCII)) {
-    options.set_enforce_strict_secure();
-  }
   if (GetContentClient()->browser()->AllowSetCookie(
           url, first_party_for_cookies, cookie, resource_context_,
           render_process_id_, render_frame_id, options)) {
diff --git a/content/browser/net/quota_policy_cookie_store_unittest.cc b/content/browser/net/quota_policy_cookie_store_unittest.cc
index 707f856..7215351 100644
--- a/content/browser/net/quota_policy_cookie_store_unittest.cc
+++ b/content/browser/net/quota_policy_cookie_store_unittest.cc
@@ -99,8 +99,7 @@
                  const base::Time& creation) {
     store_->AddCookie(*net::CanonicalCookie::Create(
         url, name, value, domain, path, creation, creation, false, false,
-        net::CookieSameSite::DEFAULT_MODE, false,
-        net::COOKIE_PRIORITY_DEFAULT));
+        net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT));
   }
 
   void DestroyStore() {
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.h b/content/browser/renderer_host/browser_compositor_view_mac.h
index 865b29f..51589ba 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.h
+++ b/content/browser/renderer_host/browser_compositor_view_mac.h
@@ -50,7 +50,8 @@
       ui::AcceleratedWidgetMacNSView* accelerated_widget_mac_ns_view,
       BrowserCompositorMacClient* client,
       bool render_widget_host_is_hidden,
-      bool ns_view_attached_to_window);
+      bool ns_view_attached_to_window,
+      const cc::FrameSinkId& frame_sink_id);
   ~BrowserCompositorMac() override;
 
   // These will not return nullptr until Destroy is called.
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.mm b/content/browser/renderer_host/browser_compositor_view_mac.mm
index bd36336..3e3776b 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.mm
+++ b/content/browser/renderer_host/browser_compositor_view_mac.mm
@@ -168,16 +168,15 @@
     ui::AcceleratedWidgetMacNSView* accelerated_widget_mac_ns_view,
     BrowserCompositorMacClient* client,
     bool render_widget_host_is_hidden,
-    bool ns_view_attached_to_window)
+    bool ns_view_attached_to_window,
+    const cc::FrameSinkId& frame_sink_id)
     : client_(client),
       accelerated_widget_mac_ns_view_(accelerated_widget_mac_ns_view),
       weak_factory_(this) {
   g_browser_compositor_count += 1;
 
   root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
-  ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-  delegated_frame_host_.reset(new DelegatedFrameHost(
-      factory->GetContextFactoryPrivate()->AllocateFrameSinkId(), this));
+  delegated_frame_host_.reset(new DelegatedFrameHost(frame_sink_id, this));
 
   SetRenderWidgetHostIsHidden(render_widget_host_is_hidden);
   SetNSViewAttachedToWindow(ns_view_attached_to_window);
diff --git a/content/browser/renderer_host/context_provider_factory_impl_android.cc b/content/browser/renderer_host/context_provider_factory_impl_android.cc
index 8aa6a18..2437ca8 100644
--- a/content/browser/renderer_host/context_provider_factory_impl_android.cc
+++ b/content/browser/renderer_host/context_provider_factory_impl_android.cc
@@ -66,7 +66,7 @@
     : gpu_channel_factory_(gpu_channel_factory),
       in_handle_pending_requests_(false),
       in_shutdown_(false),
-      next_client_id_(1u),
+      next_sink_id_(1u),
       weak_factory_(this) {
   DCHECK(gpu_channel_factory_);
 }
@@ -136,7 +136,12 @@
 }
 
 cc::FrameSinkId ContextProviderFactoryImpl::AllocateFrameSinkId() {
-  return cc::FrameSinkId(++next_client_id_, 0 /* sink_id */);
+  // The FrameSinkId generated here must be unique with
+  // RenderWidgetHostViewAndroid's
+  // FrameSinkId allocation.
+  // TODO(crbug.com/685777): Centralize allocation in one place for easier
+  // maintenance.
+  return cc::FrameSinkId(0 /* client_id */, next_sink_id_++);
 }
 
 gpu::GpuMemoryBufferManager*
diff --git a/content/browser/renderer_host/context_provider_factory_impl_android.h b/content/browser/renderer_host/context_provider_factory_impl_android.h
index cd37db92..79a529b 100644
--- a/content/browser/renderer_host/context_provider_factory_impl_android.h
+++ b/content/browser/renderer_host/context_provider_factory_impl_android.h
@@ -104,7 +104,7 @@
   base::OneShotTimer establish_gpu_channel_timeout_;
 
   std::unique_ptr<cc::SurfaceManager> surface_manager_;
-  uint32_t next_client_id_;
+  uint32_t next_sink_id_;
 
   base::WeakPtrFactory<ContextProviderFactoryImpl> weak_factory_;
 
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index a823b63..1dc0f2c 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -16,6 +16,7 @@
 #include "content/browser/renderer_host/input/gesture_event_queue.h"
 #include "content/browser/renderer_host/input/input_ack_handler.h"
 #include "content/browser/renderer_host/input/input_router_client.h"
+#include "content/browser/renderer_host/input/legacy_touch_event_queue.h"
 #include "content/browser/renderer_host/input/touch_event_queue.h"
 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
 #include "content/common/content_constants_internal.h"
@@ -90,7 +91,7 @@
       wheel_event_queue_(this,
                          base::FeatureList::IsEnabled(
                              features::kTouchpadAndWheelScrollLatching)),
-      touch_event_queue_(this, config.touch_config),
+      touch_event_queue_(new LegacyTouchEventQueue(this, config.touch_config)),
       gesture_event_queue_(this, this, config.gesture_config),
       device_scale_factor_(1.f) {
   DCHECK(sender);
@@ -174,10 +175,10 @@
       // GestureScrollUpdate event.  Eg. if the page consumes all touchmoves
       // then no scrolling really ever occurs (even though we still send
       // GestureScrollBegin).
-      touch_event_queue_.PrependTouchScrollNotification();
+      touch_event_queue_->PrependTouchScrollNotification();
       touch_scroll_started_sent_ = true;
     }
-    touch_event_queue_.OnGestureScrollEvent(gesture_event);
+    touch_event_queue_->OnGestureScrollEvent(gesture_event);
   }
 
   gesture_event_queue_.QueueEvent(gesture_event);
@@ -186,7 +187,7 @@
 void InputRouterImpl::SendTouchEvent(
     const TouchEventWithLatencyInfo& touch_event) {
   input_stream_validator_.Validate(touch_event.event);
-  touch_event_queue_.QueueEvent(touch_event);
+  touch_event_queue_->QueueEvent(touch_event);
 }
 
 // Forwards MouseEvent without passing it through
@@ -225,7 +226,7 @@
 }
 
 void InputRouterImpl::NotifySiteIsMobileOptimized(bool is_mobile_optimized) {
-  touch_event_queue_.SetIsMobileOptimizedSite(is_mobile_optimized);
+  touch_event_queue_->SetIsMobileOptimizedSite(is_mobile_optimized);
 }
 
 void InputRouterImpl::RequestNotificationWhenFlushed() {
@@ -234,7 +235,7 @@
 }
 
 bool InputRouterImpl::HasPendingEvents() const {
-  return !touch_event_queue_.empty() || !gesture_event_queue_.empty() ||
+  return !touch_event_queue_->Empty() || !gesture_event_queue_.empty() ||
          !key_queue_.empty() || !mouse_move_queue_.empty() ||
          wheel_event_queue_.has_pending() || select_message_pending_ ||
          move_caret_pending_ || active_renderer_fling_count_ > 0;
@@ -291,7 +292,7 @@
 void InputRouterImpl::OnGestureEventAck(
     const GestureEventWithLatencyInfo& event,
     InputEventAckState ack_result) {
-  touch_event_queue_.OnGestureEventAck(event, ack_result);
+  touch_event_queue_->OnGestureEventAck(event, ack_result);
   ack_handler_->OnGestureEventAck(event, ack_result);
 }
 
@@ -486,13 +487,13 @@
   if (!has_handlers)
     touch_action_filter_.ResetTouchAction();
 
-  touch_event_queue_.OnHasTouchEventHandlers(has_handlers);
+  touch_event_queue_->OnHasTouchEventHandlers(has_handlers);
   client_->OnHasTouchEventHandlers(has_handlers);
 }
 
 void InputRouterImpl::OnSetTouchAction(TouchAction touch_action) {
   // Synthetic touchstart events should get filtered out in RenderWidget.
-  DCHECK(touch_event_queue_.IsPendingAckTouchStart());
+  DCHECK(touch_event_queue_->IsPendingAckTouchStart());
   TRACE_EVENT1("input", "InputRouterImpl::OnSetTouchAction",
                "action", touch_action);
 
@@ -609,8 +610,8 @@
                                       const ui::LatencyInfo& latency,
                                       uint32_t unique_touch_event_id) {
   // |touch_event_queue_| will forward to OnTouchEventAck when appropriate.
-  touch_event_queue_.ProcessTouchAck(ack_result, latency,
-                                     unique_touch_event_id);
+  touch_event_queue_->ProcessTouchAck(ack_result, latency,
+                                      unique_touch_event_id);
 }
 
 void InputRouterImpl::UpdateTouchAckTimeoutEnabled() {
@@ -619,7 +620,7 @@
   // to page functionality, so the timeout could do more harm than good.
   const bool touch_ack_timeout_enabled =
       touch_action_filter_.allowed_touch_action() != TOUCH_ACTION_NONE;
-  touch_event_queue_.SetAckTimeoutEnabled(touch_ack_timeout_enabled);
+  touch_event_queue_->SetAckTimeoutEnabled(touch_ack_timeout_enabled);
 }
 
 void InputRouterImpl::SignalFlushedIfNecessary() {
diff --git a/content/browser/renderer_host/input/input_router_impl.h b/content/browser/renderer_host/input/input_router_impl.h
index b55d09d..4634b2bc 100644
--- a/content/browser/renderer_host/input/input_router_impl.h
+++ b/content/browser/renderer_host/input/input_router_impl.h
@@ -252,7 +252,7 @@
   bool touch_scroll_started_sent_;
 
   MouseWheelEventQueue wheel_event_queue_;
-  TouchEventQueue touch_event_queue_;
+  std::unique_ptr<TouchEventQueue> touch_event_queue_;
   GestureEventQueue gesture_event_queue_;
   TouchActionFilter touch_action_filter_;
   InputEventStreamValidator input_stream_validator_;
diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc
index ecb3143a..d7fc65b2 100644
--- a/content/browser/renderer_host/input/input_router_impl_unittest.cc
+++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -304,11 +304,11 @@
   }
 
   bool TouchEventQueueEmpty() const {
-    return input_router()->touch_event_queue_.empty();
+    return input_router()->touch_event_queue_->Empty();
   }
 
   bool TouchEventTimeoutEnabled() const {
-    return input_router()->touch_event_queue_.IsAckTimeoutEnabled();
+    return input_router()->touch_event_queue_->IsAckTimeoutEnabled();
   }
 
   void RequestNotificationWhenFlushed() const {
diff --git a/content/browser/renderer_host/input/touch_event_queue.cc b/content/browser/renderer_host/input/legacy_touch_event_queue.cc
similarity index 89%
rename from content/browser/renderer_host/input/touch_event_queue.cc
rename to content/browser/renderer_host/input/legacy_touch_event_queue.cc
index 5b594ad..9c3a1e5 100644
--- a/content/browser/renderer_host/input/touch_event_queue.cc
+++ b/content/browser/renderer_host/input/legacy_touch_event_queue.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 "content/browser/renderer_host/input/touch_event_queue.h"
+#include "content/browser/renderer_host/input/legacy_touch_event_queue.h"
 
 #include <utility>
 
@@ -50,15 +50,14 @@
 
 // Compare all properties of touch points to determine the state.
 bool HasPointChanged(const WebTouchPoint& point_1,
-    const WebTouchPoint& point_2) {
+                     const WebTouchPoint& point_2) {
   DCHECK_EQ(point_1.id, point_2.id);
   if (point_1.screenPosition != point_2.screenPosition ||
       point_1.position != point_2.position ||
       point_1.radiusX != point_2.radiusX ||
       point_1.radiusY != point_2.radiusY ||
       point_1.rotationAngle != point_2.rotationAngle ||
-      point_1.force != point_2.force ||
-      point_1.tiltX != point_2.tiltX ||
+      point_1.force != point_2.force || point_1.tiltX != point_2.tiltX ||
       point_1.tiltY != point_2.tiltY) {
     return true;
   }
@@ -67,12 +66,11 @@
 
 }  // namespace
 
-
 // Cancels a touch sequence if a touchstart or touchmove ack response is
 // sufficiently delayed.
-class TouchEventQueue::TouchTimeoutHandler {
+class LegacyTouchEventQueue::TouchTimeoutHandler {
  public:
-  TouchTimeoutHandler(TouchEventQueue* touch_queue,
+  TouchTimeoutHandler(LegacyTouchEventQueue* touch_queue,
                       base::TimeDelta desktop_timeout_delay,
                       base::TimeDelta mobile_timeout_delay)
       : touch_queue_(touch_queue),
@@ -89,9 +87,7 @@
     SetUseMobileTimeout(false);
   }
 
-  ~TouchTimeoutHandler() {
-    LogSequenceEndForUMAIfNecessary(false);
-  }
+  ~TouchTimeoutHandler() { LogSequenceEndForUMAIfNecessary(false); }
 
   void StartIfNecessary(const TouchEventWithLatencyInfo& event) {
     if (pending_ack_state_ != PENDING_ACK_NONE)
@@ -184,9 +180,7 @@
 
   bool IsTimeoutTimerRunning() const { return timeout_monitor_.IsRunning(); }
 
-  bool IsEnabled() const {
-    return enabled_ && !GetTimeoutDelay().is_zero();
-  }
+  bool IsEnabled() const { return enabled_ && !GetTimeoutDelay().is_zero(); }
 
  private:
   enum PendingAckState {
@@ -221,8 +215,8 @@
         DCHECK_EQ(pending_ack_state_, PENDING_ACK_ORIGINAL_EVENT);
         DCHECK(!timeout_monitor_.IsRunning());
         DCHECK(touch_queue_->empty());
-        TRACE_EVENT_ASYNC_STEP_INTO0(
-            "input", "TouchEventTimeout", this, "CancelEvent");
+        TRACE_EVENT_ASYNC_STEP_INTO0("input", "TouchEventTimeout", this,
+                                     "CancelEvent");
         break;
       case PENDING_ACK_NONE:
         DCHECK(!timeout_monitor_.IsRunning());
@@ -261,7 +255,7 @@
     return pending_ack_state_ != PENDING_ACK_NONE;
   }
 
-  TouchEventQueue* touch_queue_;
+  LegacyTouchEventQueue* touch_queue_;
 
   // How long to wait on a touch ack before cancelling the touch sequence.
   const base::TimeDelta desktop_timeout_delay_;
@@ -287,7 +281,7 @@
 
 // Provides touchmove slop suppression for a touch sequence until a
 // (unprevented) touch will trigger immediate scrolling.
-class TouchEventQueue::TouchMoveSlopSuppressor {
+class LegacyTouchEventQueue::TouchMoveSlopSuppressor {
  public:
   TouchMoveSlopSuppressor() : suppressing_touchmoves_(false) {}
 
@@ -311,9 +305,10 @@
         suppressing_touchmoves_ = false;
       } else {
         // No sane slop region should be larger than 60 DIPs.
-        DCHECK_LT((gfx::PointF(event.touches[0].position) -
-                   touch_start_location_).LengthSquared(),
-                  kMaxConceivablePlatformSlopRegionLengthDipsSquared);
+        DCHECK_LT(
+            (gfx::PointF(event.touches[0].position) - touch_start_location_)
+                .LengthSquared(),
+            kMaxConceivablePlatformSlopRegionLengthDipsSquared);
       }
     }
 
@@ -347,11 +342,12 @@
   CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event,
                          bool suppress_client_ack)
       : coalesced_event_(event), suppress_client_ack_(suppress_client_ack) {
-    TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this);
+    TRACE_EVENT_ASYNC_BEGIN0("input", "LegacyTouchEventQueue::QueueEvent",
+                             this);
   }
 
   ~CoalescedWebTouchEvent() {
-    TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this);
+    TRACE_EVENT_ASYNC_END0("input", "LegacyTouchEventQueue::QueueEvent", this);
   }
 
   // Coalesces the event with the existing event if possible. Returns whether
@@ -370,8 +366,8 @@
     if (uncoaleseced_events_to_ack_.empty())
       uncoaleseced_events_to_ack_.push_back(coalesced_event_);
 
-    TRACE_EVENT_INSTANT0(
-        "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_INSTANT0("input", "LegacyTouchEventQueue::MoveCoalesced",
+                         TRACE_EVENT_SCOPE_THREAD);
     coalesced_event_.CoalesceWith(event_with_latency);
     uncoaleseced_events_to_ack_.push_back(event_with_latency);
     DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
@@ -396,8 +392,7 @@
     for (WebTouchEventWithLatencyList::iterator
              iter = uncoaleseced_events_to_ack_.begin(),
              end = uncoaleseced_events_to_ack_.end();
-         iter != end;
-         ++iter) {
+         iter != end; ++iter) {
       if (optional_latency_info)
         iter->latency.AddNewLatencyFrom(*optional_latency_info);
       client->OnTouchEventAck(*iter, ack_result);
@@ -423,14 +418,8 @@
   DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
 };
 
-TouchEventQueue::Config::Config()
-    : desktop_touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)),
-      mobile_touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(1000)),
-      touch_ack_timeout_supported(false) {
-}
-
-TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
-                                 const Config& config)
+LegacyTouchEventQueue::LegacyTouchEventQueue(TouchEventQueueClient* client,
+                                             const Config& config)
     : client_(client),
       dispatching_touch_ack_(false),
       dispatching_touch_(false),
@@ -440,20 +429,17 @@
       touchmove_slop_suppressor_(new TouchMoveSlopSuppressor),
       send_touch_events_async_(false),
       last_sent_touch_timestamp_sec_(0) {
-  DCHECK(client);
   if (config.touch_ack_timeout_supported) {
     timeout_handler_.reset(
-        new TouchTimeoutHandler(this,
-                                config.desktop_touch_ack_timeout_delay,
+        new TouchTimeoutHandler(this, config.desktop_touch_ack_timeout_delay,
                                 config.mobile_touch_ack_timeout_delay));
   }
 }
 
-TouchEventQueue::~TouchEventQueue() {
-}
+LegacyTouchEventQueue::~LegacyTouchEventQueue() {}
 
-void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
-  TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");
+void LegacyTouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
+  TRACE_EVENT0("input", "LegacyTouchEventQueue::QueueEvent");
 
   // If the queueing of |event| was triggered by an ack dispatch, defer
   // processing the event until the dispatch has finished.
@@ -489,12 +475,13 @@
       base::MakeUnique<CoalescedWebTouchEvent>(event, false));
 }
 
-void TouchEventQueue::PrependTouchScrollNotification() {
-  TRACE_EVENT0("input", "TouchEventQueue::PrependTouchScrollNotification");
+void LegacyTouchEventQueue::PrependTouchScrollNotification() {
+  TRACE_EVENT0("input",
+               "LegacyTouchEventQueue::PrependTouchScrollNotification");
 
   // The queue should have an in-flight event when this method is called because
   // this method is triggered by InputRouterImpl::SendGestureEvent, which is
-  // triggered by TouchEventQueue::AckTouchEventToClient, which has just
+  // triggered by LegacyTouchEventQueue::AckTouchEventToClient, which has just
   // received an ack for the in-flight event. We leave the head of the queue
   // untouched since it is the in-flight event.
   //
@@ -516,10 +503,11 @@
   }
 }
 
-void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result,
-                                      const LatencyInfo& latency_info,
-                                      const uint32_t unique_touch_event_id) {
-  TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck");
+void LegacyTouchEventQueue::ProcessTouchAck(
+    InputEventAckState ack_result,
+    const LatencyInfo& latency_info,
+    const uint32_t unique_touch_event_id) {
+  TRACE_EVENT0("input", "LegacyTouchEventQueue::ProcessTouchAck");
 
   // We receive an ack for async touchmove from render.
   if (!ack_pending_async_touchmove_ids_.empty() &&
@@ -563,7 +551,7 @@
   TryForwardNextEventToRenderer();
 }
 
-void TouchEventQueue::TryForwardNextEventToRenderer() {
+void LegacyTouchEventQueue::TryForwardNextEventToRenderer() {
   DCHECK(!dispatching_touch_ack_);
   // If there are queued touch events, then try to forward them to the renderer
   // immediately, or ACK the events back to the client if appropriate.
@@ -586,8 +574,8 @@
   }
 }
 
-void TouchEventQueue::ForwardNextEventToRenderer() {
-  TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer");
+void LegacyTouchEventQueue::ForwardNextEventToRenderer() {
+  TRACE_EVENT0("input", "LegacyTouchEventQueue::ForwardNextEventToRenderer");
 
   DCHECK(!empty());
   DCHECK(!dispatching_touch_);
@@ -655,7 +643,7 @@
   SendTouchEventImmediately(&touch);
 }
 
-void TouchEventQueue::FlushPendingAsyncTouchmove() {
+void LegacyTouchEventQueue::FlushPendingAsyncTouchmove() {
   DCHECK(!dispatching_touch_);
   std::unique_ptr<TouchEventWithLatencyInfo> touch =
       std::move(pending_async_touchmove_);
@@ -665,13 +653,13 @@
   SendTouchEventImmediately(touch.get());
 }
 
-void TouchEventQueue::OnGestureScrollEvent(
+void LegacyTouchEventQueue::OnGestureScrollEvent(
     const GestureEventWithLatencyInfo& gesture_event) {
   if (gesture_event.event.type() == blink::WebInputEvent::GestureScrollBegin) {
     if (has_handler_for_current_sequence_ &&
         !drop_remaining_touches_in_sequence_) {
       DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves())
-          <<  "A touch handler should be offered a touchmove before scrolling.";
+          << "A touch handler should be offered a touchmove before scrolling.";
     }
 
     pending_async_touchmove_.reset();
@@ -685,7 +673,7 @@
   }
 }
 
-void TouchEventQueue::OnGestureEventAck(
+void LegacyTouchEventQueue::OnGestureEventAck(
     const GestureEventWithLatencyInfo& event,
     InputEventAckState ack_result) {
   // Throttle sending touchmove events as long as the scroll events are handled.
@@ -701,13 +689,13 @@
   }
 }
 
-void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
+void LegacyTouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
   DCHECK(!dispatching_touch_ack_);
   DCHECK(!dispatching_touch_);
   has_handlers_ = has_handlers;
 }
 
-bool TouchEventQueue::IsPendingAckTouchStart() const {
+bool LegacyTouchEventQueue::IsPendingAckTouchStart() const {
   DCHECK(!dispatching_touch_ack_);
   if (touch_queue_.empty())
     return false;
@@ -717,34 +705,39 @@
   return (event.type() == WebInputEvent::TouchStart);
 }
 
-void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
+void LegacyTouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
   if (timeout_handler_)
     timeout_handler_->SetEnabled(enabled);
 }
 
-void TouchEventQueue::SetIsMobileOptimizedSite(bool mobile_optimized_site) {
+void LegacyTouchEventQueue::SetIsMobileOptimizedSite(
+    bool mobile_optimized_site) {
   if (timeout_handler_)
     timeout_handler_->SetUseMobileTimeout(mobile_optimized_site);
 }
 
-bool TouchEventQueue::IsAckTimeoutEnabled() const {
+bool LegacyTouchEventQueue::IsAckTimeoutEnabled() const {
   return timeout_handler_ && timeout_handler_->IsEnabled();
 }
 
-bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
+bool LegacyTouchEventQueue::Empty() const {
+  return touch_queue_.empty();
+}
+
+bool LegacyTouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
   return !!pending_async_touchmove_;
 }
 
-bool TouchEventQueue::IsTimeoutRunningForTesting() const {
+bool LegacyTouchEventQueue::IsTimeoutRunningForTesting() const {
   return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
 }
 
 const TouchEventWithLatencyInfo&
-TouchEventQueue::GetLatestEventForTesting() const {
+LegacyTouchEventQueue::GetLatestEventForTesting() const {
   return touch_queue_.back()->coalesced_event();
 }
 
-void TouchEventQueue::FlushQueue() {
+void LegacyTouchEventQueue::FlushQueue() {
   DCHECK(!dispatching_touch_ack_);
   DCHECK(!dispatching_touch_);
   pending_async_touchmove_.reset();
@@ -753,17 +746,18 @@
     PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
 }
 
-void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) {
+void LegacyTouchEventQueue::PopTouchEventToClient(
+    InputEventAckState ack_result) {
   AckTouchEventToClient(ack_result, nullptr);
 }
 
-void TouchEventQueue::PopTouchEventToClient(
+void LegacyTouchEventQueue::PopTouchEventToClient(
     InputEventAckState ack_result,
     const LatencyInfo& renderer_latency_info) {
   AckTouchEventToClient(ack_result, &renderer_latency_info);
 }
 
-void TouchEventQueue::AckTouchEventToClient(
+void LegacyTouchEventQueue::AckTouchEventToClient(
     InputEventAckState ack_result,
     const ui::LatencyInfo* optional_latency_info) {
   DCHECK(!dispatching_touch_ack_);
@@ -791,7 +785,7 @@
   touch_queue_.pop_front();
 }
 
-void TouchEventQueue::SendTouchEventImmediately(
+void LegacyTouchEventQueue::SendTouchEventImmediately(
     TouchEventWithLatencyInfo* touch) {
   // TODO(crbug.com/600773): Hack to avoid cyclic reentry to this method.
   if (dispatching_touch_)
@@ -807,8 +801,7 @@
     if (last_sent_touchevent_->type() == WebInputEvent::TouchStart)
       touch->event.touchStartOrFirstTouchMove = true;
     for (unsigned int i = 0; i < last_sent_touchevent_->touchesLength; ++i) {
-      const WebTouchPoint& last_touch_point =
-          last_sent_touchevent_->touches[i];
+      const WebTouchPoint& last_touch_point = last_sent_touchevent_->touches[i];
       // Touches with same id may not have same index in Touches array.
       for (unsigned int j = 0; j < touch->event.touchesLength; ++j) {
         const WebTouchPoint& current_touchmove_point = touch->event.touches[j];
@@ -856,8 +849,8 @@
   }
 }
 
-TouchEventQueue::PreFilterResult
-TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
+LegacyTouchEventQueue::PreFilterResult
+LegacyTouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
   if (event.type() == WebInputEvent::TouchScrollStarted)
     return FORWARD_TO_RENDERER;
 
@@ -923,15 +916,15 @@
         // in the |event|.
         break;
       }
-
     }
   }
 
   return ACK_WITH_NO_CONSUMER_EXISTS;
 }
 
-void TouchEventQueue::UpdateTouchConsumerStates(const WebTouchEvent& event,
-                                                InputEventAckState ack_result) {
+void LegacyTouchEventQueue::UpdateTouchConsumerStates(
+    const WebTouchEvent& event,
+    InputEventAckState ack_result) {
   if (event.type() == WebInputEvent::TouchStart) {
     if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
       send_touch_events_async_ = false;
diff --git a/content/browser/renderer_host/input/legacy_touch_event_queue.h b/content/browser/renderer_host/input/legacy_touch_event_queue.h
new file mode 100644
index 0000000..16ab57a
--- /dev/null
+++ b/content/browser/renderer_host/input/legacy_touch_event_queue.h
@@ -0,0 +1,216 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_LEGACY_TOUCH_EVENT_QUEUE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_LEGACY_TOUCH_EVENT_QUEUE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <deque>
+#include <list>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
+#include "content/browser/renderer_host/input/touch_event_queue.h"
+#include "content/common/content_export.h"
+#include "content/common/input/input_event_ack_state.h"
+#include "third_party/WebKit/public/platform/WebInputEvent.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace content {
+
+class CoalescedWebTouchEvent;
+
+// A queue for throttling and coalescing touch-events.
+class CONTENT_EXPORT LegacyTouchEventQueue : public TouchEventQueue {
+ public:
+  // The |client| must outlive the LegacyTouchEventQueue.
+  LegacyTouchEventQueue(TouchEventQueueClient* client, const Config& config);
+
+  ~LegacyTouchEventQueue() override;
+
+  // Adds an event to the queue. The event may be coalesced with previously
+  // queued events (e.g. consecutive touch-move events can be coalesced into a
+  // single touch-move event). The event may also be immediately forwarded to
+  // the renderer (e.g. when there are no other queued touch event).
+  void QueueEvent(const TouchEventWithLatencyInfo& event) override;
+
+  // Insert a TouchScrollStarted event in the queue ahead of all not-in-flight
+  // events.
+  void PrependTouchScrollNotification() override;
+
+  // Notifies the queue that a touch-event has been processed by the renderer.
+  // At this point, if the ack is for async touchmove, remove the uncancelable
+  // touchmove from the front of the queue and decide if it should dispatch the
+  // next pending async touch move event, otherwise the queue may send one or
+  // more gesture events and/or additional queued touch-events to the renderer.
+  void ProcessTouchAck(InputEventAckState ack_result,
+                       const ui::LatencyInfo& latency_info,
+                       const uint32_t unique_touch_event_id) override;
+
+  // When GestureScrollBegin is received, we send a touch cancel to renderer,
+  // route all the following touch events directly to client, and ignore the
+  // ack for the touch cancel. When Gesture{ScrollEnd,FlingStart} is received,
+  // resume the normal flow of sending touch events to the renderer.
+  void OnGestureScrollEvent(
+      const GestureEventWithLatencyInfo& gesture_event) override;
+
+  void OnGestureEventAck(const GestureEventWithLatencyInfo& event,
+                         InputEventAckState ack_result) override;
+
+  // Notifies the queue whether the renderer has at least one touch handler.
+  void OnHasTouchEventHandlers(bool has_handlers) override;
+
+  // Returns whether the currently pending touch event (waiting ACK) is for
+  // a touch start event.
+  bool IsPendingAckTouchStart() const override;
+
+  // Sets whether a delayed touch ack will cancel and flush the current
+  // touch sequence. Note that, if the timeout was previously disabled, enabling
+  // it will take effect only for the following touch sequence.
+  void SetAckTimeoutEnabled(bool enabled) override;
+
+  // Sets whether the current site has a mobile friendly viewport. This
+  // determines which ack timeout delay will be used for *future* touch events.
+  // The default assumption is that the site is *not* mobile-optimized.
+  void SetIsMobileOptimizedSite(bool mobile_optimized_site) override;
+
+  // Whether ack timeout behavior is supported and enabled for the current site.
+  bool IsAckTimeoutEnabled() const override;
+
+  bool Empty() const override;
+
+  bool empty() const WARN_UNUSED_RESULT { return Empty(); }
+
+  size_t size() const { return touch_queue_.size(); }
+
+  bool has_handlers() const { return has_handlers_; }
+
+  size_t uncancelable_touch_moves_pending_ack_count() const {
+    return ack_pending_async_touchmove_ids_.size();
+  }
+
+ private:
+  class TouchTimeoutHandler;
+  class TouchMoveSlopSuppressor;
+  friend class TouchTimeoutHandler;
+  friend class TouchEventQueueTest;
+
+  bool HasPendingAsyncTouchMoveForTesting() const;
+  bool IsTimeoutRunningForTesting() const;
+  const TouchEventWithLatencyInfo& GetLatestEventForTesting() const;
+
+  // Empties the queue of touch events. This may result in any number of gesture
+  // events being sent to the renderer.
+  void FlushQueue();
+
+  // Walks the queue, checking each event with |FilterBeforeForwarding()|.
+  // If allowed, forwards the touch event and stops processing further events.
+  // Otherwise, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|.
+  void TryForwardNextEventToRenderer();
+
+  // Forwards the event at the head of the queue to the renderer.
+  void ForwardNextEventToRenderer();
+
+  // Pops the touch-event from the head of the queue and acks it to the client.
+  void PopTouchEventToClient(InputEventAckState ack_result);
+
+  // Pops the touch-event from the top of the queue and acks it to the client,
+  // updating the event with |renderer_latency_info|.
+  void PopTouchEventToClient(InputEventAckState ack_result,
+                             const ui::LatencyInfo& renderer_latency_info);
+
+  // Ack all coalesced events at the head of the queue to the client with
+  // |ack_result|, updating the acked events with |optional_latency_info| if it
+  // exists, and popping the head of the queue.
+  void AckTouchEventToClient(InputEventAckState ack_result,
+                             const ui::LatencyInfo* optional_latency_info);
+
+  // Dispatch |touch| to the client. Before dispatching, updates pointer
+  // states in touchmove events for pointers that have not changed position.
+  void SendTouchEventImmediately(TouchEventWithLatencyInfo* touch);
+
+  enum PreFilterResult {
+    ACK_WITH_NO_CONSUMER_EXISTS,
+    ACK_WITH_NOT_CONSUMED,
+    FORWARD_TO_RENDERER,
+  };
+  // Filter touches prior to forwarding to the renderer, e.g., if the renderer
+  // has no touch handler.
+  PreFilterResult FilterBeforeForwarding(const blink::WebTouchEvent& event);
+  void ForwardToRenderer(const TouchEventWithLatencyInfo& event);
+  void UpdateTouchConsumerStates(const blink::WebTouchEvent& event,
+                                 InputEventAckState ack_result);
+  void FlushPendingAsyncTouchmove();
+
+  // Handles touch event forwarding and ack'ed event dispatch.
+  TouchEventQueueClient* client_;
+
+  std::list<std::unique_ptr<CoalescedWebTouchEvent>> touch_queue_;
+
+  // Position of the first touch in the most recent sequence forwarded to the
+  // client.
+  gfx::PointF touch_sequence_start_position_;
+
+  // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|.
+  // True within the scope of |AckTouchEventToClient()|.
+  bool dispatching_touch_ack_;
+
+  // Used to prevent touch timeout scheduling and increase the count for async
+  // touchmove if we receive a synchronous ack after forwarding a touch event
+  // to the client.
+  bool dispatching_touch_;
+
+  // Whether the renderer has at least one touch handler.
+  bool has_handlers_;
+
+  // Whether any pointer in the touch sequence reported having a consumer.
+  bool has_handler_for_current_sequence_;
+
+  // Whether to allow any remaining touches for the current sequence. Note that
+  // this is a stricter condition than an empty |touch_consumer_states_|, as it
+  // also prevents forwarding of touchstart events for new pointers in the
+  // current sequence. This is only used when the event is synthetically
+  // cancelled after a touch timeout.
+  bool drop_remaining_touches_in_sequence_;
+
+  // Optional handler for timed-out touch event acks.
+  std::unique_ptr<TouchTimeoutHandler> timeout_handler_;
+
+  // Suppression of TouchMove's within a slop region when a sequence has not yet
+  // been preventDefaulted.
+  std::unique_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_;
+
+  // Whether touch events should remain buffered and dispatched asynchronously
+  // while a scroll sequence is active.  In this mode, touchmove's are throttled
+  // and ack'ed immediately, but remain buffered in |pending_async_touchmove_|
+  // until a sufficient time period has elapsed since the last sent touch event.
+  // For details see the design doc at http://goo.gl/lVyJAa.
+  bool send_touch_events_async_;
+  std::unique_ptr<TouchEventWithLatencyInfo> pending_async_touchmove_;
+
+  // For uncancelable touch moves, not only we send a fake ack, but also a real
+  // ack from render, which we use to decide when to send the next async
+  // touchmove. This can help avoid the touch event queue keep growing when
+  // render handles touchmove slow. We use a queue
+  // ack_pending_async_touchmove_ids to store the recent dispatched
+  // uncancelable touchmoves which are still waiting for their acks back from
+  // render. We do not put them back to the front the touch_event_queue any
+  // more.
+  std::deque<uint32_t> ack_pending_async_touchmove_ids_;
+
+  double last_sent_touch_timestamp_sec_;
+
+  // Event is saved to compare pointer positions for new touchmove events.
+  std::unique_ptr<blink::WebTouchEvent> last_sent_touchevent_;
+
+  DISALLOW_COPY_AND_ASSIGN(LegacyTouchEventQueue);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_LEGACY_TOUCH_EVENT_QUEUE_H_
diff --git a/content/browser/renderer_host/input/touch_event_queue.h b/content/browser/renderer_host/input/touch_event_queue.h
index f039e14..9384ee4 100644
--- a/content/browser/renderer_host/input/touch_event_queue.h
+++ b/content/browser/renderer_host/input/touch_event_queue.h
@@ -8,22 +8,14 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <deque>
-#include <list>
-#include <memory>
-
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "content/browser/renderer_host/event_with_latency_info.h"
 #include "content/common/content_export.h"
 #include "content/common/input/input_event_ack_state.h"
-#include "third_party/WebKit/public/platform/WebInputEvent.h"
-#include "ui/gfx/geometry/point_f.h"
 
 namespace content {
 
-class CoalescedWebTouchEvent;
-
 // Interface with which TouchEventQueue can forward touch events, and dispatch
 // touch event responses.
 class CONTENT_EXPORT TouchEventQueueClient {
@@ -45,7 +37,12 @@
 class CONTENT_EXPORT TouchEventQueue {
  public:
   struct CONTENT_EXPORT Config {
-    Config();
+    Config()
+        : desktop_touch_ack_timeout_delay(
+              base::TimeDelta::FromMilliseconds(200)),
+          mobile_touch_ack_timeout_delay(
+              base::TimeDelta::FromMilliseconds(1000)),
+          touch_ack_timeout_supported(false) {}
 
     // Touch ack timeout delay for desktop sites. If zero, timeout behavior
     // is disabled for such sites. Defaults to 200ms.
@@ -60,188 +57,62 @@
     bool touch_ack_timeout_supported;
   };
 
-  // The |client| must outlive the TouchEventQueue.
-  TouchEventQueue(TouchEventQueueClient* client, const Config& config);
+  TouchEventQueue() {}
 
-  ~TouchEventQueue();
+  virtual ~TouchEventQueue() {}
 
   // Adds an event to the queue. The event may be coalesced with previously
   // queued events (e.g. consecutive touch-move events can be coalesced into a
   // single touch-move event). The event may also be immediately forwarded to
   // the renderer (e.g. when there are no other queued touch event).
-  void QueueEvent(const TouchEventWithLatencyInfo& event);
+  virtual void QueueEvent(const TouchEventWithLatencyInfo& event) = 0;
 
   // Insert a TouchScrollStarted event in the queue ahead of all not-in-flight
   // events.
-  void PrependTouchScrollNotification();
+  virtual void PrependTouchScrollNotification() = 0;
 
   // Notifies the queue that a touch-event has been processed by the renderer.
   // At this point, if the ack is for async touchmove, remove the uncancelable
   // touchmove from the front of the queue and decide if it should dispatch the
   // next pending async touch move event, otherwise the queue may send one or
   // more gesture events and/or additional queued touch-events to the renderer.
-  void ProcessTouchAck(InputEventAckState ack_result,
-                       const ui::LatencyInfo& latency_info,
-                       const uint32_t unique_touch_event_id);
+  virtual void ProcessTouchAck(InputEventAckState ack_result,
+                               const ui::LatencyInfo& latency_info,
+                               const uint32_t unique_touch_event_id) = 0;
 
   // When GestureScrollBegin is received, we send a touch cancel to renderer,
   // route all the following touch events directly to client, and ignore the
   // ack for the touch cancel. When Gesture{ScrollEnd,FlingStart} is received,
   // resume the normal flow of sending touch events to the renderer.
-  void OnGestureScrollEvent(const GestureEventWithLatencyInfo& gesture_event);
+  virtual void OnGestureScrollEvent(
+      const GestureEventWithLatencyInfo& gesture_event) = 0;
 
-  void OnGestureEventAck(
-      const GestureEventWithLatencyInfo& event,
-      InputEventAckState ack_result);
+  virtual void OnGestureEventAck(const GestureEventWithLatencyInfo& event,
+                                 InputEventAckState ack_result) = 0;
 
   // Notifies the queue whether the renderer has at least one touch handler.
-  void OnHasTouchEventHandlers(bool has_handlers);
+  virtual void OnHasTouchEventHandlers(bool has_handlers) = 0;
 
   // Returns whether the currently pending touch event (waiting ACK) is for
   // a touch start event.
-  bool IsPendingAckTouchStart() const;
+  virtual bool IsPendingAckTouchStart() const = 0;
 
   // Sets whether a delayed touch ack will cancel and flush the current
   // touch sequence. Note that, if the timeout was previously disabled, enabling
   // it will take effect only for the following touch sequence.
-  void SetAckTimeoutEnabled(bool enabled);
+  virtual void SetAckTimeoutEnabled(bool enabled) = 0;
 
   // Sets whether the current site has a mobile friendly viewport. This
   // determines which ack timeout delay will be used for *future* touch events.
   // The default assumption is that the site is *not* mobile-optimized.
-  void SetIsMobileOptimizedSite(bool mobile_optimized_site);
+  virtual void SetIsMobileOptimizedSite(bool mobile_optimized_site) = 0;
 
   // Whether ack timeout behavior is supported and enabled for the current site.
-  bool IsAckTimeoutEnabled() const;
+  virtual bool IsAckTimeoutEnabled() const = 0;
 
-  bool empty() const WARN_UNUSED_RESULT {
-    return touch_queue_.empty();
-  }
-
-  size_t size() const {
-    return touch_queue_.size();
-  }
-
-  bool has_handlers() const { return has_handlers_; }
-
-  size_t uncancelable_touch_moves_pending_ack_count() const {
-    return ack_pending_async_touchmove_ids_.size();
-  }
+  virtual bool Empty() const WARN_UNUSED_RESULT = 0;
 
  private:
-  class TouchTimeoutHandler;
-  class TouchMoveSlopSuppressor;
-  friend class TouchTimeoutHandler;
-  friend class TouchEventQueueTest;
-
-  bool HasPendingAsyncTouchMoveForTesting() const;
-  bool IsTimeoutRunningForTesting() const;
-  const TouchEventWithLatencyInfo& GetLatestEventForTesting() const;
-
-  // Empties the queue of touch events. This may result in any number of gesture
-  // events being sent to the renderer.
-  void FlushQueue();
-
-  // Walks the queue, checking each event with |FilterBeforeForwarding()|.
-  // If allowed, forwards the touch event and stops processing further events.
-  // Otherwise, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|.
-  void TryForwardNextEventToRenderer();
-
-  // Forwards the event at the head of the queue to the renderer.
-  void ForwardNextEventToRenderer();
-
-  // Pops the touch-event from the head of the queue and acks it to the client.
-  void PopTouchEventToClient(InputEventAckState ack_result);
-
-  // Pops the touch-event from the top of the queue and acks it to the client,
-  // updating the event with |renderer_latency_info|.
-  void PopTouchEventToClient(InputEventAckState ack_result,
-                             const ui::LatencyInfo& renderer_latency_info);
-
-  // Ack all coalesced events at the head of the queue to the client with
-  // |ack_result|, updating the acked events with |optional_latency_info| if it
-  // exists, and popping the head of the queue.
-  void AckTouchEventToClient(InputEventAckState ack_result,
-                             const ui::LatencyInfo* optional_latency_info);
-
-  // Dispatch |touch| to the client. Before dispatching, updates pointer
-  // states in touchmove events for pointers that have not changed position.
-  void SendTouchEventImmediately(TouchEventWithLatencyInfo* touch);
-
-  enum PreFilterResult {
-    ACK_WITH_NO_CONSUMER_EXISTS,
-    ACK_WITH_NOT_CONSUMED,
-    FORWARD_TO_RENDERER,
-  };
-  // Filter touches prior to forwarding to the renderer, e.g., if the renderer
-  // has no touch handler.
-  PreFilterResult FilterBeforeForwarding(const blink::WebTouchEvent& event);
-  void ForwardToRenderer(const TouchEventWithLatencyInfo& event);
-  void UpdateTouchConsumerStates(const blink::WebTouchEvent& event,
-                                 InputEventAckState ack_result);
-  void FlushPendingAsyncTouchmove();
-
-  // Handles touch event forwarding and ack'ed event dispatch.
-  TouchEventQueueClient* client_;
-
-  std::list<std::unique_ptr<CoalescedWebTouchEvent>> touch_queue_;
-
-  // Position of the first touch in the most recent sequence forwarded to the
-  // client.
-  gfx::PointF touch_sequence_start_position_;
-
-  // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|.
-  // True within the scope of |AckTouchEventToClient()|.
-  bool dispatching_touch_ack_;
-
-  // Used to prevent touch timeout scheduling and increase the count for async
-  // touchmove if we receive a synchronous ack after forwarding a touch event
-  // to the client.
-  bool dispatching_touch_;
-
-  // Whether the renderer has at least one touch handler.
-  bool has_handlers_;
-
-  // Whether any pointer in the touch sequence reported having a consumer.
-  bool has_handler_for_current_sequence_;
-
-  // Whether to allow any remaining touches for the current sequence. Note that
-  // this is a stricter condition than an empty |touch_consumer_states_|, as it
-  // also prevents forwarding of touchstart events for new pointers in the
-  // current sequence. This is only used when the event is synthetically
-  // cancelled after a touch timeout.
-  bool drop_remaining_touches_in_sequence_;
-
-  // Optional handler for timed-out touch event acks.
-  std::unique_ptr<TouchTimeoutHandler> timeout_handler_;
-
-  // Suppression of TouchMove's within a slop region when a sequence has not yet
-  // been preventDefaulted.
-  std::unique_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_;
-
-  // Whether touch events should remain buffered and dispatched asynchronously
-  // while a scroll sequence is active.  In this mode, touchmove's are throttled
-  // and ack'ed immediately, but remain buffered in |pending_async_touchmove_|
-  // until a sufficient time period has elapsed since the last sent touch event.
-  // For details see the design doc at http://goo.gl/lVyJAa.
-  bool send_touch_events_async_;
-  std::unique_ptr<TouchEventWithLatencyInfo> pending_async_touchmove_;
-
-  // For uncancelable touch moves, not only we send a fake ack, but also a real
-  // ack from render, which we use to decide when to send the next async
-  // touchmove. This can help avoid the touch event queue keep growing when
-  // render handles touchmove slow. We use a queue
-  // ack_pending_async_touchmove_ids to store the recent dispatched
-  // uncancelable touchmoves which are still waiting for their acks back from
-  // render. We do not put them back to the front the touch_event_queue any
-  // more.
-  std::deque<uint32_t> ack_pending_async_touchmove_ids_;
-
-  double last_sent_touch_timestamp_sec_;
-
-  // Event is saved to compare pointer positions for new touchmove events.
-  std::unique_ptr<blink::WebTouchEvent> last_sent_touchevent_;
-
   DISALLOW_COPY_AND_ASSIGN(TouchEventQueue);
 };
 
diff --git a/content/browser/renderer_host/input/touch_event_queue_unittest.cc b/content/browser/renderer_host/input/touch_event_queue_unittest.cc
index 7da1fc5..88b6123 100644
--- a/content/browser/renderer_host/input/touch_event_queue_unittest.cc
+++ b/content/browser/renderer_host/input/touch_event_queue_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/renderer_host/input/legacy_touch_event_queue.h"
 #include "content/browser/renderer_host/input/timeout_monitor.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/input/web_touch_event_traits.h"
@@ -326,11 +327,11 @@
   }
 
   void ResetQueueWithConfig(const TouchEventQueue::Config& config) {
-    queue_.reset(new TouchEventQueue(this, config));
+    queue_.reset(new LegacyTouchEventQueue(this, config));
     queue_->OnHasTouchEventHandlers(true);
   }
 
-  std::unique_ptr<TouchEventQueue> queue_;
+  std::unique_ptr<LegacyTouchEventQueue> queue_;
   size_t acked_event_count_;
   WebTouchEvent last_acked_event_;
   std::vector<WebTouchEvent> sent_events_;
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index d72a8ab9..c391a17 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -449,8 +449,15 @@
   // layer is managed by the DelegatedFrameHost.
   view_.SetLayer(cc::Layer::Create());
   if (using_browser_compositor_) {
+    // This FrameSinkId must be unique with ContextProviderFactory's FrameSinkId
+    // allocation.
+    // TODO(crbug.com/685777): Centralize allocation in one place for easier
+    // maintenance.
+    cc::FrameSinkId frame_sink_id = cc::FrameSinkId(
+        base::checked_cast<uint32_t>(host_->GetProcess()->GetID()),
+        base::checked_cast<uint32_t>(host_->GetRoutingID()));
     delegated_frame_host_.reset(new ui::DelegatedFrameHostAndroid(
-        &view_, cached_background_color_, this));
+        &view_, cached_background_color_, this, frame_sink_id));
   }
 
   host_->SetView(this);
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 5e0d302..9491607e 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1915,6 +1915,10 @@
 
   // GuestViews have two RenderWidgetHostViews and so we need to make sure
   // we don't have FrameSinkId collisions.
+  // The FrameSinkId generated here must be unique with FrameSinkId allocated
+  // in ContextFactoryPrivate.
+  // TODO(crbug.com/685777): Centralize allocation in one place for easier
+  // maintenance.
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   cc::FrameSinkId frame_sink_id =
       is_guest_view_hack_
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index a879a62..f7435a2 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -470,8 +470,23 @@
   [cocoa_view_ setLayer:background_layer_];
   [cocoa_view_ setWantsLayer:YES];
 
-  browser_compositor_.reset(new BrowserCompositorMac(
-      this, this, render_widget_host_->is_hidden(), [cocoa_view_ window]));
+  // GuestViews have two RenderWidgetHostViews and so we need to make sure
+  // we don't have FrameSinkId collisions.
+  // The FrameSinkId generated here must be unique with FrameSinkId allocated
+  // in ContextFactoryPrivate.
+  // TODO(crbug.com/685777): Centralize allocation in one place for easier
+  // maintenance.
+  ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
+  cc::FrameSinkId frame_sink_id =
+      is_guest_view_hack_
+          ? factory->GetContextFactoryPrivate()->AllocateFrameSinkId()
+          : cc::FrameSinkId(base::checked_cast<uint32_t>(
+                                render_widget_host_->GetProcess()->GetID()),
+                            base::checked_cast<uint32_t>(
+                                render_widget_host_->GetRoutingID()));
+  browser_compositor_.reset(
+      new BrowserCompositorMac(this, this, render_widget_host_->is_hidden(),
+                               [cocoa_view_ window], frame_sink_id));
 
   display::Screen::GetScreen()->AddObserver(this);
 
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index fd9a51a..412213d 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -563,9 +563,10 @@
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& jframe_name,
     const JavaParamRef<jstring>& jmessage,
+    const JavaParamRef<jstring>& jsource_origin,
     const JavaParamRef<jstring>& jtarget_origin,
     const JavaParamRef<jintArray>& jsent_ports) {
-  base::string16 source_origin;
+  base::string16 source_origin(ConvertJavaStringToUTF16(env, jsource_origin));
   base::string16 target_origin(ConvertJavaStringToUTF16(env, jtarget_origin));
   base::string16 message(ConvertJavaStringToUTF16(env, jmessage));
   std::vector<int> ports;
diff --git a/content/browser/web_contents/web_contents_android.h b/content/browser/web_contents/web_contents_android.h
index 08adb58..ab17833 100644
--- a/content/browser/web_contents/web_contents_android.h
+++ b/content/browser/web_contents/web_contents_android.h
@@ -136,6 +136,7 @@
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& jframe_name,
       const base::android::JavaParamRef<jstring>& jmessage,
+      const base::android::JavaParamRef<jstring>& jsource_origin,
       const base::android::JavaParamRef<jstring>& jtarget_origin,
       const base::android::JavaParamRef<jintArray>& jsent_ports);
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index e87694f..5150f32 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -336,10 +336,10 @@
     }
 
     @Override
-    public void postMessageToFrame(
-            String frameName, String message, String targetOrigin, int[] sentPortIds) {
-        nativePostMessageToFrame(
-                mNativeWebContentsAndroid, frameName, message, targetOrigin, sentPortIds);
+    public void postMessageToFrame(String frameName, String message,
+            String sourceOrigin, String targetOrigin, int[] sentPortIds) {
+        nativePostMessageToFrame(mNativeWebContentsAndroid, frameName, message,
+                sourceOrigin, targetOrigin, sentPortIds);
     }
 
     @Override
@@ -539,7 +539,7 @@
     private native void nativeAddMessageToDevToolsConsole(
             long nativeWebContentsAndroid, int level, String message);
     private native void nativePostMessageToFrame(long nativeWebContentsAndroid, String frameName,
-            String message, String targetOrigin, int[] sentPortIds);
+            String message, String sourceOrigin, String targetOrigin, int[] sentPortIds);
     private native void nativeCreateMessageChannel(
             long nativeWebContentsAndroid, AppWebMessagePort[] ports);
     private native boolean nativeHasAccessedInitialDocument(
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
index c8a70ab0..22bb994 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
@@ -268,8 +268,8 @@
     /**
      * Dispatches a Message event to the specified frame.
      */
-    void postMessageToFrame(
-            String frameName, String message, String targetOrigin, int[] sentPortIds);
+    void postMessageToFrame(String frameName, String message,
+            String sourceOrigin, String targetOrigin, int[] sentPortIds);
 
     /**
      * Creates a message channel for sending postMessage requests and returns the ports for
diff --git a/content/public/test/find_test_utils.cc b/content/public/test/find_test_utils.cc
new file mode 100644
index 0000000..e7e04d3
--- /dev/null
+++ b/content/public/test/find_test_utils.cc
@@ -0,0 +1,149 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/test/find_test_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+const int kInvalidId = -1;
+
+}  // namespace
+
+FindResults::FindResults(int request_id,
+                         int number_of_matches,
+                         int active_match_ordinal)
+    : request_id(request_id),
+      number_of_matches(number_of_matches),
+      active_match_ordinal(active_match_ordinal) {}
+
+FindResults::FindResults() : FindResults(kInvalidId, 0, 0) {}
+
+FindTestWebContentsDelegate::FindTestWebContentsDelegate()
+    : last_request_id_(kInvalidId),
+      last_finished_request_id_(kInvalidId),
+      next_reply_received_(false),
+      record_replies_(false),
+      waiting_for_(NOTHING) {}
+
+FindTestWebContentsDelegate::~FindTestWebContentsDelegate() {}
+
+const FindResults& FindTestWebContentsDelegate::GetFindResults() const {
+  return current_results_;
+}
+
+void FindTestWebContentsDelegate::WaitForFinalReply() {
+  if (last_finished_request_id_ >= last_request_id_)
+    return;
+
+  WaitFor(FINAL_REPLY);
+}
+
+void FindTestWebContentsDelegate::WaitForNextReply() {
+  if (next_reply_received_)
+    return;
+
+  WaitFor(NEXT_REPLY);
+}
+
+void FindTestWebContentsDelegate::MarkNextReply() {
+  next_reply_received_ = false;
+}
+
+void FindTestWebContentsDelegate::UpdateLastRequest(int request_id) {
+  last_request_id_ = request_id;
+}
+
+void FindTestWebContentsDelegate::StartReplyRecord() {
+  reply_record_.clear();
+  record_replies_ = true;
+}
+
+const std::vector<FindResults>& FindTestWebContentsDelegate::GetReplyRecord() {
+  record_replies_ = false;
+  return reply_record_;
+}
+
+#if defined(OS_ANDROID)
+void FindTestWebContentsDelegate::WaitForMatchRects() {
+  WaitFor(MATCH_RECTS);
+}
+#endif
+
+void FindTestWebContentsDelegate::FindReply(WebContents* web_contents,
+                                            int request_id,
+                                            int number_of_matches,
+                                            const gfx::Rect& selection_rect,
+                                            int active_match_ordinal,
+                                            bool final_update) {
+  if (record_replies_) {
+    reply_record_.emplace_back(
+        request_id, number_of_matches, active_match_ordinal);
+  }
+
+  // Update the current results.
+  if (request_id > current_results_.request_id)
+    current_results_.request_id = request_id;
+  if (number_of_matches != -1)
+    current_results_.number_of_matches = number_of_matches;
+  if (active_match_ordinal != -1)
+    current_results_.active_match_ordinal = active_match_ordinal;
+
+  if (!final_update)
+    return;
+
+  if (request_id > last_finished_request_id_)
+    last_finished_request_id_ = request_id;
+  next_reply_received_ = true;
+
+  // If we are waiting for this find reply, stop waiting.
+  if (waiting_for_ == NEXT_REPLY ||
+      (waiting_for_ == FINAL_REPLY &&
+       last_finished_request_id_ >= last_request_id_)) {
+    StopWaiting();
+  }
+}
+
+void FindTestWebContentsDelegate::WaitFor(WaitingFor wait_for) {
+  ASSERT_EQ(NOTHING, waiting_for_);
+  ASSERT_NE(NOTHING, wait_for);
+
+  // Wait for |wait_for|.
+  waiting_for_ = wait_for;
+  message_loop_runner_ = new content::MessageLoopRunner;
+  message_loop_runner_->Run();
+
+  // Done waiting.
+  waiting_for_ = NOTHING;
+  message_loop_runner_ = nullptr;
+}
+
+void FindTestWebContentsDelegate::StopWaiting() {
+  if (!message_loop_runner_.get())
+    return;
+
+  ASSERT_NE(NOTHING, waiting_for_);
+  message_loop_runner_->Quit();
+}
+
+#if defined(OS_ANDROID)
+void FindTestWebContentsDelegate::FindMatchRectsReply(
+    WebContents* web_contents,
+    int version,
+    const std::vector<gfx::RectF>& rects,
+    const gfx::RectF& active_rect) {
+  // Update the current rects.
+  find_match_rects_ = rects;
+  active_match_rect_ = active_rect;
+
+  // If we are waiting for match rects, stop waiting.
+  if (waiting_for_ == MATCH_RECTS)
+    StopWaiting();
+}
+#endif
+
+}  // namespace content
diff --git a/content/public/test/find_test_utils.h b/content/public/test/find_test_utils.h
new file mode 100644
index 0000000..2d2b16d
--- /dev/null
+++ b/content/public/test/find_test_utils.h
@@ -0,0 +1,142 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_TEST_FIND_TEST_UTILS_H_
+#define CONTENT_PUBLIC_TEST_FIND_TEST_UTILS_H_
+
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/test/test_utils.h"
+
+namespace content {
+
+// The results of a find request.
+struct FindResults {
+  FindResults(int request_id,
+              int number_of_matches,
+              int active_match_ordinal);
+  FindResults();
+
+  int request_id;
+  int number_of_matches;
+  int active_match_ordinal;
+};
+
+// This test delegate is used during find-in-page tests, in order to directly
+// access find replies going through the WebContentsDelegate. Tests functions in
+// this delegate allow for waiting on specific or all find replies to come in,
+// and observe find results within them.
+class FindTestWebContentsDelegate : public WebContentsDelegate {
+ public:
+  FindTestWebContentsDelegate();
+  ~FindTestWebContentsDelegate() override;
+
+  // Returns the current find results.
+  const FindResults& GetFindResults() const;
+
+  // Waits for all pending replies to be received.
+  void WaitForFinalReply();
+
+  // Waits for the next find reply. This is useful for waiting for a single
+  // match to be activated, or for a new frame to be searched.
+  void WaitForNextReply();
+
+  // Indicates that the next find reply from this point will be the one to wait
+  // for when WaitForNextReply() is called. It may be the case that the reply
+  // comes before the call to WaitForNextReply(), in which case it will return
+  // immediately.
+  void MarkNextReply();
+
+  // Called when a new find request is issued, so the delegate knows the last
+  // request ID.
+  void UpdateLastRequest(int request_id);
+
+  // From when this function is called, all replies coming in via FindReply()
+  // will be recorded. These replies can be retrieved via GetReplyRecord().
+  void StartReplyRecord();
+
+  // Retreives the results from the find replies recorded since the last call to
+  // StartReplyRecord(). Calling this function also stops the recording new find
+  // replies.
+  const std::vector<FindResults>& GetReplyRecord();
+
+#if defined(OS_ANDROID)
+  // Waits for all of the find match rects to be received.
+  void WaitForMatchRects();
+
+  const std::vector<gfx::RectF>& find_match_rects() const {
+    return find_match_rects_;
+  }
+
+  const gfx::RectF& active_match_rect() const {
+    return active_match_rect_;
+  }
+#endif
+
+ private:
+  enum WaitingFor {
+    NOTHING,
+    FINAL_REPLY,
+    NEXT_REPLY,
+#if defined(OS_ANDROID)
+    MATCH_RECTS
+#endif
+  };
+
+  // WebContentsDelegate override.
+  void FindReply(WebContents* web_contents,
+                 int request_id,
+                 int number_of_matches,
+                 const gfx::Rect& selection_rect,
+                 int active_match_ordinal,
+                 bool final_update) override;
+
+  // Uses |message_loop_runner_| to wait for various things.
+  void WaitFor(WaitingFor wait_for);
+
+  // Stop waiting for |waiting_for_|.
+  void StopWaiting();
+
+#if defined(OS_ANDROID)
+  // WebContentsDelegate override.
+  void FindMatchRectsReply(WebContents* web_contents,
+                           int version,
+                           const std::vector<gfx::RectF>& rects,
+                           const gfx::RectF& active_rect) override;
+
+  std::vector<gfx::RectF> find_match_rects_;
+
+  gfx::RectF active_match_rect_;
+#endif
+
+  // The latest known results from the current find request.
+  FindResults current_results_;
+
+  // The ID of the last find request issued.
+  int last_request_id_;
+
+  // The ID of the last find request to finish (all replies received).
+  int last_finished_request_id_;
+
+  // Indicates whether the next reply after MarkNextReply() has been received.
+  bool next_reply_received_;
+
+  // Indicates whether the find results from incoming find replies are currently
+  // being recorded.
+  bool record_replies_;
+
+  // A record of all find replies that have come in via FindReply() since
+  // StartReplyRecor() was last called.
+  std::vector<FindResults> reply_record_;
+
+  // Indicates what |message_loop_runner_| is waiting for, if anything.
+  WaitingFor waiting_for_;
+
+  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(FindTestWebContentsDelegate);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_TEST_FIND_TEST_UTILS_H_a
diff --git a/content/shell/browser/shell_network_delegate.cc b/content/shell/browser/shell_network_delegate.cc
index 976a236..dc58d455 100644
--- a/content/shell/browser/shell_network_delegate.cc
+++ b/content/shell/browser/shell_network_delegate.cc
@@ -113,8 +113,4 @@
       switches::kEnableExperimentalWebPlatformFeatures);
 }
 
-bool ShellNetworkDelegate::OnAreStrictSecureCookiesEnabled() const {
-  return OnAreExperimentalCookieFeaturesEnabled();
-}
-
 }  // namespace content
diff --git a/content/shell/browser/shell_network_delegate.h b/content/shell/browser/shell_network_delegate.h
index 45040c6..65b2ee0d 100644
--- a/content/shell/browser/shell_network_delegate.h
+++ b/content/shell/browser/shell_network_delegate.h
@@ -55,7 +55,6 @@
   bool OnCanAccessFile(const net::URLRequest& request,
                        const base::FilePath& path) const override;
   bool OnAreExperimentalCookieFeaturesEnabled() const override;
-  bool OnAreStrictSecureCookiesEnabled() const override;
 
   DISALLOW_COPY_AND_ASSIGN(ShellNetworkDelegate);
 };
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 05e0baa7..1fcd658 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -52,6 +52,8 @@
     "../public/test/download_test_observer.h",
     "../public/test/fake_speech_recognition_manager.cc",
     "../public/test/fake_speech_recognition_manager.h",
+    "../public/test/find_test_utils.cc",
+    "../public/test/find_test_utils.h",
     "../public/test/frame_load_waiter.cc",
     "../public/test/frame_load_waiter.h",
     "../public/test/javascript_test_observer.cc",
diff --git a/content/test/data/find_in_pdf_page.pdf b/content/test/data/find_in_pdf_page.pdf
new file mode 100644
index 0000000..3593892e
--- /dev/null
+++ b/content/test/data/find_in_pdf_page.pdf
Binary files differ
diff --git a/device/vr/android/gvr/gvr_delegate.h b/device/vr/android/gvr/gvr_delegate.h
index b96e366..57d4d87 100644
--- a/device/vr/android/gvr/gvr_delegate.h
+++ b/device/vr/android/gvr/gvr_delegate.h
@@ -22,7 +22,8 @@
  public:
   virtual void SetWebVRSecureOrigin(bool secure_origin) = 0;
   virtual void SubmitWebVRFrame() = 0;
-  virtual void UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
+  virtual void UpdateWebVRTextureBounds(int16_t frame_index,
+                                        const gvr::Rectf& left_bounds,
                                         const gvr::Rectf& right_bounds) = 0;
   virtual gvr::Sizei GetWebVRCompositorSurfaceSize() = 0;
   virtual void SetWebVRRenderSurfaceSize(int width, int height) = 0;
diff --git a/device/vr/android/gvr/gvr_device.cc b/device/vr/android/gvr/gvr_device.cc
index 18b5cfd..a8efa67a 100644
--- a/device/vr/android/gvr/gvr_device.cc
+++ b/device/vr/android/gvr/gvr_device.cc
@@ -173,7 +173,8 @@
     delegate_->SubmitWebVRFrame();
 }
 
-void GvrDevice::UpdateLayerBounds(mojom::VRLayerBoundsPtr left_bounds,
+void GvrDevice::UpdateLayerBounds(int16_t frame_index,
+                                  mojom::VRLayerBoundsPtr left_bounds,
                                   mojom::VRLayerBoundsPtr right_bounds) {
   if (!delegate_)
     return;
@@ -190,7 +191,8 @@
   right_gvr_bounds.right = right_bounds->left + right_bounds->width;
   right_gvr_bounds.bottom = 1.0f - (right_bounds->top + right_bounds->height);
 
-  delegate_->UpdateWebVRTextureBounds(left_gvr_bounds, right_gvr_bounds);
+  delegate_->UpdateWebVRTextureBounds(frame_index, left_gvr_bounds,
+                                      right_gvr_bounds);
 }
 
 void GvrDevice::GetVRVSyncProvider(mojom::VRVSyncProviderRequest request) {
diff --git a/device/vr/android/gvr/gvr_device.h b/device/vr/android/gvr/gvr_device.h
index 20db1172..8ca44fa9 100644
--- a/device/vr/android/gvr/gvr_device.h
+++ b/device/vr/android/gvr/gvr_device.h
@@ -31,7 +31,8 @@
   void ExitPresent() override;
 
   void SubmitFrame(mojom::VRPosePtr pose) override;
-  void UpdateLayerBounds(mojom::VRLayerBoundsPtr left_bounds,
+  void UpdateLayerBounds(int16_t frame_index,
+                         mojom::VRLayerBoundsPtr left_bounds,
                          mojom::VRLayerBoundsPtr right_bounds) override;
   void GetVRVSyncProvider(mojom::VRVSyncProviderRequest request) override;
 
diff --git a/device/vr/test/fake_vr_device.cc b/device/vr/test/fake_vr_device.cc
index cd478b2..d0853de 100644
--- a/device/vr/test/fake_vr_device.cc
+++ b/device/vr/test/fake_vr_device.cc
@@ -73,7 +73,8 @@
 
 void FakeVRDevice::SubmitFrame(mojom::VRPosePtr pose) {}
 
-void FakeVRDevice::UpdateLayerBounds(mojom::VRLayerBoundsPtr leftBounds,
+void FakeVRDevice::UpdateLayerBounds(int16_t frame_index,
+                                     mojom::VRLayerBoundsPtr leftBounds,
                                      mojom::VRLayerBoundsPtr rightBounds) {}
 
 void FakeVRDevice::GetVRVSyncProvider(mojom::VRVSyncProviderRequest request) {}
diff --git a/device/vr/test/fake_vr_device.h b/device/vr/test/fake_vr_device.h
index 6d6acb3..188d82e 100644
--- a/device/vr/test/fake_vr_device.h
+++ b/device/vr/test/fake_vr_device.h
@@ -29,7 +29,8 @@
   void SetSecureOrigin(bool secure_origin) override;
   void ExitPresent() override;
   void SubmitFrame(mojom::VRPosePtr pose) override;
-  void UpdateLayerBounds(mojom::VRLayerBoundsPtr leftBounds,
+  void UpdateLayerBounds(int16_t frame_index,
+                         mojom::VRLayerBoundsPtr leftBounds,
                          mojom::VRLayerBoundsPtr rightBounds) override;
   void GetVRVSyncProvider(mojom::VRVSyncProviderRequest request) override;
 
diff --git a/device/vr/vr_device.h b/device/vr/vr_device.h
index 165bee0..d2ed8fa 100644
--- a/device/vr/vr_device.h
+++ b/device/vr/vr_device.h
@@ -30,7 +30,8 @@
   virtual void SetSecureOrigin(bool secure_origin) = 0;
   virtual void ExitPresent() = 0;
   virtual void SubmitFrame(mojom::VRPosePtr pose) = 0;
-  virtual void UpdateLayerBounds(mojom::VRLayerBoundsPtr left_bounds,
+  virtual void UpdateLayerBounds(int16_t frame_index,
+                                 mojom::VRLayerBoundsPtr left_bounds,
                                  mojom::VRLayerBoundsPtr right_bounds) = 0;
   virtual void GetVRVSyncProvider(mojom::VRVSyncProviderRequest request) = 0;
 
diff --git a/device/vr/vr_display_impl.cc b/device/vr/vr_display_impl.cc
index db268c9..c99b7dfc 100644
--- a/device/vr/vr_display_impl.cc
+++ b/device/vr/vr_display_impl.cc
@@ -67,12 +67,14 @@
   device_->SubmitFrame(std::move(pose));
 }
 
-void VRDisplayImpl::UpdateLayerBounds(mojom::VRLayerBoundsPtr left_bounds,
+void VRDisplayImpl::UpdateLayerBounds(int16_t frame_index,
+                                      mojom::VRLayerBoundsPtr left_bounds,
                                       mojom::VRLayerBoundsPtr right_bounds) {
   if (!device_->IsAccessAllowed(this))
     return;
 
-  device_->UpdateLayerBounds(std::move(left_bounds), std::move(right_bounds));
+  device_->UpdateLayerBounds(frame_index, std::move(left_bounds),
+                             std::move(right_bounds));
 }
 
 void VRDisplayImpl::GetVRVSyncProvider(mojom::VRVSyncProviderRequest request) {
diff --git a/device/vr/vr_display_impl.h b/device/vr/vr_display_impl.h
index 1e52a6e..02a1e1a 100644
--- a/device/vr/vr_display_impl.h
+++ b/device/vr/vr_display_impl.h
@@ -36,7 +36,8 @@
   void ExitPresent() override;
   void SubmitFrame(mojom::VRPosePtr pose) override;
 
-  void UpdateLayerBounds(mojom::VRLayerBoundsPtr left_bounds,
+  void UpdateLayerBounds(int16_t frame_index,
+                         mojom::VRLayerBoundsPtr left_bounds,
                          mojom::VRLayerBoundsPtr right_bounds) override;
   void GetVRVSyncProvider(mojom::VRVSyncProviderRequest request) override;
 
diff --git a/device/vr/vr_service.mojom b/device/vr/vr_service.mojom
index b7d85158..19717b8 100644
--- a/device/vr/vr_service.mojom
+++ b/device/vr/vr_service.mojom
@@ -1,5 +1,6 @@
 // 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.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
 
 module device.mojom;
 
@@ -23,9 +24,6 @@
   array<float, 3>? linearVelocity;
   array<float, 3>? angularAcceleration;
   array<float, 3>? linearAcceleration;
-  // The poseIndex is a sequential ID that's incremented on each distinct
-  // getPose result, it may wrap around for long sessions.
-  uint32 poseIndex;
 };
 
 struct VRDisplayCapabilities {
@@ -72,17 +70,19 @@
   UNMOUNTED = 3
 };
 
-// TODO(shaobo.yan@intel.com) : Add comments to describe these interfaces about how to use and where they live.
+// TODO(shaobo.yan@intel.com) : Add comments to describe these interfaces about
+// how to use and where they live.
 interface VRService {
-  // TODO(shaobo.yan@intel.com) : Use a factory function which took a VRServiceClient
-  // so we would never have a half-initialized VRService.
+  // TODO(shaobo.yan@intel.com) : Use a factory function which took a
+  // VRServiceClient so we would never have a half-initialized VRService.
   SetClient(VRServiceClient client) => (uint32 numberOfConnectedDevices);
   // Inform the service that the page is listening for vrdisplayactivate events.
   SetListeningForActivate(bool listening);
 };
 
 interface VRServiceClient {
-  OnDisplayConnected(VRDisplay display, VRDisplayClient& request, VRDisplayInfo displayInfo);
+  OnDisplayConnected(VRDisplay display, VRDisplayClient& request,
+                     VRDisplayInfo displayInfo);
 };
 
 interface VRDisplay {
@@ -91,12 +91,16 @@
   RequestPresent(bool secureOrigin) => (bool success);
   ExitPresent();
   SubmitFrame(VRPose? pose);
-  UpdateLayerBounds(VRLayerBounds leftBounds, VRLayerBounds rightBounds);
+  UpdateLayerBounds(int16 frameId, VRLayerBounds leftBounds,
+                    VRLayerBounds rightBounds);
   GetVRVSyncProvider(VRVSyncProvider& request);
 };
 
 interface VRVSyncProvider {
-  GetVSync() => (VRPose? pose, mojo.common.mojom.TimeDelta time);
+  // The frameId maps a VSync to a frame arriving from the compositor. IDs will
+  // be reused after the frame arrives from the compositor. Negative IDs imply
+  // no mapping.
+  GetVSync() => (VRPose? pose, mojo.common.mojom.TimeDelta time, int16 frameId);
 };
 
 interface VRDisplayClient {
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index bf50a92..8ee9a89 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -64,6 +64,8 @@
   sources = [
     "browser/api/cast_channel/cast_test_util.cc",
     "browser/api/cast_channel/cast_test_util.h",
+    "browser/api/declarative/test_rules_registry.cc",
+    "browser/api/declarative/test_rules_registry.h",
     "browser/api/dns/mock_host_resolver_creator.cc",
     "browser/api/dns/mock_host_resolver_creator.h",
     "browser/api/storage/settings_test_util.cc",
diff --git a/extensions/browser/api/declarative/BUILD.gn b/extensions/browser/api/declarative/BUILD.gn
index d5e6020..0dc1732b 100644
--- a/extensions/browser/api/declarative/BUILD.gn
+++ b/extensions/browser/api/declarative/BUILD.gn
@@ -14,8 +14,6 @@
     "rules_registry.h",
     "rules_registry_service.cc",
     "rules_registry_service.h",
-    "test_rules_registry.cc",
-    "test_rules_registry.h",
   ]
 
   deps = [
diff --git a/extensions/browser/api/hid/hid_api.cc b/extensions/browser/api/hid/hid_api.cc
index 48e0ac8..6f816ef 100644
--- a/extensions/browser/api/hid/hid_api.cc
+++ b/extensions/browser/api/hid/hid_api.cc
@@ -22,27 +22,6 @@
 #include "extensions/common/api/hid.h"
 #include "net/base/io_buffer.h"
 
-// The normal EXTENSION_FUNCTION_VALIDATE macro doesn't work well here. It's
-// used in functions that returns a bool. However, EXTENSION_FUNCTION_VALIDATE
-// returns a smart pointer on failure.
-//
-// With C++11, this is problematic since a smart pointer that uses explicit
-// operator bool won't allow this conversion, since it's not in a context (such
-// as a conditional) where a contextual conversion to bool would be allowed.
-// TODO(rdevlin.cronin): restructure this code to remove the need for the
-// additional macro.
-#ifdef NDEBUG
-#define EXTENSION_FUNCTION_VALIDATE_RETURN_FALSE_ON_ERROR(test) \
-  do {                                                          \
-    if (!(test)) {                                              \
-      this->set_bad_message(true);                              \
-      return false;                                             \
-    }                                                           \
-  } while (0)
-#else  // NDEBUG
-#define EXTENSION_FUNCTION_VALIDATE_RETURN_FALSE_ON_ERROR(test) CHECK(test)
-#endif  // NDEBUG
-
 namespace hid = extensions::api::hid;
 
 using device::HidConnection;
@@ -250,7 +229,7 @@
 }
 
 ExtensionFunction::ResponseAction HidConnectionIoFunction::Run() {
-  EXTENSION_FUNCTION_VALIDATE(ValidateParameters());
+  EXTENSION_FUNCTION_VALIDATE(ReadParameters());
 
   ApiResourceManager<HidConnectionResource>* connection_manager =
       ApiResourceManager<HidConnectionResource>::Get(browser_context());
@@ -270,9 +249,10 @@
 
 HidReceiveFunction::~HidReceiveFunction() {}
 
-bool HidReceiveFunction::ValidateParameters() {
+bool HidReceiveFunction::ReadParameters() {
   parameters_ = hid::Receive::Params::Create(*args_);
-  EXTENSION_FUNCTION_VALIDATE_RETURN_FALSE_ON_ERROR(parameters_);
+  if (!parameters_)
+    return false;
   set_connection_id(parameters_->connection_id);
   return true;
 }
@@ -300,9 +280,10 @@
 
 HidSendFunction::~HidSendFunction() {}
 
-bool HidSendFunction::ValidateParameters() {
+bool HidSendFunction::ReadParameters() {
   parameters_ = hid::Send::Params::Create(*args_);
-  EXTENSION_FUNCTION_VALIDATE_RETURN_FALSE_ON_ERROR(parameters_);
+  if (!parameters_)
+    return false;
   set_connection_id(parameters_->connection_id);
   return true;
 }
@@ -329,9 +310,10 @@
 
 HidReceiveFeatureReportFunction::~HidReceiveFeatureReportFunction() {}
 
-bool HidReceiveFeatureReportFunction::ValidateParameters() {
+bool HidReceiveFeatureReportFunction::ReadParameters() {
   parameters_ = hid::ReceiveFeatureReport::Params::Create(*args_);
-  EXTENSION_FUNCTION_VALIDATE_RETURN_FALSE_ON_ERROR(parameters_);
+  if (!parameters_)
+    return false;
   set_connection_id(parameters_->connection_id);
   return true;
 }
@@ -358,9 +340,10 @@
 
 HidSendFeatureReportFunction::~HidSendFeatureReportFunction() {}
 
-bool HidSendFeatureReportFunction::ValidateParameters() {
+bool HidSendFeatureReportFunction::ReadParameters() {
   parameters_ = hid::SendFeatureReport::Params::Create(*args_);
-  EXTENSION_FUNCTION_VALIDATE_RETURN_FALSE_ON_ERROR(parameters_);
+  if (!parameters_)
+    return false;
   set_connection_id(parameters_->connection_id);
   return true;
 }
diff --git a/extensions/browser/api/hid/hid_api.h b/extensions/browser/api/hid/hid_api.h
index f98677e..1d00566 100644
--- a/extensions/browser/api/hid/hid_api.h
+++ b/extensions/browser/api/hid/hid_api.h
@@ -112,7 +112,8 @@
  protected:
   ~HidConnectionIoFunction() override;
 
-  virtual bool ValidateParameters() = 0;
+  // Returns true if params were successfully read from |args_|.
+  virtual bool ReadParameters() = 0;
   virtual void StartWork(device::HidConnection* connection) = 0;
 
   void set_connection_id(int connection_id) { connection_id_ = connection_id; }
@@ -134,7 +135,7 @@
   ~HidReceiveFunction() override;
 
   // HidConnectionIoFunction:
-  bool ValidateParameters() override;
+  bool ReadParameters() override;
   void StartWork(device::HidConnection* connection) override;
 
   void OnFinished(bool success,
@@ -156,7 +157,7 @@
   ~HidSendFunction() override;
 
   // HidConnectionIoFunction:
-  bool ValidateParameters() override;
+  bool ReadParameters() override;
   void StartWork(device::HidConnection* connection) override;
 
   void OnFinished(bool success);
@@ -177,7 +178,7 @@
   ~HidReceiveFeatureReportFunction() override;
 
   // HidConnectionIoFunction:
-  bool ValidateParameters() override;
+  bool ReadParameters() override;
   void StartWork(device::HidConnection* connection) override;
 
   void OnFinished(bool success,
@@ -199,7 +200,7 @@
   ~HidSendFeatureReportFunction() override;
 
   // HidConnectionIoFunction:
-  bool ValidateParameters() override;
+  bool ReadParameters() override;
   void StartWork(device::HidConnection* connection) override;
 
   void OnFinished(bool success);
diff --git a/gin/BUILD.gn b/gin/BUILD.gn
index 362ab0e..5f0dc5d1 100644
--- a/gin/BUILD.gn
+++ b/gin/BUILD.gn
@@ -204,6 +204,7 @@
     "test/run_all_unittests.cc",
     "test/run_js_tests.cc",
     "v8_isolate_memory_dump_provider_unittest.cc",
+    "v8_platform_unittest.cc",
     "wrappable_unittest.cc",
   ]
 
diff --git a/gin/v8_platform.cc b/gin/v8_platform.cc
index 6e5491bf..4fe7c7c 100644
--- a/gin/v8_platform.cc
+++ b/gin/v8_platform.cc
@@ -217,12 +217,18 @@
   }
 
   void AddObserver(v8::Platform::TraceStateObserver* observer) {
-    base::AutoLock lock(mutex_);
-    DCHECK(!observers_.count(observer));
-    observers_.insert(observer);
-    if (observers_.size() == 1) {
-      base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
+    {
+      base::AutoLock lock(mutex_);
+      DCHECK(!observers_.count(observer));
+      if (observers_.empty()) {
+        base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(
+            this);
+      }
+      observers_.insert(observer);
     }
+    // Fire the observer if recording is already in progress.
+    if (base::trace_event::TraceLog::GetInstance()->IsEnabled())
+      observer->OnTraceEnabled();
   }
 
   void RemoveObserver(v8::Platform::TraceStateObserver* observer) {
diff --git a/gin/v8_platform_unittest.cc b/gin/v8_platform_unittest.cc
new file mode 100644
index 0000000..52d58624
--- /dev/null
+++ b/gin/v8_platform_unittest.cc
@@ -0,0 +1,62 @@
+// 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 "gin/public/v8_platform.h"
+
+#include "base/trace_event/trace_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestTraceStateObserver
+    : public NON_EXPORTED_BASE(v8::Platform::TraceStateObserver) {
+ public:
+  void OnTraceEnabled() final { ++enabled_; }
+  void OnTraceDisabled() final { ++disabled_; }
+  int Enabled() { return enabled_; }
+  int Disabled() { return disabled_; }
+
+ private:
+  int enabled_ = 0;
+  int disabled_ = 0;
+};
+
+namespace gin {
+
+TEST(V8PlatformTest, TraceStateObserverAPI) {
+  TestTraceStateObserver* test_observer = new TestTraceStateObserver();
+  ASSERT_EQ(0, test_observer->Enabled());
+  ASSERT_EQ(0, test_observer->Disabled());
+
+  V8Platform::Get()->AddTraceStateObserver(test_observer);
+  base::trace_event::TraceLog::GetInstance()->SetEnabled(
+      base::trace_event::TraceConfig("*", ""),
+      base::trace_event::TraceLog::RECORDING_MODE);
+  ASSERT_EQ(1, test_observer->Enabled());
+  ASSERT_EQ(0, test_observer->Disabled());
+  base::trace_event::TraceLog::GetInstance()->SetDisabled();
+  ASSERT_EQ(1, test_observer->Enabled());
+  ASSERT_EQ(1, test_observer->Disabled());
+
+  V8Platform::Get()->RemoveTraceStateObserver(test_observer);
+  base::trace_event::TraceLog::GetInstance()->SetEnabled(
+      base::trace_event::TraceConfig("*", ""),
+      base::trace_event::TraceLog::RECORDING_MODE);
+  base::trace_event::TraceLog::GetInstance()->SetDisabled();
+  ASSERT_EQ(1, test_observer->Enabled());
+  ASSERT_EQ(1, test_observer->Disabled());
+}
+
+TEST(V8PlatformTest, TraceStateObserverFired) {
+  TestTraceStateObserver* test_observer = new TestTraceStateObserver();
+  ASSERT_EQ(0, test_observer->Enabled());
+  ASSERT_EQ(0, test_observer->Disabled());
+
+  base::trace_event::TraceLog::GetInstance()->SetEnabled(
+      base::trace_event::TraceConfig("*", ""),
+      base::trace_event::TraceLog::RECORDING_MODE);
+  V8Platform::Get()->AddTraceStateObserver(test_observer);
+  ASSERT_EQ(1, test_observer->Enabled());
+  ASSERT_EQ(0, test_observer->Disabled());
+}
+
+}  // namespace gin
diff --git a/headless/public/util/generic_url_request_job_test.cc b/headless/public/util/generic_url_request_job_test.cc
index e1942f8..79dc9e2 100644
--- a/headless/public/util/generic_url_request_job_test.cc
+++ b/headless/public/util/generic_url_request_job_test.cc
@@ -281,7 +281,7 @@
       base::Time(), base::Time(),
       /* secure */ false,
       /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
-      /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+      net::COOKIE_PRIORITY_DEFAULT));
 
   // Matching secure cookie.
   cookies->push_back(*net::CanonicalCookie::Create(
@@ -289,7 +289,7 @@
       base::Time(), base::Time(),
       /* secure */ true,
       /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
-      /* enforce_strict_secure */ true, net::COOKIE_PRIORITY_DEFAULT));
+      net::COOKIE_PRIORITY_DEFAULT));
 
   // Matching http-only cookie.
   cookies->push_back(*net::CanonicalCookie::Create(
@@ -297,7 +297,7 @@
       base::Time(), base::Time(),
       /* secure */ false,
       /* http_only */ true, net::CookieSameSite::NO_RESTRICTION,
-      /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+      net::COOKIE_PRIORITY_DEFAULT));
 
   // Matching cookie with path.
   cookies->push_back(*net::CanonicalCookie::Create(
@@ -305,7 +305,7 @@
       "/widgets", base::Time(), base::Time(),
       /* secure */ false,
       /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
-      /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+      net::COOKIE_PRIORITY_DEFAULT));
 
   // Matching cookie with subdomain.
   cookies->push_back(*net::CanonicalCookie::Create(
@@ -313,7 +313,7 @@
       "cdn.example.com", "/", base::Time(), base::Time(),
       /* secure */ false,
       /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
-      /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+      net::COOKIE_PRIORITY_DEFAULT));
 
   // Non-matching cookie (different site).
   cookies->push_back(*net::CanonicalCookie::Create(
@@ -321,7 +321,7 @@
       base::Time(), base::Time(),
       /* secure */ false,
       /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
-      /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+      net::COOKIE_PRIORITY_DEFAULT));
 
   // Non-matching cookie (different path).
   cookies->push_back(*net::CanonicalCookie::Create(
@@ -329,7 +329,7 @@
       "/gadgets", base::Time(), base::Time(),
       /* secure */ false,
       /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
-      /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+      net::COOKIE_PRIORITY_DEFAULT));
 
   std::string reply =
       "{\"url\":\"https://example.com\","
diff --git a/headless/public/util/testing/generic_url_request_mocks.cc b/headless/public/util/testing/generic_url_request_mocks.cc
index adcabbe..b9f94c1 100644
--- a/headless/public/util/testing/generic_url_request_mocks.cc
+++ b/headless/public/util/testing/generic_url_request_mocks.cc
@@ -65,7 +65,6 @@
     bool secure,
     bool http_only,
     net::CookieSameSite same_site,
-    bool enforce_strict_secure,
     net::CookiePriority priority,
     const SetCookiesCallback& callback) {
   CHECK(false);
diff --git a/headless/public/util/testing/generic_url_request_mocks.h b/headless/public/util/testing/generic_url_request_mocks.h
index fb8f0be..4727961 100644
--- a/headless/public/util/testing/generic_url_request_mocks.h
+++ b/headless/public/util/testing/generic_url_request_mocks.h
@@ -69,7 +69,6 @@
                                  bool secure,
                                  bool http_only,
                                  net::CookieSameSite same_site,
-                                 bool enforce_strict_secure,
                                  net::CookiePriority priority,
                                  const SetCookiesCallback& callback) override;
 
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 2c41052..7684aa2 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -6,6 +6,7 @@
 
 #include "base/critical_closure.h"
 #import "base/mac/bind_objc_block.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/metrics/metrics_service.h"
 #import "ios/chrome/app/main_application_delegate.h"
 #import "ios/chrome/app/application_delegate/app_navigation.h"
diff --git a/ios/chrome/app/application_delegate/memory_warning_helper.mm b/ios/chrome/app/application_delegate/memory_warning_helper.mm
index d8eeb93..a3a3c38b 100644
--- a/ios/chrome/app/application_delegate/memory_warning_helper.mm
+++ b/ios/chrome/app/application_delegate/memory_warning_helper.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/app/application_delegate/memory_warning_helper.h"
 
 #include "base/memory/memory_pressure_listener.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "ios/chrome/browser/crash_report/breakpad_helper.h"
 #import "ios/chrome/browser/metrics/previous_session_info.h"
 
diff --git a/ios/chrome/app/application_delegate/metrics_mediator.mm b/ios/chrome/app/application_delegate/metrics_mediator.mm
index 0f0385e1..3b5ecee 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/app/application_delegate/metrics_mediator.h"
 
 #include "base/mac/bind_objc_block.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/crash/core/common/crash_keys.h"
diff --git a/ios/chrome/app/chrome_app_startup_parameters.mm b/ios/chrome/app/chrome_app_startup_parameters.mm
index b00dc5c..f2384cf 100644
--- a/ios/chrome/app/chrome_app_startup_parameters.mm
+++ b/ios/chrome/app/chrome_app_startup_parameters.mm
@@ -7,7 +7,7 @@
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
diff --git a/ios/chrome/app/spotlight/actions_spotlight_manager.mm b/ios/chrome/app/spotlight/actions_spotlight_manager.mm
index 6b010f2..e26959db 100644
--- a/ios/chrome/app/spotlight/actions_spotlight_manager.mm
+++ b/ios/chrome/app/spotlight/actions_spotlight_manager.mm
@@ -8,7 +8,7 @@
 
 #include "base/ios/weak_nsobject.h"
 #include "base/mac/foundation_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/app_startup_parameters.h"
 #include "ios/chrome/browser/experimental_flags.h"
diff --git a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
index edbfd6a..7cee4eb9 100644
--- a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
+++ b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
@@ -9,7 +9,7 @@
 #import <CoreSpotlight/CoreSpotlight.h>
 
 #include "base/ios/weak_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/version.h"
 #include "components/bookmarks/browser/base_bookmark_model_observer.h"
diff --git a/ios/chrome/app/spotlight/spotlight_util.mm b/ios/chrome/app/spotlight/spotlight_util.mm
index 93b24168d..fd33c146 100644
--- a/ios/chrome/app/spotlight/spotlight_util.mm
+++ b/ios/chrome/app/spotlight/spotlight_util.mm
@@ -6,7 +6,7 @@
 
 #import <CoreSpotlight/CoreSpotlight.h>
 
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/spotlight/spotlight_provider.h"
diff --git a/ios/chrome/browser/crash_report/crash_report_background_uploader.mm b/ios/chrome/browser/crash_report/crash_report_background_uploader.mm
index dc1fa999..c0a63d6 100644
--- a/ios/chrome/browser/crash_report/crash_report_background_uploader.mm
+++ b/ios/chrome/browser/crash_report/crash_report_background_uploader.mm
@@ -9,7 +9,7 @@
 #include "base/logging.h"
 #include "base/mac/scoped_block.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/time/time.h"
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
index b0a42be..12efac9 100644
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
+++ b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
@@ -12,7 +12,7 @@
 #import "base/ios/weak_nsobject.h"
 #include "base/logging.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/version.h"
 #include "components/google/core/browser/google_util.h"
 #include "components/version_info/version_info.h"
diff --git a/ios/chrome/browser/installation_notifier.mm b/ios/chrome/browser/installation_notifier.mm
index 9a1f3b7e..d68fc922 100644
--- a/ios/chrome/browser/installation_notifier.mm
+++ b/ios/chrome/browser/installation_notifier.mm
@@ -10,7 +10,7 @@
 #include <memory>
 
 #include "base/logging.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "ios/web/public/web_thread.h"
 #include "net/base/backoff_entry.h"
 #include "url/gurl.h"
diff --git a/ios/chrome/browser/metrics/first_user_action_recorder.cc b/ios/chrome/browser/metrics/first_user_action_recorder.cc
index ab1d6bf..b85e8fa 100644
--- a/ios/chrome/browser/metrics/first_user_action_recorder.cc
+++ b/ios/chrome/browser/metrics/first_user_action_recorder.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "base/location.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
index e4d23c7..7cbd896 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
@@ -15,7 +15,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/process/process_metrics.h"
 #include "base/rand_util.h"
 #include "base/strings/string16.h"
diff --git a/ios/chrome/browser/metrics/new_tab_page_uma.mm b/ios/chrome/browser/metrics/new_tab_page_uma.mm
index 47375cc..8405afa1 100644
--- a/ios/chrome/browser/metrics/new_tab_page_uma.mm
+++ b/ios/chrome/browser/metrics/new_tab_page_uma.mm
@@ -4,7 +4,7 @@
 
 #import "ios/chrome/browser/metrics/new_tab_page_uma.h"
 
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/google/core/browser/google_util.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder.mm b/ios/chrome/browser/metrics/tab_usage_recorder.mm
index 34733bc..14405ac 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder.mm
+++ b/ios/chrome/browser/metrics/tab_usage_recorder.mm
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/metrics/tab_usage_recorder.h"
 
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/metrics/previous_session_info.h"
 #import "ios/chrome/browser/tabs/tab.h"
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
index fe6d78da..5df921a 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
@@ -118,10 +118,6 @@
   PrefService* prefs = chrome_browser_state->GetPrefs();
 
   // Create the ContentSuggestionsService.
-  State state =
-      base::FeatureList::IsEnabled(ntp_snippets::kContentSuggestionsFeature)
-          ? State::ENABLED
-          : State::DISABLED;
   SigninManager* signin_manager =
       ios::SigninManagerFactory::GetForBrowserState(chrome_browser_state);
   HistoryService* history_service =
@@ -131,11 +127,9 @@
       ntp_snippets::BuildSelectedCategoryRanker(
           prefs, base::MakeUnique<base::DefaultClock>());
   std::unique_ptr<ContentSuggestionsService> service =
-      base::MakeUnique<ContentSuggestionsService>(state, signin_manager,
-                                                  history_service, prefs,
-                                                  std::move(category_ranker));
-  if (state == State::DISABLED)
-    return std::move(service);
+      base::MakeUnique<ContentSuggestionsService>(
+          State::ENABLED, signin_manager, history_service, prefs,
+          std::move(category_ranker));
 
   // Create the BookmarkSuggestionsProvider.
   if (base::FeatureList::IsEnabled(ntp_snippets::kBookmarkSuggestionsFeature)) {
diff --git a/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.mm b/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.mm
index 623a8ef8..a05bbb4 100644
--- a/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.mm
+++ b/ios/chrome/browser/physical_web/physical_web_initial_state_recorder.mm
@@ -8,7 +8,7 @@
 #import <CoreLocation/CoreLocation.h>
 
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/physical_web/physical_web_constants.h"
 #include "ios/chrome/browser/pref_names.h"
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
index 0cbc02d..89e9c86 100644
--- a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
+++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
@@ -192,7 +192,7 @@
   try_number_ = 0;
   timer_.reset(new base::Timer(false, true));
   const base::TimeDelta kDelayUntilLoadingProgressIsChecked =
-      base::TimeDelta::FromSeconds(1);
+      base::TimeDelta::FromMilliseconds(1500);
   timer_->Start(
       FROM_HERE, kDelayUntilLoadingProgressIsChecked,
       base::Bind(
diff --git a/ios/chrome/browser/share_extension/share_extension_item_receiver.mm b/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
index 2a2d88a..3273a20e 100644
--- a/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
+++ b/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
@@ -10,7 +10,7 @@
 #include "base/mac/bind_objc_block.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ios/chrome/browser/signin/authentication_service.mm b/ios/chrome/browser/signin/authentication_service.mm
index ad5b717..37501c3 100644
--- a/ios/chrome/browser/signin/authentication_service.mm
+++ b/ios/chrome/browser/signin/authentication_service.mm
@@ -8,7 +8,7 @@
 
 #include "base/auto_reset.h"
 #include "base/location.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
diff --git a/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm b/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
index 7a3d65f3..46ce678f 100644
--- a/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
+++ b/ios/chrome/browser/ssl/ios_ssl_blocking_page.mm
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/security_interstitials/core/metrics_helper.h"
diff --git a/ios/chrome/browser/ssl/ios_ssl_error_handler.mm b/ios/chrome/browser/ssl/ios_ssl_error_handler.mm
index d856e38..4b28441 100644
--- a/ios/chrome/browser/ssl/ios_ssl_error_handler.mm
+++ b/ios/chrome/browser/ssl/ios_ssl_error_handler.mm
@@ -7,7 +7,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/mac/bind_objc_block.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/captive_portal/captive_portal_detector.h"
 #include "components/security_interstitials/core/ssl_error_ui.h"
 #include "ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h"
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index e2b6d8c..5d6f32ce 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -19,7 +19,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/objc_property_releaser.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/scoped_observer.h"
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index b42dfb37..0d324f9 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -12,7 +12,7 @@
 #import "base/ios/crb_protocol_observers.h"
 #include "base/logging.h"
 #import "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
index a1b8c467..49dbd24 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index f38c59b5..07a0021d 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -30,7 +30,7 @@
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/chrome/browser/ui/contextual_search/contextual_search_metrics.mm b/ios/chrome/browser/ui/contextual_search/contextual_search_metrics.mm
index 0dd4438..53449fb6 100644
--- a/ios/chrome/browser/ui/contextual_search/contextual_search_metrics.mm
+++ b/ios/chrome/browser/ui/contextual_search/contextual_search_metrics.mm
@@ -6,7 +6,7 @@
 
 #include <map>
 
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/time/time.h"
 
diff --git a/ios/chrome/browser/ui/downloads/download_manager_controller.mm b/ios/chrome/browser/ui/downloads/download_manager_controller.mm
index 95b28be..96cede0 100644
--- a/ios/chrome/browser/ui/downloads/download_manager_controller.mm
+++ b/ios/chrome/browser/ui/downloads/download_manager_controller.mm
@@ -15,7 +15,7 @@
 #include "base/mac/objc_property_releaser.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/ref_counted.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
diff --git a/ios/chrome/browser/ui/first_run/first_run_util.mm b/ios/chrome/browser/ui/first_run/first_run_util.mm
index fa64cbc..7fa7cfc 100644
--- a/ios/chrome/browser/ui/first_run/first_run_util.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_util.mm
@@ -6,9 +6,9 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/time/time.h"
-
 #include "components/signin/core/browser/signin_manager.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/crash_report/breakpad_helper.h"
@@ -23,6 +23,7 @@
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/web/public/web_thread.h"
 #import "ui/gfx/ios/NSString+CrStringDrawing.h"
+
 NSString* const kChromeFirstRunUIWillFinishNotification =
     @"kChromeFirstRunUIWillFinishNotification";
 
diff --git a/ios/chrome/browser/ui/history/history_service_facade.mm b/ios/chrome/browser/ui/history/history_service_facade.mm
index 26d0251..d228b76 100644
--- a/ios/chrome/browser/ui/history/history_service_facade.mm
+++ b/ios/chrome/browser/ui/history/history_service_facade.mm
@@ -11,7 +11,7 @@
 #include "base/i18n/time_formatting.h"
 #include "base/mac/bind_objc_block.h"
 #include "base/memory/weak_ptr.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ios/chrome/browser/ui/ntp/google_landing_controller.mm b/ios/chrome/browser/ui/ntp/google_landing_controller.mm
index edec1eab..72bf5d67 100644
--- a/ios/chrome/browser/ui/ntp/google_landing_controller.mm
+++ b/ios/chrome/browser/ui/ntp/google_landing_controller.mm
@@ -13,7 +13,7 @@
 #include "base/mac/bind_objc_block.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.mm
index a27ab4490..af1dac78 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.mm
@@ -8,7 +8,7 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "components/omnibox/browser/autocomplete_match.h"
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
index 607b18e..e74c2c9 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
@@ -10,7 +10,7 @@
 #include "base/logging.h"
 #include "base/mac/objc_property_releaser.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
diff --git a/ios/chrome/browser/ui/preload_controller.mm b/ios/chrome/browser/ui/preload_controller.mm
index 5c4c43d3..bd46dc6 100644
--- a/ios/chrome/browser/ui/preload_controller.mm
+++ b/ios/chrome/browser/ui/preload_controller.mm
@@ -9,7 +9,7 @@
 #include "base/ios/device_util.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
diff --git a/ios/chrome/browser/ui/promos/signin_promo_view_controller.mm b/ios/chrome/browser/ui/promos/signin_promo_view_controller.mm
index a9c154ceb..b1cebbb9 100644
--- a/ios/chrome/browser/ui/promos/signin_promo_view_controller.mm
+++ b/ios/chrome/browser/ui/promos/signin_promo_view_controller.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/ui/promos/signin_promo_view_controller.h"
 
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/version.h"
diff --git a/ios/chrome/browser/ui/settings/native_apps_collection_view_controller.mm b/ios/chrome/browser/ui/settings/native_apps_collection_view_controller.mm
index 77f1eb36..aba164f 100644
--- a/ios/chrome/browser/ui/settings/native_apps_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/native_apps_collection_view_controller.mm
@@ -11,7 +11,7 @@
 #include "base/logging.h"
 #import "base/mac/foundation_util.h"
 #import "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/chrome/browser/ui/stack_view/stack_view_controller.mm b/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
index 190b7ca..7df24be6 100644
--- a/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
+++ b/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
@@ -19,7 +19,7 @@
 #import "base/mac/objc_property_releaser.h"
 #include "base/mac/scoped_block.h"
 #import "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/chrome/browser/ui/sync/sync_util.mm b/ios/chrome/browser/ui/sync/sync_util.mm
index 0e82b796..0ff0f5a 100644
--- a/ios/chrome/browser/ui/sync/sync_util.mm
+++ b/ios/chrome/browser/ui/sync/sync_util.mm
@@ -4,7 +4,7 @@
 
 #import "ios/chrome/browser/ui/sync/sync_util.h"
 
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/infobars/core/infobar_manager.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
index 22febcab..db8af15 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -277,6 +277,11 @@
 
 // Verifies that copying and pasting a URL includes the hidden protocol prefix.
 - (void)testCopyPasteURL {
+  // TODO(crbug.com/686069): Re-enable this test.  It is failing on iOS 9.
+  if (!base::ios::IsRunningOnIOS10OrLater()) {
+    EARL_GREY_TEST_DISABLED(@"Disabled on iOS 9.");
+  }
+
   std::map<GURL, std::string> responses;
   const GURL URL = web::test::HttpServer::MakeUrl("http://testPage");
   const GURL secondURL = web::test::HttpServer::MakeUrl("http://pastePage");
diff --git a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
index 73cacaa..498ce10 100644
--- a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
@@ -17,7 +17,7 @@
 #include "base/logging.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/chrome/browser/web/external_app_launcher.mm b/ios/chrome/browser/web/external_app_launcher.mm
index 0af7de4..74b50230 100644
--- a/ios/chrome/browser/web/external_app_launcher.mm
+++ b/ios/chrome/browser/web/external_app_launcher.mm
@@ -6,7 +6,7 @@
 
 #include "base/ios/weak_nsobject.h"
 #include "base/logging.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/experimental_flags.h"
diff --git a/ios/chrome/common/physical_web/physical_web_request.mm b/ios/chrome/common/physical_web/physical_web_request.mm
index 18c43f30..cbee220c 100644
--- a/ios/chrome/common/physical_web/physical_web_request.mm
+++ b/ios/chrome/common/physical_web/physical_web_request.mm
@@ -9,7 +9,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_block.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/version_info/version_info.h"
 #include "google_apis/google_api_keys.h"
diff --git a/ios/chrome/test/app/histogram_test_util.mm b/ios/chrome/test/app/histogram_test_util.mm
index 3c67f631..000415e 100644
--- a/ios/chrome/test/app/histogram_test_util.mm
+++ b/ios/chrome/test/app/histogram_test_util.mm
@@ -7,7 +7,7 @@
 #import <Foundation/Foundation.h>
 
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/histogram_samples.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/metrics/sample_map.h"
diff --git a/ios/chrome/today_extension/today_metrics_logger.mm b/ios/chrome/today_extension/today_metrics_logger.mm
index 08093ddc..6fcfa802 100644
--- a/ios/chrome/today_extension/today_metrics_logger.mm
+++ b/ios/chrome/today_extension/today_metrics_logger.mm
@@ -11,6 +11,7 @@
 #include "base/mac/bind_objc_block.h"
 #import "base/mac/scoped_nsobject.h"
 #include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ios/chrome/today_extension/today_view_controller.mm b/ios/chrome/today_extension/today_view_controller.mm
index 69325cc8..eedd5c1c 100644
--- a/ios/chrome/today_extension/today_view_controller.mm
+++ b/ios/chrome/today_extension/today_view_controller.mm
@@ -18,6 +18,7 @@
 #include "base/mac/foundation_util.h"
 #import "base/mac/scoped_block.h"
 #import "base/mac/scoped_nsobject.h"
+#include "base/metrics/histogram_macros.h"
 #import "base/metrics/user_metrics_action.h"
 #import "base/path_service.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/clean/chrome/browser/ui/actions/tools_menu_actions.h b/ios/clean/chrome/browser/ui/actions/tools_menu_actions.h
index 75903b8..cd61a13 100644
--- a/ios/clean/chrome/browser/ui/actions/tools_menu_actions.h
+++ b/ios/clean/chrome/browser/ui/actions/tools_menu_actions.h
@@ -25,6 +25,9 @@
 // Displays the Tools menu.
 - (void)showToolsMenu:(id)sender;
 
+// Displays the Share menu.
+- (void)showShareMenu:(id)sender;
+
 @end
 
 #endif  // IOS_CLEAN_CHROME_BROWSER_UI_ACTIONS_TOOLS_MENU_ACTIONS_H_
diff --git a/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.mm b/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.mm
index bd98c14..c45b32a 100644
--- a/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.mm
+++ b/ios/clean/chrome/browser/ui/tab/tab_container_view_controller.mm
@@ -16,7 +16,7 @@
 #endif
 
 namespace {
-CGFloat kToolbarHeight = 56.0;
+CGFloat kToolbarHeight = 44.0;
 }
 
 @interface TabContainerViewController ()
@@ -275,9 +275,14 @@
 }
 
 - (void)updateToolbarConstraints {
+  // HACK: This background is added so the status bar portion of the view is not
+  // transparent. This needs to be implemented properly for the top toolbar
+  // case.
+  self.view.backgroundColor = [UIColor lightGrayColor];
   UIView* toolbarView = self.toolbarViewController.view;
   self.toolbarConstraints = @[
-    [toolbarView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
+    [toolbarView.topAnchor constraintEqualToAnchor:self.view.topAnchor
+                                          constant:20.0],
     [toolbarView.heightAnchor constraintEqualToConstant:kToolbarHeight],
     [toolbarView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
     [toolbarView.trailingAnchor
diff --git a/ios/clean/chrome/browser/ui/toolbar/toolbar_view_controller.mm b/ios/clean/chrome/browser/ui/toolbar/toolbar_view_controller.mm
index 4ffb1376a..6b4b556 100644
--- a/ios/clean/chrome/browser/ui/toolbar/toolbar_view_controller.mm
+++ b/ios/clean/chrome/browser/ui/toolbar/toolbar_view_controller.mm
@@ -20,57 +20,36 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// Stackview Vertical Margin.
+CGFloat kVerticalMargin = 5.0f;
+}  // namespace
+
 @interface ToolbarViewController ()<ToolsMenuActions>
 @property(nonatomic, weak) UITextField* omnibox;
-@property(nonatomic, weak) UIButton* toolsMenu;
+@property(nonatomic, strong) UIButton* backButton;
+@property(nonatomic, strong) UIButton* forwardButton;
+@property(nonatomic, strong) UIButton* tabSwitcherButton;
+@property(nonatomic, strong) UIButton* toolsMenuButton;
+@property(nonatomic, strong) UIButton* shareButton;
+@property(nonatomic, strong) UIButton* reloadButton;
 @end
 
 @implementation ToolbarViewController
 @synthesize toolbarCommandHandler = _toolbarCommandHandler;
 @synthesize omnibox = _omnibox;
-@synthesize toolsMenu = _toolsMenu;
+@synthesize backButton = _backButton;
+@synthesize forwardButton = _forwardButton;
+@synthesize tabSwitcherButton = _tabSwitcherButton;
+@synthesize toolsMenuButton = _toolsMenuButton;
+@synthesize shareButton = _shareButton;
+@synthesize reloadButton = _reloadButton;
 
 - (void)viewDidLoad {
   self.view.backgroundColor = [UIColor lightGrayColor];
 
-  // Navigation buttons
-  UIButton* backButton = [UIButton buttonWithType:UIButtonTypeSystem];
-  backButton.translatesAutoresizingMaskIntoConstraints = NO;
-  [backButton setImage:NativeReversableImage(IDR_IOS_TOOLBAR_LIGHT_BACK, YES)
-              forState:UIControlStateNormal];
-  [backButton
-      setImage:NativeReversableImage(IDR_IOS_TOOLBAR_LIGHT_BACK_PRESSED, YES)
-      forState:UIControlStateHighlighted];
-  [backButton
-      setImage:NativeReversableImage(IDR_IOS_TOOLBAR_LIGHT_BACK_DISABLED, YES)
-      forState:UIControlStateDisabled];
-  [backButton addTarget:nil
-                 action:@selector(goBack:)
-       forControlEvents:UIControlEventTouchUpInside];
-
-  UIButton* forwardButton = [UIButton buttonWithType:UIButtonTypeSystem];
-  forwardButton.translatesAutoresizingMaskIntoConstraints = NO;
-  [forwardButton
-      setImage:NativeReversableImage(IDR_IOS_TOOLBAR_LIGHT_FORWARD, YES)
-      forState:UIControlStateNormal];
-  [forwardButton
-      setImage:NativeReversableImage(IDR_IOS_TOOLBAR_LIGHT_FORWARD_PRESSED, YES)
-      forState:UIControlStateHighlighted];
-  [forwardButton setImage:NativeReversableImage(
-                              IDR_IOS_TOOLBAR_LIGHT_FORWARD_DISABLED, YES)
-                 forState:UIControlStateDisabled];
-  [forwardButton addTarget:nil
-                    action:@selector(goForward:)
-          forControlEvents:UIControlEventTouchUpInside];
-
-  // Tab switcher button.
-  UIButton* tabSwitcher = [UIButton buttonWithType:UIButtonTypeSystem];
-  tabSwitcher.translatesAutoresizingMaskIntoConstraints = NO;
-  [tabSwitcher setImage:[UIImage imageNamed:@"tabswitcher_tab_switcher_button"]
-               forState:UIControlStateNormal];
-  [tabSwitcher addTarget:nil
-                  action:@selector(toggleTabStrip:)
-        forControlEvents:UIControlEventTouchUpInside];
+  [self setUpToolbarButtons];
+  [self setUpRegularWidthToolbarButtons];
 
   // Placeholder omnibox.
   UITextField* omnibox = [[UITextField alloc] initWithFrame:CGRectZero];
@@ -79,37 +58,119 @@
   omnibox.enabled = NO;
   self.omnibox = omnibox;
 
-  // Tools menu button.
-  UIButton* toolsMenu = [UIButton buttonWithType:UIButtonTypeSystem];
-  toolsMenu.translatesAutoresizingMaskIntoConstraints = NO;
-  [toolsMenu setImageEdgeInsets:UIEdgeInsetsMakeDirected(0, -3, 0, 0)];
-  [toolsMenu
-      setImage:[[UIImage imageNamed:@"tabswitcher_menu"]
-                   imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]
-      forState:UIControlStateNormal];
-  [toolsMenu addTarget:nil
-                action:@selector(showToolsMenu:)
-      forControlEvents:UIControlEventTouchUpInside];
-  self.toolsMenu = toolsMenu;
-
   // Stack view to contain toolbar items.
   UIStackView* toolbarItems = [[UIStackView alloc] initWithArrangedSubviews:@[
-    backButton, forwardButton, omnibox, tabSwitcher, toolsMenu
+    self.backButton, self.forwardButton, self.reloadButton, omnibox,
+    self.shareButton, self.tabSwitcherButton, self.toolsMenuButton
   ]];
+  [self hideButtonsForSize:self.view.bounds.size];
   toolbarItems.translatesAutoresizingMaskIntoConstraints = NO;
   toolbarItems.spacing = 16.0;
   toolbarItems.distribution = UIStackViewDistributionFillProportionally;
   [self.view addSubview:toolbarItems];
+
+  // Set constraints.
   [NSLayoutConstraint activateConstraints:@[
+    [toolbarItems.topAnchor constraintEqualToAnchor:self.view.topAnchor
+                                           constant:kVerticalMargin],
+    [toolbarItems.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor
+                                              constant:-kVerticalMargin],
     [toolbarItems.leadingAnchor
         constraintEqualToAnchor:self.view.layoutMarginsGuide.leadingAnchor],
     [toolbarItems.trailingAnchor
         constraintEqualToAnchor:self.view.layoutMarginsGuide.trailingAnchor],
-    [toolbarItems.bottomAnchor
-        constraintEqualToAnchor:self.view.layoutMarginsGuide.bottomAnchor],
   ]];
 }
 
+#pragma mark - UIContentContainer
+
+- (void)viewWillTransitionToSize:(CGSize)size
+       withTransitionCoordinator:
+           (id<UIViewControllerTransitionCoordinator>)coordinator {
+  [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
+  [self hideButtonsForSize:size];
+}
+
+#pragma mark - View Setup
+
+// Hide buttons for different Size Classes.
+- (void)hideButtonsForSize:(CGSize)size {
+  // PLACEHOLDER: Create a method that checks the Class Size using CGSize and
+  // device as parameters. This could take place in a subclass of UIButton.
+  if (size.width <= 670.0) {
+    self.shareButton.hidden = YES;
+    self.reloadButton.hidden = YES;
+  } else {
+    self.shareButton.hidden = NO;
+    self.reloadButton.hidden = NO;
+  }
+}
+
+- (void)setUpToolbarButtons {
+  // Back button
+  self.backButton = [self
+      buttonWithImageForNormalState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_BACK, YES)
+                   highlightedState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_BACK_PRESSED, YES)
+                      disabledState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_BACK_DISABLED,
+                                        YES)
+                             action:@selector(goBack:)];
+
+  // Forward button
+  self.forwardButton = [self
+      buttonWithImageForNormalState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_FORWARD, YES)
+                   highlightedState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_FORWARD_PRESSED,
+                                        YES)
+                      disabledState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_FORWARD_DISABLED,
+                                        YES)
+                             action:@selector(goForward:)];
+
+  // Tab switcher button.
+  self.tabSwitcherButton =
+      [self buttonWithImageForNormalState:
+                [UIImage imageNamed:@"tabswitcher_tab_switcher_button"]
+                         highlightedState:nil
+                            disabledState:nil
+                                   action:@selector(toggleTabStrip:)];
+
+  // Tools menu button.
+  self.toolsMenuButton = [self
+      buttonWithImageForNormalState:[UIImage imageNamed:@"tabswitcher_menu"]
+                   highlightedState:nil
+                      disabledState:nil
+                             action:@selector(showToolsMenu:)];
+  [self.toolsMenuButton
+      setImageEdgeInsets:UIEdgeInsetsMakeDirected(0, -3, 0, 0)];
+}
+
+- (void)setUpRegularWidthToolbarButtons {
+  // Share button.
+  self.shareButton = [self
+      buttonWithImageForNormalState:NativeImage(IDR_IOS_TOOLBAR_LIGHT_SHARE)
+                   highlightedState:NativeImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_SHARE_PRESSED)
+                      disabledState:NativeImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_SHARE_DISABLED)
+                             action:@selector(showShareMenu:)];
+
+  // Reload button.
+  self.reloadButton = [self
+      buttonWithImageForNormalState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_RELOAD, YES)
+                   highlightedState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_RELOAD_PRESSED,
+                                        YES)
+                      disabledState:NativeReversableImage(
+                                        IDR_IOS_TOOLBAR_LIGHT_RELOAD_DISABLED,
+                                        YES)
+                             action:@selector(reload:)];
+}
+
 #pragma mark - Public API
 
 - (void)setCurrentPageText:(NSString*)text {
@@ -119,7 +180,8 @@
 #pragma mark - ZoomTransitionDelegate
 
 - (CGRect)rectForZoomWithKey:(NSObject*)key inView:(UIView*)view {
-  return [view convertRect:self.toolsMenu.bounds fromView:self.toolsMenu];
+  return [view convertRect:self.toolsMenuButton.bounds
+                  fromView:self.toolsMenuButton];
 }
 
 #pragma mark - ToolsMenuActions
@@ -132,4 +194,27 @@
   [self.toolbarCommandHandler closeToolsMenu];
 }
 
+#pragma mark - Helper Methods
+// Constructor for a Toolbar button.
+- (UIButton*)buttonWithImageForNormalState:(UIImage*)normalImage
+                          highlightedState:(UIImage*)highlightedImage
+                             disabledState:(UIImage*)disabledImage
+                                    action:(SEL)actionSelector {
+  UIButton* button = [UIButton buttonWithType:UIButtonTypeSystem];
+  [button setImage:normalImage forState:UIControlStateNormal];
+  [button setImage:highlightedImage forState:UIControlStateHighlighted];
+  [button setImage:disabledImage forState:UIControlStateDisabled];
+  [button addTarget:nil
+                action:actionSelector
+      forControlEvents:UIControlEventTouchUpInside];
+  button.translatesAutoresizingMaskIntoConstraints = NO;
+
+  [button
+      setContentCompressionResistancePriority:UILayoutPriorityRequired
+                                      forAxis:UILayoutConstraintAxisHorizontal];
+  [button setContentHuggingPriority:UILayoutPriorityRequired
+                            forAxis:UILayoutConstraintAxisHorizontal];
+  return button;
+}
+
 @end
diff --git a/ios/net/cookies/cookie_cache_unittest.cc b/ios/net/cookies/cookie_cache_unittest.cc
index f1ec53a..a3438ae44 100644
--- a/ios/net/cookies/cookie_cache_unittest.cc
+++ b/ios/net/cookies/cookie_cache_unittest.cc
@@ -19,7 +19,7 @@
                            const std::string& value) {
   return *CanonicalCookie::Create(url, name, value, std::string(), url.path(),
                                   base::Time(), base::Time(), false, false,
-                                  net::CookieSameSite::DEFAULT_MODE, false,
+                                  net::CookieSameSite::DEFAULT_MODE,
                                   net::COOKIE_PRIORITY_DEFAULT);
 }
 
diff --git a/ios/net/cookies/cookie_store_ios.h b/ios/net/cookies/cookie_store_ios.h
index b58d14aa..4ef0d6e 100644
--- a/ios/net/cookies/cookie_store_ios.h
+++ b/ios/net/cookies/cookie_store_ios.h
@@ -96,7 +96,6 @@
                                  bool secure,
                                  bool http_only,
                                  CookieSameSite same_site,
-                                 bool enforce_strict_secure,
                                  CookiePriority priority,
                                  const SetCookiesCallback& callback) override;
   void GetCookiesWithOptionsAsync(const GURL& url,
diff --git a/ios/net/cookies/cookie_store_ios.mm b/ios/net/cookies/cookie_store_ios.mm
index 6853244..e0e5280 100644
--- a/ios/net/cookies/cookie_store_ios.mm
+++ b/ios/net/cookies/cookie_store_ios.mm
@@ -18,7 +18,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/sys_string_conversions.h"
@@ -388,7 +388,6 @@
     bool secure,
     bool http_only,
     CookieSameSite same_site,
-    bool enforce_strict_secure,
     CookiePriority priority,
     const SetCookiesCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -397,8 +396,8 @@
     case NOT_SYNCHRONIZED:
       cookie_monster_->SetCookieWithDetailsAsync(
           url, name, value, domain, path, creation_time, expiration_time,
-          last_access_time, secure, http_only, same_site, enforce_strict_secure,
-          priority, WrapSetCallback(callback));
+          last_access_time, secure, http_only, same_site, priority,
+          WrapSetCallback(callback));
       break;
     case SYNCHRONIZED:
       // If cookies are not allowed, they are stashed in the CookieMonster, and
@@ -415,7 +414,7 @@
       std::unique_ptr<net::CanonicalCookie> canonical_cookie =
           net::CanonicalCookie::Create(
               url, name, value, domain, path, creation_time, expiration_time,
-              secure, http_only, same_site, enforce_strict_secure, priority);
+              secure, http_only, same_site, priority);
 
       if (canonical_cookie) {
         NSHTTPCookie* cookie =
diff --git a/ios/net/cookies/cookie_store_ios_unittest.mm b/ios/net/cookies/cookie_store_ios_unittest.mm
index 25338d42..6bc192bf 100644
--- a/ios/net/cookies/cookie_store_ios_unittest.mm
+++ b/ios/net/cookies/cookie_store_ios_unittest.mm
@@ -46,7 +46,6 @@
   static const bool filters_schemes = false;
   static const bool has_path_prefix_bug = true;
   static const int creation_time_granularity_in_ms = 1000;
-  static const bool enforce_strict_secure = false;
 
   base::MessageLoop loop_;
 };
@@ -113,7 +112,7 @@
                                      base::Time(),  // expires
                                      false,         // secure
                                      false,         // httponly
-                                     net::CookieSameSite::DEFAULT_MODE, false,
+                                     net::CookieSameSite::DEFAULT_MODE,
                                      net::COOKIE_PRIORITY_DEFAULT));
     cookies.push_back(std::move(bad_canonical_cookie));
     loaded_callback_.Run(std::move(cookies));
diff --git a/ios/net/cookies/system_cookie_util.mm b/ios/net/cookies/system_cookie_util.mm
index 59d808e..b4e3075 100644
--- a/ios/net/cookies/system_cookie_util.mm
+++ b/ios/net/cookies/system_cookie_util.mm
@@ -8,7 +8,7 @@
 #include <stddef.h>
 
 #include "base/logging.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #include "net/cookies/cookie_constants.h"
diff --git a/ios/public/provider/chrome/browser/spotlight/BUILD.gn b/ios/public/provider/chrome/browser/spotlight/BUILD.gn
index 816bf722..dda4cba 100644
--- a/ios/public/provider/chrome/browser/spotlight/BUILD.gn
+++ b/ios/public/provider/chrome/browser/spotlight/BUILD.gn
@@ -14,6 +14,7 @@
 }
 
 source_set("test_support") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "test_spotlight_provider.h",
diff --git a/ios/public/provider/chrome/browser/spotlight/test_spotlight_provider.mm b/ios/public/provider/chrome/browser/spotlight/test_spotlight_provider.mm
index 4dcdac2d..9e356fd 100644
--- a/ios/public/provider/chrome/browser/spotlight/test_spotlight_provider.mm
+++ b/ios/public/provider/chrome/browser/spotlight/test_spotlight_provider.mm
@@ -4,6 +4,10 @@
 
 #import "ios/public/provider/chrome/browser/spotlight/test_spotlight_provider.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 bool TestSpotlightProvider::IsSpotlightEnabled() {
   return true;
 }
diff --git a/ios/showcase/BUILD.gn b/ios/showcase/BUILD.gn
index ade3d34..01c96180 100644
--- a/ios/showcase/BUILD.gn
+++ b/ios/showcase/BUILD.gn
@@ -30,6 +30,7 @@
     "//ios/showcase/suggestions",
     "//ios/showcase/tab_grid",
     "//ios/showcase/tab_strip",
+    "//ios/showcase/toolbar",
     "//ios/showcase/uikit_table_view_cell",
   ]
 }
diff --git a/ios/showcase/DEPS b/ios/showcase/DEPS
index 9e52e39..78a1dcb 100644
--- a/ios/showcase/DEPS
+++ b/ios/showcase/DEPS
@@ -3,4 +3,5 @@
   "+ios/clean",
   "+ios/third_party/material_components_ios",
   "+ios/third_party/material_roboto_font_loader_ios",
+  "+ui/base",
 ]
diff --git a/ios/showcase/core/BUILD.gn b/ios/showcase/core/BUILD.gn
index 6145738..87344118 100644
--- a/ios/showcase/core/BUILD.gn
+++ b/ios/showcase/core/BUILD.gn
@@ -13,9 +13,11 @@
   ]
   deps = [
     "//base",
+    "//ios/chrome/app/startup",
     "//ios/showcase/common",
     "//ios/third_party/material_components_ios",
     "//ios/third_party/material_roboto_font_loader_ios",
+    "//ui/base",
   ]
   libs = [ "UIKit.framework" ]
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -27,6 +29,7 @@
   ]
   deps = [
     ":core",
+    "//base",
 
     # Needed to disable the tests hooks.
     "//ios/chrome/app:tests_fake_hook",
diff --git a/ios/showcase/core/app_delegate.mm b/ios/showcase/core/app_delegate.mm
index fda5403..5fdc493 100644
--- a/ios/showcase/core/app_delegate.mm
+++ b/ios/showcase/core/app_delegate.mm
@@ -4,11 +4,14 @@
 
 #import "ios/showcase/core/app_delegate.h"
 
+#include "base/memory/ptr_util.h"
+#include "ios/chrome/app/startup/ios_chrome_main.h"
 #import "ios/showcase/core/showcase_model.h"
 #import "ios/showcase/core/showcase_view_controller.h"
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MDCTypographyAdditions/MDFRobotoFontLoader+MDCTypographyAdditions.h"
 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
+#include "ui/base/resource/resource_bundle.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -19,6 +22,9 @@
 
 - (BOOL)application:(UIApplication*)application
     didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
+  base::MakeUnique<IOSChromeMain>();
+  ResourceBundle::InitSharedInstanceWithLocale(
+      std::string(), nullptr, ResourceBundle::LOAD_COMMON_RESOURCES);
   [MDCTypography setFontLoader:[MDFRobotoFontLoader sharedInstance]];
   self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   ShowcaseViewController* viewController =
@@ -27,6 +33,7 @@
       initWithRootViewController:viewController];
   self.window.rootViewController = navigationController;
   [self.window makeKeyAndVisible];
+
   return YES;
 }
 
diff --git a/ios/showcase/core/main.mm b/ios/showcase/core/main.mm
index ca3097d..89c2c69f 100644
--- a/ios/showcase/core/main.mm
+++ b/ios/showcase/core/main.mm
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #import <UIKit/UIKit.h>
+
+#include "base/at_exit.h"
 #import "ios/showcase/core/app_delegate.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -10,6 +12,10 @@
 #endif
 
 int main(int argc, char* argv[]) {
+  // This needs to be stack allocated and live for the lifetime of
+  // the app.
+  base::AtExitManager at_exit;
+
   @autoreleasepool {
     return UIApplicationMain(argc, argv, nil,
                              NSStringFromClass([AppDelegate class]));
diff --git a/ios/showcase/core/showcase_model.mm b/ios/showcase/core/showcase_model.mm
index 8576ae91..7aafb0c 100644
--- a/ios/showcase/core/showcase_model.mm
+++ b/ios/showcase/core/showcase_model.mm
@@ -18,7 +18,7 @@
   return @[
     @{
       showcase::kClassForDisplayKey : @"SettingsViewController",
-      showcase::kClassForInstantiationKey : @"SettingsCoordinator",
+      showcase::kClassForInstantiationKey : @"SCSettingsCoordinator",
       showcase::kUseCaseKey : @"Main settings screen",
     },
     @{
@@ -46,6 +46,11 @@
       showcase::kClassForInstantiationKey : @"SCTabStripCoordinator",
       showcase::kUseCaseKey : @"Tab strip container",
     },
+    @{
+      showcase::kClassForDisplayKey : @"ToolbarViewController",
+      showcase::kClassForInstantiationKey : @"SCToolbarCoordinator",
+      showcase::kUseCaseKey : @"Toolbar",
+    },
   ];
 }
 
diff --git a/ios/showcase/settings/BUILD.gn b/ios/showcase/settings/BUILD.gn
index 6810351..8c5d114 100644
--- a/ios/showcase/settings/BUILD.gn
+++ b/ios/showcase/settings/BUILD.gn
@@ -4,8 +4,8 @@
 
 source_set("settings") {
   sources = [
-    "settings_coordinator.h",
-    "settings_coordinator.mm",
+    "sc_settings_coordinator.h",
+    "sc_settings_coordinator.mm",
   ]
   deps = [
     "//base",
diff --git a/ios/showcase/settings/settings_coordinator.h b/ios/showcase/settings/sc_settings_coordinator.h
similarity index 61%
rename from ios/showcase/settings/settings_coordinator.h
rename to ios/showcase/settings/sc_settings_coordinator.h
index d1036b80..b611b7c 100644
--- a/ios/showcase/settings/settings_coordinator.h
+++ b/ios/showcase/settings/sc_settings_coordinator.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_SHOWCASE_SETTINGS_SETTINGS_COORDINATOR_H_
-#define IOS_SHOWCASE_SETTINGS_SETTINGS_COORDINATOR_H_
+#ifndef IOS_SHOWCASE_SETTINGS_SC_SETTINGS_COORDINATOR_H_
+#define IOS_SHOWCASE_SETTINGS_SC_SETTINGS_COORDINATOR_H_
 
 #import <UIKit/UIKit.h>
 
 #import "ios/showcase/common/coordinator.h"
 
-@interface SettingsCoordinator : NSObject<Coordinator>
+@interface SCSettingsCoordinator : NSObject<Coordinator>
 
 // Redefined to be a UINavigationController.
 @property(nonatomic, weak) UINavigationController* baseViewController;
 
 @end
 
-#endif  // IOS_SHOWCASE_SETTINGS_SETTINGS_COORDINATOR_H_
+#endif  // IOS_SHOWCASE_SETTINGS_SC_SETTINGS_COORDINATOR_H_
diff --git a/ios/showcase/settings/settings_coordinator.mm b/ios/showcase/settings/sc_settings_coordinator.mm
similarity index 85%
rename from ios/showcase/settings/settings_coordinator.mm
rename to ios/showcase/settings/sc_settings_coordinator.mm
index 8e28d44..92bd771 100644
--- a/ios/showcase/settings/settings_coordinator.mm
+++ b/ios/showcase/settings/sc_settings_coordinator.mm
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/showcase/settings/settings_coordinator.h"
+#import "ios/showcase/settings/sc_settings_coordinator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@implementation SettingsCoordinator
+@implementation SCSettingsCoordinator
 
 @synthesize baseViewController;
 
diff --git a/ios/showcase/toolbar/BUILD.gn b/ios/showcase/toolbar/BUILD.gn
new file mode 100644
index 0000000..c9bd5eb41
--- /dev/null
+++ b/ios/showcase/toolbar/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("toolbar") {
+  sources = [
+    "sc_toolbar_coordinator.h",
+    "sc_toolbar_coordinator.mm",
+  ]
+  deps = [
+    "//ios/clean/chrome/browser/ui/toolbar:toolbar_ui",
+    "//ios/showcase/common",
+  ]
+  libs = [ "UIKit.framework" ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/showcase/toolbar/sc_toolbar_coordinator.h b/ios/showcase/toolbar/sc_toolbar_coordinator.h
new file mode 100644
index 0000000..ff6fe19e
--- /dev/null
+++ b/ios/showcase/toolbar/sc_toolbar_coordinator.h
@@ -0,0 +1,18 @@
+// 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 IOS_SHOWCASE_TOOLBAR_SC_TOOLBAR_COORDINATOR_H_
+#define IOS_SHOWCASE_TOOLBAR_SC_TOOLBAR_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/showcase/common/coordinator.h"
+
+@interface SCToolbarCoordinator : NSObject<Coordinator>
+// Redefined to be a UINavigationController.
+@property(nonatomic, weak) UINavigationController* baseViewController;
+
+@end
+
+#endif  // IOS_SHOWCASE_TOOLBAR_SC_TOOLBAR_COORDINATOR_H_
diff --git a/ios/showcase/toolbar/sc_toolbar_coordinator.mm b/ios/showcase/toolbar/sc_toolbar_coordinator.mm
new file mode 100644
index 0000000..3291310
--- /dev/null
+++ b/ios/showcase/toolbar/sc_toolbar_coordinator.mm
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/showcase/toolbar/sc_toolbar_coordinator.h"
+
+#import "ios/clean/chrome/browser/ui/toolbar/toolbar_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation SCToolbarCoordinator
+@synthesize baseViewController = _baseViewController;
+
+- (void)start {
+  ToolbarViewController* viewController = [[ToolbarViewController alloc] init];
+  viewController.title = @"Toolbar";
+  [self.baseViewController pushViewController:viewController animated:YES];
+}
+
+@end
diff --git a/ios/web/app/web_main_loop.mm b/ios/web/app/web_main_loop.mm
index b5e59fc..a378af7c 100644
--- a/ios/web/app/web_main_loop.mm
+++ b/ios/web/app/web_main_loop.mm
@@ -12,7 +12,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/power_monitor/power_monitor_device_source.h"
diff --git a/ios/web/public/image_fetcher/webp_decoder.mm b/ios/web/public/image_fetcher/webp_decoder.mm
index 95b18936c..e783844 100644
--- a/ios/web/public/image_fetcher/webp_decoder.mm
+++ b/ios/web/public/image_fetcher/webp_decoder.mm
@@ -9,7 +9,7 @@
 #import <UIKit/UIKit.h>
 
 #include "base/logging.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/web_state/crw_pass_kit_downloader.mm b/ios/web/web_state/crw_pass_kit_downloader.mm
index 587aa886..f07ce29 100644
--- a/ios/web/web_state/crw_pass_kit_downloader.mm
+++ b/ios/web/web_state/crw_pass_kit_downloader.mm
@@ -8,7 +8,7 @@
 
 #include "base/mac/scoped_block.h"
 #include "base/macros.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #include "net/http/http_response_headers.h"
 #include "net/url_request/url_fetcher.h"
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index 0abc9d2..8e25f6a 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -9,7 +9,7 @@
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/sys_string_conversions.h"
 #import "ios/web/public/web_state/context_menu_params.h"
 #import "ios/web/public/web_state/js/crw_js_injection_evaluator.h"
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 8c572b7..89771e9 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -29,7 +29,7 @@
 #include "base/mac/scoped_cftyperef.h"
 #import "base/mac/scoped_nsobject.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/string_util.h"
diff --git a/net/base/layered_network_delegate.cc b/net/base/layered_network_delegate.cc
index 914e62fd..f99d6e1 100644
--- a/net/base/layered_network_delegate.cc
+++ b/net/base/layered_network_delegate.cc
@@ -232,16 +232,9 @@
   return nested_network_delegate_->AreExperimentalCookieFeaturesEnabled();
 }
 
-bool LayeredNetworkDelegate::OnAreStrictSecureCookiesEnabled() const {
-  OnAreStrictSecureCookiesEnabledInternal();
-  return nested_network_delegate_->AreStrictSecureCookiesEnabled();
-}
-
 void LayeredNetworkDelegate::OnAreExperimentalCookieFeaturesEnabledInternal()
     const {}
 
-void LayeredNetworkDelegate::OnAreStrictSecureCookiesEnabledInternal() const {}
-
 bool LayeredNetworkDelegate::
     OnCancelURLRequestWithPolicyViolatingReferrerHeader(
         const URLRequest& request,
diff --git a/net/base/layered_network_delegate.h b/net/base/layered_network_delegate.h
index 496f98e..1666d86a 100644
--- a/net/base/layered_network_delegate.h
+++ b/net/base/layered_network_delegate.h
@@ -82,7 +82,6 @@
   bool OnCanEnablePrivacyMode(const GURL& url,
                               const GURL& first_party_for_cookies) const final;
   bool OnAreExperimentalCookieFeaturesEnabled() const final;
-  bool OnAreStrictSecureCookiesEnabled() const final;
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const URLRequest& request,
       const GURL& target_url,
@@ -152,7 +151,6 @@
       const GURL& first_party_for_cookies) const;
 
   virtual void OnAreExperimentalCookieFeaturesEnabledInternal() const;
-  virtual void OnAreStrictSecureCookiesEnabledInternal() const;
 
   virtual void OnCancelURLRequestWithPolicyViolatingReferrerHeaderInternal(
       const URLRequest& request,
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index 81164320..960f05f 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -176,10 +176,6 @@
   return OnAreExperimentalCookieFeaturesEnabled();
 }
 
-bool NetworkDelegate::AreStrictSecureCookiesEnabled() const {
-  return OnAreStrictSecureCookiesEnabled();
-}
-
 bool NetworkDelegate::CancelURLRequestWithPolicyViolatingReferrerHeader(
     const URLRequest& request,
     const GURL& target_url,
diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h
index 0fccae1..f602f3c 100644
--- a/net/base/network_delegate.h
+++ b/net/base/network_delegate.h
@@ -107,10 +107,6 @@
 
   bool AreExperimentalCookieFeaturesEnabled() const;
 
-  // TODO(jww): Remove this once we ship strict secure cookies:
-  // https://crbug.com/546820
-  bool AreStrictSecureCookiesEnabled() const;
-
   bool CancelURLRequestWithPolicyViolatingReferrerHeader(
       const URLRequest& request,
       const GURL& target_url,
@@ -287,13 +283,6 @@
   // false otherwise.
   virtual bool OnAreExperimentalCookieFeaturesEnabled() const = 0;
 
-  // Returns true if the embedder has enabled experimental features or
-  // specifically strict secure cookies, and false otherwise.
-  //
-  // TODO(jww): Remove this once we ship strict secure cookies:
-  // https://crbug.com/546820.
-  virtual bool OnAreStrictSecureCookiesEnabled() const = 0;
-
   // Called when the |referrer_url| for requesting |target_url| during handling
   // of the |request| is does not comply with the referrer policy (e.g. a
   // secure referrer for an insecure initial target).
diff --git a/net/base/network_delegate_impl.cc b/net/base/network_delegate_impl.cc
index be48c3f..52d4efc 100644
--- a/net/base/network_delegate_impl.cc
+++ b/net/base/network_delegate_impl.cc
@@ -107,10 +107,6 @@
   return false;
 }
 
-bool NetworkDelegateImpl::OnAreStrictSecureCookiesEnabled() const {
-  return false;
-}
-
 bool NetworkDelegateImpl::OnCancelURLRequestWithPolicyViolatingReferrerHeader(
     const URLRequest& request,
     const GURL& target_url,
diff --git a/net/base/network_delegate_impl.h b/net/base/network_delegate_impl.h
index d025a768..8acdd72 100644
--- a/net/base/network_delegate_impl.h
+++ b/net/base/network_delegate_impl.h
@@ -197,10 +197,6 @@
   // Returns true if the embedder has enabled experimental cookie features.
   bool OnAreExperimentalCookieFeaturesEnabled() const override;
 
-  // TODO(jww): Remove this once we ship strict secure cookies:
-  // https://crbug.com/546820
-  bool OnAreStrictSecureCookiesEnabled() const override;
-
   // Called when the |referrer_url| for requesting |target_url| during handling
   // of the |request| is does not comply with the referrer policy (e.g. a
   // secure referrer for an insecure initial target).
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index de2fbbe6..2b53e4ee 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -195,8 +195,7 @@
   // origins", if the cookie's "secure-only-flag" is "true" and the requesting
   // URL does not have a secure scheme, the cookie should be thrown away.
   // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone
-  if (options.enforce_strict_secure() && parsed_cookie.IsSecure() &&
-      !url.SchemeIsCryptographic()) {
+  if (parsed_cookie.IsSecure() && !url.SchemeIsCryptographic()) {
     VLOG(kVlogSetCookies)
         << "Create() is trying to create a secure cookie from an insecure URL";
     return nullptr;
@@ -240,7 +239,6 @@
     bool secure,
     bool http_only,
     CookieSameSite same_site,
-    bool enforce_strict_secure,
     CookiePriority priority) {
   // Expect valid attribute tokens and values, as defined by the ParsedCookie
   // logic, otherwise don't create the cookie.
@@ -260,7 +258,7 @@
     return nullptr;
   }
 
-  if (enforce_strict_secure && secure && !url.SchemeIsCryptographic())
+  if (secure && !url.SchemeIsCryptographic())
     return nullptr;
 
   std::string parsed_path = ParsedCookie::ParseValueString(path);
diff --git a/net/cookies/canonical_cookie.h b/net/cookies/canonical_cookie.h
index 2ef9131..18d9302e 100644
--- a/net/cookies/canonical_cookie.h
+++ b/net/cookies/canonical_cookie.h
@@ -52,7 +52,6 @@
                                                  bool secure,
                                                  bool http_only,
                                                  CookieSameSite same_site,
-                                                 bool enforce_strict_secure,
                                                  CookiePriority priority);
 
   // Creates a canonical cookie from unparsed attribute values.
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index fa9eb6c..2386e85 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -20,7 +20,7 @@
 
   std::unique_ptr<CanonicalCookie> cookie(CanonicalCookie::Create(
       url, "A", "2", std::string(), "/test", current_time, base::Time(), false,
-      false, CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   EXPECT_EQ("A", cookie->Name());
   EXPECT_EQ("2", cookie->Value());
   EXPECT_EQ("www.example.com", cookie->Domain());
@@ -31,7 +31,7 @@
 
   std::unique_ptr<CanonicalCookie> cookie2(CanonicalCookie::Create(
       url, "A", "2", ".www.example.com", std::string(), current_time,
-      base::Time(), false, false, CookieSameSite::DEFAULT_MODE, false,
+      base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
       COOKIE_PRIORITY_DEFAULT));
   EXPECT_EQ("A", cookie2->Name());
   EXPECT_EQ("2", cookie2->Value());
@@ -64,11 +64,11 @@
   EXPECT_EQ("/", cookie->Path());
   EXPECT_FALSE(cookie->IsSecure());
 
-  // Test creating secure cookies. RFC 6265 allows insecure urls to set secure
-  // cookies.
+  // Test creating secure cookies.
+  // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone disallows
+  // insecure URLs from setting secure cookies.
   cookie = CanonicalCookie::Create(url, "A=2; Secure", creation_time, options);
-  EXPECT_TRUE(cookie.get());
-  EXPECT_TRUE(cookie->IsSecure());
+  EXPECT_FALSE(cookie.get());
 
   // Test creating http only cookies.
   cookie =
@@ -93,10 +93,9 @@
 
   // Test the creating cookies using specific parameter instead of a cookie
   // string.
-  cookie = CanonicalCookie::Create(url, "A", "2", "www.example.com", "/test",
-                                   creation_time, base::Time(), false, false,
-                                   CookieSameSite::DEFAULT_MODE, false,
-                                   COOKIE_PRIORITY_DEFAULT);
+  cookie = CanonicalCookie::Create(
+      url, "A", "2", "www.example.com", "/test", creation_time, base::Time(),
+      false, false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT);
   EXPECT_EQ("A", cookie->Name());
   EXPECT_EQ("2", cookie->Value());
   EXPECT_EQ(".www.example.com", cookie->Domain());
@@ -105,10 +104,9 @@
   EXPECT_FALSE(cookie->IsHttpOnly());
   EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie->SameSite());
 
-  cookie = CanonicalCookie::Create(url, "A", "2", ".www.example.com", "/test",
-                                   creation_time, base::Time(), false, false,
-                                   CookieSameSite::DEFAULT_MODE, false,
-                                   COOKIE_PRIORITY_DEFAULT);
+  cookie = CanonicalCookie::Create(
+      url, "A", "2", ".www.example.com", "/test", creation_time, base::Time(),
+      false, false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT);
   EXPECT_EQ("A", cookie->Name());
   EXPECT_EQ("2", cookie->Value());
   EXPECT_EQ(".www.example.com", cookie->Domain());
@@ -167,7 +165,7 @@
 }
 
 TEST(CanonicalCookieTest, IsEquivalent) {
-  GURL url("http://www.example.com/");
+  GURL url("https://www.example.com/");
   std::string cookie_name = "A";
   std::string cookie_value = "2EDA-EF";
   std::string cookie_domain = ".www.example.com";
@@ -181,25 +179,22 @@
   // Test that a cookie is equivalent to itself.
   std::unique_ptr<CanonicalCookie> cookie(CanonicalCookie::Create(
       url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
-      expiration_time, secure, httponly, same_site, false,
-      COOKIE_PRIORITY_MEDIUM));
+      expiration_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM));
   EXPECT_TRUE(cookie->IsEquivalent(*cookie));
   EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*cookie));
 
   // Test that two identical cookies are equivalent.
   std::unique_ptr<CanonicalCookie> other_cookie(CanonicalCookie::Create(
       url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
-      expiration_time, secure, httponly, same_site, false,
-      COOKIE_PRIORITY_MEDIUM));
+      expiration_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM));
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
   EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
 
   // Tests that use different variations of attribute values that
   // DON'T affect cookie equivalence.
-  other_cookie =
-      CanonicalCookie::Create(url, cookie_name, "2", cookie_domain, cookie_path,
-                              creation_time, expiration_time, secure, httponly,
-                              same_site, false, COOKIE_PRIORITY_HIGH);
+  other_cookie = CanonicalCookie::Create(
+      url, cookie_name, "2", cookie_domain, cookie_path, creation_time,
+      expiration_time, secure, httponly, same_site, COOKIE_PRIORITY_HIGH);
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
   EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
   EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
@@ -208,29 +203,28 @@
       creation_time + base::TimeDelta::FromMinutes(2);
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, "2", cookie_domain, cookie_path, other_creation_time,
-      expiration_time, secure, httponly, same_site, false,
-      COOKIE_PRIORITY_MEDIUM);
+      expiration_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM);
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
   EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
   EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
 
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
-      expiration_time, true, httponly, same_site, false, COOKIE_PRIORITY_LOW);
+      expiration_time, true, httponly, same_site, COOKIE_PRIORITY_LOW);
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
   EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
   EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
 
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
-      expiration_time, secure, true, same_site, false, COOKIE_PRIORITY_LOW);
+      expiration_time, secure, true, same_site, COOKIE_PRIORITY_LOW);
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
   EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
   EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
 
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
-      expiration_time, secure, httponly, CookieSameSite::STRICT_MODE, false,
+      expiration_time, secure, httponly, CookieSameSite::STRICT_MODE,
       COOKIE_PRIORITY_LOW);
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
   EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
@@ -239,8 +233,7 @@
   // Cookies whose names mismatch are not equivalent.
   other_cookie = CanonicalCookie::Create(
       url, "B", cookie_value, cookie_domain, cookie_path, creation_time,
-      expiration_time, secure, httponly, same_site, false,
-      COOKIE_PRIORITY_MEDIUM);
+      expiration_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM);
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
   EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
   EXPECT_FALSE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
@@ -250,8 +243,7 @@
   // rules of 'IsEquivalentForSecureCookieMatching'.
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, cookie_value, std::string(), cookie_path, creation_time,
-      expiration_time, secure, httponly, same_site, false,
-      COOKIE_PRIORITY_MEDIUM);
+      expiration_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM);
   EXPECT_TRUE(cookie->IsDomainCookie());
   EXPECT_FALSE(other_cookie->IsDomainCookie());
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
@@ -262,7 +254,7 @@
   // 'www.example.com', but they are equivalent for secure cookie matching.
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, cookie_value, ".example.com", cookie_path,
-      creation_time, expiration_time, secure, httponly, same_site, false,
+      creation_time, expiration_time, secure, httponly, same_site,
       COOKIE_PRIORITY_MEDIUM);
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
   EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
@@ -275,15 +267,14 @@
   // equivalent for secure cookie matching to |other_cookie| set on '/'.
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, cookie_value, cookie_domain, "/test", creation_time,
-      expiration_time, secure, httponly, same_site, false,
-      COOKIE_PRIORITY_MEDIUM);
+      expiration_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM);
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
   EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
   EXPECT_FALSE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
 
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, cookie_value, cookie_domain, cookie_path + "/subpath",
-      creation_time, expiration_time, secure, httponly, same_site, false,
+      creation_time, expiration_time, secure, httponly, same_site,
       COOKIE_PRIORITY_MEDIUM);
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
   EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
@@ -291,8 +282,7 @@
 
   other_cookie = CanonicalCookie::Create(
       url, cookie_name, cookie_value, cookie_domain, "/", creation_time,
-      expiration_time, secure, httponly, same_site, false,
-      COOKIE_PRIORITY_MEDIUM);
+      expiration_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM);
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
   EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
   EXPECT_FALSE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
@@ -563,8 +553,8 @@
 
   // Rules don't apply for a typoed prefix.
   EXPECT_TRUE(CanonicalCookie::Create(
-      http_url, "__host-A=B; Domain=" + domain + "; Path=/; Secure;",
-      creation_time, options));
+      http_url, "__host-A=B; Domain=" + domain + "; Path=/;", creation_time,
+      options));
   EXPECT_TRUE(CanonicalCookie::Create(
       https_url, "__HostA=B; Domain=" + domain + "; Secure;", creation_time,
       options));
@@ -575,7 +565,6 @@
   GURL https_url("https://www.example.com");
   base::Time creation_time = base::Time::Now();
   CookieOptions options;
-  options.set_enforce_strict_secure();
 
   std::unique_ptr<CanonicalCookie> http_cookie_no_secure(
       CanonicalCookie::Create(http_url, "a=b", creation_time, options));
@@ -594,19 +583,19 @@
   std::unique_ptr<CanonicalCookie> http_cookie_no_secure_extended(
       CanonicalCookie::Create(
           http_url, "a", "b", "", "", creation_time, creation_time, false,
-          false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT));
+          false, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> http_cookie_secure_extended(
       CanonicalCookie::Create(
           http_url, "a", "b", "", "", creation_time, creation_time, true, false,
-          CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT));
+          CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> https_cookie_no_secure_extended(
       CanonicalCookie::Create(
           https_url, "a", "b", "", "", creation_time, creation_time, false,
-          false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT));
+          false, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> https_cookie_secure_extended(
       CanonicalCookie::Create(
           https_url, "a", "b", "", "", creation_time, creation_time, true,
-          false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT));
+          false, CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   EXPECT_TRUE(http_cookie_no_secure_extended.get());
   EXPECT_FALSE(http_cookie_secure_extended.get());
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 533c07b6..59a9924 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -414,7 +414,6 @@
                            bool secure,
                            bool http_only,
                            CookieSameSite same_site,
-                           bool enforce_strict_secure,
                            CookiePriority priority,
                            const SetCookiesCallback& callback)
       : CookieMonsterTask(cookie_monster),
@@ -429,7 +428,6 @@
         secure_(secure),
         http_only_(http_only),
         same_site_(same_site),
-        enforce_strict_secure_(enforce_strict_secure),
         priority_(priority),
         callback_(callback) {}
 
@@ -451,7 +449,6 @@
   bool secure_;
   bool http_only_;
   CookieSameSite same_site_;
-  bool enforce_strict_secure_;
   CookiePriority priority_;
   SetCookiesCallback callback_;
 
@@ -461,8 +458,7 @@
 void CookieMonster::SetCookieWithDetailsTask::Run() {
   bool success = this->cookie_monster()->SetCookieWithDetails(
       url_, name_, value_, domain_, path_, creation_time_, expiration_time_,
-      last_access_time_, secure_, http_only_, same_site_,
-      enforce_strict_secure_, priority_);
+      last_access_time_, secure_, http_only_, same_site_, priority_);
   if (!callback_.is_null())
     callback_.Run(success);
 }
@@ -855,13 +851,11 @@
     bool secure,
     bool http_only,
     CookieSameSite same_site,
-    bool enforce_strict_secure,
     CookiePriority priority,
     const SetCookiesCallback& callback) {
   scoped_refptr<SetCookieWithDetailsTask> task = new SetCookieWithDetailsTask(
       this, url, name, value, domain, path, creation_time, expiration_time,
-      last_access_time, secure, http_only, same_site, enforce_strict_secure,
-      priority, callback);
+      last_access_time, secure, http_only, same_site, priority, callback);
   DoCookieTaskForURL(task, url);
 }
 
@@ -1048,7 +1042,6 @@
                                          bool secure,
                                          bool http_only,
                                          CookieSameSite same_site,
-                                         bool enforce_strict_secure,
                                          CookiePriority priority) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -1067,7 +1060,7 @@
 
   std::unique_ptr<CanonicalCookie> cc(CanonicalCookie::Create(
       url, name, value, domain, path, actual_creation_time, expiration_time,
-      secure, http_only, same_site, enforce_strict_secure, priority));
+      secure, http_only, same_site, priority));
 
   if (!cc.get())
     return false;
@@ -1079,8 +1072,6 @@
   options.set_include_httponly();
   options.set_same_site_cookie_mode(
       CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
-  if (enforce_strict_secure)
-    options.set_enforce_strict_secure();
   return SetCanonicalCookie(std::move(cc), url, options);
 }
 
@@ -1616,8 +1607,7 @@
                                               const CanonicalCookie& ecc,
                                               const GURL& source_url,
                                               bool skip_httponly,
-                                              bool already_expired,
-                                              bool enforce_strict_secure) {
+                                              bool already_expired) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   bool found_equivalent_cookie = false;
@@ -1632,15 +1622,13 @@
     CanonicalCookie* cc = curit->second.get();
     ++its.first;
 
-    // If strict secure cookies is being enforced, then the equivalency
-    // requirements are looser. If the cookie is being set from an insecure
-    // scheme, then if a cookie already exists with the same name and it is
-    // Secure, then the cookie should *not* be updated if they domain-match and
-    // ignoring the path attribute.
+    // If the cookie is being set from an insecure scheme, then if a cookie
+    // already exists with the same name and it is Secure, then the cookie
+    // should *not* be updated if they domain-match and ignoring the path
+    // attribute.
     //
-    // See: https://tools.ietf.org/html/draft-west-leave-secure-cookies-alone
-    if (enforce_strict_secure && cc->IsSecure() &&
-        !source_url.SchemeIsCryptographic() &&
+    // See: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone
+    if (cc->IsSecure() && !source_url.SchemeIsCryptographic() &&
         ecc.IsEquivalentForSecureCookieMatching(*cc)) {
       skipped_secure_cookie = true;
       histogram_cookie_delete_equivalent_->Add(
@@ -1765,16 +1753,11 @@
   bool already_expired = cc->IsExpired(creation_time);
 
   if (DeleteAnyEquivalentCookie(key, *cc, source_url,
-                                options.exclude_httponly(), already_expired,
-                                options.enforce_strict_secure())) {
+                                options.exclude_httponly(), already_expired)) {
     std::string error;
-    if (options.enforce_strict_secure()) {
-      error =
-          "SetCookie() not clobbering httponly cookie or secure cookie for "
-          "insecure scheme";
-    } else {
-      error = "SetCookie() not clobbering httponly cookie";
-    }
+    error =
+        "SetCookie() not clobbering httponly cookie or secure cookie for "
+        "insecure scheme";
 
     VLOG(kVlogSetCookies) << error;
     return false;
@@ -1802,7 +1785,7 @@
   // make sure that we garbage collect...  We can also make the assumption that
   // if a cookie was set, in the common case it will be used soon after,
   // and we will purge the expired cookies in GetCookies().
-  GarbageCollect(creation_time, key, options.enforce_strict_secure());
+  GarbageCollect(creation_time, key);
 
   return true;
 }
@@ -1880,8 +1863,7 @@
 // Domain expiry behavior is unchanged by key/expiry scheme (the
 // meaning of the key is different, but that's not visible to this routine).
 size_t CookieMonster::GarbageCollect(const Time& current,
-                                     const std::string& key,
-                                     bool enforce_strict_secure) {
+                                     const std::string& key) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   size_t num_deleted = 0;
@@ -1939,11 +1921,6 @@
 
       size_t quota = 0;
       for (const auto& purge_round : purge_rounds) {
-        // Only observe the non-secure purge rounds if strict secure cookies is
-        // enabled.
-        if (!enforce_strict_secure && purge_round.protect_secure_cookies)
-          continue;
-
         // Adjust quota according to the priority of cookies. Each round should
         // protect certain number of cookies in order to avoid starvation.
         // For example, when each round starts to remove cookies, the number of
@@ -1962,11 +1939,9 @@
             break;
         }
         size_t just_deleted = 0u;
-        // Purge up to |purge_goal| for all cookies at the given priority. This
-        // path will always execute if strict secure cookies is disabled since
-        // |purge_goal| must be positive because of the for-loop guard. If
-        // strict secure cookies is enabled, this path will be taken only if the
-        // initial non-secure purge did not evict enough cookies.
+        // Purge up to |purge_goal| for all cookies at the given priority.  This
+        // path will be taken only if the initial non-secure purge did not evict
+        // enough cookies.
         if (purge_goal > 0) {
           just_deleted = PurgeLeastRecentMatches(
               cookie_its, purge_round.priority, quota, purge_goal,
@@ -1996,27 +1971,22 @@
       size_t purge_goal = cookie_its.size() - (kMaxCookies - kPurgeCookies);
       DCHECK(purge_goal > kPurgeCookies);
 
-      if (enforce_strict_secure) {
-        CookieItVector secure_cookie_its;
-        CookieItVector non_secure_cookie_its;
-        SplitCookieVectorIntoSecureAndNonSecure(cookie_its, &secure_cookie_its,
-                                                &non_secure_cookie_its);
-        size_t non_secure_purge_goal =
-            std::min<size_t>(purge_goal, non_secure_cookie_its.size() - 1);
+      CookieItVector secure_cookie_its;
+      CookieItVector non_secure_cookie_its;
+      SplitCookieVectorIntoSecureAndNonSecure(cookie_its, &secure_cookie_its,
+                                              &non_secure_cookie_its);
+      size_t non_secure_purge_goal =
+          std::min<size_t>(purge_goal, non_secure_cookie_its.size() - 1);
 
-        size_t just_deleted = GarbageCollectLeastRecentlyAccessed(
-            current, safe_date, non_secure_purge_goal, non_secure_cookie_its);
-        num_deleted += just_deleted;
+      size_t just_deleted = GarbageCollectLeastRecentlyAccessed(
+          current, safe_date, non_secure_purge_goal, non_secure_cookie_its);
+      num_deleted += just_deleted;
 
-        if (just_deleted < purge_goal) {
-          size_t secure_purge_goal = std::min<size_t>(
-              purge_goal - just_deleted, secure_cookie_its.size() - 1);
-          num_deleted += GarbageCollectLeastRecentlyAccessed(
-              current, safe_date, secure_purge_goal, secure_cookie_its);
-        }
-      } else {
+      if (just_deleted < purge_goal && secure_cookie_its.size() > 0) {
+        size_t secure_purge_goal = std::min<size_t>(
+            purge_goal - just_deleted, secure_cookie_its.size() - 1);
         num_deleted += GarbageCollectLeastRecentlyAccessed(
-            current, safe_date, purge_goal, cookie_its);
+            current, safe_date, secure_purge_goal, secure_cookie_its);
       }
     }
   }
@@ -2126,8 +2096,7 @@
   // Find boundary to cookies older than safe_date.
   CookieItVector::iterator global_purge_it = LowerBoundAccessDate(
       cookie_its.begin(), cookie_its.begin() + purge_goal, safe_date);
-  // Only delete the old cookies, and if strict secure is enabled, delete
-  // non-secure ones first.
+  // Only delete the old cookies and delete non-secure ones first.
   size_t num_deleted =
       GarbageCollectDeleteRange(current, DELETE_COOKIE_EVICTED_GLOBAL,
                                 cookie_its.begin(), global_purge_it);
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 52d4320..ff7c47f8 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -164,7 +164,6 @@
                                  bool secure,
                                  bool http_only,
                                  CookieSameSite same_site,
-                                 bool enforce_strict_secure,
                                  CookiePriority priority,
                                  const SetCookiesCallback& callback) override;
   void GetCookiesWithOptionsAsync(const GURL& url,
@@ -265,13 +264,11 @@
   FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, CookieSourceHistogram);
 
   // For kSafeFromGlobalPurgeDays in CookieStore.
-  FRIEND_TEST_ALL_PREFIXES(CookieMonsterStrictSecureTest, EvictSecureCookies);
+  FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, EvictSecureCookies);
 
   // For CookieDeleteEquivalent histogram enum.
   FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest,
                            CookieDeleteEquivalentHistogramTest);
-  FRIEND_TEST_ALL_PREFIXES(CookieMonsterStrictSecureTest,
-                           CookieDeleteEquivalentHistogramTest);
 
   // Internal reasons for deletion, used to populate informative histograms
   // and to provide a public cause for onCookieChange notifications.
@@ -412,7 +409,6 @@
                             bool secure,
                             bool http_only,
                             CookieSameSite same_site,
-                            bool enforce_strict_secure,
                             CookiePriority priority);
 
   CookieList GetAllCookies();
@@ -508,17 +504,15 @@
   // |source_url| is the URL that is attempting to set the cookie.
   // If |skip_httponly| is true, httponly cookies will not be deleted.  The
   // return value will be true if |skip_httponly| skipped an httponly cookie or
-  // |enforce_strict_secure| is true and the cookie to
-  // delete was Secure and the scheme of |ecc| is insecure.  |key| is the key to
-  // find the cookie in cookies_; see the comment before the CookieMap typedef
-  // for details.
+  // the cookie to delete was Secure and the scheme of |ecc| is insecure.  |key|
+  // is the key to find the cookie in cookies_; see the comment before the
+  // CookieMap typedef for details.
   // NOTE: There should never be more than a single matching equivalent cookie.
   bool DeleteAnyEquivalentCookie(const std::string& key,
                                  const CanonicalCookie& ecc,
                                  const GURL& source_url,
                                  bool skip_httponly,
-                                 bool already_expired,
-                                 bool enforce_strict_secure);
+                                 bool already_expired);
 
   // Inserts |cc| into cookies_. Returns an iterator that points to the inserted
   // cookie in cookies_. Guarantee: all iterators to cookies_ remain valid.
@@ -562,9 +556,7 @@
   // constants for details.
   //
   // Returns the number of cookies deleted (useful for debugging).
-  size_t GarbageCollect(const base::Time& current,
-                        const std::string& key,
-                        bool enforce_strict_secure);
+  size_t GarbageCollect(const base::Time& current, const std::string& key);
 
   // Helper for GarbageCollect(). Deletes up to |purge_goal| cookies with a
   // priority less than or equal to |priority| from |cookies|, while ensuring
diff --git a/net/cookies/cookie_monster_store_test.cc b/net/cookies/cookie_monster_store_test.cc
index 1375134a..558d4bc 100644
--- a/net/cookies/cookie_monster_store_test.cc
+++ b/net/cookies/cookie_monster_store_test.cc
@@ -133,7 +133,7 @@
   return CanonicalCookie::Create(url, pc.Name(), pc.Value(), url.host(),
                                  cookie_path, creation_time, cookie_expires,
                                  pc.IsSecure(), pc.IsHttpOnly(), pc.SameSite(),
-                                 false, pc.Priority());
+                                 pc.Priority());
 }
 
 void AddCookieToList(const GURL& url,
@@ -235,10 +235,13 @@
             ? current - base::TimeDelta::FromDays(days_old)
             : current;
 
+    // The URL must be HTTPS since |secure| can be true or false, and because
+    // strict secure cookies are enforced, the cookie will fail to be created if
+    // |secure| is true but the URL is an insecure scheme.
     std::unique_ptr<CanonicalCookie> cc(CanonicalCookie::Create(
-        GURL(base::StringPrintf("http://h%05d.izzle/", i)), "a", "1",
+        GURL(base::StringPrintf("https://h%05d.izzle/", i)), "a", "1",
         std::string(), "/path", creation_time, expiration_time, secure, false,
-        CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+        CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
     cc->SetLastAccessDate(last_access_time);
     store->AddCookie(*cc);
   }
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 7003288..706c75f 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -108,31 +108,12 @@
   static const bool filters_schemes = true;
   static const bool has_path_prefix_bug = false;
   static const int creation_time_granularity_in_ms = 0;
-  static const bool enforce_strict_secure = false;
-};
-
-struct CookieMonsterEnforcingStrictSecure {
-  static std::unique_ptr<CookieStore> Create() {
-    return base::MakeUnique<CookieMonster>(nullptr, nullptr);
-  }
-
-  static const bool supports_http_only = true;
-  static const bool supports_non_dotted_domains = true;
-  static const bool preserves_trailing_dots = true;
-  static const bool filters_schemes = true;
-  static const bool has_path_prefix_bug = false;
-  static const int creation_time_granularity_in_ms = 0;
-  static const bool enforce_strict_secure = true;
 };
 
 INSTANTIATE_TYPED_TEST_CASE_P(CookieMonster,
                               CookieStoreTest,
                               CookieMonsterTestTraits);
 
-INSTANTIATE_TYPED_TEST_CASE_P(CookieMonsterStrictSecure,
-                              CookieStoreTest,
-                              CookieMonsterEnforcingStrictSecure);
-
 template <typename T>
 class CookieMonsterTestBase : public CookieStoreTest<T> {
  public:
@@ -805,8 +786,6 @@
 };
 
 using CookieMonsterTest = CookieMonsterTestBase<CookieMonsterTestTraits>;
-using CookieMonsterStrictSecureTest =
-    CookieMonsterTestBase<CookieMonsterEnforcingStrictSecure>;
 
 // TODO(erikwright): Replace the other callbacks and synchronous helper methods
 // in this test suite with these Mocks.
@@ -864,7 +843,7 @@
   cookie_monster->SetCookieWithDetailsAsync(
       cc.url, cc.name, cc.value, cc.domain, cc.path, base::Time(),
       cc.expiration_time, base::Time(), cc.secure, cc.http_only, cc.same_site,
-      false /* enforces strict secure cookies */, cc.priority, callback->Get());
+      cc.priority, callback->Get());
 }
 
 ACTION_P2(GetAllCookiesAction, cookie_monster, callback) {
@@ -1063,11 +1042,11 @@
   list.push_back(*CanonicalCookie::Create(
       http_www_google_.url(), "A", "B", http_www_google_.domain(), "/",
       base::Time::Now(), base::Time(), false, true,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   list.push_back(*CanonicalCookie::Create(
       http_www_google_.url(), "C", "D", http_www_google_.domain(), "/",
       base::Time::Now(), base::Time(), false, true,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   BeginWith(
       SetAllCookiesAction(&cookie_monster(), list, &set_cookies_callback));
@@ -1565,7 +1544,7 @@
   TestPriorityAwareGarbageCollectHelperSecure();
 }
 
-TEST_F(CookieMonsterStrictSecureTest, TestPriorityAwareGarbageCollectionMixed) {
+TEST_F(CookieMonsterTest, TestPriorityAwareGarbageCollectionMixed) {
   TestPriorityAwareGarbageCollectHelperMixed();
 }
 
@@ -2557,15 +2536,15 @@
   list.push_back(*CanonicalCookie::Create(
       http_www_google_.url(), "A", "B", http_www_google_.url().host(), "/",
       base::Time::Now(), base::Time(), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   list.push_back(*CanonicalCookie::Create(
       http_www_google_.url(), "W", "X", http_www_google_.url().host(), "/bar",
       base::Time::Now(), base::Time(), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   list.push_back(*CanonicalCookie::Create(
       http_www_google_.url(), "Y", "Z", http_www_google_.url().host(), "/",
       base::Time::Now(), base::Time(), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   // SetAllCookies must not flush.
   ASSERT_EQ(0, store->flush_count());
@@ -2600,51 +2579,51 @@
   std::unique_ptr<CanonicalCookie> cookie1(CanonicalCookie::Create(
       http_www_google_.url(), "A", "B", http_www_google_.url().host(), "/",
       creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
-      false, COOKIE_PRIORITY_DEFAULT));
+      COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie2(CanonicalCookie::Create(
       http_www_google_.url(), "C", "D", http_www_google_.url().host(), "/",
       creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
-      false, COOKIE_PRIORITY_DEFAULT));
+      COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie3(CanonicalCookie::Create(
       http_www_google_.url(), "E", "F", http_www_google_.url().host(), "/",
       creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
-      false, COOKIE_PRIORITY_DEFAULT));
+      COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie4(CanonicalCookie::Create(
       http_www_google_.url(), "G", "H", http_www_google_.url().host(), "/",
       creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
-      false, COOKIE_PRIORITY_DEFAULT));
+      COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie4_with_new_value(
       CanonicalCookie::Create(
           http_www_google_.url(), "G", "iamnew", http_www_google_.url().host(),
           "/", creation_time, base::Time(), false, false,
-          CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+          CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie5(CanonicalCookie::Create(
       http_www_google_.url(), "I", "J", http_www_google_.url().host(), "/",
       creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
-      false, COOKIE_PRIORITY_DEFAULT));
+      COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie5_with_new_creation_time(
       CanonicalCookie::Create(
           http_www_google_.url(), "I", "J", http_www_google_.url().host(), "/",
-          now, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, false,
+          now, base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
           COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie6(CanonicalCookie::Create(
       http_www_google_.url(), "K", "L", http_www_google_.url().host(), "/foo",
       creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
-      false, COOKIE_PRIORITY_DEFAULT));
+      COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie6_with_new_path(
       CanonicalCookie::Create(
           http_www_google_.url(), "K", "L", http_www_google_.url().host(),
           "/bar", creation_time, base::Time(), false, false,
-          CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+          CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie7(CanonicalCookie::Create(
       http_www_google_.url(), "M", "N", http_www_google_.url().host(), "/foo",
       creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE,
-      false, COOKIE_PRIORITY_DEFAULT));
+      COOKIE_PRIORITY_DEFAULT));
   std::unique_ptr<CanonicalCookie> cookie7_with_new_path(
       CanonicalCookie::Create(
           http_www_google_.url(), "M", "N", http_www_google_.url().host(),
           "/bar", creation_time, base::Time(), false, false,
-          CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+          CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   CookieList old_cookies;
   old_cookies.push_back(*cookie1);
@@ -2861,7 +2840,7 @@
       "\x05"
       "boo",
       domain, path, now2, later, false, false, CookieSameSite::DEFAULT_MODE,
-      false, COOKIE_PRIORITY_DEFAULT);
+      COOKIE_PRIORITY_DEFAULT);
   initial_cookies.push_back(std::move(cc));
 
   AddCookieToList(url, "hello=world; path=" + path, now3, &initial_cookies);
@@ -2900,48 +2879,47 @@
       CookieMonster::COOKIE_SOURCE_NONSECURE_COOKIE_CRYPTOGRAPHIC_SCHEME, 1);
 
   // Set a secure cookie on a non-cryptographic scheme.
-  EXPECT_TRUE(
+  EXPECT_FALSE(
       SetCookie(cm.get(), http_www_google_.url(), "D=E; path=/; Secure"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 3);
+  histograms.ExpectTotalCount(cookie_source_histogram, 2);
   histograms.ExpectBucketCount(
       cookie_source_histogram,
-      CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 1);
+      CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 0);
 
   // Overwrite a secure cookie (set by a cryptographic scheme) on a
   // non-cryptographic scheme.
-  EXPECT_TRUE(
+  EXPECT_FALSE(
       SetCookie(cm.get(), http_www_google_.url(), "A=B; path=/; Secure"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 4);
+  histograms.ExpectTotalCount(cookie_source_histogram, 2);
   histograms.ExpectBucketCount(
       cookie_source_histogram,
       CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_CRYPTOGRAPHIC_SCHEME, 1);
   histograms.ExpectBucketCount(
       cookie_source_histogram,
-      CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 2);
+      CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 0);
 
-  // Test that clearing a secure cookie on a http:// URL does not get
-  // counted.
+  // Test that attempting to clear a secure cookie on a http:// URL does
+  // nothing.
   EXPECT_TRUE(
       SetCookie(cm.get(), https_www_google_.url(), "F=G; path=/; Secure"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 5);
+  histograms.ExpectTotalCount(cookie_source_histogram, 3);
   std::string cookies1 = GetCookies(cm.get(), https_www_google_.url());
   EXPECT_NE(std::string::npos, cookies1.find("F=G"));
-  EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(),
-                        "F=G; path=/; Expires=Thu, 01-Jan-1970 00:00:01 GMT"));
+  EXPECT_FALSE(SetCookie(cm.get(), http_www_google_.url(),
+                         "F=G; path=/; Expires=Thu, 01-Jan-1970 00:00:01 GMT"));
   std::string cookies2 = GetCookies(cm.get(), https_www_google_.url());
-  EXPECT_EQ(std::string::npos, cookies2.find("F=G"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 5);
+  EXPECT_NE(std::string::npos, cookies2.find("F=G"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 3);
 
   // Set a non-secure cookie on a non-cryptographic scheme.
   EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "H=I; path=/"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 6);
+  histograms.ExpectTotalCount(cookie_source_histogram, 4);
   histograms.ExpectBucketCount(
       cookie_source_histogram,
       CookieMonster::COOKIE_SOURCE_NONSECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 1);
 }
 
-// Test that cookie delete equivalent histograms are recorded correctly when
-// strict secure cookies are not enabled.
+// Test that cookie delete equivalent histograms are recorded correctly.
 TEST_F(CookieMonsterTest, CookieDeleteEquivalentHistogramTest) {
   base::HistogramTester histograms;
   const std::string cookie_source_histogram = "Cookie.CookieDeleteEquivalent";
@@ -2971,47 +2949,57 @@
 
   // Set a non-secure cookie from an insecure origin that matches the name of an
   // already existing cookie and additionally is equivalent to the existing
-  // cookie.
-  EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=B;"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 5);
+  // cookie. This should fail since it's trying to overwrite a secure cookie.
+  EXPECT_FALSE(SetCookie(cm.get(), http_www_google_.url(), "A=B;"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 6);
   histograms.ExpectBucketCount(cookie_source_histogram,
                                CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
                                4);
   histograms.ExpectBucketCount(cookie_source_histogram,
                                CookieMonster::COOKIE_DELETE_EQUIVALENT_FOUND,
-                               1);
+                               0);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_DELETE_EQUIVALENT_SKIPPING_SECURE, 1);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_DELETE_EQUIVALENT_WOULD_HAVE_DELETED, 1);
 
   // Set a non-secure cookie from an insecure origin that matches the name of an
-  // already existing cookie but is not equivalent.
-  EXPECT_TRUE(
+  // already existing cookie but is not equivalent. This should fail since it's
+  // trying to shadow a secure cookie.
+  EXPECT_FALSE(
       SetCookie(cm.get(), http_www_google_.url(), "A=C; path=/some/path"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 6);
+  histograms.ExpectTotalCount(cookie_source_histogram, 8);
   histograms.ExpectBucketCount(cookie_source_histogram,
                                CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
                                5);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_DELETE_EQUIVALENT_SKIPPING_SECURE, 2);
 
   // Set a secure cookie from a secure origin that matches the name of an
   // already existing cookies and is equivalent.
   EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(), "A=D; secure"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 8);
+  histograms.ExpectTotalCount(cookie_source_histogram, 10);
   histograms.ExpectBucketCount(cookie_source_histogram,
                                CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
                                6);
   histograms.ExpectBucketCount(cookie_source_histogram,
                                CookieMonster::COOKIE_DELETE_EQUIVALENT_FOUND,
-                               2);
+                               1);
 
   // Set a secure cookie from a secure origin that matches the name of an
   // already existing cookie and is not equivalent.
   EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(),
                         "A=E; secure; path=/some/other/path"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 9);
+  histograms.ExpectTotalCount(cookie_source_histogram, 11);
   histograms.ExpectBucketCount(cookie_source_histogram,
                                CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
                                7);
 }
 
-TEST_F(CookieMonsterStrictSecureTest, SetSecureCookies) {
+TEST_F(CookieMonsterTest, SetSecureCookies) {
   std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr));
   GURL http_url("http://www.google.com");
   GURL http_superdomain_url("http://google.com");
@@ -3077,7 +3065,6 @@
   // version of the cookie still does not overwrite it.
   CookieOptions include_httponly;
   include_httponly.set_include_httponly();
-  include_httponly.set_enforce_strict_secure();
   EXPECT_TRUE(SetCookieWithOptions(cm.get(), https_url, "C=D; httponly",
                                    include_httponly));
   // Note that the lack of an explicit options object below uses the default,
@@ -3085,8 +3072,8 @@
   EXPECT_FALSE(SetCookie(cm.get(), https_url, "C=E; Secure"));
 }
 
-// Tests for behavior if strict secure cookies is enabled.
-TEST_F(CookieMonsterStrictSecureTest, EvictSecureCookies) {
+// Tests for behavior for strict secure cookies.
+TEST_F(CookieMonsterTest, EvictSecureCookies) {
   // Hard-coding limits in the test, but use DCHECK_EQ to enforce constraint.
   DCHECK_EQ(180U, CookieMonster::kDomainMaxCookies);
   DCHECK_EQ(150U, CookieMonster::kDomainMaxCookies -
@@ -3215,7 +3202,7 @@
 
 // Tests that strict secure cookies doesn't trip equivalent cookie checks
 // accidentally. Regression test for https://crbug.com/569943.
-TEST_F(CookieMonsterStrictSecureTest, EquivalentCookies) {
+TEST_F(CookieMonsterTest, EquivalentCookies) {
   std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr));
   GURL http_url("http://www.google.com");
   GURL http_superdomain_url("http://google.com");
@@ -3234,83 +3221,6 @@
   EXPECT_FALSE(SetCookie(cm.get(), http_url, "A=D; domain=google.com"));
 }
 
-// Test that cookie delete equivalent histograms are recorded correctly for
-// strict secure cookies.
-TEST_F(CookieMonsterStrictSecureTest, CookieDeleteEquivalentHistogramTest) {
-  base::HistogramTester histograms;
-  const std::string cookie_source_histogram = "Cookie.CookieDeleteEquivalent";
-
-  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr));
-
-  // Set a secure cookie from a secure origin
-  EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(), "A=B; Secure"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 1);
-  histograms.ExpectBucketCount(cookie_source_histogram,
-                               CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
-                               1);
-
-  // Set a new cookie with a different name from a variety of origins (including
-  // the same one).
-  EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(), "B=A;"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 2);
-  histograms.ExpectBucketCount(cookie_source_histogram,
-                               CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
-                               2);
-  EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "C=A;"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 3);
-  histograms.ExpectBucketCount(cookie_source_histogram,
-                               CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
-                               3);
-
-  // Set a non-secure cookie from an insecure origin that matches the name of an
-  // already existing cookie and additionally is equivalent to the existing
-  // cookie.
-  EXPECT_FALSE(SetCookie(cm.get(), http_www_google_.url(), "A=B;"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 6);
-  histograms.ExpectBucketCount(cookie_source_histogram,
-                               CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
-                               4);
-  histograms.ExpectBucketCount(
-      cookie_source_histogram,
-      CookieMonster::COOKIE_DELETE_EQUIVALENT_SKIPPING_SECURE, 1);
-  histograms.ExpectBucketCount(
-      cookie_source_histogram,
-      CookieMonster::COOKIE_DELETE_EQUIVALENT_WOULD_HAVE_DELETED, 1);
-
-  // Set a non-secure cookie from an insecure origin that matches the name of an
-  // already existing cookie but is not equivalent.
-  EXPECT_FALSE(
-      SetCookie(cm.get(), http_www_google_.url(), "A=B; path=/some/path"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 8);
-  histograms.ExpectBucketCount(cookie_source_histogram,
-                               CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
-                               5);
-  histograms.ExpectBucketCount(
-      cookie_source_histogram,
-      CookieMonster::COOKIE_DELETE_EQUIVALENT_SKIPPING_SECURE, 2);
-
-  // Set a secure cookie from a secure origin that matches the name of an
-  // already existing cookies and is equivalent.
-  EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(), "A=B; secure"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 10);
-  histograms.ExpectBucketCount(cookie_source_histogram,
-                               CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
-                               6);
-  histograms.ExpectBucketCount(cookie_source_histogram,
-                               CookieMonster::COOKIE_DELETE_EQUIVALENT_FOUND,
-                               1);
-
-  // Set a secure cookie from a secure origin that matches the name of an
-  // already existing cookie and is not equivalent.
-  EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(),
-                        "A=C; secure; path=/some/path"));
-  histograms.ExpectTotalCount(cookie_source_histogram, 11);
-  histograms.ExpectBucketCount(cookie_source_histogram,
-                               CookieMonster::COOKIE_DELETE_EQUIVALENT_ATTEMPT,
-                               7);
-}
-
 class CookieMonsterNotificationTest : public CookieMonsterTest {
  public:
   CookieMonsterNotificationTest()
diff --git a/net/cookies/cookie_options.cc b/net/cookies/cookie_options.cc
index 8698afd..3a4e584ec 100644
--- a/net/cookies/cookie_options.cc
+++ b/net/cookies/cookie_options.cc
@@ -11,7 +11,6 @@
 CookieOptions::CookieOptions()
     : exclude_httponly_(true),
       same_site_cookie_mode_(SameSiteCookieMode::DO_NOT_INCLUDE),
-      enforce_strict_secure_(false),
       update_access_time_(true),
       server_time_() {}
 
diff --git a/net/cookies/cookie_options.h b/net/cookies/cookie_options.h
index d1c6afc637..53a4d0f 100644
--- a/net/cookies/cookie_options.h
+++ b/net/cookies/cookie_options.h
@@ -50,11 +50,6 @@
     return same_site_cookie_mode_;
   }
 
-  // TODO(jww): Remove once we decide whether to ship modifying 'secure' cookies
-  // only from secure schemes. https://crbug.com/546820
-  void set_enforce_strict_secure() { enforce_strict_secure_ = true; }
-  bool enforce_strict_secure() const { return enforce_strict_secure_; }
-
   // |server_time| indicates what the server sending us the Cookie thought the
   // current time was when the cookie was produced.  This is used to adjust for
   // clock skew between server and host.
@@ -70,7 +65,6 @@
  private:
   bool exclude_httponly_;
   SameSiteCookieMode same_site_cookie_mode_;
-  bool enforce_strict_secure_;
   bool update_access_time_;
   base::Time server_time_;
 };
diff --git a/net/cookies/cookie_store.h b/net/cookies/cookie_store.h
index f81fa23a..a2be255 100644
--- a/net/cookies/cookie_store.h
+++ b/net/cookies/cookie_store.h
@@ -124,7 +124,6 @@
       bool secure,
       bool http_only,
       CookieSameSite same_site,
-      bool enforce_strict_secure,
       CookiePriority priority,
       const SetCookiesCallback& callback) = 0;
 
diff --git a/net/cookies/cookie_store_test_helpers.cc b/net/cookies/cookie_store_test_helpers.cc
index 5ae3629a..bed506f1 100644
--- a/net/cookies/cookie_store_test_helpers.cc
+++ b/net/cookies/cookie_store_test_helpers.cc
@@ -87,7 +87,6 @@
     bool secure,
     bool http_only,
     CookieSameSite same_site,
-    bool enforce_strict_secure,
     CookiePriority priority,
     const SetCookiesCallback& callback) {
   NOTREACHED();
diff --git a/net/cookies/cookie_store_test_helpers.h b/net/cookies/cookie_store_test_helpers.h
index 687261e..bb6d656b 100644
--- a/net/cookies/cookie_store_test_helpers.h
+++ b/net/cookies/cookie_store_test_helpers.h
@@ -42,7 +42,6 @@
                                  bool secure,
                                  bool http_only,
                                  CookieSameSite same_site,
-                                 bool enforce_strict_secure,
                                  CookiePriority priority,
                                  const SetCookiesCallback& callback) override;
 
diff --git a/net/cookies/cookie_store_unittest.h b/net/cookies/cookie_store_unittest.h
index f7ba3d8..48d9d8c5 100644
--- a/net/cookies/cookie_store_unittest.h
+++ b/net/cookies/cookie_store_unittest.h
@@ -65,9 +65,6 @@
 //   // Time to wait between two cookie insertions to ensure that cookies have
 //   // different creation times.
 //   static const int creation_time_granularity_in_ms;
-//
-//   // The cookie store enforces secure flag requires a secure scheme.
-//   static const bool enforce_strict_secure;
 // };
 
 template <class CookieStoreTestTraits>
@@ -177,8 +174,7 @@
     ResultSavingCookieCallback<bool> callback;
     cs->SetCookieWithDetailsAsync(
         url, name, value, domain, path, creation_time, expiration_time,
-        last_access_time, secure, http_only, same_site,
-        false /* enforces strict secure cookies */, priority,
+        last_access_time, secure, http_only, same_site, priority,
         base::Bind(&ResultSavingCookieCallback<bool>::Run,
                    base::Unretained(&callback)));
     callback.WaitUntilDone();
@@ -202,8 +198,6 @@
     CookieOptions options;
     if (!CookieStoreTestTraits::supports_http_only)
       options.set_include_httponly();
-    if (CookieStoreTestTraits::enforce_strict_secure)
-      options.set_enforce_strict_secure();
     return SetCookieWithOptions(cs, url, cookie_line, options);
   }
 
@@ -355,10 +349,16 @@
       cs, this->www_google_bar_.url(), "C", "D", this->www_google_bar_.domain(),
       "/bar", two_hours_ago, base::Time(), one_hour_ago, false, true,
       CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
-  EXPECT_TRUE(this->SetCookieWithDetails(
+  // Because of strict secure cookies, a cookie made by an HTTP URL should fail
+  // to create a cookie with a the secure attribute.
+  EXPECT_FALSE(this->SetCookieWithDetails(
       cs, this->http_www_google_.url(), "E", "F", std::string(), std::string(),
       base::Time(), base::Time(), base::Time(), true, false,
       CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
+  EXPECT_TRUE(this->SetCookieWithDetails(
+      cs, this->https_www_google_.url(), "E", "F", std::string(), std::string(),
+      base::Time(), base::Time(), base::Time(), true, false,
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   // Test that malformed attributes fail to set the cookie.
   EXPECT_FALSE(this->SetCookieWithDetails(
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store_perftest.cc b/net/extras/sqlite/sqlite_persistent_cookie_store_perftest.cc
index 60a03f5..f73ba2f8 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store_perftest.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store_perftest.cc
@@ -82,7 +82,7 @@
         t += base::TimeDelta::FromInternalValue(10);
         store_->AddCookie(*CanonicalCookie::Create(
             gurl, base::StringPrintf("Cookie_%d", cookie_num), "1", domain_name,
-            "/", t, t, false, false, CookieSameSite::DEFAULT_MODE, false,
+            "/", t, t, false, false, CookieSameSite::DEFAULT_MODE,
             COOKIE_PRIORITY_DEFAULT));
       }
     }
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc b/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
index dfec9ed..2c9b7a1 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
@@ -175,7 +175,7 @@
                  const base::Time& creation) {
     store_->AddCookie(*CanonicalCookie::Create(
         url, name, value, domain, path, creation, creation, false, false,
-        CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+        CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   }
 
   void AddCookieWithExpiration(const GURL& url,
@@ -187,7 +187,7 @@
                                const base::Time& expiration) {
     store_->AddCookie(*CanonicalCookie::Create(
         url, name, value, domain, path, creation, expiration, false, false,
-        CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+        CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   }
 
   std::string ReadRawDBContents() {
@@ -461,7 +461,7 @@
   store_->AddCookie(*CanonicalCookie::Create(
       GURL("http://sessioncookie.com"), "C", "D", std::string(), "/",
       base::Time::Now(), base::Time(), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   // Force the store to write its data to the disk.
   DestroyStore();
@@ -488,7 +488,7 @@
   store_->AddCookie(*CanonicalCookie::Create(
       GURL("http://sessioncookie.com"), "C", "D", std::string(), "/",
       base::Time::Now(), base::Time(), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   // Force the store to write its data to the disk.
   DestroyStore();
@@ -518,13 +518,13 @@
   store_->AddCookie(*CanonicalCookie::Create(
       GURL("http://sessioncookie.com"), kSessionName, "val", std::string(), "/",
       base::Time::Now(), base::Time(), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
   // Add a persistent cookie.
   store_->AddCookie(*CanonicalCookie::Create(
       GURL("http://sessioncookie.com"), kPersistentName, "val", std::string(),
       "/", base::Time::Now() - base::TimeDelta::FromDays(1),
       base::Time::Now() + base::TimeDelta::FromDays(1), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   // Force the store to write its data to the disk.
   DestroyStore();
@@ -565,21 +565,21 @@
       GURL(kURL), kLowName, kCookieValue, std::string(), kCookiePath,
       base::Time::Now() - base::TimeDelta::FromMinutes(1),
       base::Time::Now() + base::TimeDelta::FromDays(1), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_LOW));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_LOW));
 
   // Add a medium-priority persistent cookie.
   store_->AddCookie(*CanonicalCookie::Create(
       GURL(kURL), kMediumName, kCookieValue, std::string(), kCookiePath,
       base::Time::Now() - base::TimeDelta::FromMinutes(2),
       base::Time::Now() + base::TimeDelta::FromDays(1), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_MEDIUM));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_MEDIUM));
 
   // Add a high-priority peristent cookie.
   store_->AddCookie(*CanonicalCookie::Create(
       GURL(kURL), kHighName, kCookieValue, std::string(), kCookiePath,
       base::Time::Now() - base::TimeDelta::FromMinutes(3),
       base::Time::Now() + base::TimeDelta::FromDays(1), false, false,
-      CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_HIGH));
+      CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_HIGH));
 
   // Force the store to write its data to the disk.
   DestroyStore();
@@ -626,21 +626,21 @@
       GURL(kURL), kNoneName, kCookieValue, std::string(), kCookiePath,
       base::Time::Now() - base::TimeDelta::FromMinutes(1),
       base::Time::Now() + base::TimeDelta::FromDays(1), false, false,
-      CookieSameSite::NO_RESTRICTION, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT));
 
   // Add a lax-samesite persistent cookie.
   store_->AddCookie(*CanonicalCookie::Create(
       GURL(kURL), kLaxName, kCookieValue, std::string(), kCookiePath,
       base::Time::Now() - base::TimeDelta::FromMinutes(2),
       base::Time::Now() + base::TimeDelta::FromDays(1), false, false,
-      CookieSameSite::LAX_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT));
 
   // Add a strict-samesite persistent cookie.
   store_->AddCookie(*CanonicalCookie::Create(
       GURL(kURL), kStrictName, kCookieValue, std::string(), kCookiePath,
       base::Time::Now() - base::TimeDelta::FromMinutes(3),
       base::Time::Now() + base::TimeDelta::FromDays(1), false, false,
-      CookieSameSite::STRICT_MODE, false, COOKIE_PRIORITY_DEFAULT));
+      CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT));
 
   // Force the store to write its data to the disk.
   DestroyStore();
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index efc3ea9..f2cef59 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -1399,17 +1399,6 @@
 
   const base::TimeTicks now = tick_clock_->NowTicks();
 
-  if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
-    // Read the effective connection type from the cached estimate.
-    last_effective_connection_type_computation_ = now;
-    network_quality_ = cached_network_quality.network_quality();
-    effective_connection_type_ =
-        cached_network_quality.effective_connection_type();
-
-    if (effective_connection_type_ != EFFECTIVE_CONNECTION_TYPE_UNKNOWN)
-      NotifyObserversOfEffectiveConnectionTypeChanged();
-  }
-
   if (cached_network_quality.network_quality().downstream_throughput_kbps() !=
       nqe::internal::kInvalidThroughput) {
     ThroughputObservation througphput_observation(
@@ -1439,6 +1428,7 @@
     rtt_observations_.AddObservation(rtt_observation);
     NotifyObserversOfRTT(rtt_observation);
   }
+  ComputeEffectiveConnectionType();
   return true;
 }
 
diff --git a/net/nqe/network_quality_estimator_params.cc b/net/nqe/network_quality_estimator_params.cc
index 28b873de..10294264 100644
--- a/net/nqe/network_quality_estimator_params.cc
+++ b/net/nqe/network_quality_estimator_params.cc
@@ -232,30 +232,30 @@
   }
 
   typical_network_quality[EFFECTIVE_CONNECTION_TYPE_SLOW_2G] = NetworkQuality(
-      // Set to the 83rd percentile of 2G RTT observations on Android. This
+      // Set to the 77.5th percentile of 2G RTT observations on Android. This
       // corresponds to the median RTT observation when effective connection
       // type is Slow 2G.
       base::TimeDelta::FromMilliseconds(3600),
-      base::TimeDelta::FromMilliseconds(3000), kInvalidThroughput);
+      base::TimeDelta::FromMilliseconds(3000), 40);
 
   typical_network_quality[EFFECTIVE_CONNECTION_TYPE_2G] = NetworkQuality(
       // Set to the 58th percentile of 2G RTT observations on Android. This
       // corresponds to the median RTT observation when effective connection
       // type is 2G.
       base::TimeDelta::FromMilliseconds(1800),
-      base::TimeDelta::FromMilliseconds(1500), kInvalidThroughput);
+      base::TimeDelta::FromMilliseconds(1500), 75);
 
   typical_network_quality[EFFECTIVE_CONNECTION_TYPE_3G] = NetworkQuality(
       // Set to the 75th percentile of 3G RTT observations on Android. This
       // corresponds to the median RTT observation when effective connection
       // type is 3G.
       base::TimeDelta::FromMilliseconds(450),
-      base::TimeDelta::FromMilliseconds(400), kInvalidThroughput);
+      base::TimeDelta::FromMilliseconds(400), 400);
 
   // Set to the 25th percentile of 3G RTT observations on Android.
-  typical_network_quality[EFFECTIVE_CONNECTION_TYPE_4G] = NetworkQuality(
-      base::TimeDelta::FromMilliseconds(175),
-      base::TimeDelta::FromMilliseconds(125), kInvalidThroughput);
+  typical_network_quality[EFFECTIVE_CONNECTION_TYPE_4G] =
+      NetworkQuality(base::TimeDelta::FromMilliseconds(175),
+                     base::TimeDelta::FromMilliseconds(125), 1600);
 
   static_assert(
       EFFECTIVE_CONNECTION_TYPE_4G + 1 == EFFECTIVE_CONNECTION_TYPE_LAST,
diff --git a/net/nqe/network_quality_estimator_unittest.cc b/net/nqe/network_quality_estimator_unittest.cc
index 8886358..b587d46 100644
--- a/net/nqe/network_quality_estimator_unittest.cc
+++ b/net/nqe/network_quality_estimator_unittest.cc
@@ -2710,13 +2710,12 @@
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500),
             rtt_observer.last_rtt(
                 NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE));
-  EXPECT_EQ(0u, throughput_observer.observations().size());
+  EXPECT_EQ(1u, throughput_observer.observations().size());
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(1800),
             rtt_throughput_observer.http_rtt());
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500),
             rtt_throughput_observer.transport_rtt());
-  EXPECT_EQ(nqe::internal::kInvalidThroughput,
-            rtt_throughput_observer.downstream_throughput_kbps());
+  EXPECT_EQ(75, rtt_throughput_observer.downstream_throughput_kbps());
   EXPECT_LE(
       1u,
       effective_connection_type_observer.effective_connection_types().size());
@@ -2738,13 +2737,12 @@
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(3000),
             rtt_observer.last_rtt(
                 NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE));
-  EXPECT_EQ(0u, throughput_observer.observations().size());
+  EXPECT_EQ(2U, throughput_observer.observations().size());
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(3600),
             rtt_throughput_observer.http_rtt());
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(3000),
             rtt_throughput_observer.transport_rtt());
-  EXPECT_EQ(nqe::internal::kInvalidThroughput,
-            rtt_throughput_observer.downstream_throughput_kbps());
+  EXPECT_EQ(40, rtt_throughput_observer.downstream_throughput_kbps());
   EXPECT_LE(
       2u,
       effective_connection_type_observer.effective_connection_types().size());
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 67151f6..b3053b2 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -755,11 +755,6 @@
     options.set_include_httponly();
     options.set_server_time(response_date);
 
-    if (network_delegate() &&
-        network_delegate()->AreStrictSecureCookiesEnabled()) {
-      options.set_enforce_strict_secure();
-    }
-
     // Set all cookies, without waiting for them to be set. Any subsequent read
     // will see the combined result of all cookie operation.
     const base::StringPiece name("Set-Cookie");
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 7ce435a..97ccc71 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -659,10 +659,6 @@
   return experimental_cookie_features_enabled_;
 }
 
-bool TestNetworkDelegate::OnAreStrictSecureCookiesEnabled() const {
-  return experimental_cookie_features_enabled_;
-}
-
 bool TestNetworkDelegate::OnCancelURLRequestWithPolicyViolatingReferrerHeader(
     const URLRequest& request,
     const GURL& target_url,
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h
index 3a90f5f7..201f07b 100644
--- a/net/url_request/url_request_test_util.h
+++ b/net/url_request/url_request_test_util.h
@@ -358,7 +358,6 @@
   bool OnCanAccessFile(const URLRequest& request,
                        const base::FilePath& path) const override;
   bool OnAreExperimentalCookieFeaturesEnabled() const override;
-  bool OnAreStrictSecureCookiesEnabled() const override;
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const URLRequest& request,
       const GURL& target_url,
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index ebebfac..a9d1c68 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -738,7 +738,6 @@
 class TestExperimentalFeaturesNetworkDelegate : public TestNetworkDelegate {
  public:
   bool OnAreExperimentalCookieFeaturesEnabled() const override { return true; }
-  bool OnAreStrictSecureCookiesEnabled() const override { return true; }
 };
 
 // OCSPErrorTestDelegate caches the SSLInfo passed to OnSSLCertificateError.
diff --git a/remoting/branding_Chrome b/remoting/branding_Chrome
index 86d6fa2e..4c247fca 100644
--- a/remoting/branding_Chrome
+++ b/remoting/branding_Chrome
@@ -1,6 +1,10 @@
 APK_PACKAGE_NAME=com.google.chromeremotedesktop
 DAEMON_FILE_NAME=Chrome Remote Desktop Host Service
 IT2ME_HOST_DESCRIPTION=Remote Assistance Host for Chrome Remote Desktop
+IOS_BUNDLE_ID=com.google.Chrome
+IOS_DISPLAY_NAME=Chrome Remote Desktop for iOS
+IOS_EXEC_NAME=ChromeRemoteDesktopiOS
+IOS_PRODUCT_NAME=Chrome Remote Desktop for iOS
 MAC_BUNDLE_ID=com.google.Chrome
 MAC_CREATOR=rimZ
 MAC_HOST_BUNDLE_ID=com.google.chromeremotedesktop.me2me-host
diff --git a/remoting/branding_Chromium b/remoting/branding_Chromium
index 3c3a43d..3013e6d 100644
--- a/remoting/branding_Chromium
+++ b/remoting/branding_Chromium
@@ -1,6 +1,10 @@
 APK_PACKAGE_NAME=org.chromium.chromoting
 DAEMON_FILE_NAME=Chromoting Host Service
 IT2ME_HOST_DESCRIPTION=Remote Assistance Host for Chromoting
+IOS_BUNDLE_ID=org.chromium.Chromium
+IOS_DISPLAY_NAME=Chromoting for iOS
+IOS_EXEC_NAME=chromoting
+IOS_PRODUCT_NAME=ChromotingForIos
 MAC_BUNDLE_ID=org.chromium.Chromium
 MAC_CREATOR=Cr24
 MAC_HOST_BUNDLE_ID=org.chromium.chromoting.me2me-host
diff --git a/remoting/client/ios/BUILD.gn b/remoting/client/ios/BUILD.gn
index b2644f1..5a086032 100644
--- a/remoting/client/ios/BUILD.gn
+++ b/remoting/client/ios/BUILD.gn
@@ -9,7 +9,6 @@
 
   deps = [
     ":ios_core",
-    ":main",
     "./bridge:all",
   ]
 }
@@ -23,30 +22,6 @@
   ]
 }
 
-source_set("main") {
-  sources = [
-    "app_delegate.h",
-    "app_delegate.mm",
-    "example_view_controller.h",
-    "example_view_controller.mm",
-    "main.mm",
-  ]
-
-  deps = [
-    "//base",
-    "//remoting/base",
-    "//remoting/client",
-    "//remoting/client/ios/bridge",
-    "//remoting/protocol",
-    "//third_party/google_toolbox_for_mac",
-    "//ui/base",
-    "//ui/gfx",
-    "//ui/resources",
-  ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
 source_set("ios_core") {
   sources = [
     "app_runtime.cc",
diff --git a/remoting/client/ios/app/BUILD.gn b/remoting/client/ios/app/BUILD.gn
new file mode 100644
index 0000000..2e13eef
--- /dev/null
+++ b/remoting/client/ios/app/BUILD.gn
@@ -0,0 +1,81 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/mac/tweak_info_plist.gni")
+import("//build/config/ios/rules.gni")
+import("//build/config/chrome_build.gni")
+import("//build/util/process_version.gni")
+import("//remoting/build/config/remoting_build.gni")
+
+group("all") {
+  testonly = true
+
+  deps = [
+    ":ios_remoting_app",
+  ]
+}
+
+source_set("main") {
+  sources = [
+    "app_delegate.h",
+    "app_delegate.mm",
+    "example_view_controller.h",
+    "example_view_controller.mm",
+    "main.mm",
+  ]
+
+  deps = [
+    "//base",
+    "//remoting/base",
+    "//remoting/client",
+    "//remoting/protocol",
+    "//ui/base",
+    "//ui/gfx",
+    "//ui/resources",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+tweak_info_plist("tweak_info_plist") {
+  info_plist = "//remoting/client/ios/app/resources/Info.plist"
+  args = [ "--platform=ios" ]
+}
+
+ios_app_bundle("ios_remoting_app") {
+  output_name = "remoting-ios"
+
+  entitlements_path = "resources/Remoting.entitlements"
+  info_plist_target = ":tweak_info_plist"
+
+  extra_substitutions = [
+    "BUNDLE_IDENTIFIER=$remoting_ios_bundle_id",
+    "DISPLAY_NAME=$remoting_ios_display_name",
+    "EXECUTABLE_NAME=$remoting_ios_executable_name",
+    "MINIMUM_OS_VERSION=7.0",
+    "PRODUCT_NAME=$remoting_ios_product_name",
+    "VERSION_FULL=$remoting_version_full",
+    "VERSION_SHORT=$remoting_version_short",
+  ]
+
+  libs = [
+    "Accelerate.framework",
+    "AudioToolbox.framework",
+    "CoreAudio.framework",
+    "CoreData.framework",
+    "CoreMIDI.framework",
+    "CoreVideo.framework",
+    "GLKit.framework",
+    "OpenGLES.framework",
+    "Webkit.framework",
+    "SafariServices.framework",
+    "SystemConfiguration.framework",
+  ]
+
+  deps = [
+    ":main",
+    "//base",
+    "//remoting/client/ios/display",
+  ]
+}
diff --git a/remoting/client/ios/app/app_delegate.h b/remoting/client/ios/app/app_delegate.h
new file mode 100644
index 0000000..ceef1d5
--- /dev/null
+++ b/remoting/client/ios/app/app_delegate.h
@@ -0,0 +1,18 @@
+// 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 REMOTING_CLIENT_IOS_APP_APP_DELEGATE_H_
+#define REMOTING_CLIENT_IOS_APP_APP_DELEGATE_H_
+
+#import <UIKit/UIKit.h>
+
+// Default created delegate class for the entire application.
+@interface AppDelegate : UIResponder<UIApplicationDelegate>
+
+@property(strong, nonatomic) UIWindow* window;
+
+@end
+
+#endif  // REMOTING_CLIENT_IOS_APP_APP_DELEGATE_H_
+
diff --git a/remoting/client/ios/app_delegate.mm b/remoting/client/ios/app/app_delegate.mm
similarity index 89%
rename from remoting/client/ios/app_delegate.mm
rename to remoting/client/ios/app/app_delegate.mm
index 484bd243..bceb7db 100644
--- a/remoting/client/ios/app_delegate.mm
+++ b/remoting/client/ios/app/app_delegate.mm
@@ -6,13 +6,13 @@
 #error "This file requires ARC support."
 #endif
 
-#import "remoting/client/ios/app_delegate.h"
+#import "remoting/client/ios/app/app_delegate.h"
 
 #include "base/logging.h"
 
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#import "remoting/client/ios/example_view_controller.h"
+#import "remoting/client/ios/app/example_view_controller.h"
 
 
 @implementation AppDelegate
diff --git a/remoting/client/ios/app/example_view_controller.h b/remoting/client/ios/app/example_view_controller.h
new file mode 100644
index 0000000..7973b4a
--- /dev/null
+++ b/remoting/client/ios/app/example_view_controller.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_CLIENT_IOS_APP_EXAMPLE_VIEW_CONTROLLER_H_
+#define REMOTING_CLIENT_IOS_APP_EXAMPLE_VIEW_CONTROLLER_H_
+
+#import <GLKit/GLKit.h>
+#import "remoting/client/ios/display/gl_display_handler.h"
+#include "remoting/client/ios/app_runtime.h"
+
+#include "remoting/client/software_video_renderer.h"
+#include "remoting/codec/video_encoder_verbatim.h"
+#include "remoting/proto/video.pb.h"
+#import "remoting/client/ios/client_gestures.h"
+
+@interface ExampleViewController : GLKViewController {
+ @private
+
+  // The GLES3 context being drawn to.
+  EAGLContext* _context;
+  remoting::ios::AppRuntime* _runtime;
+  GlDisplayHandler* _display_handler;
+  remoting::SoftwareVideoRenderer* _video_renderer;
+
+  remoting::VideoEncoderVerbatim _encoder;
+  ClientGestures* _gestures;
+}
+
+@end
+
+#endif  // REMOTING_CLIENT_IOS_APP_EXAMPLE_VIEW_CONTROLLER_H_
diff --git a/remoting/client/ios/app/example_view_controller.mm b/remoting/client/ios/app/example_view_controller.mm
new file mode 100644
index 0000000..a4e3b38
--- /dev/null
+++ b/remoting/client/ios/app/example_view_controller.mm
@@ -0,0 +1,87 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#import "remoting/client/ios/app/example_view_controller.h"
+
+#import "remoting/client/display/sys_opengl.h"
+
+#include "base/message_loop/message_loop.h"
+#include "remoting/protocol/frame_stats.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
+
+@interface ExampleViewController ()
+
+// Helper functions dealing with the GL Context.
+- (void)setupGL;
+- (void)tearDownGL;
+
+@end
+
+@implementation ExampleViewController
+
+#pragma mark - UIViewController
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  _gestures = [[ClientGestures alloc] initWithView:self.view];
+  // TODO(nicholss): For prod code, make sure to check for ES3 support and
+  // fall back to ES2 if needed.
+  _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
+  _runtime = new remoting::ios::AppRuntime();
+  static_cast<GLKView*>(self.view).context = _context;
+  [self setupGL];
+}
+
+- (void)viewDidUnload {
+  [super viewDidUnload];
+  [self tearDownGL];
+
+  if ([EAGLContext currentContext] == _context) {
+    [EAGLContext setCurrentContext:nil];
+  }
+  _context = nil;
+}
+
+- (void)setupGL {
+  _display_handler = [[GlDisplayHandler alloc] initWithRuntime:_runtime];
+  [_display_handler created];
+}
+
+- (void)tearDownGL {
+  // TODO(nicholss): Implement this in a real application.
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+  [super viewWillAppear:animated];
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+  _video_renderer =
+      (remoting::SoftwareVideoRenderer*)[_display_handler CreateVideoRenderer]
+          .get();
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+  [super viewWillDisappear:NO];
+}
+
+- (void)viewDidLayoutSubviews {
+  [super viewDidLayoutSubviews];
+}
+
+#pragma mark - GLKViewDelegate
+
+// In general, avoid expensive work in this function to maximize frame rate.
+- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect {
+  if (_display_handler) {
+    [_display_handler glkView:view drawInRect:rect];
+  }
+}
+
+@end
diff --git a/remoting/client/ios/main.mm b/remoting/client/ios/app/main.mm
similarity index 87%
rename from remoting/client/ios/main.mm
rename to remoting/client/ios/app/main.mm
index 8043a72..408b288 100644
--- a/remoting/client/ios/main.mm
+++ b/remoting/client/ios/app/main.mm
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -10,7 +10,7 @@
 
 #include "base/at_exit.h"
 #include "base/command_line.h"
-#import "remoting/client/ios/app_delegate.h"
+#import "remoting/client/ios/app/app_delegate.h"
 
 int main(int argc, char* argv[]) {
   // This class is designed to fulfill the dependents needs when it goes out of
diff --git a/remoting/client/ios/remoting_ios-Info.plist b/remoting/client/ios/app/resources/Info.plist
similarity index 90%
rename from remoting/client/ios/remoting_ios-Info.plist
rename to remoting/client/ios/app/resources/Info.plist
index a4cd5da0..4033728f 100644
--- a/remoting/client/ios/remoting_ios-Info.plist
+++ b/remoting/client/ios/app/resources/Info.plist
@@ -5,7 +5,7 @@
 	<key>CFBundleDevelopmentRegion</key>
 	<string>en</string>
 	<key>CFBundleDisplayName</key>
-	<string>Chromoting Demo</string>
+	<string>${DISPLAY_NAME}</string>
 	<key>CFBundleExecutable</key>
 	<string>${EXECUTABLE_NAME}</string>
 	<key>CFBundleIdentifier</key>
@@ -17,15 +17,15 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.0.11</string>
+	<string>${VERSION_SHORT}</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>1.0.11.0</string>
+	<string>${VERSION_FULL}</string>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>
 	<key>MinimumOSVersion</key>
-	<string>7.0</string>
+	<string>${MINIMUM_OS_VERSION}</string>
 	<key>UIRequiredDeviceCapabilities</key>
 	<array>
 		<string>armv7</string>
diff --git a/remoting/client/ios/app/resources/Remoting.entitlements b/remoting/client/ios/app/resources/Remoting.entitlements
new file mode 100644
index 0000000..d666505
--- /dev/null
+++ b/remoting/client/ios/app/resources/Remoting.entitlements
@@ -0,0 +1,17 @@
+<?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>application-identifier</key>
+	<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
+	<key>keychain-access-groups</key>
+	<array>
+		<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
+		<string>$(AppIdentifierPrefix)com.google.common.SSO</string>
+	</array>
+	<key>com.apple.security.application-groups</key>
+	<array>
+		<string>group.com.google.chrome</string>
+	</array>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/remoting/client/ios/app_delegate.h b/remoting/client/ios/app_delegate.h
deleted file mode 100644
index 1732280a..0000000
--- a/remoting/client/ios/app_delegate.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_CLIENT_IOS_APP_DELEGATE_H_
-#define REMOTING_CLIENT_IOS_APP_DELEGATE_H_
-
-#import <UIKit/UIKit.h>
-
-// Default created delegate class for the entire application.
-@interface AppDelegate : UIResponder<UIApplicationDelegate>
-
-@property(strong, nonatomic) UIWindow* window;
-
-@end
-
-#endif  // REMOTING_CLIENT_IOS_APP_DELEGATE_H_
-
diff --git a/remoting/client/ios/display/BUILD.gn b/remoting/client/ios/display/BUILD.gn
new file mode 100644
index 0000000..548667d8
--- /dev/null
+++ b/remoting/client/ios/display/BUILD.gn
@@ -0,0 +1,45 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("all") {
+  testonly = true
+
+  deps = [
+    ":display",
+  ]
+}
+
+group("all_test") {
+  testonly = true
+
+  deps = []
+}
+
+source_set("display") {
+  sources = [
+    "gl_demo_screen.h",
+    "gl_demo_screen.mm",
+    "gl_display_handler.h",
+    "gl_display_handler.mm",
+  ]
+
+  public_deps = [
+    "//remoting/client",
+    "//remoting/client/display",
+    "//remoting/client/ios/bridge",
+    "//third_party/webrtc/base:rtc_base_approved",
+  ]
+
+  deps = [
+    "//base",
+  ]
+
+  libs = [
+    "CoreGraphics.framework",
+    "GLKit.framework",
+    "OpenGLES.framework",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/remoting/client/ios/display/gl_demo_screen.h b/remoting/client/ios/display/gl_demo_screen.h
new file mode 100644
index 0000000..e522bbd
--- /dev/null
+++ b/remoting/client/ios/display/gl_demo_screen.h
@@ -0,0 +1,40 @@
+// 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 REMOTING_CLIENT_IOS_DISPLAY_GL_DEMO_SCREEN_H_
+#define REMOTING_CLIENT_IOS_DISPLAY_GL_DEMO_SCREEN_H_
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "remoting/client/display/drawable.h"
+#include "remoting/client/display/sys_opengl.h"
+
+namespace remoting {
+
+// This class draws the desktop on the canvas.
+class GlDemoScreen : public Drawable {
+ public:
+  GlDemoScreen();
+  ~GlDemoScreen() override;
+
+  // Drawable implementation.
+  void SetCanvas(base::WeakPtr<Canvas> canvas) override;
+  bool Draw() override;
+  base::WeakPtr<Drawable> GetWeakPtr() override;
+  int GetZIndex() override;
+
+ private:
+  base::WeakPtr<Canvas> canvas_;
+  int square_size_ = 0;
+  GLuint program_;
+
+  base::ThreadChecker thread_checker_;
+  base::WeakPtrFactory<Drawable> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(GlDemoScreen);
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_CLIENT_IOS_DISPLAY_GL_DEMO_SCREEN_H_
diff --git a/remoting/client/ios/display/gl_demo_screen.mm b/remoting/client/ios/display/gl_demo_screen.mm
new file mode 100644
index 0000000..db549817
--- /dev/null
+++ b/remoting/client/ios/display/gl_demo_screen.mm
@@ -0,0 +1,115 @@
+// 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 "remoting/client/ios/display/gl_demo_screen.h"
+
+#include "base/logging.h"
+#include "remoting/client/display/canvas.h"
+#include "remoting/client/display/gl_math.h"
+
+namespace remoting {
+
+namespace {
+
+const GLfloat square[] = {-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0};
+
+const GLchar* fragmentShaderSource =
+    "precision mediump float;"
+    "void main() {"
+    "  gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);"
+    "} ";
+
+const GLchar* vertexShaderSource =
+    "precision mediump float;"
+    "attribute vec4 a_position;"
+    "void main() {"
+    "  gl_Position = a_position;"
+    "}";
+
+const GLchar* a_position = "a_position";
+
+}  // namespace
+
+// This is a demo screen that can be added to the renderer to test the drawable
+// integration. This will draw an expanding checkerboard pattern to the screen.
+GlDemoScreen::GlDemoScreen() : weak_factory_(this)  {}
+
+GlDemoScreen::~GlDemoScreen() {}
+
+void GlDemoScreen::SetCanvas(base::WeakPtr<Canvas> canvas) {
+  canvas_ = canvas;
+
+  // Create and compile vertex shader.
+  GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
+  glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
+  glCompileShader(vertexShader);
+
+  // Create and compile fragment shader.
+  GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+  glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
+  glCompileShader(fragmentShader);
+
+  // Create and link program.
+  program_ = glCreateProgram();
+  glAttachShader(program_, vertexShader);
+  glAttachShader(program_, fragmentShader);
+  glLinkProgram(program_);
+}
+
+int GlDemoScreen::GetZIndex() {
+  return Drawable::DESKTOP + 1;
+}
+
+bool GlDemoScreen::Draw() {
+  if (!canvas_) {
+    return false;
+  }
+
+  // TODO(nicholss): width and height should be dynamic based on the canvas.
+  int width = 640;
+  int height = 1024;
+  square_size_++;
+  if (square_size_ > 300) {
+    square_size_ = 1;
+  }
+
+  // Set the viewport.
+  glViewport(0, 0, width, height);
+
+  // Clear.
+  glClearColor(0, 1, 0, 1);
+  glClear(GL_COLOR_BUFFER_BIT);
+
+  // Use program.
+  glUseProgram(program_);
+
+  int skip = 0;
+  for (int i = 0; i < width; i += square_size_) {
+    if (skip == square_size_) {
+      skip = 0;
+    } else {
+      skip = square_size_;
+    }
+    for (int j = skip; j < height; j += square_size_ * 2) {
+      glViewport(i, j, square_size_, square_size_);
+
+      // Send geometry to vertex shader.
+      GLuint aPosition = glGetAttribLocation(program_, a_position);
+
+      glVertexAttribPointer(aPosition, 2, GL_FLOAT, GL_FALSE, 0, square);
+      glEnableVertexAttribArray(aPosition);
+
+      // Draw.
+      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    }
+  }
+  return true;
+}
+
+base::WeakPtr<Drawable> GlDemoScreen::GetWeakPtr() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return weak_factory_.GetWeakPtr();
+}
+
+}  // namespace remoting
diff --git a/remoting/client/ios/display/gl_display_handler.h b/remoting/client/ios/display/gl_display_handler.h
new file mode 100644
index 0000000..39aad3e
--- /dev/null
+++ b/remoting/client/ios/display/gl_display_handler.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_CLIENT_IOS_DISPLAY_GL_DISPLAY_HANDLER_H_
+#define REMOTING_CLIENT_IOS_DISPLAY_GL_DISPLAY_HANDLER_H_
+
+#import "remoting/client/display/sys_opengl.h"
+#include "remoting/client/client_context.h"
+#include "remoting/protocol/frame_consumer.h"
+#include "remoting/protocol/video_renderer.h"
+
+namespace remoting {
+namespace ios {
+
+class AppRuntime;
+
+}  // namespace ios
+}  // namespace remoting
+
+@interface GlDisplayHandler : NSObject {
+}
+
+- (id)initWithRuntime:(remoting::ios::AppRuntime*)runtime;
+
+- (void)created;
+- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect;
+- (std::unique_ptr<remoting::protocol::VideoRenderer>)CreateVideoRenderer;
+
+@end
+
+#endif  // REMOTING_CLIENT_IOS_DISPLAY_GL_DISPLAY_HANDLER_H_
diff --git a/remoting/client/ios/display/gl_display_handler.mm b/remoting/client/ios/display/gl_display_handler.mm
new file mode 100644
index 0000000..414665f
--- /dev/null
+++ b/remoting/client/ios/display/gl_display_handler.mm
@@ -0,0 +1,155 @@
+// 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.
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+
+#import <Foundation/Foundation.h>
+#import <GLKit/GLKit.h>
+
+#import "remoting/client/display/sys_opengl.h"
+#import "remoting/client/ios/display/gl_demo_screen.h"
+#import "remoting/client/ios/display/gl_display_handler.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "remoting/client/chromoting_client.h"
+#include "remoting/client/display/gl_canvas.h"
+#include "remoting/client/display/gl_renderer.h"
+#include "remoting/client/display/gl_renderer_delegate.h"
+#include "remoting/client/dual_buffer_frame_consumer.h"
+#include "remoting/client/ios/app_runtime.h"
+#include "remoting/client/software_video_renderer.h"
+
+namespace remoting {
+namespace GlDisplayHandler {
+
+// The core that lives on the display thread.
+class Core :  public GlRendererDelegate {
+ public:
+  Core(remoting::ios::AppRuntime* runtime);
+  ~Core() override;
+
+  // GlRendererDelegate interface.
+  bool CanRenderFrame() override;
+  void OnFrameRendered() override;
+  void OnSizeChanged(int width, int height) override;
+
+  void Created();
+  void SurfaceChanged(int width, int height);
+  std::unique_ptr<protocol::FrameConsumer> GrabFrameConsumer();
+  base::WeakPtr<Core> GetWeakPtr();
+
+ private:
+  // Will be std::move'd when GrabFrameConsumer() is called.
+  remoting::ios::AppRuntime* runtime_;
+  std::unique_ptr<DualBufferFrameConsumer> owned_frame_consumer_;
+
+  base::WeakPtr<DualBufferFrameConsumer> frame_consumer_;
+  EAGLContext* eagl_context_;
+  GlRenderer renderer_;
+  GlDemoScreen demo_screen_;
+
+  // Used on display thread.
+  base::WeakPtr<Core> weak_ptr_;
+  base::WeakPtrFactory<Core> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+Core::Core(remoting::ios::AppRuntime* runtime)
+    : runtime_(runtime), weak_factory_(this) {
+  weak_ptr_ = weak_factory_.GetWeakPtr();
+  renderer_.SetDelegate(weak_ptr_);
+  owned_frame_consumer_.reset(new remoting::DualBufferFrameConsumer(
+      base::Bind(&remoting::GlRenderer::OnFrameReceived,
+                 renderer_.GetWeakPtr()),
+      runtime_->display_task_runner(),
+      remoting::protocol::FrameConsumer::PixelFormat::FORMAT_RGBA));
+  frame_consumer_ = owned_frame_consumer_->GetWeakPtr();
+  renderer_.AddDrawable(demo_screen_.GetWeakPtr());
+}
+
+Core::~Core() {}
+
+bool Core::CanRenderFrame() {
+  DCHECK(runtime_->display_task_runner()->BelongsToCurrentThread());
+  return eagl_context_ != NULL;
+}
+
+std::unique_ptr<protocol::FrameConsumer> Core::GrabFrameConsumer() {
+  DCHECK(owned_frame_consumer_) << "The frame consumer is already grabbed.";
+  return std::move(owned_frame_consumer_);
+}
+
+void Core::OnFrameRendered() {
+  // Nothing to do.
+}
+
+void Core::OnSizeChanged(int width, int height) {
+  // Nothing to do.
+}
+
+void Core::Created() {
+  DCHECK(runtime_->display_task_runner()->BelongsToCurrentThread());
+  DCHECK(!eagl_context_);
+
+  eagl_context_ = [EAGLContext currentContext];
+  renderer_.RequestCanvasSize();
+
+  renderer_.OnSurfaceCreated(base::MakeUnique<GlCanvas>(
+      static_cast<int>([eagl_context_ API])));
+}
+
+void Core::SurfaceChanged(int width, int height) {
+  DCHECK(runtime_->display_task_runner()->BelongsToCurrentThread());
+  renderer_.OnSurfaceChanged(width, height);
+}
+
+base::WeakPtr<remoting::GlDisplayHandler::Core> Core::GetWeakPtr() {
+  return weak_ptr_;
+}
+
+}  // namespace GlDisplayHandler
+}  // namespace remoting
+
+@interface GlDisplayHandler ()
+@property(nonatomic) remoting::GlDisplayHandler::Core* core_;
+@property(nonatomic) remoting::ios::AppRuntime* runtime_;
+@end
+
+@implementation GlDisplayHandler
+
+@synthesize core_ = _core_;
+@synthesize runtime_ = _runtime_;
+
+- (id)initWithRuntime:(remoting::ios::AppRuntime*)runtime {
+  self.runtime_ = runtime;
+  return self;
+}
+
+- (void)created {
+  _core_ = new remoting::GlDisplayHandler::Core(self.runtime_);
+
+  self.runtime_->display_task_runner()->PostTask(
+      FROM_HERE, base::Bind(&remoting::GlDisplayHandler::Core::Created,
+                            self.core_->GetWeakPtr()));
+}
+
+- (std::unique_ptr<remoting::protocol::VideoRenderer>)CreateVideoRenderer {
+  return base::MakeUnique<remoting::SoftwareVideoRenderer>(
+      _core_->GrabFrameConsumer());
+}
+
+// In general, avoid expensive work in this function to maximize frame rate.
+- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect {
+  if (_core_) {
+    _core_->SurfaceChanged(rect.size.width, rect.size.height);
+  }
+}
+
+@end
diff --git a/remoting/client/ios/example_view_controller.h b/remoting/client/ios/example_view_controller.h
deleted file mode 100644
index d6fd3b74..0000000
--- a/remoting/client/ios/example_view_controller.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_CLIENT_IOS_EXAMPLE_VIEW_CONTROLLER_H_
-#define REMOTING_CLIENT_IOS_EXAMPLE_VIEW_CONTROLLER_H_
-
-#import <GLKit/GLKit.h>
-
-@interface ExampleViewController : GLKViewController {
- @private
-
-  // The GLES2 context being drawn too.
-  EAGLContext* _context;
-
-}
-
-@end
-
-#endif  // REMOTING_CLIENT_IOS_EXAMPLE_VIEW_CONTROLLER_H_
diff --git a/remoting/client/ios/example_view_controller.mm b/remoting/client/ios/example_view_controller.mm
deleted file mode 100644
index 64fd9ac..0000000
--- a/remoting/client/ios/example_view_controller.mm
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-#import "remoting/client/ios/example_view_controller.h"
-
-#import "remoting/client/display/sys_opengl.h"
-
-@interface ExampleViewController()
-
-// Helper functions dealing with the GL Context.
-- (void)setupGL;
-- (void)tearDownGL;
-
-@end
-
-@implementation ExampleViewController
-
-#pragma mark - UIViewController
-
-- (void)viewDidLoad {
-  [super viewDidLoad];
-
-  _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
-  static_cast<GLKView*>(self.view).context = _context;
-
-  [self setupGL];
-}
-
-- (void)viewDidUnload {
-  [super viewDidUnload];
-  [self tearDownGL];
-
-  if ([EAGLContext currentContext] == _context) {
-    [EAGLContext setCurrentContext:nil];
-  }
-  _context = nil;
-}
-
-- (void)setupGL {
-  [EAGLContext setCurrentContext:_context];
-}
-
-- (void)tearDownGL {
-  [EAGLContext setCurrentContext:_context];
-}
-
-- (void)viewWillAppear:(BOOL)animated {
-  [super viewWillAppear:animated];
-}
-
-- (void)viewDidAppear:(BOOL)animated {
-}
-
-- (void)viewWillDisappear:(BOOL)animated {
-  [super viewWillDisappear:NO];
-}
-
-- (void)viewDidLayoutSubviews {
-  [super viewDidLayoutSubviews];
-}
-
-#pragma mark - GLKViewDelegate
-
-// In general, avoid expensive work in this function to maximize frame rate.
-- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect {
-  // Clear to give the background color.
-  glClearColor(0.0, 40.0, 0.0, 1.0);
-  glClear(GL_COLOR_BUFFER_BIT);
-}
-
-@end
diff --git a/remoting/remoting_version.gni b/remoting/remoting_version.gni
index 8a0df67..3dad3af4 100644
--- a/remoting/remoting_version.gni
+++ b/remoting/remoting_version.gni
@@ -45,6 +45,13 @@
   _template += "uninstaller_bundle_id = \"@MAC_UNINSTALLER_BUNDLE_ID@\""
 }
 
+if (is_ios) {
+  _template += "ios_bundle_id = \"@IOS_BUNDLE_ID@\""
+  _template += "ios_display_name = \"@IOS_DISPLAY_NAME@\""
+  _template += "ios_exec_name = \"@IOS_EXEC_NAME@\""
+  _template += "ios_product_name = \"@IOS_PRODUCT_NAME@\""
+}
+
 _result = exec_script(_version_py_abspath,
                       [
                         "-f",
@@ -89,3 +96,10 @@
                   [ "$host_uninstaller_name" ],
                   "trim string")
 }
+
+if (is_ios) {
+  remoting_ios_bundle_id = _result.ios_bundle_id
+  remoting_ios_display_name = _result.ios_display_name
+  remoting_ios_executable_name = _result.ios_exec_name
+  remoting_ios_product_name = _result.ios_product_name
+}
diff --git a/services/ui/surfaces/display_compositor.cc b/services/ui/surfaces/display_compositor.cc
index 4c3e068..c00278a 100644
--- a/services/ui/surfaces/display_compositor.cc
+++ b/services/ui/surfaces/display_compositor.cc
@@ -34,7 +34,6 @@
     cc::mojom::DisplayCompositorRequest request,
     cc::mojom::DisplayCompositorClientPtr client)
     : manager_(cc::SurfaceManager::LifetimeType::REFERENCES),
-      reference_manager_(&manager_),
       gpu_service_(std::move(gpu_service)),
       gpu_memory_buffer_manager_(std::move(gpu_memory_buffer_manager)),
       image_factory_(image_factory),
@@ -149,10 +148,6 @@
       base::MakeUnique<cc::TextureMailboxDeleter>(task_runner_.get()));
 }
 
-const cc::SurfaceId& DisplayCompositor::GetRootSurfaceId() const {
-  return reference_manager_->GetRootSurfaceId();
-}
-
 void DisplayCompositor::OnSurfaceCreated(const cc::SurfaceInfo& surface_info) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK_GT(surface_info.device_scale_factor(), 0.0f);
diff --git a/services/ui/surfaces/display_compositor.h b/services/ui/surfaces/display_compositor.h
index 938cfb0..db1af60 100644
--- a/services/ui/surfaces/display_compositor.h
+++ b/services/ui/surfaces/display_compositor.h
@@ -33,7 +33,6 @@
 
 namespace cc {
 class Display;
-class SurfaceManager;
 class SyntheticBeginFrameSource;
 }
 
@@ -100,8 +99,6 @@
       cc::mojom::MojoCompositorFrameSinkClientPtr client,
       cc::mojom::DisplayPrivateRequest display_private_request);
 
-  const cc::SurfaceId& GetRootSurfaceId() const;
-
   // cc::SurfaceObserver implementation.
   void OnSurfaceCreated(const cc::SurfaceInfo& surface_info) override;
   void OnSurfaceDamaged(const cc::SurfaceId& surface_id,
@@ -112,10 +109,6 @@
   // access to a valid pointer for the entirety of their liftimes.
   cc::SurfaceManager manager_;
 
-  // Will normally point to |manager_| as it provides the interface. For tests
-  // it will be swapped out with a mock implementation.
-  cc::SurfaceReferenceManager* reference_manager_;
-
   scoped_refptr<gpu::InProcessCommandBuffer::Service> gpu_service_;
   std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_;
   gpu::ImageFactory* image_factory_;
diff --git a/testing/buildbot/OWNERS b/testing/buildbot/OWNERS
index c9853594..2123387 100644
--- a/testing/buildbot/OWNERS
+++ b/testing/buildbot/OWNERS
@@ -12,6 +12,7 @@
 stip@chromium.org
 thakis@chromium.org
 
+per-file chromium.android*.json=bpastene@chromium.org
 per-file chromium.android*.json=jbudorick@chromium.org
 
 # For Site Isolation FYI tests:
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 10c45a7..afdd79086 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -567,7 +567,6 @@
 crbug.com/635619 virtual/layout_ng/fast/block/float/clear-to-fit.html [ Skip ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/containing-block-change-compositing.html [ Skip ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/crash-on-absolute-positioning.html [ Skip ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/display-flow-root-001.html [ Skip ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/dynamic-unfloat-pref-width.html [ Skip ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/editable-text-overlapping-float.html [ Skip ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/element-clears-float-without-clearance.html [ Skip ]
@@ -819,7 +818,7 @@
 
 crbug.com/685851 [ Win ] fast/table/backgr_position-table-column-group.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_position-table-column-group-collapsed-border.html [ Pass Failure ]
-crbug.com/685851 [ Win ] fast/table/backgr_layers-show-collapsed-border.html [ Pass Failure ]
+crbug.com/685851 [ Win10 ] fast/table/backgr_layers-show-collapsed-border.html [ Failure Pass ]
 crbug.com/685851 [ Win ] fast/table/backgr_simple-table-column.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_position-table-row.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-row.html [ Pass Failure ]
@@ -831,7 +830,7 @@
 crbug.com/685851 [ Win ] fast/table/backgr_position-table-column-collapsed-border.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_simple-table-collapsed-border.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-row-group.html [ Pass Failure ]
-crbug.com/685851 [ Win Mac ] fast/table/backgr_simple-table-cell.html [ Pass Failure ]
+crbug.com/685851 [ Win10 ] fast/table/backgr_simple-table-cell.html [ Failure Pass ]
 crbug.com/685851 [ Win ] fast/table/backgr_position-table.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_simple-table-row-group.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-collapsed-border.html [ Pass Failure ]
@@ -841,22 +840,22 @@
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-row-collapsed-border.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-column-group-collapsed-border.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_simple-table.html [ Pass Failure ]
-crbug.com/685851 [ Win ] fast/table/backgr_simple-table-row.html [ Pass Failure ]
+crbug.com/685851 [ Win10 ] fast/table/backgr_simple-table-row.html [ Failure Pass ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-cell-collapsed-border.html [ Pass Failure ]
-crbug.com/685851 [ Win ] fast/table/backgr_simple-table-row-group-collapsed-border.html [ Pass Failure ]
+crbug.com/685851 [ Win10 ] fast/table/backgr_simple-table-row-group-collapsed-border.html [ Failure Pass ]
 crbug.com/685851 [ Win ] fast/table/backgr_simple-table-cell-collapsed-border.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_position-table-row-group.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_simple-table-column-group.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_simple-table-column-group-collapsed-border.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-column.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_position-table-cell-collapsed-border.html [ Pass Failure ]
-crbug.com/685851 [ Win ] fast/table/backgr_layers-show.html [ Pass Failure ]
+crbug.com/685851 [ Win10 ] fast/table/backgr_layers-show.html [ Failure Pass ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-row-group-collapsed-border.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-quirks.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-quirks-collapsed-border.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table-column-group.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_simple-table-column-collapsed-border.html [ Pass Failure ]
-crbug.com/685851 [ Win ] fast/table/backgr_position-table-cell.html [ Pass Failure ]
+crbug.com/685851 [ Win10 ] fast/table/backgr_position-table-cell.html [ Failure Pass ]
 crbug.com/685851 [ Win ] fast/table/backgr_border-table.html [ Pass Failure ]
 crbug.com/685851 [ Win ] fast/table/backgr_layers-hide-collapsed-border.html [ Pass Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/display-flow-root-001-expected.html b/third_party/WebKit/LayoutTests/fast/block/float/display-flow-root-001-expected.html
deleted file mode 100644
index fcb5ac27..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/display-flow-root-001-expected.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE HTML>
-<!--
-     Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/
--->
-<html><head>
-  <meta charset="utf-8">
-  <title>Reference: display:flow-root</title>
-  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322191">
-  <style type="text/css">
-html,body {
-  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
-}
-
-.float {
-  float: left;
-  width: 20px;
-  height: 40px;
-  background: pink;
-}
-
-.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden;}
-
-  </style>
-</head>
-<body>
-
-<div style="border:1px solid">
-  <div style="margin: 40px 0">
-    <div>x</div>
-  </div>
-</div>
-
-<div style="border:1px solid">
-  <div class="float"></div>
-  <div class="clearfix"></div>
-</div>
-
-<div style="border:1px solid">
-  <div class="float"></div>
-  <div style="display:block; border:1px solid; margin-left:20px">x</div>
-</div>
-
-<span>
-  <span style="display:block; background:grey; margin:20px 0 0 21px"><div style="padding:20px">x</div></span>
-</span>
-
-<div style="border:3px solid; height:10px;"></div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/display-flow-root-001.html b/third_party/WebKit/LayoutTests/fast/block/float/display-flow-root-001.html
deleted file mode 100644
index 3d1dcb02..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/display-flow-root-001.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE HTML>
-<!--
-     Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/
--->
-<html><head>
-  <meta charset="utf-8">
-  <title>CSS Display Test: display:flow-root</title>
-  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322191">
-  <link rel="help" href="https://drafts.csswg.org/css-display-3/#valdef-display-flow-root">
-  <link rel="match" href="display-flow-root-001-ref.html">
-  <style type="text/css">
-html,body {
-  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
-}
-
-.float {
-  float: left;
-  width: 20px;
-  height: 40px;
-  background: pink;
-}
-
-  </style>
-</head>
-<body>
-
-<div style="border:1px solid">
-  <!-- this tests that the flow-root margins don't collapse with its children. -->
-  <span style="display:flow-root; margin: 20px 0">
-    <div style="margin: 20px 0">x</div>
-  </span>
-</div>
-
-<div style="border:1px solid">
-  <!-- this tests that the flow-root grows to fit child floats -->
-  <span style="display:flow-root"><div class="float"></div></span>
-</div>
-
-<div style="border:1px solid; margin-bottom:20px">
-  <!-- this tests that a float does not intrude into flow-root box -->
-  <div class="float"></div>
-  <span style="display:flow-root; border:1px solid">x</span>
-</div>
-
-<span>
-  <!-- this tests that a flow-root box is constructed also in the "ibsplit" case -->
-  <span style="display:flow-root; background:grey;"><div style="margin:20px">x</div></span>
-</span>
-
-<span style="display:flow-root; border:3px solid; height:10px;">
-  <!-- this tests that a flow-root fills the available width, and that 'height' applies -->
-</span>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/table/backgr_simple-table-cell-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/table/backgr_simple-table-cell-expected.png
index a7f9b81..06d918c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/table/backgr_simple-table-cell-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/table/backgr_simple-table-cell-expected.png
Binary files differ
diff --git a/third_party/WebKit/Source/bindings/modules/v8/generated.gni b/third_party/WebKit/Source/bindings/modules/v8/generated.gni
index d8fa33f..5dab9cbb 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/generated.gni
+++ b/third_party/WebKit/Source/bindings/modules/v8/generated.gni
@@ -24,6 +24,8 @@
   "$bindings_modules_v8_output_dir/BooleanOrConstrainBooleanParameters.h",
   "$bindings_modules_v8_output_dir/BooleanOrMediaTrackConstraints.cpp",
   "$bindings_modules_v8_output_dir/BooleanOrMediaTrackConstraints.h",
+  "$bindings_modules_v8_output_dir/ByteStringSequenceSequenceOrDictionaryOrHeaders.cpp",
+  "$bindings_modules_v8_output_dir/ByteStringSequenceSequenceOrDictionaryOrHeaders.h",
   "$bindings_modules_v8_output_dir/ClientOrServiceWorkerOrMessagePort.cpp",
   "$bindings_modules_v8_output_dir/ClientOrServiceWorkerOrMessagePort.h",
   "$bindings_modules_v8_output_dir/CSSImageValueOrHTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrImageBitmapOrOffscreenCanvas.cpp",
diff --git a/third_party/WebKit/Source/core/frame/BrowserControls.h b/third_party/WebKit/Source/core/frame/BrowserControls.h
index cbabd91..10c1f86a 100644
--- a/third_party/WebKit/Source/core/frame/BrowserControls.h
+++ b/third_party/WebKit/Source/core/frame/BrowserControls.h
@@ -49,8 +49,6 @@
   // scroll amount.
   FloatSize scrollBy(FloatSize scrollDelta);
 
-  WebBrowserControlsState permittedState() const { return m_permittedState; }
-
  private:
   explicit BrowserControls(const FrameHost&);
   void resetBaseline();
diff --git a/third_party/WebKit/Source/devtools/front_end/console/consoleView.css b/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
index 6a35556..81dc4de 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
+++ b/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
@@ -105,7 +105,7 @@
 .message-level-icon, .command-result-icon {
     position: absolute;
     left: -17px;
-    top: 5px;
+    top: 4px;
     -webkit-user-select: none;
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
index e556a60..667fe18 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
@@ -101,9 +101,9 @@
   'smallicon-thick-right-arrow': {x: -180, y: 0, width: 10, height: 10, spritesheet: 'smallicons'},
   'smallicon-thick-left-arrow':
       {x: -180, y: 0, width: 10, height: 10, spritesheet: 'smallicons', transform: 'rotate(180deg)'},
-  'smallicon-user-command': {x: 0, y: -20, width: 10, height: 10, spritesheet: 'smallicons'},
+  'smallicon-user-command': {x: 0, y: -19, width: 10, height: 10, spritesheet: 'smallicons'},
   'smallicon-text-prompt': {x: -20, y: -20, width: 10, height: 10, spritesheet: 'smallicons'},
-  'smallicon-command-result': {x: -40, y: -20, width: 10, height: 10, spritesheet: 'smallicons'},
+  'smallicon-command-result': {x: -40, y: -19, width: 10, height: 10, spritesheet: 'smallicons'},
   'smallicon-shadow': {x: -60, y: -20, width: 10, height: 10, spritesheet: 'smallicons', isMask: true},
   'smallicon-bezier': {x: -80, y: -20, width: 10, height: 10, spritesheet: 'smallicons', isMask: true},
   'smallicon-dropdown-arrow': {x: -18, y: -96, width: 12, height: 12, spritesheet: 'largeicons', isMask: true},
diff --git a/third_party/WebKit/Source/modules/fetch/BUILD.gn b/third_party/WebKit/Source/modules/fetch/BUILD.gn
index ac9e3e4..3248deb0 100644
--- a/third_party/WebKit/Source/modules/fetch/BUILD.gn
+++ b/third_party/WebKit/Source/modules/fetch/BUILD.gn
@@ -40,6 +40,5 @@
     "RequestInit.h",
     "Response.cpp",
     "Response.h",
-    "ResponseInit.h",
   ]
 }
diff --git a/third_party/WebKit/Source/modules/fetch/Response.cpp b/third_party/WebKit/Source/modules/fetch/Response.cpp
index fc32fe8..12e7678 100644
--- a/third_party/WebKit/Source/modules/fetch/Response.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Response.cpp
@@ -14,6 +14,7 @@
 #include "bindings/core/v8/V8FormData.h"
 #include "bindings/core/v8/V8HiddenValue.h"
 #include "bindings/core/v8/V8URLSearchParams.h"
+#include "bindings/modules/v8/ByteStringSequenceSequenceOrDictionaryOrHeaders.h"
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/DOMArrayBufferView.h"
 #include "core/dom/URLSearchParams.h"
@@ -127,7 +128,7 @@
 
 Response* Response::create(ScriptState* scriptState,
                            ScriptValue bodyValue,
-                           const Dictionary& init,
+                           const ResponseInit& init,
                            ExceptionState& exceptionState) {
   v8::Local<v8::Value> body = bodyValue.v8Value();
   v8::Isolate* isolate = scriptState->isolate();
@@ -180,8 +181,7 @@
         new BodyStreamBuffer(scriptState, new FormDataBytesConsumer(string));
     contentType = "text/plain;charset=UTF-8";
   }
-  return create(scriptState, bodyBuffer, contentType,
-                ResponseInit(init, exceptionState), exceptionState);
+  return create(scriptState, bodyBuffer, contentType, init, exceptionState);
 }
 
 Response* Response::create(ScriptState* scriptState,
@@ -189,7 +189,7 @@
                            const String& contentType,
                            const ResponseInit& init,
                            ExceptionState& exceptionState) {
-  unsigned short status = init.status;
+  unsigned short status = init.status();
 
   // "1. If |init|'s status member is not in the range 200 to 599, inclusive,
   //     throw a RangeError."
@@ -203,7 +203,7 @@
 
   // "2. If |init|'s statusText member does not match the Reason-Phrase
   // token production, throw a TypeError."
-  if (!isValidReasonPhrase(init.statusText)) {
+  if (!isValidReasonPhrase(init.statusText())) {
     exceptionState.throwTypeError("Invalid statusText");
     return nullptr;
   }
@@ -213,26 +213,25 @@
   Response* r = new Response(scriptState->getExecutionContext());
 
   // "4. Set |r|'s response's status to |init|'s status member."
-  r->m_response->setStatus(init.status);
+  r->m_response->setStatus(init.status());
 
   // "5. Set |r|'s response's status message to |init|'s statusText member."
-  r->m_response->setStatusMessage(AtomicString(init.statusText));
+  r->m_response->setStatusMessage(AtomicString(init.statusText()));
 
   // "6. If |init|'s headers member is present, run these substeps:"
-  if (init.headers) {
+  if (init.hasHeaders()) {
     // "1. Empty |r|'s response's header list."
     r->m_response->headerList()->clearList();
     // "2. Fill |r|'s Headers object with |init|'s headers member. Rethrow
     // any exceptions."
-    r->m_headers->fillWith(init.headers.get(), exceptionState);
-    if (exceptionState.hadException())
-      return nullptr;
-  } else if (!init.headersDictionary.isUndefinedOrNull()) {
-    // "1. Empty |r|'s response's header list."
-    r->m_response->headerList()->clearList();
-    // "2. Fill |r|'s Headers object with |init|'s headers member. Rethrow
-    // any exceptions."
-    r->m_headers->fillWith(init.headersDictionary, exceptionState);
+    if (init.headers().isByteStringSequenceSequence()) {
+      r->m_headers->fillWith(init.headers().getAsByteStringSequenceSequence(),
+                             exceptionState);
+    } else if (init.headers().isDictionary()) {
+      r->m_headers->fillWith(init.headers().getAsDictionary(), exceptionState);
+    } else if (init.headers().isHeaders()) {
+      r->m_headers->fillWith(init.headers().getAsHeaders(), exceptionState);
+    }
     if (exceptionState.hadException())
       return nullptr;
   }
diff --git a/third_party/WebKit/Source/modules/fetch/Response.h b/third_party/WebKit/Source/modules/fetch/Response.h
index 370c168..291c6fb 100644
--- a/third_party/WebKit/Source/modules/fetch/Response.h
+++ b/third_party/WebKit/Source/modules/fetch/Response.h
@@ -36,7 +36,7 @@
   static Response* create(ScriptState*, ExceptionState&);
   static Response* create(ScriptState*,
                           ScriptValue body,
-                          const Dictionary&,
+                          const ResponseInit&,
                           ExceptionState&);
 
   static Response* create(ScriptState*,
diff --git a/third_party/WebKit/Source/modules/fetch/Response.idl b/third_party/WebKit/Source/modules/fetch/Response.idl
index 91db9f7..2332d3b 100644
--- a/third_party/WebKit/Source/modules/fetch/Response.idl
+++ b/third_party/WebKit/Source/modules/fetch/Response.idl
@@ -10,7 +10,7 @@
     ActiveScriptWrappable,
     // TODO(yhirano): We use "any" for body because the IDL processor doesn't
     // recognize ReadableStream implemented with V8 extras. Fix it.
-    Constructor(optional any body, optional Dictionary responseInitDict),
+    Constructor(optional any body, optional ResponseInit init),
     ConstructorCallWith=ScriptState,
     DependentLifetime,
     Exposed=(Window,Worker),
diff --git a/third_party/WebKit/Source/modules/fetch/ResponseInit.h b/third_party/WebKit/Source/modules/fetch/ResponseInit.h
deleted file mode 100644
index ecd2c041..0000000
--- a/third_party/WebKit/Source/modules/fetch/ResponseInit.h
+++ /dev/null
@@ -1,46 +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 ResponseInit_h
-#define ResponseInit_h
-
-#include "bindings/core/v8/Dictionary.h"
-#include "modules/fetch/Headers.h"
-#include "platform/heap/Handle.h"
-#include "wtf/RefPtr.h"
-
-namespace blink {
-
-// FIXME: Use IDL dictionary instead of this class.
-class ResponseInit {
-  STACK_ALLOCATED();
-
- public:
-  ResponseInit() : status(200), statusText("OK") {}
-  explicit ResponseInit(const Dictionary& options,
-                        ExceptionState& exceptionState)
-      : status(200), statusText("OK") {
-    DictionaryHelper::get(options, "status", status);
-    // FIXME: Spec uses ByteString for statusText. http://crbug.com/347426
-    DictionaryHelper::get(options, "statusText", statusText);
-    DictionaryHelper::get(options, "headers", headers);
-    if (!headers) {
-      Vector<Vector<String>> headersVector;
-      if (DictionaryHelper::get(options, "headers", headersVector,
-                                exceptionState))
-        headers = Headers::create(headersVector, exceptionState);
-      else
-        DictionaryHelper::get(options, "headers", headersDictionary);
-    }
-  }
-
-  unsigned short status;
-  String statusText;
-  Member<Headers> headers;
-  Dictionary headersDictionary;
-};
-
-}  // namespace blink
-
-#endif  // ResponseInit_h
diff --git a/third_party/WebKit/Source/modules/fetch/ResponseInit.idl b/third_party/WebKit/Source/modules/fetch/ResponseInit.idl
new file mode 100644
index 0000000..6829802
--- /dev/null
+++ b/third_party/WebKit/Source/modules/fetch/ResponseInit.idl
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://fetch.spec.whatwg.org/#responseinit
+
+// The actual typedef in the spec is
+//    (sequence<sequence<ByteString>> or record<ByteString,ByteString>)
+// which also implicitly includes Headers since it is iterable.
+// Blink's WebIDL code does not support record<K,V> yet, so we have to make do
+// with the (Dictionary or Headers) part instead.
+// See http://crbug.com/685754.
+typedef (sequence<sequence<ByteString>> or Dictionary or Headers) HeadersInit;
+
+dictionary ResponseInit {
+    unsigned short status = 200;
+    ByteString statusText = "OK";
+    HeadersInit headers;
+};
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index 08fb41a..30642ad 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -416,6 +416,7 @@
                     "encryptedmedia/MediaKeySystemConfiguration.idl",
                     "encryptedmedia/MediaKeySystemMediaCapability.idl",
                     "eventsource/EventSourceInit.idl",
+                    "fetch/ResponseInit.idl",
                     "filesystem/FileSystemFlags.idl",
                     "gamepad/GamepadEventInit.idl",
                     "geolocation/PositionOptions.idl",
@@ -724,6 +725,8 @@
   "$blink_modules_output_dir/encryptedmedia/MediaKeySystemMediaCapability.h",
   "$blink_modules_output_dir/eventsource/EventSourceInit.cpp",
   "$blink_modules_output_dir/eventsource/EventSourceInit.h",
+  "$blink_modules_output_dir/fetch/ResponseInit.cpp",
+  "$blink_modules_output_dir/fetch/ResponseInit.h",
   "$blink_modules_output_dir/filesystem/FileSystemFlags.cpp",
   "$blink_modules_output_dir/filesystem/FileSystemFlags.h",
   "$blink_modules_output_dir/gamepad/GamepadEventInit.cpp",
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
index 43cd97d4..6be80f1 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
@@ -500,7 +500,8 @@
     m_layer.setRightBounds({0.5f, 0.0f, 0.5f, 1.0f});
   }
 
-  m_display->UpdateLayerBounds(std::move(leftBounds), std::move(rightBounds));
+  m_display->UpdateLayerBounds(m_frameId, std::move(leftBounds),
+                               std::move(rightBounds));
 }
 
 HeapVector<VRLayer> VRDisplay::getLayers() {
@@ -542,6 +543,12 @@
     return;
   }
 
+  // No frame Id to write before submitting the frame.
+  if (m_frameId < 0) {
+    m_display->SubmitFrame(m_framePose.Clone());
+    return;
+  }
+
   // Write the frame number for the pose used into a bottom left pixel block.
   // It is read by chrome/browser/android/vr_shell/vr_shell.cc to associate
   // the correct corresponding pose for submission.
@@ -558,12 +565,12 @@
   // since the final rendering hides the edges via a vignette effect.
   gl->Scissor(0, 0, 4, 4);
   gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-  int idx = m_framePose->poseIndex;
   // Careful with the arithmetic here. Float color 1.f is equivalent to int 255.
   // Use the low byte of the index as the red component, and store an arbitrary
   // magic number in green/blue. This number must match the reading code in
   // vr_shell.cc. Avoid all-black/all-white.
-  gl->ClearColor((idx & 255) / 255.0f, kWebVrPosePixelMagicNumbers[0] / 255.0f,
+  gl->ClearColor((m_frameId & 255) / 255.0f,
+                 kWebVrPosePixelMagicNumbers[0] / 255.0f,
                  kWebVrPosePixelMagicNumbers[1] / 255.0f, 1.0f);
   gl->Clear(GL_COLOR_BUFFER_BIT);
 
@@ -619,7 +626,8 @@
 }
 
 void VRDisplay::OnVSync(device::mojom::blink::VRPosePtr pose,
-                        mojo::common::mojom::blink::TimeDeltaPtr time) {
+                        mojo::common::mojom::blink::TimeDeltaPtr time,
+                        int16_t frameId) {
   WTF::TimeDelta timeDelta =
       WTF::TimeDelta::FromMicroseconds(time->microseconds);
   // The VSync provider cannot shut down before replying to pending callbacks,
@@ -646,6 +654,7 @@
 
   AutoReset<bool> animating(&m_inAnimationFrame, true);
   m_framePose = std::move(pose);
+  m_frameId = frameId;
   m_pendingRaf = false;
   m_scriptedAnimationController->serviceScriptedAnimations(
       m_timebase + timeDelta.InSecondsF());
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.h b/third_party/WebKit/Source/modules/vr/VRDisplay.h
index a3b8adc..c756314 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.h
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.h
@@ -131,7 +131,8 @@
   void OnDeactivate(device::mojom::blink::VRDisplayEventReason) override;
 
   void OnVSync(device::mojom::blink::VRPosePtr,
-               mojo::common::mojom::blink::TimeDeltaPtr);
+               mojo::common::mojom::blink::TimeDeltaPtr,
+               int16_t frameId);
   void ConnectVSyncProvider();
 
   ScriptedAnimationController& ensureScriptedAnimationController(Document*);
@@ -147,6 +148,7 @@
   Member<VREyeParameters> m_eyeParametersLeft;
   Member<VREyeParameters> m_eyeParametersRight;
   device::mojom::blink::VRPosePtr m_framePose;
+  int16_t m_frameId;
   VRLayer m_layer;
   double m_depthNear = 0.01;
   double m_depthFar = 10000.0;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index 0e621fd..fe11916 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -1771,7 +1771,6 @@
   // viewport with the browser controls shown.
   IntSize ICBSize = m_size;
   if (RuntimeEnabledFeatures::inertTopControlsEnabled() &&
-      browserControls().permittedState() == WebBrowserControlsBoth &&
       !browserControls().shrinkViewport())
     ICBSize.expand(0, -browserControls().height());
 
@@ -1793,20 +1792,8 @@
 void WebViewImpl::updateBrowserControlsState(WebBrowserControlsState constraint,
                                              WebBrowserControlsState current,
                                              bool animate) {
-  WebBrowserControlsState oldPermittedState =
-      browserControls().permittedState();
-
   browserControls().updateConstraintsAndState(constraint, current, animate);
 
-  // If the controls are going from a locked to an unlocked state, or
-  // vice-versa, then we need to force a recompute of the ICB size since that
-  // depends on the permitted browser controls state.
-  if (oldPermittedState != constraint &&
-      (oldPermittedState == WebBrowserControlsBoth ||
-       constraint == WebBrowserControlsBoth)) {
-    performResize();
-  }
-
   if (m_layerTreeView)
     m_layerTreeView->updateBrowserControlsState(constraint, current, animate);
 }
@@ -1848,7 +1835,8 @@
   return page()->frameHost().browserControls();
 }
 
-void WebViewImpl::resizeViewWhileAnchored(float browserControlsHeight,
+void WebViewImpl::resizeViewWhileAnchored(FrameView* view,
+                                          float browserControlsHeight,
                                           bool browserControlsShrinkLayout) {
   DCHECK(mainFrameImpl());
 
@@ -1912,10 +1900,12 @@
   if (isRotation) {
     RotationViewportAnchor anchor(*view, visualViewport, viewportAnchorCoords,
                                   pageScaleConstraintsSet());
-    resizeViewWhileAnchored(browserControlsHeight, browserControlsShrinkLayout);
+    resizeViewWhileAnchored(view, browserControlsHeight,
+                            browserControlsShrinkLayout);
   } else {
     ResizeViewportAnchor::ResizeScope resizeScope(*m_resizeViewportAnchor);
-    resizeViewWhileAnchored(browserControlsHeight, browserControlsShrinkLayout);
+    resizeViewWhileAnchored(view, browserControlsHeight,
+                            browserControlsShrinkLayout);
   }
   sendResizeEventAndRepaint();
 }
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index f648bad5..f6589a64 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -520,7 +520,8 @@
   IntSize contentsSize() const;
 
   void performResize();
-  void resizeViewWhileAnchored(float browserControlsHeight,
+  void resizeViewWhileAnchored(FrameView*,
+                               float browserControlsHeight,
                                bool browserControlsShrinkLayout);
 
   // Overrides the compositor visibility. See the description of
diff --git a/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp b/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
index e0e5ba0..428d053 100644
--- a/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
+++ b/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
@@ -726,86 +726,6 @@
   EXPECT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
 }
 
-// Ensure that browser controls do not affect the layout by showing and hiding
-// except for position: fixed elements.
-TEST_F(BrowserControlsTest, MAYBE(AffectLayoutHeightWhenConstrained)) {
-  // Initialize with the browser controls showing.
-  WebViewImpl* webView = initialize("percent-height.html");
-  webView->resizeWithBrowserControls(WebSize(400, 300), 100.f, true);
-  webView->updateBrowserControlsState(WebBrowserControlsBoth,
-                                      WebBrowserControlsShown, false);
-  webView->browserControls().setShownRatio(1);
-  webView->updateAllLifecyclePhases();
-
-  Element* absPos = getElementById(WebString::fromUTF8("abs"));
-  Element* fixedPos = getElementById(WebString::fromUTF8("fixed"));
-
-  ASSERT_EQ(100.f, webView->browserControls().contentOffset());
-
-  // Hide the browser controls.
-  verticalScroll(-100.f);
-  webView->resizeWithBrowserControls(WebSize(400, 400), 100.f, false);
-  webView->updateAllLifecyclePhases();
-  ASSERT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
-
-  // Now lock the controls in a hidden state. The layout and elements should
-  // resize without a WebView::resize.
-  webView->updateBrowserControlsState(WebBrowserControlsHidden,
-                                      WebBrowserControlsBoth, false);
-
-  EXPECT_FLOAT_EQ(200.f, absPos->getBoundingClientRect()->height());
-  EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height());
-
-  EXPECT_EQ(400, frame()->view()->layoutSize(IncludeScrollbars).height());
-
-  // Unlock the controls, the sizes should change even though the controls are
-  // still hidden.
-  webView->updateBrowserControlsState(WebBrowserControlsBoth,
-                                      WebBrowserControlsBoth, false);
-
-  EXPECT_FLOAT_EQ(150.f, absPos->getBoundingClientRect()->height());
-  EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height());
-
-  EXPECT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
-
-  // Now lock the controls in a shown state.
-  webView->updateBrowserControlsState(WebBrowserControlsShown,
-                                      WebBrowserControlsBoth, false);
-  webView->resizeWithBrowserControls(WebSize(400, 300), 100.f, true);
-
-  EXPECT_FLOAT_EQ(150.f, absPos->getBoundingClientRect()->height());
-  EXPECT_FLOAT_EQ(150.f, fixedPos->getBoundingClientRect()->height());
-
-  EXPECT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
-
-  // Shown -> Hidden
-  webView->resizeWithBrowserControls(WebSize(400, 400), 100.f, false);
-  webView->updateBrowserControlsState(WebBrowserControlsHidden,
-                                      WebBrowserControlsBoth, false);
-
-  EXPECT_FLOAT_EQ(200.f, absPos->getBoundingClientRect()->height());
-  EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height());
-
-  EXPECT_EQ(400, frame()->view()->layoutSize(IncludeScrollbars).height());
-
-  // Go from Unlocked and showing, to locked and hidden but issue the resize
-  // before the constraint update to check for race issues.
-  webView->updateBrowserControlsState(WebBrowserControlsBoth,
-                                      WebBrowserControlsShown, false);
-  webView->resizeWithBrowserControls(WebSize(400, 300), 100.f, true);
-  ASSERT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height());
-  webView->updateAllLifecyclePhases();
-
-  webView->resizeWithBrowserControls(WebSize(400, 400), 100.f, false);
-  webView->updateBrowserControlsState(WebBrowserControlsHidden,
-                                      WebBrowserControlsHidden, false);
-
-  EXPECT_FLOAT_EQ(200.f, absPos->getBoundingClientRect()->height());
-  EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height());
-
-  EXPECT_EQ(400, frame()->view()->layoutSize(IncludeScrollbars).height());
-}
-
 // Ensure that browser controls do not affect vh units.
 TEST_F(BrowserControlsTest, MAYBE(DontAffectVHUnits)) {
   // Initialize with the browser controls showing.
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index feb9e24..721dfb2b 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -51,11 +51,12 @@
 
 }  // namespace
 
-DelegatedFrameHostAndroid::DelegatedFrameHostAndroid(ui::ViewAndroid* view,
-                                                     SkColor background_color,
-                                                     Client* client)
-    : frame_sink_id_(
-          ui::ContextProviderFactory::GetInstance()->AllocateFrameSinkId()),
+DelegatedFrameHostAndroid::DelegatedFrameHostAndroid(
+    ui::ViewAndroid* view,
+    SkColor background_color,
+    Client* client,
+    const cc::FrameSinkId& frame_sink_id)
+    : frame_sink_id_(frame_sink_id),
       view_(view),
       client_(client),
       background_layer_(cc::SolidColorLayer::Create()) {
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index 977f03b0..e95f505 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -41,7 +41,8 @@
 
   DelegatedFrameHostAndroid(ViewAndroid* view,
                             SkColor background_color,
-                            Client* client);
+                            Client* client,
+                            const cc::FrameSinkId& frame_sink_id);
 
   ~DelegatedFrameHostAndroid() override;
 
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index cdb7b52..ff90047a 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -127,7 +127,7 @@
 InProcessContextFactory::InProcessContextFactory(
     bool context_factory_for_test,
     cc::SurfaceManager* surface_manager)
-    : next_surface_client_id_(1u),
+    : next_surface_sink_id_(1u),
       use_test_surface_(true),
       context_factory_for_test_(context_factory_for_test),
       surface_manager_(surface_manager) {
@@ -281,7 +281,12 @@
 }
 
 cc::FrameSinkId InProcessContextFactory::AllocateFrameSinkId() {
-  return cc::FrameSinkId(next_surface_client_id_++, 0);
+  // The FrameSinkId generated here must be unique with
+  // RenderWidgetHostViewAura's
+  // and RenderWidgetHostViewMac's FrameSinkId allocation.
+  // TODO(crbug.com/685777): Centralize allocation in one place for easier
+  // maintenance.
+  return cc::FrameSinkId(0, next_surface_sink_id_++);
 }
 
 cc::SurfaceManager* InProcessContextFactory::GetSurfaceManager() {
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index 8d91dc2..72924b6 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -84,7 +84,7 @@
   cc::TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
   cc::TestImageFactory image_factory_;
   cc::TestTaskGraphRunner task_graph_runner_;
-  uint32_t next_surface_client_id_;
+  uint32_t next_surface_sink_id_;
   bool use_test_surface_;
   bool context_factory_for_test_;
   cc::SurfaceManager* surface_manager_;