diff --git a/DEPS b/DEPS
index 60d168b..6f9c7c0 100644
--- a/DEPS
+++ b/DEPS
@@ -87,8 +87,11 @@
   # as an expression.
   'cros_download_vm': '"{cros_board}" == "amd64-generic"',
 
-  # ANGLE's deps are relative to the angle_root variable.
+  # ANGLE's deps are relative to the angle_root variable, except for what's
+  # shared with chromium, such as build/, testing/, etc which are relative to
+  # src_root.
   'angle_root': 'src/third_party/angle',
+  'src_root': 'src',
 
   'android_git': 'https://android.googlesource.com',
   'aomedia_git': 'https://aomedia.googlesource.com',
@@ -109,7 +112,7 @@
   # 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': '4edb5e25157f5d256f626aa32a476ffdb01cf295',
+  'v8_revision': 'b141a3197a5f400e9d9b68a35ab5f8d27cf4aa64',
   # 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.
@@ -129,7 +132,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': 'bb3f58dbcb7195e9e641fcc64e2ab537cf3fbc61',
+  'pdfium_revision': '2894645e6c187e75cfd016121e6f2857641575a1',
   # 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.
@@ -165,7 +168,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': '88afab4ff0f02bc2ad4d25642ecb5ce2d8cd8b76',
+  'catapult_revision': 'b273e0cd217d87f33caacb8ed15d85801fcca9ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -181,7 +184,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'feed_revision': 'd97e635aa74d7d067a32cca5a8b65a39f4855e85',
+  'feed_revision': '89e2c00bd3b9b2f0ce981f5c1d07a40c1e20eac1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_build-tools_version
   # and whatever else without interference from each other.
@@ -213,7 +216,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': 'ab45d69154344cc5b6a80948e674aa001c126a97',
+  'spv_tools_revision': 'b407163ef346bf852716970960a5f031c4aec9ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -605,7 +608,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'b708a096f473b55b8a72f4599067202d56b4f90b',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0a3c5082ad50716d3e3df4934b5e32096d94ca3f',
       'condition': 'checkout_linux',
   },
 
@@ -630,7 +633,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c68a1753c53a02b1bd292035e4a72a18e55814f3',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1e488131ffcde9e4df573c9be3514c0cdc42d28b',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -959,7 +962,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'f7eaeb14ee78953f87983e40c282df21fd04148c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'f0988af921fbbc8912dd6b2c2606e22ac301c889',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1111,7 +1114,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6d2f3f4cb8bac1f7c4a945c73d07a33df74f22f9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1927dfafabc06a7eced67d1d3fd60f2c0c88c152',
+    Var('webrtc_git') + '/src.git' + '@' + '9b1d67982f2631af48e3da503af10bd9ef989bd1',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1142,7 +1145,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@55fbadac66fec33a1181244ac29caca7c285ab35',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@baa4e9b9a71c839b6cec3e1ddfdbcfdcc74a7e87',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_url_checker_delegate_impl.cc b/android_webview/browser/aw_url_checker_delegate_impl.cc
index a3b20fb5..b3a5031 100644
--- a/android_webview/browser/aw_url_checker_delegate_impl.cc
+++ b/android_webview/browser/aw_url_checker_delegate_impl.cc
@@ -10,9 +10,11 @@
 #include "android_webview/browser/aw_safe_browsing_whitelist_manager.h"
 #include "android_webview/browser/net/aw_web_resource_request.h"
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/task/post_task.h"
 #include "components/safe_browsing/db/database_manager.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/features.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "components/security_interstitials/core/urls.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -32,7 +34,11 @@
           {safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
            safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
            safe_browsing::SB_THREAT_TYPE_URL_UNWANTED})),
-      whitelist_manager_(whitelist_manager) {}
+      whitelist_manager_(whitelist_manager) {
+  if (base::FeatureList::IsEnabled(safe_browsing::kBillingInterstitial)) {
+    threat_types_.insert(safe_browsing::SB_THREAT_TYPE_BILLING);
+  }
+}
 
 AwUrlCheckerDelegateImpl::~AwUrlCheckerDelegateImpl() = default;
 
diff --git a/android_webview/browser/deferred_gpu_command_service.cc b/android_webview/browser/deferred_gpu_command_service.cc
index c645f864..ca7382dd 100644
--- a/android_webview/browser/deferred_gpu_command_service.cc
+++ b/android_webview/browser/deferred_gpu_command_service.cc
@@ -72,6 +72,10 @@
 
   bool ShouldYield() override { return false; }
 
+  // Should not be called because BlockThreadOnWaitSyncToken() returns true,
+  // and the client should not disable sequences to wait for sync tokens.
+  void SetEnabled(bool enabled) override { NOTREACHED(); }
+
   void ScheduleTask(base::OnceClosure task,
                     std::vector<gpu::SyncToken> sync_token_fences) override {
     uint32_t order_num =
@@ -283,6 +287,10 @@
   }
 }
 
+bool DeferredGpuCommandService::BlockThreadOnWaitSyncToken() const {
+  return true;
+}
+
 bool DeferredGpuCommandService::CanSupportThreadedTextureMailbox() const {
   return gpu_info_.can_support_threaded_texture_mailbox;
 }
diff --git a/android_webview/browser/deferred_gpu_command_service.h b/android_webview/browser/deferred_gpu_command_service.h
index 0a84ddf..27d9cae 100644
--- a/android_webview/browser/deferred_gpu_command_service.h
+++ b/android_webview/browser/deferred_gpu_command_service.h
@@ -49,6 +49,7 @@
   // gpu::CommandBufferTaskExecutor implementation.
   bool ForceVirtualizedGLContexts() const override;
   bool ShouldCreateMemoryTracker() const override;
+  bool BlockThreadOnWaitSyncToken() const override;
   std::unique_ptr<gpu::CommandBufferTaskExecutor::Sequence> CreateSequence()
       override;
   void ScheduleOutOfOrderTask(base::OnceClosure task) override;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConversionHelper.java b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConversionHelper.java
index 3d6494d..d0ba8fc 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConversionHelper.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConversionHelper.java
@@ -13,18 +13,23 @@
  * constants in WebViewClient.
  */
 public final class AwSafeBrowsingConversionHelper {
-    /** The resource was blocked for an unknown reason */
+    /** The resource was blocked for an unknown reason. */
     public static final int SAFE_BROWSING_THREAT_UNKNOWN =
             WebViewClient.SAFE_BROWSING_THREAT_UNKNOWN;
-    /** The resource was blocked because it contains malware */
+    /** The resource was blocked because it contains malware. */
     public static final int SAFE_BROWSING_THREAT_MALWARE =
             WebViewClient.SAFE_BROWSING_THREAT_MALWARE;
-    /** The resource was blocked because it contains deceptive content */
+    /** The resource was blocked because it contains deceptive content. */
     public static final int SAFE_BROWSING_THREAT_PHISHING =
             WebViewClient.SAFE_BROWSING_THREAT_PHISHING;
-    /** The resource was blocked because it contains unwanted software */
+    /** The resource was blocked because it contains unwanted software. */
     public static final int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE =
             WebViewClient.SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE;
+    /** The resource was blocked because it may trick the user into a billing agreement. */
+    // TODO(ntfschr): add a new int to the SDK.
+    public static final int SAFE_BROWSING_THREAT_BILLING =
+            WebViewClient.SAFE_BROWSING_THREAT_UNKNOWN;
+
     /**
      * Converts the threat type value from SafeBrowsing code to the WebViewClient constant.
      */
@@ -36,6 +41,8 @@
                 return SAFE_BROWSING_THREAT_PHISHING;
             case SBThreatType.URL_UNWANTED:
                 return SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE;
+            case SBThreatType.BILLING:
+                return SAFE_BROWSING_THREAT_BILLING;
             default:
                 return SAFE_BROWSING_THREAT_UNKNOWN;
         }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index ea2e4e7..eba1882 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -101,6 +101,7 @@
     private static final int PHISHING_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
     private static final int MALWARE_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
     private static final int UNWANTED_SOFTWARE_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
+    private static final int BILLING_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
     private static final int IFRAME_EMBEDDER_BACKGROUND_COLOR = Color.rgb(10, 10, 10);
 
     private static final String RESOURCE_PATH = "/android_webview/test/data";
@@ -114,6 +115,7 @@
     private static final String MALWARE_HTML_PATH = RESOURCE_PATH + "/malware.html";
     private static final String UNWANTED_SOFTWARE_HTML_PATH =
             RESOURCE_PATH + "/unwanted_software.html";
+    private static final String BILLING_HTML_PATH = RESOURCE_PATH + "/billing.html";
 
     // A gray page with an iframe to MALWARE_HTML_PATH
     private static final String IFRAME_HTML_PATH = RESOURCE_PATH + "/iframe.html";
@@ -130,9 +132,12 @@
     public static class MockSafeBrowsingApiHandler implements SafeBrowsingApiHandler {
         private Observer mObserver;
         private static final String SAFE_METADATA = "{}";
+
+        // These codes are defined in "safebrowsing.proto".
         private static final int PHISHING_CODE = 5;
         private static final int MALWARE_CODE = 4;
         private static final int UNWANTED_SOFTWARE_CODE = 3;
+        private static final int BILLING_CODE = 15;
 
         // Mock time it takes for a lookup request to complete.
         private static final long CHECK_DELTA_US = 10;
@@ -166,6 +171,9 @@
             } else if (uri.endsWith(UNWANTED_SOFTWARE_HTML_PATH)
                     && Arrays.binarySearch(threatsOfInterest, UNWANTED_SOFTWARE_CODE) >= 0) {
                 metadata = buildMetadataFromCode(UNWANTED_SOFTWARE_CODE);
+            } else if (uri.endsWith(BILLING_HTML_PATH)
+                    && Arrays.binarySearch(threatsOfInterest, BILLING_CODE) >= 0) {
+                metadata = buildMetadataFromCode(BILLING_CODE);
             } else {
                 metadata = SAFE_METADATA;
             }
@@ -486,6 +494,48 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add("disable-features=BillingInterstitial")
+    public void testSafeBrowsingDoesNotBlockBillingPages() throws Throwable {
+        // TODO(ntfschr): this is a temporary check until we launch support for Billing warnings
+        // (http://crbug/887186).
+        loadGreenPage();
+        final String responseUrl = mTestServer.getURL(BILLING_HTML_PATH);
+        mActivityTestRule.loadUrlSync(
+                mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
+        assertTargetPageHasLoaded(BILLING_PAGE_BACKGROUND_COLOR);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add("enable-features=BillingInterstitial")
+    public void testSafeBrowsingBlocksBillingPages() throws Throwable {
+        loadGreenPage();
+        loadPathAndWaitForInterstitial(BILLING_HTML_PATH);
+        assertGreenPageNotShowing();
+        assertTargetPageNotShowing(BILLING_PAGE_BACKGROUND_COLOR);
+        // Assume that we are rendering the interstitial, since we see neither the previous page nor
+        // the target page
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add("enable-features=BillingInterstitial")
+    public void testSafeBrowsingOnSafeBrowsingHitBillingCode() throws Throwable {
+        loadGreenPage();
+        loadPathAndWaitForInterstitial(BILLING_HTML_PATH);
+
+        // Check onSafeBrowsingHit arguments
+        final String responseUrl = mTestServer.getURL(BILLING_HTML_PATH);
+        Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url);
+        Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_BILLING,
+                mContentsClient.getLastThreatType());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
     public void testSafeBrowsingBlocksPhishingPages() throws Throwable {
         loadGreenPage();
         loadPathAndWaitForInterstitial(PHISHING_HTML_PATH);
@@ -752,6 +802,18 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add("enable-features=BillingInterstitial")
+    public void testSafeBrowsingCanShowQuietBillingInterstitial() throws Throwable {
+        mAwContents.setCanShowBigInterstitial(false);
+        loadGreenPage();
+        loadPathAndWaitForInterstitial(BILLING_HTML_PATH);
+        assertGreenPageNotShowing();
+        assertTargetPageNotShowing(BILLING_PAGE_BACKGROUND_COLOR);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
     public void testSafeBrowsingProceedQuietInterstitial() throws Throwable {
         mAwContents.setCanShowBigInterstitial(false);
         int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
diff --git a/android_webview/test/data/billing.html b/android_webview/test/data/billing.html
new file mode 100644
index 0000000..ba2a808
--- /dev/null
+++ b/android_webview/test/data/billing.html
@@ -0,0 +1,14 @@
+<html>
+  <head>
+    <style>
+      div {
+        background-color: rgb(0,0,255);
+        height: 100%;
+        width: 100%;
+      }
+    </style>
+  </head>
+  <body>
+    <div></div>
+  </body>
+</html>
diff --git a/android_webview/ui/grit_strings_whitelist.txt b/android_webview/ui/grit_strings_whitelist.txt
index efd4782..b1d47c34 100644
--- a/android_webview/ui/grit_strings_whitelist.txt
+++ b/android_webview/ui/grit_strings_whitelist.txt
@@ -16,6 +16,10 @@
 IDS_HARMFUL_V3_EXPLANATION_PARAGRAPH
 IDS_HARMFUL_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE
 IDS_HARMFUL_V3_PROCEED_PARAGRAPH
+IDS_BILLING_HEADING
+IDS_BILLING_PRIMARY_PARAGRAPH
+IDS_BILLING_PRIMARY_BUTTON
+IDS_BILLING_PROCEED_BUTTON
 IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE
 IDS_SAFE_BROWSING_SCOUT_REPORTING_AGREE
 IDS_PHISHING_V4_HEADING
@@ -28,6 +32,8 @@
 IDS_PHISHING_WEBVIEW_EXPLANATION_PARAGRAPH
 IDS_HARMFUL_WEBVIEW_HEADING
 IDS_HARMFUL_WEBVIEW_EXPLANATION_PARAGRAPH
+IDS_BILLING_WEBVIEW_HEADING
+IDS_BILLING_WEBVIEW_EXPLANATION_PARAGRAPH
 IDS_SB_UNDER_CONSTRUCTION
 IDS_AUTOFILL_CC_AMEX
 IDS_AUTOFILL_CC_AMEX_SHORT
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index e14e8f0..477f37cf 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -240,6 +240,8 @@
     "assistant/util/assistant_util.h",
     "assistant/util/deep_link_util.cc",
     "assistant/util/deep_link_util.h",
+    "assistant/util/histogram_util.cc",
+    "assistant/util/histogram_util.h",
     "assistant/util/views_util.cc",
     "assistant/util/views_util.h",
     "autoclick/autoclick_controller.cc",
@@ -1141,8 +1143,6 @@
     "wm/gestures/overview_gesture_handler.h",
     "wm/immersive_context_ash.cc",
     "wm/immersive_context_ash.h",
-    "wm/immersive_focus_watcher_classic.cc",
-    "wm/immersive_focus_watcher_classic.h",
     "wm/immersive_gesture_handler_classic.cc",
     "wm/immersive_gesture_handler_classic.h",
     "wm/immersive_handler_factory_ash.cc",
diff --git a/ash/app_list/app_list_presenter_delegate_impl.cc b/ash/app_list/app_list_presenter_delegate_impl.cc
index e6ee690..a875edfb 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.cc
+++ b/ash/app_list/app_list_presenter_delegate_impl.cc
@@ -25,6 +25,7 @@
 #include "base/command_line.h"
 #include "chromeos/chromeos_switches.h"
 #include "ui/aura/window.h"
+#include "ui/display/manager/display_manager.h"
 #include "ui/events/event.h"
 #include "ui/keyboard/keyboard_controller.h"
 #include "ui/views/widget/widget.h"
@@ -54,7 +55,9 @@
 
 AppListPresenterDelegateImpl::AppListPresenterDelegateImpl(
     AppListControllerImpl* controller)
-    : controller_(controller) {}
+    : controller_(controller), display_observer_(this) {
+  display_observer_.Add(display::Screen::GetScreen());
+}
 
 AppListPresenterDelegateImpl::~AppListPresenterDelegateImpl() {
   Shell::Get()->RemovePreTargetHandler(this);
@@ -77,11 +80,19 @@
   aura::Window* root_window = Shell::GetRootWindowForDisplayId(display_id);
 
   app_list::AppListView::InitParams params;
-  params.parent =
+  aura::Window* parent_window =
       RootWindowController::ForWindow(root_window)
           ->GetContainer(IsHomeLauncherEnabledInTabletMode()
                              ? kShellWindowId_AppListTabletModeContainer
                              : kShellWindowId_AppListContainer);
+
+  // Snap the window bounds to fit the screen size (See
+  // https://crbug.com/884889).
+  const gfx::Rect bounds = ash::screen_util::SnapBoundsToDisplayEdge(
+      parent_window->GetBoundsInScreen(), parent_window);
+  parent_window->SetBoundsInScreen(
+      bounds, Shell::Get()->display_manager()->GetDisplayForId(display_id));
+  params.parent = parent_window;
   params.initial_apps_page = current_apps_page;
   params.is_tablet_mode = Shell::Get()
                               ->tablet_mode_controller()
@@ -171,6 +182,21 @@
   controller_->OnTargetVisibilityChanged(visible);
 }
 
+void AppListPresenterDelegateImpl::OnDisplayMetricsChanged(
+    const display::Display& display,
+    uint32_t changed_metrics) {
+  if (!presenter_->GetWindow())
+    return;
+
+  // Snap the window bounds to fit the screen size (See
+  // https://crbug.com/884889).
+  aura::Window* parent_window = presenter_->GetWindow()->parent();
+  const gfx::Rect bounds = ash::screen_util::SnapBoundsToDisplayEdge(
+      parent_window->GetBoundsInScreen(), parent_window);
+  parent_window->SetBoundsInScreen(bounds, display);
+  view_->OnParentWindowBoundsChanged();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // AppListPresenterDelegateImpl, private:
 
diff --git a/ash/app_list/app_list_presenter_delegate_impl.h b/ash/app_list/app_list_presenter_delegate_impl.h
index a82e5e4..8bf76ff 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.h
+++ b/ash/app_list/app_list_presenter_delegate_impl.h
@@ -10,6 +10,8 @@
 #include "ash/app_list/presenter/app_list_presenter_delegate.h"
 #include "ash/ash_export.h"
 #include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "ui/display/display_observer.h"
 #include "ui/events/event_handler.h"
 #include "ui/keyboard/keyboard_controller_observer.h"
 
@@ -19,6 +21,10 @@
 class AppListViewDelegate;
 }  // namespace app_list
 
+namespace display {
+class Screen;
+}  // namespace display
+
 namespace ui {
 class LocatedEvent;
 }  // namespace ui
@@ -33,7 +39,8 @@
 // update its layout as necessary.
 class ASH_EXPORT AppListPresenterDelegateImpl
     : public app_list::AppListPresenterDelegate,
-      public ui::EventHandler {
+      public ui::EventHandler,
+      public display::DisplayObserver {
  public:
   explicit AppListPresenterDelegateImpl(AppListControllerImpl* controller);
   ~AppListPresenterDelegateImpl() override;
@@ -56,6 +63,10 @@
   void OnVisibilityChanged(bool visible, aura::Window* root_window) override;
   void OnTargetVisibilityChanged(bool visible) override;
 
+  // DisplayObserver overrides:
+  void OnDisplayMetricsChanged(const display::Display& display,
+                               uint32_t changed_metrics) override;
+
  private:
   void ProcessLocatedEvent(ui::LocatedEvent* event);
 
@@ -75,6 +86,9 @@
   // Not owned, owns this class.
   AppListControllerImpl* const controller_ = nullptr;
 
+  // An observer that notifies AppListView when the display has changed.
+  ScopedObserver<display::Screen, display::DisplayObserver> display_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(AppListPresenterDelegateImpl);
 };
 
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index fe8395f..cea51a7 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -306,7 +306,6 @@
       model_(delegate->GetModel()),
       search_model_(delegate->GetSearchModel()),
       is_background_blur_enabled_(app_list_features::IsBackgroundBlurEnabled()),
-      display_observer_(this),
       hide_view_animation_observer_(
           std::make_unique<HideViewAnimationObserver>()),
       transition_animation_observer_(
@@ -321,7 +320,6 @@
       weak_ptr_factory_(this) {
   CHECK(delegate);
 
-  display_observer_.Add(display::Screen::GetScreen());
   // Enable arrow key in FocusManager. Arrow left/right and up/down triggers
   // the same focus movement as tab/shift+tab.
   views::FocusManager::set_arrow_key_traversal_enabled(true);
@@ -577,8 +575,8 @@
   SetBackgroundShieldColor();
   if (is_background_blur_enabled_ && !IsHomeLauncherEnabledInTabletMode()) {
     app_list_background_shield_mask_ = views::Painter::CreatePaintedLayer(
-        views::Painter::CreateSolidRoundRectPainter(
-            SK_ColorBLACK, AppListConfig::instance().blur_radius()));
+        views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK,
+                                                    kAppListBackgroundRadius));
     app_list_background_shield_mask_->layer()->SetFillsBoundsOpaquely(false);
     app_list_background_shield_->layer()->SetMaskLayer(
         app_list_background_shield_mask_->layer());
@@ -1607,8 +1605,7 @@
   return false;
 }
 
-void AppListView::OnDisplayMetricsChanged(const display::Display& display,
-                                          uint32_t changed_metrics) {
+void AppListView::OnParentWindowBoundsChanged() {
   // Set the |fullscreen_widget_| size to fit the new display metrics.
   fullscreen_widget_->GetNativeView()->SetBounds(
       GetPreferredWidgetBoundsForState(app_list_state_));
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index dc053031..84df413 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -14,9 +14,7 @@
 #include "ash/public/cpp/app_list/app_list_constants.h"
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
 #include "build/build_config.h"
-#include "ui/display/display_observer.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
@@ -25,7 +23,7 @@
 }
 
 namespace display {
-class Screen;
+class Display;
 }
 
 namespace ui {
@@ -49,8 +47,7 @@
 // and hosts a AppsGridView and passes AppListModel to it for display.
 // TODO(newcomer|weidongg): Organize the cc file to match the order of
 // definitions in this header.
-class APP_LIST_EXPORT AppListView : public views::WidgetDelegateView,
-                                    public display::DisplayObserver {
+class APP_LIST_EXPORT AppListView : public views::WidgetDelegateView {
  public:
   class TestApi {
    public:
@@ -187,6 +184,9 @@
   // Called when on-screen keyboard's visibility is changed.
   void OnScreenKeyboardShown(bool shown);
 
+  // Called when parent window's bounds is changed.
+  void OnParentWindowBoundsChanged();
+
   // If the on-screen keyboard is shown, hide it. Return whether keyboard was
   // hidden
   bool CloseKeyboardIfVisible();
@@ -321,10 +321,6 @@
   // Overridden from views::WidgetDelegateView:
   views::View* GetInitiallyFocusedView() override;
 
-  // Overridden from DisplayObserver:
-  void OnDisplayMetricsChanged(const display::Display& display,
-                               uint32_t changed_metrics) override;
-
   // Gets app list background opacity during dragging.
   float GetAppListBackgroundOpacityDuringDragging();
 
@@ -392,8 +388,6 @@
   const bool is_background_blur_enabled_;
   // The state of the app list, controlled via SetState().
   AppListViewState app_list_state_ = AppListViewState::PEEKING;
-  // An observer that notifies AppListView when the display has changed.
-  ScopedObserver<display::Screen, display::DisplayObserver> display_observer_;
 
   // A widget observer that sets the AppListView state when the widget is
   // closed.
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index 01a8917d..7ecf779 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -4,7 +4,6 @@
 
 #include "ash/assistant/assistant_interaction_controller.h"
 
-#include <map>
 #include <utility>
 
 #include "ash/assistant/assistant_controller.h"
@@ -16,6 +15,7 @@
 #include "ash/assistant/model/assistant_ui_element.h"
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/util/deep_link_util.h"
+#include "ash/assistant/util/histogram_util.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -90,8 +90,8 @@
 void AssistantInteractionController::OnDeepLinkReceived(
     assistant::util::DeepLinkType type,
     const std::map<std::string, std::string>& params) {
-  using assistant::util::DeepLinkType;
   using assistant::util::DeepLinkParam;
+  using assistant::util::DeepLinkType;
 
   if (type == DeepLinkType::kWhatsOnMyScreen) {
     StartScreenContextInteraction();
@@ -226,6 +226,12 @@
   StopActiveInteraction(false);
 }
 
+void AssistantInteractionController::OnResponseChanged(
+    const std::shared_ptr<AssistantResponse>& response) {
+  assistant::util::IncrementAssistantQueryCountForEntryPoint(
+      assistant_controller_->ui_controller()->model()->entry_point());
+}
+
 void AssistantInteractionController::OnResponseDestroying(
     AssistantResponse& response) {
   response.RemoveObserver(this);
@@ -247,6 +253,13 @@
 
 void AssistantInteractionController::OnInteractionStarted(
     bool is_voice_interaction) {
+  if (is_voice_interaction) {
+    // If the Assistant UI is not visible yet, and |is_voice_interaction| is
+    // true, then it will be sure that Assistant is fired via OKG. ShowUi will
+    // not update the Assistant entry point if the UI is already visible.
+    assistant_controller_->ui_controller()->ShowUi(AssistantSource::kHotword);
+  }
+
   model_.SetInteractionState(InteractionState::kActive);
 
   // In the case of a voice interaction, we assume that the mic is open and
@@ -339,7 +352,8 @@
 }
 
 void AssistantInteractionController::OnHtmlResponse(
-    const std::string& response) {
+    const std::string& response,
+    const std::string& fallback) {
   if (model_.interaction_state() != InteractionState::kActive) {
     return;
   }
@@ -352,7 +366,7 @@
   }
 
   model_.pending_response()->AddUiElement(
-      std::make_unique<AssistantCardElement>(response));
+      std::make_unique<AssistantCardElement>(response, fallback));
 }
 
 void AssistantInteractionController::OnSuggestionChipPressed(
diff --git a/ash/assistant/assistant_interaction_controller.h b/ash/assistant/assistant_interaction_controller.h
index 4fc28a3..f56686f1 100644
--- a/ash/assistant/assistant_interaction_controller.h
+++ b/ash/assistant/assistant_interaction_controller.h
@@ -5,6 +5,7 @@
 #ifndef ASH_ASSISTANT_ASSISTANT_INTERACTION_CONTROLLER_H_
 #define ASH_ASSISTANT_ASSISTANT_INTERACTION_CONTROLLER_H_
 
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -67,6 +68,8 @@
   // AssistantInteractionModelObserver:
   void OnInteractionStateChanged(InteractionState interaction_state) override;
   void OnInputModalityChanged(InputModality input_modality) override;
+  void OnResponseChanged(
+      const std::shared_ptr<AssistantResponse>& response) override;
 
   // AssistantResponseObserver:
   void OnResponseDestroying(AssistantResponse& response) override;
@@ -85,7 +88,8 @@
   void OnInteractionStarted(bool is_voice_interaction) override;
   void OnInteractionFinished(
       AssistantInteractionResolution resolution) override;
-  void OnHtmlResponse(const std::string& response) override;
+  void OnHtmlResponse(const std::string& response,
+                      const std::string& fallback) override;
   void OnSuggestionsResponse(
       std::vector<AssistantSuggestionPtr> response) override;
   void OnTextResponse(const std::string& response) override;
diff --git a/ash/assistant/assistant_notification_controller.cc b/ash/assistant/assistant_notification_controller.cc
index 61d7ba6..0f3c727 100644
--- a/ash/assistant/assistant_notification_controller.cc
+++ b/ash/assistant/assistant_notification_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/assistant/assistant_notification_controller.h"
 
 #include "ash/assistant/assistant_controller.h"
+#include "ash/assistant/util/deep_link_util.h"
 #include "ash/new_window_controller.h"
 #include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
@@ -54,16 +55,27 @@
 
   void Click(const base::Optional<int>& button_index,
              const base::Optional<base::string16>& reply) override {
+    const auto& action_url =
+        button_index.has_value()
+            ? notification_->buttons[button_index.value()]->action_url
+            : notification_->action_url;
     // Open the action url if it is valid.
-    if (notification_->action_url.is_valid() && assistant_controller_) {
-      assistant_controller_->OpenUrl(notification_->action_url);
+    if (action_url.is_valid() &&
+        (action_url.SchemeIsHTTPOrHTTPS() ||
+         assistant::util::IsDeepLinkUrl(action_url)) &&
+        assistant_controller_) {
+      assistant_controller_->OpenUrl(action_url);
+      Close(/*by_user=*/true);
       return;
     }
 
     if (notification_controller_) {
-      // TODO(wutao): support buttons with different |action_index|.
+      // Action index 0 is the top level action and the first button's action
+      // index is 1.
+      const int action_index =
+          button_index.has_value() ? button_index.value() + 1 : 0;
       notification_controller_->RetrieveNotification(notification_.Clone(),
-                                                     /*action_index=*/0);
+                                                     action_index);
     }
   }
 
@@ -138,13 +150,17 @@
 
   message_center::MessageCenter* message_center =
       message_center::MessageCenter::Get();
-  message_center::RichNotificationData optional_field;
+  message_center::RichNotificationData data;
+  for (const auto& button : notification->buttons) {
+    data.buttons.push_back(
+        message_center::ButtonInfo(base::UTF8ToUTF16(button->label)));
+  }
 
   std::unique_ptr<message_center::Notification> system_notification =
       message_center::Notification::CreateSystemNotification(
           message_center::NOTIFICATION_TYPE_SIMPLE,
           GetNotificationId(notification->grouping_key), title, message,
-          display_source, GURL(), notifier_id_, optional_field,
+          display_source, GURL(), notifier_id_, data,
           new AssistantNotificationDelegate(weak_factory_.GetWeakPtr(),
                                             assistant_controller_->GetWeakPtr(),
                                             notification.Clone()),
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index 2fd1a2b..eed37365 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -104,7 +104,7 @@
 
   // If there is an active interaction, we need to show Assistant UI if it is
   // not already showing. We don't have enough information here to know what
-  // the interaction source is, but at the moment we have no need to know.
+  // the interaction source is.
   ShowUi(AssistantSource::kUnspecified);
 }
 
diff --git a/ash/assistant/model/assistant_ui_element.cc b/ash/assistant/model/assistant_ui_element.cc
index 422af0e..8c5d3fd 100644
--- a/ash/assistant/model/assistant_ui_element.cc
+++ b/ash/assistant/model/assistant_ui_element.cc
@@ -8,9 +8,11 @@
 
 // AssistantCardElement --------------------------------------------------------
 
-AssistantCardElement::AssistantCardElement(const std::string& html)
+AssistantCardElement::AssistantCardElement(const std::string& html,
+                                           const std::string& fallback)
     : AssistantUiElement(AssistantUiElementType::kCard),
       html_(html),
+      fallback_(fallback),
       id_token_(base::UnguessableToken::Create()) {}
 
 AssistantCardElement::~AssistantCardElement() = default;
diff --git a/ash/assistant/model/assistant_ui_element.h b/ash/assistant/model/assistant_ui_element.h
index 6e0ca60..67c9df2c 100644
--- a/ash/assistant/model/assistant_ui_element.h
+++ b/ash/assistant/model/assistant_ui_element.h
@@ -44,11 +44,14 @@
 // An Assistant UI element that will be rendered as an HTML card.
 class AssistantCardElement : public AssistantUiElement {
  public:
-  explicit AssistantCardElement(const std::string& html);
+  explicit AssistantCardElement(const std::string& html,
+                                const std::string& fallback);
   ~AssistantCardElement() override;
 
   const std::string& html() const { return html_; }
 
+  const std::string& fallback() const { return fallback_; }
+
   const base::UnguessableToken& id_token() const { return id_token_; }
 
   const base::Optional<base::UnguessableToken>& embed_token() const {
@@ -62,6 +65,7 @@
 
  private:
   const std::string html_;
+  const std::string fallback_;
   base::UnguessableToken id_token_;
   base::Optional<base::UnguessableToken> embed_token_ = base::nullopt;
 
diff --git a/ash/assistant/model/assistant_ui_model.cc b/ash/assistant/model/assistant_ui_model.cc
index 6374ffe..c46d0d4a 100644
--- a/ash/assistant/model/assistant_ui_model.cc
+++ b/ash/assistant/model/assistant_ui_model.cc
@@ -36,6 +36,10 @@
   const AssistantVisibility old_visibility = visibility_;
   visibility_ = visibility;
 
+  // Cache the Assistant entry point used for query count UMA metric.
+  if (visibility == AssistantVisibility::kVisible)
+    entry_point_ = source;
+
   NotifyUiVisibilityChanged(old_visibility, source);
 }
 
diff --git a/ash/assistant/model/assistant_ui_model.h b/ash/assistant/model/assistant_ui_model.h
index efed31b..93ecfc7 100644
--- a/ash/assistant/model/assistant_ui_model.h
+++ b/ash/assistant/model/assistant_ui_model.h
@@ -13,16 +13,21 @@
 
 class AssistantUiModelObserver;
 
-// Enumeration of Assistant entry/exit points.
+// Enumeration of Assistant entry/exit points, also recorded in histograms.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. Only append to this enum is allowed
+// if the possible source grows.
 enum class AssistantSource {
-  kUnspecified,
-  kDeepLink,
-  kHotkey,
-  kHotword,
-  kLauncherSearchBox,
-  kLongPressLauncher,
-  kSetup,
-  kStylus,
+  kUnspecified = 0,
+  kDeepLink = 1,
+  kHotkey = 2,
+  kHotword = 3,
+  kLauncherSearchBox = 4,
+  kLongPressLauncher = 5,
+  kSetup = 6,
+  kStylus = 7,
+  // Special enumerator value used by histogram macros.
+  kMaxValue = kStylus
 };
 
 // Enumeration of Assistant UI modes.
@@ -66,6 +71,9 @@
   // Returns the current usable work area.
   const gfx::Rect& usable_work_area() const { return usable_work_area_; }
 
+  // Returns the UI entry point. Only valid while UI is visible.
+  AssistantSource entry_point() const { return entry_point_; }
+
  private:
   void NotifyUiModeChanged();
   void NotifyUiVisibilityChanged(AssistantVisibility old_visibility,
@@ -76,6 +84,8 @@
 
   AssistantVisibility visibility_ = AssistantVisibility::kClosed;
 
+  AssistantSource entry_point_ = AssistantSource::kUnspecified;
+
   base::ObserverList<AssistantUiModelObserver>::Unchecked observers_;
 
   // Usable work area for Assistant. Value is only meaningful when Assistant
diff --git a/ash/assistant/ui/main_stage/assistant_main_stage.cc b/ash/assistant/ui/main_stage/assistant_main_stage.cc
index f774e25..4d1bc53 100644
--- a/ash/assistant/ui/main_stage/assistant_main_stage.cc
+++ b/ash/assistant/ui/main_stage/assistant_main_stage.cc
@@ -623,6 +623,10 @@
                   ui::LayerAnimationElement::AnimatableProperty::OPACITY,
                   kFooterEntryAnimationFadeInDelay),
               CreateOpacityElement(1.f, kFooterEntryAnimationFadeInDuration)));
+    } else {
+      // A pending query is present so we simulate a change event to synchronize
+      // view state with interaction model state.
+      OnPendingQueryChanged(pending_query);
     }
 
     return;
diff --git a/ash/assistant/ui/main_stage/ui_element_container_view.cc b/ash/assistant/ui/main_stage/ui_element_container_view.cc
index ab21c4d8..2683a664 100644
--- a/ash/assistant/ui/main_stage/ui_element_container_view.cc
+++ b/ash/assistant/ui/main_stage/ui_element_container_view.cc
@@ -27,6 +27,7 @@
 #include "ui/events/event.h"
 #include "ui/events/event_sink.h"
 #include "ui/events/event_utils.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/layout/box_layout.h"
@@ -90,8 +91,9 @@
 class CardElementViewHolder : public views::NativeViewHost,
                               public views::ViewObserver {
  public:
-  explicit CardElementViewHolder(views::View* card_element_view)
-      : card_element_view_(card_element_view) {
+  explicit CardElementViewHolder(const AssistantCardElement* card_element)
+      : card_element_view_(app_list::AnswerCardContentsRegistry::Get()->GetView(
+            card_element->embed_token().value())) {
     views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
 
     params.name = GetClassName();
@@ -107,6 +109,9 @@
     contents_view_->AddChildView(card_element_view_);
 
     card_element_view_->AddObserver(this);
+
+    // OverrideDescription() doesn't work. Only names are read automatically.
+    GetViewAccessibility().OverrideName(card_element->fallback());
   }
 
   ~CardElementViewHolder() override {
@@ -398,9 +403,7 @@
   // When the card has been rendered in the same process, its view is
   // available in the AnswerCardContentsRegistry's token-to-view map.
   if (app_list::AnswerCardContentsRegistry::Get()) {
-    CardElementViewHolder* view_holder = new CardElementViewHolder(
-        app_list::AnswerCardContentsRegistry::Get()->GetView(
-            card_element->embed_token().value()));
+    auto* view_holder = new CardElementViewHolder(card_element);
 
     if (is_first_card_) {
       is_first_card_ = false;
@@ -482,9 +485,9 @@
             CreateOpacityElement(1.f, kUiElementAnimationFadeInDuration)));
   }
 
-  // TODO(luciferleo): Add ChromeVox description for WebView.
-  // Let screen reader read the query result. We don't read when there is TTS to
-  // avoid speaking over the server response.
+  // Let screen reader read the query result. This includes the text response
+  // and the card fallback text, but webview result is not included.
+  // We don't read when there is TTS to avoid speaking over the server response.
   const AssistantResponse* response =
       assistant_controller_->interaction_controller()->model()->response();
   if (!response->has_tts())
diff --git a/ash/assistant/util/histogram_util.cc b/ash/assistant/util/histogram_util.cc
new file mode 100644
index 0000000..6830c48e
--- /dev/null
+++ b/ash/assistant/util/histogram_util.cc
@@ -0,0 +1,20 @@
+// Copyright 2018 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 "ash/assistant/util/histogram_util.h"
+
+#include "ash/assistant/model/assistant_ui_model.h"
+#include "base/metrics/histogram_macros.h"
+
+namespace ash {
+namespace assistant {
+namespace util {
+
+void IncrementAssistantQueryCountForEntryPoint(AssistantSource entry_point) {
+  UMA_HISTOGRAM_ENUMERATION("Assistant.QueryCountPerEntryPoint", entry_point);
+}
+
+}  // namespace util
+}  // namespace assistant
+}  // namespace ash
diff --git a/ash/assistant/util/histogram_util.h b/ash/assistant/util/histogram_util.h
new file mode 100644
index 0000000..8381284a
--- /dev/null
+++ b/ash/assistant/util/histogram_util.h
@@ -0,0 +1,22 @@
+// Copyright 2018 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 ASH_ASSISTANT_UTIL_HISTOGRAM_UTIL_H_
+#define ASH_ASSISTANT_UTIL_HISTOGRAM_UTIL_H_
+
+namespace ash {
+
+enum class AssistantSource;
+
+namespace assistant {
+namespace util {
+
+// Increment number of queries fired for each entry point.
+void IncrementAssistantQueryCountForEntryPoint(AssistantSource entry_point);
+
+}  // namespace util
+}  // namespace assistant
+}  // namespace ash
+
+#endif  // ASH_ASSISTANT_UTIL_HISTOGRAM_UTIL_H_
diff --git a/ash/login/resources/default_200_percent/common/fingerprint_unlock_spinner.png b/ash/login/resources/default_200_percent/common/fingerprint_unlock_spinner.png
index 78ec9fed..b92eed1 100644
--- a/ash/login/resources/default_200_percent/common/fingerprint_unlock_spinner.png
+++ b/ash/login/resources/default_200_percent/common/fingerprint_unlock_spinner.png
Binary files differ
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index b8753420..edf9730 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -70,6 +70,7 @@
     "frame_utils.h",
     "gesture_action_type.h",
     "immersive/immersive_context.h",
+    "immersive/immersive_focus_watcher.cc",
     "immersive/immersive_focus_watcher.h",
     "immersive/immersive_fullscreen_controller.cc",
     "immersive/immersive_fullscreen_controller.h",
@@ -135,6 +136,7 @@
     "//ui/views",
     "//ui/views/mus",
     "//ui/wm",
+    "//ui/wm/public",
   ]
 
   public_deps = [
diff --git a/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc b/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
index 755c065..9352491e 100644
--- a/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
@@ -158,12 +158,8 @@
     : frame_(frame),
       delegate_(delegate),
       model_(std::make_unique<DefaultCaptionButtonModel>(frame)) {
-  constexpr int kTouchOptimizedCaptionButtonsSpacing = 8;
-  auto layout = std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kHorizontal, gfx::Insets(),
-      ui::MaterialDesignController::IsTouchOptimizedUiEnabled()
-          ? kTouchOptimizedCaptionButtonsSpacing
-          : 0);
+  auto layout =
+      std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal);
   layout->set_cross_axis_alignment(
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
   layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
diff --git a/chrome/browser/ui/views/frame/immersive_focus_watcher_mus.cc b/ash/public/cpp/immersive/immersive_focus_watcher.cc
similarity index 76%
rename from chrome/browser/ui/views/frame/immersive_focus_watcher_mus.cc
rename to ash/public/cpp/immersive/immersive_focus_watcher.cc
index 59820189..d2fa620 100644
--- a/chrome/browser/ui/views/frame/immersive_focus_watcher_mus.cc
+++ b/ash/public/cpp/immersive/immersive_focus_watcher.cc
@@ -2,19 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/frame/immersive_focus_watcher_mus.h"
+#include "ash/public/cpp/immersive/immersive_focus_watcher.h"
 
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ui/aura/client/transient_window_client.h"
-#include "ui/aura/mus/focus_synchronizer.h"
-#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
-#include "ui/views/mus/mus_client.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/public/activation_client.h"
 
+namespace ash {
+
 namespace {
 
 // Returns the BubbleDialogDelegateView corresponding to |maybe_bubble| if
@@ -63,9 +62,9 @@
 // so that bubbles which are not activatable and bubbles which do not close
 // upon deactivation also keep the top-of-window views revealed for the
 // duration of their visibility.
-class ImmersiveFocusWatcherMus::BubbleObserver : public aura::WindowObserver {
+class ImmersiveFocusWatcher::BubbleObserver : public aura::WindowObserver {
  public:
-  explicit BubbleObserver(ash::ImmersiveFullscreenController* controller);
+  explicit BubbleObserver(ImmersiveFullscreenController* controller);
   ~BubbleObserver() override;
 
   // Start / stop observing changes to |bubble|'s visibility.
@@ -80,27 +79,27 @@
   void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
   void OnWindowDestroying(aura::Window* window) override;
 
-  ash::ImmersiveFullscreenController* controller_;
+  ImmersiveFullscreenController* controller_;
 
   std::set<aura::Window*> bubbles_;
 
   // Lock which keeps the top-of-window views revealed based on whether any of
   // |bubbles_| is visible.
-  std::unique_ptr<ash::ImmersiveRevealedLock> revealed_lock_;
+  std::unique_ptr<ImmersiveRevealedLock> revealed_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(BubbleObserver);
 };
 
-ImmersiveFocusWatcherMus::BubbleObserver::BubbleObserver(
-    ash::ImmersiveFullscreenController* controller)
+ImmersiveFocusWatcher::BubbleObserver::BubbleObserver(
+    ImmersiveFullscreenController* controller)
     : controller_(controller) {}
 
-ImmersiveFocusWatcherMus::BubbleObserver::~BubbleObserver() {
+ImmersiveFocusWatcher::BubbleObserver::~BubbleObserver() {
   for (aura::Window* bubble : bubbles_)
     bubble->RemoveObserver(this);
 }
 
-void ImmersiveFocusWatcherMus::BubbleObserver::StartObserving(
+void ImmersiveFocusWatcher::BubbleObserver::StartObserving(
     aura::Window* bubble) {
   if (bubbles_.insert(bubble).second) {
     bubble->AddObserver(this);
@@ -108,7 +107,7 @@
   }
 }
 
-void ImmersiveFocusWatcherMus::BubbleObserver::StopObserving(
+void ImmersiveFocusWatcher::BubbleObserver::StopObserving(
     aura::Window* bubble) {
   if (bubbles_.erase(bubble)) {
     bubble->RemoveObserver(this);
@@ -116,7 +115,7 @@
   }
 }
 
-void ImmersiveFocusWatcherMus::BubbleObserver::UpdateRevealedLock() {
+void ImmersiveFocusWatcher::BubbleObserver::UpdateRevealedLock() {
   bool has_visible_bubble = false;
   for (aura::Window* bubble : bubbles_) {
     if (bubble->IsVisible()) {
@@ -132,7 +131,7 @@
       // weird for the top-of-window views to animate and the bubble not to
       // animate along with the top-of-window views.
       revealed_lock_.reset(controller_->GetRevealedLock(
-          ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
+          ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
     }
   } else {
     revealed_lock_.reset();
@@ -149,35 +148,37 @@
   }
 }
 
-void ImmersiveFocusWatcherMus::BubbleObserver::OnWindowVisibilityChanged(
+void ImmersiveFocusWatcher::BubbleObserver::OnWindowVisibilityChanged(
     aura::Window*,
     bool visible) {
   UpdateRevealedLock();
 }
 
-void ImmersiveFocusWatcherMus::BubbleObserver::OnWindowDestroying(
+void ImmersiveFocusWatcher::BubbleObserver::OnWindowDestroying(
     aura::Window* window) {
   StopObserving(window);
 }
 
-ImmersiveFocusWatcherMus::ImmersiveFocusWatcherMus(
-    ash::ImmersiveFullscreenController* controller)
+ImmersiveFocusWatcher::ImmersiveFocusWatcher(
+    ImmersiveFullscreenController* controller)
     : immersive_fullscreen_controller_(controller) {
   GetWidget()->GetFocusManager()->AddFocusChangeListener(this);
   aura::client::GetTransientWindowClient()->AddObserver(this);
-  ::wm::GetActivationClient(GetWidgetWindow())->AddObserver(this);
+  ::wm::GetActivationClient(GetWidgetWindow()->GetRootWindow())
+      ->AddObserver(this);
   RecreateBubbleObserver();
 }
 
-ImmersiveFocusWatcherMus::~ImmersiveFocusWatcherMus() {
+ImmersiveFocusWatcher::~ImmersiveFocusWatcher() {
   aura::client::GetTransientWindowClient()->RemoveObserver(this);
   GetWidget()->GetFocusManager()->RemoveFocusChangeListener(this);
-  auto* activation_client = ::wm::GetActivationClient(GetWidgetWindow());
+  auto* activation_client =
+      ::wm::GetActivationClient(GetWidgetWindow()->GetRootWindow());
   if (activation_client)
     activation_client->RemoveObserver(this);
 }
 
-void ImmersiveFocusWatcherMus::UpdateFocusRevealedLock() {
+void ImmersiveFocusWatcher::UpdateFocusRevealedLock() {
   views::Widget* widget = GetWidget();
   views::View* top_container =
       immersive_fullscreen_controller_->top_container();
@@ -188,10 +189,9 @@
       hold_lock = true;
   } else {
     aura::Window* native_window = GetWidgetWindow();
-    aura::Window* active_window = views::MusClient::Get()
-                                      ->window_tree_client()
-                                      ->focus_synchronizer()
-                                      ->active_focus_client_root();
+    aura::Window* active_window =
+        ::wm::GetActivationClient(native_window->GetRootWindow())
+            ->GetActiveWindow();
     if (GetAnchorView(active_window)) {
       // BubbleObserver will already have locked the top-of-window views if the
       // bubble is anchored to a child of |top_container|. Don't acquire
@@ -219,26 +219,26 @@
   if (hold_lock) {
     if (!lock_.get()) {
       lock_.reset(immersive_fullscreen_controller_->GetRevealedLock(
-          ash::ImmersiveFullscreenController::ANIMATE_REVEAL_YES));
+          ImmersiveFullscreenController::ANIMATE_REVEAL_YES));
     }
   } else {
     lock_.reset();
   }
 }
 
-void ImmersiveFocusWatcherMus::ReleaseLock() {
+void ImmersiveFocusWatcher::ReleaseLock() {
   lock_.reset();
 }
 
-views::Widget* ImmersiveFocusWatcherMus::GetWidget() {
+views::Widget* ImmersiveFocusWatcher::GetWidget() {
   return immersive_fullscreen_controller_->widget();
 }
 
-aura::Window* ImmersiveFocusWatcherMus::GetWidgetWindow() {
-  return GetWidget()->GetNativeWindow()->GetRootWindow();
+aura::Window* ImmersiveFocusWatcher::GetWidgetWindow() {
+  return GetWidget()->GetNativeWindow();
 }
 
-void ImmersiveFocusWatcherMus::RecreateBubbleObserver() {
+void ImmersiveFocusWatcher::RecreateBubbleObserver() {
   bubble_observer_.reset(new BubbleObserver(immersive_fullscreen_controller_));
   const std::vector<aura::Window*> transient_children =
       aura::client::GetTransientWindowClient()->GetTransientChildren(
@@ -253,22 +253,22 @@
   }
 }
 
-void ImmersiveFocusWatcherMus::OnWillChangeFocus(views::View* focused_before,
-                                                 views::View* focused_now) {}
+void ImmersiveFocusWatcher::OnWillChangeFocus(views::View* focused_before,
+                                              views::View* focused_now) {}
 
-void ImmersiveFocusWatcherMus::OnDidChangeFocus(views::View* focused_before,
-                                                views::View* focused_now) {
+void ImmersiveFocusWatcher::OnDidChangeFocus(views::View* focused_before,
+                                             views::View* focused_now) {
   UpdateFocusRevealedLock();
 }
 
-void ImmersiveFocusWatcherMus::OnWindowActivated(
+void ImmersiveFocusWatcher::OnWindowActivated(
     ::wm::ActivationChangeObserver::ActivationReason reason,
     aura::Window* gaining_active,
     aura::Window* losing_active) {
   UpdateFocusRevealedLock();
 }
 
-void ImmersiveFocusWatcherMus::OnTransientChildWindowAdded(
+void ImmersiveFocusWatcher::OnTransientChildWindowAdded(
     aura::Window* window,
     aura::Window* transient) {
   views::View* anchor = GetAnchorView(transient);
@@ -281,8 +281,10 @@
   }
 }
 
-void ImmersiveFocusWatcherMus::OnTransientChildWindowRemoved(
+void ImmersiveFocusWatcher::OnTransientChildWindowRemoved(
     aura::Window* window,
     aura::Window* transient) {
   bubble_observer_->StopObserving(transient);
 }
+
+}  // namespace ash
diff --git a/ash/public/cpp/immersive/immersive_focus_watcher.h b/ash/public/cpp/immersive/immersive_focus_watcher.h
index 873b171..7799b06f 100644
--- a/ash/public/cpp/immersive/immersive_focus_watcher.h
+++ b/ash/public/cpp/immersive/immersive_focus_watcher.h
@@ -1,27 +1,79 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2018 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 ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FOCUS_WATCHER_H_
 #define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FOCUS_WATCHER_H_
 
-#include "ash/public/cpp/ash_public_export.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/client/transient_window_client_observer.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/wm/public/activation_change_observer.h"
 
 namespace ash {
+class ImmersiveFullscreenController;
+class ImmersiveRevealedLock;
 
 // ImmersiveFocusWatcher is responsible for grabbing a reveal lock based on
-// activation and/or focus.
-class ASH_PUBLIC_EXPORT ImmersiveFocusWatcher {
+// activation and/or focus. This implementation grabs a lock if views focus is
+// in the top view, a bubble is showing that is anchored to the top view, or
+// the focused window is a transient child of the top view's widget.
+class ImmersiveFocusWatcher
+    : public views::FocusChangeListener,
+      public aura::client::TransientWindowClientObserver,
+      public ::wm::ActivationChangeObserver {
  public:
-  virtual ~ImmersiveFocusWatcher() {}
+  explicit ImmersiveFocusWatcher(ImmersiveFullscreenController* controller);
+  ~ImmersiveFocusWatcher() override;
 
   // Forces updating the status of the lock. That is, this determines whether
   // a lock should be held and updates accordingly. The lock is automatically
   // maintained, but this function may be called to force an update.
-  virtual void UpdateFocusRevealedLock() = 0;
+  void UpdateFocusRevealedLock();
 
   // Explicitly releases the lock, does nothing if a lock is not held.
-  virtual void ReleaseLock() = 0;
+  void ReleaseLock();
+
+ private:
+  class BubbleObserver;
+
+  views::Widget* GetWidget();
+  aura::Window* GetWidgetWindow();
+
+  // Recreate |bubble_observer_| and start observing any bubbles anchored to a
+  // child of |top_container_|.
+  void RecreateBubbleObserver();
+
+  // views::FocusChangeListener overrides:
+  void OnWillChangeFocus(views::View* focused_before,
+                         views::View* focused_now) override;
+  void OnDidChangeFocus(views::View* focused_before,
+                        views::View* focused_now) override;
+
+  // aura::client::TransientWindowClientObserver overrides:
+  void OnTransientChildWindowAdded(aura::Window* window,
+                                   aura::Window* transient) override;
+  void OnTransientChildWindowRemoved(aura::Window* window,
+                                     aura::Window* transient) override;
+
+  // ::wm::ActivationChangeObserver:
+  void OnWindowActivated(
+      ::wm::ActivationChangeObserver::ActivationReason reason,
+      aura::Window* gaining_active,
+      aura::Window* losing_active) override;
+
+  ImmersiveFullscreenController* immersive_fullscreen_controller_;
+
+  // Lock which keeps the top-of-window views revealed based on the focused view
+  // and the active widget. Acquiring the lock never triggers a reveal because
+  // a view is not focusable till a reveal has made it visible.
+  std::unique_ptr<ImmersiveRevealedLock> lock_;
+
+  // Manages bubbles which are anchored to a child of
+  // |ImmersiveFullscreenController::top_container_|.
+  std::unique_ptr<BubbleObserver> bubble_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImmersiveFocusWatcher);
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/immersive/immersive_fullscreen_controller.cc b/ash/public/cpp/immersive/immersive_fullscreen_controller.cc
index b3783b12..0c934ae 100644
--- a/ash/public/cpp/immersive/immersive_fullscreen_controller.cc
+++ b/ash/public/cpp/immersive/immersive_fullscreen_controller.cc
@@ -327,8 +327,7 @@
 
   aura::Env* env = widget_->GetNativeWindow()->env();
   if (enable) {
-    immersive_focus_watcher_ =
-        ImmersiveHandlerFactory::Get()->CreateFocusWatcher(this);
+    immersive_focus_watcher_ = std::make_unique<ImmersiveFocusWatcher>(this);
     immersive_gesture_handler_ =
         ImmersiveHandlerFactory::Get()->CreateGestureHandler(this);
     std::set<ui::EventType> types = {
diff --git a/ash/public/cpp/immersive/immersive_handler_factory.h b/ash/public/cpp/immersive/immersive_handler_factory.h
index 5274167..418153e 100644
--- a/ash/public/cpp/immersive/immersive_handler_factory.h
+++ b/ash/public/cpp/immersive/immersive_handler_factory.h
@@ -11,7 +11,6 @@
 
 namespace ash {
 
-class ImmersiveFocusWatcher;
 class ImmersiveFullscreenController;
 class ImmersiveGestureHandler;
 
@@ -20,9 +19,6 @@
  public:
   static ImmersiveHandlerFactory* Get() { return instance_; }
 
-  virtual std::unique_ptr<ImmersiveFocusWatcher> CreateFocusWatcher(
-      ImmersiveFullscreenController* controller) = 0;
-
   virtual std::unique_ptr<ImmersiveGestureHandler> CreateGestureHandler(
       ImmersiveFullscreenController* controller) = 0;
 
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index e906d68..56beca4e 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -97,10 +97,8 @@
 
 // TODO(sammiequon): This should be the same as IsTabletModeEnabled once home
 // launcher flag is removed.
-bool IsHomeLauncherEnabled() {
-  return Shell::Get()
-      ->app_list_controller()
-      ->IsHomeLauncherEnabledInTabletMode();
+bool IsHomeLauncherEnabledInTabletMode() {
+  return app_list_features::IsHomeLauncherEnabled() && IsTabletModeEnabled();
 }
 
 }  // namespace
@@ -549,7 +547,7 @@
 
   // If the app list is active and the home launcher is not shown, hide the
   // shelf background to prevent overlap.
-  if (is_app_list_visible_ && !IsHomeLauncherEnabled())
+  if (is_app_list_visible_ && !IsHomeLauncherEnabledInTabletMode())
     return SHELF_BACKGROUND_APP_LIST;
 
   if (state_.visibility_state != SHELF_AUTO_HIDE &&
@@ -1001,7 +999,7 @@
   if (visibility_state != SHELF_AUTO_HIDE)
     return SHELF_AUTO_HIDE_HIDDEN;
 
-  if (shelf_widget_->IsShowingAppList() && !IsHomeLauncherEnabled())
+  if (shelf_widget_->IsShowingAppList() && !IsHomeLauncherEnabledInTabletMode())
     return SHELF_AUTO_HIDE_SHOWN;
 
   if (shelf_widget_->status_area_widget() &&
@@ -1221,7 +1219,7 @@
     }
 
     // Disable the shelf dragging if the fullscreen app list is opened.
-    if (is_app_list_visible_ && !IsHomeLauncherEnabled())
+    if (is_app_list_visible_ && !IsHomeLauncherEnabledInTabletMode())
       return;
 
     gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
@@ -1391,7 +1389,7 @@
   // In overview mode, app list for tablet mode is hidden temporarily and will
   // be shown automatically after overview mode ends. So prevent opening it
   // here.
-  if (IsHomeLauncherEnabled())
+  if (IsHomeLauncherEnabledInTabletMode())
     return false;
 
   return true;
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index cd55ca0f..6a5997ae 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -189,7 +189,9 @@
 }
 
 void ShelfWidget::DelegateView::UpdateBackgroundBlur() {
+  // Blur only if the background is visible.
   const bool should_blur_background =
+      opaque_background_.visible() &&
       shelf_widget_->shelf_layout_manager()->ShouldBlurShelfBackground();
   if (should_blur_background == background_is_currently_blurred_)
     return;
@@ -208,6 +210,19 @@
   const ShelfBackgroundType background_type =
       shelf_widget_->GetBackgroundType();
 
+  // If the app list is showing in clamshell mode, we should hide the shelf.
+  // otherwise, we should show it again. This creates a 'blending' effect
+  // between the two
+  if (background_type == SHELF_BACKGROUND_APP_LIST) {
+    opaque_background_.SetVisible(false);
+    UpdateBackgroundBlur();
+    return;
+  }
+
+  if (!opaque_background_.visible()) {
+    opaque_background_.SetVisible(true);
+  }
+
   // Show rounded corners except in maximized and split modes.
   if (background_type == SHELF_BACKGROUND_MAXIMIZED ||
       background_type == SHELF_BACKGROUND_SPLIT_VIEW) {
diff --git a/ash/wm/immersive_focus_watcher_classic.cc b/ash/wm/immersive_focus_watcher_classic.cc
deleted file mode 100644
index 91ade230..0000000
--- a/ash/wm/immersive_focus_watcher_classic.cc
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/wm/immersive_focus_watcher_classic.h"
-
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
-#include "ui/aura/window.h"
-#include "ui/views/bubble/bubble_dialog_delegate_view.h"
-#include "ui/views/view.h"
-#include "ui/views/widget/widget.h"
-#include "ui/wm/core/transient_window_manager.h"
-#include "ui/wm/core/window_util.h"
-#include "ui/wm/public/activation_client.h"
-
-namespace ash {
-namespace {
-
-// Returns the BubbleDialogDelegateView corresponding to |maybe_bubble| if
-// |maybe_bubble| is a bubble.
-views::BubbleDialogDelegateView* AsBubbleDialogDelegate(
-    aura::Window* maybe_bubble) {
-  if (!maybe_bubble)
-    return nullptr;
-  views::Widget* widget = views::Widget::GetWidgetForNativeView(maybe_bubble);
-  if (!widget)
-    return nullptr;
-  return widget->widget_delegate()->AsBubbleDialogDelegate();
-}
-
-views::View* GetAnchorView(aura::Window* maybe_bubble) {
-  views::BubbleDialogDelegateView* bubble_dialog =
-      AsBubbleDialogDelegate(maybe_bubble);
-  return bubble_dialog ? bubble_dialog->GetAnchorView() : nullptr;
-}
-
-// Returns true if |maybe_transient| is a transient child of |toplevel|.
-bool IsWindowTransientChildOf(aura::Window* maybe_transient,
-                              aura::Window* toplevel) {
-  if (!maybe_transient || !toplevel)
-    return false;
-
-  for (aura::Window* window = maybe_transient; window;
-       window = ::wm::GetTransientParent(window)) {
-    if (window == toplevel)
-      return true;
-  }
-  return false;
-}
-
-}  // namespace
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Class which keeps the top-of-window views revealed as long as one of the
-// bubbles it is observing is visible. The logic to keep the top-of-window
-// views revealed based on the visibility of bubbles anchored to
-// children of |ImmersiveFullscreenController::top_container_| is separate from
-// the logic related to |ImmersiveFullscreenController::focus_revealed_lock_|
-// so that bubbles which are not activatable and bubbles which do not close
-// upon deactivation also keep the top-of-window views revealed for the
-// duration of their visibility.
-class ImmersiveFocusWatcherClassic::BubbleObserver
-    : public aura::WindowObserver {
- public:
-  explicit BubbleObserver(ImmersiveFullscreenController* controller);
-  ~BubbleObserver() override;
-
-  // Start / stop observing changes to |bubble|'s visibility.
-  void StartObserving(aura::Window* bubble);
-  void StopObserving(aura::Window* bubble);
-
- private:
-  // Updates |revealed_lock_| based on whether any of |bubbles_| is visible.
-  void UpdateRevealedLock();
-
-  // aura::WindowObserver overrides:
-  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
-  void OnWindowDestroying(aura::Window* window) override;
-
-  ImmersiveFullscreenController* controller_;
-
-  std::set<aura::Window*> bubbles_;
-
-  // Lock which keeps the top-of-window views revealed based on whether any of
-  // |bubbles_| is visible.
-  std::unique_ptr<ImmersiveRevealedLock> revealed_lock_;
-
-  DISALLOW_COPY_AND_ASSIGN(BubbleObserver);
-};
-
-ImmersiveFocusWatcherClassic::BubbleObserver::BubbleObserver(
-    ImmersiveFullscreenController* controller)
-    : controller_(controller) {}
-
-ImmersiveFocusWatcherClassic::BubbleObserver::~BubbleObserver() {
-  for (aura::Window* bubble : bubbles_)
-    bubble->RemoveObserver(this);
-}
-
-void ImmersiveFocusWatcherClassic::BubbleObserver::StartObserving(
-    aura::Window* bubble) {
-  if (bubbles_.insert(bubble).second) {
-    bubble->AddObserver(this);
-    UpdateRevealedLock();
-  }
-}
-
-void ImmersiveFocusWatcherClassic::BubbleObserver::StopObserving(
-    aura::Window* bubble) {
-  if (bubbles_.erase(bubble)) {
-    bubble->RemoveObserver(this);
-    UpdateRevealedLock();
-  }
-}
-
-void ImmersiveFocusWatcherClassic::BubbleObserver::UpdateRevealedLock() {
-  bool has_visible_bubble = false;
-  for (aura::Window* bubble : bubbles_) {
-    if (bubble->IsVisible()) {
-      has_visible_bubble = true;
-      break;
-    }
-  }
-
-  bool was_revealed = controller_->IsRevealed();
-  if (has_visible_bubble) {
-    if (!revealed_lock_.get()) {
-      // Reveal the top-of-window views without animating because it looks
-      // weird for the top-of-window views to animate and the bubble not to
-      // animate along with the top-of-window views.
-      revealed_lock_.reset(controller_->GetRevealedLock(
-          ImmersiveFullscreenController::ANIMATE_REVEAL_NO));
-    }
-  } else {
-    revealed_lock_.reset();
-  }
-
-  if (!was_revealed && revealed_lock_.get()) {
-    // Currently, there is no nice way for bubbles to reposition themselves
-    // whenever the anchor view moves. Tell the bubbles to reposition themselves
-    // explicitly instead. The hidden bubbles are also repositioned because
-    // BubbleDialogDelegateView does not reposition its widget as a result of a
-    // visibility change.
-    for (aura::Window* bubble : bubbles_)
-      AsBubbleDialogDelegate(bubble)->OnAnchorBoundsChanged();
-  }
-}
-
-void ImmersiveFocusWatcherClassic::BubbleObserver::OnWindowVisibilityChanged(
-    aura::Window*,
-    bool visible) {
-  UpdateRevealedLock();
-}
-
-void ImmersiveFocusWatcherClassic::BubbleObserver::OnWindowDestroying(
-    aura::Window* window) {
-  StopObserving(window);
-}
-
-ImmersiveFocusWatcherClassic::ImmersiveFocusWatcherClassic(
-    ImmersiveFullscreenController* controller)
-    : immersive_fullscreen_controller_(controller) {
-  GetWidget()->GetFocusManager()->AddFocusChangeListener(this);
-  GetWidget()->AddObserver(this);
-  ::wm::TransientWindowManager::GetOrCreate(GetWidgetWindow())
-      ->AddObserver(this);
-  RecreateBubbleObserver();
-}
-
-ImmersiveFocusWatcherClassic::~ImmersiveFocusWatcherClassic() {
-  ::wm::TransientWindowManager::GetOrCreate(GetWidgetWindow())
-      ->RemoveObserver(this);
-  GetWidget()->GetFocusManager()->RemoveFocusChangeListener(this);
-  GetWidget()->RemoveObserver(this);
-}
-
-void ImmersiveFocusWatcherClassic::UpdateFocusRevealedLock() {
-  views::Widget* widget = GetWidget();
-  views::View* top_container =
-      immersive_fullscreen_controller_->top_container();
-  bool hold_lock = false;
-  if (widget->IsActive()) {
-    views::View* focused_view = widget->GetFocusManager()->GetFocusedView();
-    if (top_container->Contains(focused_view))
-      hold_lock = true;
-  } else {
-    aura::Window* native_window = widget->GetNativeWindow();
-    aura::Window* active_window =
-        ::wm::GetActivationClient(native_window->GetRootWindow())
-            ->GetActiveWindow();
-    if (GetAnchorView(active_window)) {
-      // BubbleObserver will already have locked the top-of-window views if the
-      // bubble is anchored to a child of |top_container|. Don't acquire
-      // |lock_| here for the sake of simplicity.
-      // Note: Instead of checking for the existence of the |anchor_view|,
-      // the existence of the |anchor_widget| is performed to avoid the case
-      // where the view is already gone (and the widget is still running).
-    } else {
-      // The currently active window is not |native_window| and it is not a
-      // bubble with an anchor view. The top-of-window views should be revealed
-      // if:
-      // 1) The active window is a transient child of |native_window|.
-      // 2) The top-of-window views are already revealed. This restriction
-      //    prevents a transient window opened by the web contents while the
-      //    top-of-window views are hidden from from initiating a reveal.
-      // The top-of-window views will stay revealed till |native_window| is
-      // reactivated.
-      if (immersive_fullscreen_controller_->IsRevealed() &&
-          IsWindowTransientChildOf(active_window, native_window)) {
-        hold_lock = true;
-      }
-    }
-  }
-
-  if (hold_lock) {
-    if (!lock_.get()) {
-      lock_.reset(immersive_fullscreen_controller_->GetRevealedLock(
-          ImmersiveFullscreenController::ANIMATE_REVEAL_YES));
-    }
-  } else {
-    lock_.reset();
-  }
-}
-
-void ImmersiveFocusWatcherClassic::ReleaseLock() {
-  lock_.reset();
-}
-
-views::Widget* ImmersiveFocusWatcherClassic::GetWidget() {
-  return immersive_fullscreen_controller_->widget();
-}
-
-aura::Window* ImmersiveFocusWatcherClassic::GetWidgetWindow() {
-  return GetWidget()->GetNativeWindow();
-}
-
-void ImmersiveFocusWatcherClassic::RecreateBubbleObserver() {
-  bubble_observer_.reset(new BubbleObserver(immersive_fullscreen_controller_));
-  const std::vector<aura::Window*> transient_children =
-      ::wm::GetTransientChildren(GetWidgetWindow());
-  for (size_t i = 0; i < transient_children.size(); ++i) {
-    aura::Window* transient_child = transient_children[i];
-    views::View* anchor_view = GetAnchorView(transient_child);
-    if (anchor_view &&
-        immersive_fullscreen_controller_->top_container()->Contains(
-            anchor_view))
-      bubble_observer_->StartObserving(transient_child);
-  }
-}
-
-void ImmersiveFocusWatcherClassic::OnWillChangeFocus(
-    views::View* focused_before,
-    views::View* focused_now) {}
-
-void ImmersiveFocusWatcherClassic::OnDidChangeFocus(views::View* focused_before,
-                                                    views::View* focused_now) {
-  UpdateFocusRevealedLock();
-}
-
-void ImmersiveFocusWatcherClassic::OnWidgetActivationChanged(
-    views::Widget* widget,
-    bool active) {
-  UpdateFocusRevealedLock();
-}
-
-void ImmersiveFocusWatcherClassic::OnTransientChildAdded(
-    aura::Window* window,
-    aura::Window* transient) {
-  views::View* anchor = GetAnchorView(transient);
-  if (anchor &&
-      immersive_fullscreen_controller_->top_container()->Contains(anchor)) {
-    // Observe the aura::Window because the BubbleDelegateView may not be
-    // parented to the widget's root view yet so |bubble_delegate->GetWidget()|
-    // may still return NULL.
-    bubble_observer_->StartObserving(transient);
-  }
-}
-
-void ImmersiveFocusWatcherClassic::OnTransientChildRemoved(
-    aura::Window* window,
-    aura::Window* transient) {
-  bubble_observer_->StopObserving(transient);
-}
-
-}  // namespace ash
diff --git a/ash/wm/immersive_focus_watcher_classic.h b/ash/wm/immersive_focus_watcher_classic.h
deleted file mode 100644
index 95f7422e..0000000
--- a/ash/wm/immersive_focus_watcher_classic.h
+++ /dev/null
@@ -1,77 +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 ASH_WM_IMMERSIVE_FOCUS_WATCHER_CLASSIC_H_
-#define ASH_WM_IMMERSIVE_FOCUS_WATCHER_CLASSIC_H_
-
-#include "ash/ash_export.h"
-#include "ash/public/cpp/immersive/immersive_focus_watcher.h"
-#include "ui/views/focus/focus_manager.h"
-#include "ui/views/widget/widget_observer.h"
-#include "ui/wm/core/transient_window_observer.h"
-
-namespace ash {
-
-class ImmersiveFullscreenController;
-class ImmersiveRevealedLock;
-
-// ImmersiveFocusWatcher is responsible for grabbing a reveal lock based on
-// activation and/or focus. This implementation grabs a lock if views focus is
-// in the top view, or a bubble is showing that is anchored to the top view.
-class ASH_EXPORT ImmersiveFocusWatcherClassic
-    : public ImmersiveFocusWatcher,
-      public views::FocusChangeListener,
-      public views::WidgetObserver,
-      public ::wm::TransientWindowObserver {
- public:
-  explicit ImmersiveFocusWatcherClassic(
-      ImmersiveFullscreenController* controller);
-  ~ImmersiveFocusWatcherClassic() override;
-
-  // ImmersiveFocusWatcher:
-  void UpdateFocusRevealedLock() override;
-  void ReleaseLock() override;
-
- private:
-  class BubbleObserver;
-
-  views::Widget* GetWidget();
-  aura::Window* GetWidgetWindow();
-
-  // Recreate |bubble_observer_| and start observing any bubbles anchored to a
-  // child of |top_container_|.
-  void RecreateBubbleObserver();
-
-  // views::FocusChangeObserver overrides:
-  void OnWillChangeFocus(views::View* focused_before,
-                         views::View* focused_now) override;
-  void OnDidChangeFocus(views::View* focused_before,
-                        views::View* focused_now) override;
-
-  // views::WidgetObserver overrides:
-  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
-
-  // ::wm::TransientWindowObserver overrides:
-  void OnTransientChildAdded(aura::Window* window,
-                             aura::Window* transient) override;
-  void OnTransientChildRemoved(aura::Window* window,
-                               aura::Window* transient) override;
-
-  ImmersiveFullscreenController* immersive_fullscreen_controller_;
-
-  // Lock which keeps the top-of-window views revealed based on the focused view
-  // and the active widget. Acquiring the lock never triggers a reveal because
-  // a view is not focusable till a reveal has made it visible.
-  std::unique_ptr<ImmersiveRevealedLock> lock_;
-
-  // Manages bubbles which are anchored to a child of
-  // |ImmersiveFullscreenController::top_container_|.
-  std::unique_ptr<BubbleObserver> bubble_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImmersiveFocusWatcherClassic);
-};
-
-}  // namespace ash
-
-#endif  // ASH_WM_IMMERSIVE_FOCUS_WATCHER_CLASSIC_H_
diff --git a/ash/wm/immersive_handler_factory_ash.cc b/ash/wm/immersive_handler_factory_ash.cc
index 9c883ac..d88adb8 100644
--- a/ash/wm/immersive_handler_factory_ash.cc
+++ b/ash/wm/immersive_handler_factory_ash.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "ash/wm/immersive_focus_watcher_classic.h"
 #include "ash/wm/immersive_gesture_handler_classic.h"
 
 namespace ash {
@@ -15,12 +14,6 @@
 
 ImmersiveHandlerFactoryAsh::~ImmersiveHandlerFactoryAsh() = default;
 
-std::unique_ptr<ImmersiveFocusWatcher>
-ImmersiveHandlerFactoryAsh::CreateFocusWatcher(
-    ImmersiveFullscreenController* controller) {
-  return std::make_unique<ImmersiveFocusWatcherClassic>(controller);
-}
-
 std::unique_ptr<ImmersiveGestureHandler>
 ImmersiveHandlerFactoryAsh::CreateGestureHandler(
     ImmersiveFullscreenController* controller) {
diff --git a/ash/wm/immersive_handler_factory_ash.h b/ash/wm/immersive_handler_factory_ash.h
index 0363a1b..a6eeecc1 100644
--- a/ash/wm/immersive_handler_factory_ash.h
+++ b/ash/wm/immersive_handler_factory_ash.h
@@ -17,8 +17,6 @@
   ~ImmersiveHandlerFactoryAsh() override;
 
   // ImmersiveHandlerFactory:
-  std::unique_ptr<ImmersiveFocusWatcher> CreateFocusWatcher(
-      ImmersiveFullscreenController* controller) override;
   std::unique_ptr<ImmersiveGestureHandler> CreateGestureHandler(
       ImmersiveFullscreenController* controller) override;
 
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 88ceede..2ce2a57 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -348,11 +348,11 @@
     for (std::unique_ptr<WindowGrid>& window_grid : grid_list_) {
       window_grid->PrepareForOverview();
 
-      // Check if there is any window that's being dragged in the grid. If so,
-      // do not do the animation when entering overview.
-      aura::Window* dragged_window =
-          GetDraggedWindow(window_grid->root_window(), mru_window_list);
-      if (dragged_window) {
+      // Do not animate if there is any window that is being dragged in the
+      // grid.
+      if (enter_exit_overview_type_ == EnterExitOverviewType::kWindowDragged) {
+        if (!mru_window_list.empty())
+          DCHECK(GetDraggedWindow(window_grid->root_window(), mru_window_list));
         window_grid->PositionWindows(/*animate=*/false);
       } else if (enter_exit_overview_type_ ==
                  EnterExitOverviewType::kWindowsMinimized) {
@@ -361,7 +361,8 @@
       } else {
         // EnterExitOverviewType::kSwipeFromShelf is an exit only type, so it
         // should not appear here.
-        DCHECK_EQ(enter_exit_overview_type_, EnterExitOverviewType::kNormal);
+        DCHECK_NE(enter_exit_overview_type_,
+                  EnterExitOverviewType::kSwipeFromShelf);
         window_grid->CalculateWindowListAnimationStates(
             /*selected_item=*/nullptr, OverviewTransition::kEnter);
         window_grid->PositionWindows(/*animate=*/true, /*ignore_item=*/nullptr,
diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h
index 3b8faac..4156fdc0 100644
--- a/ash/wm/overview/window_selector.h
+++ b/ash/wm/overview/window_selector.h
@@ -63,8 +63,6 @@
   };
 
   // Enum describing the different ways overview can be entered or exited.
-  // TODO(minch|xdai): Investigate if we should add kWindowDragged and/or
-  // kWindowSnapped to this.
   enum class EnterExitOverviewType {
     // The default way, window(s) animate from their initial bounds to the grid
     // bounds. Window(s) that are not visible to the user do not get animated.
@@ -80,9 +78,11 @@
     // code does not need to handle any animations. This is an exit only type.
     kSwipeFromShelf,
     // Overview can be opened by start dragging a window from top or be closed
-    // if the dragged window restores back to maximized/full-screened. Used as
-    // an exit type only currently to avoid the update bounds animation of the
-    // windows in overview grid on overview mode ended.
+    // if the dragged window restores back to maximized/full-screened. On enter
+    // this mode is same as kNormal, except when all windows are minimized, the
+    // launcher does not animate in. On exit this mode is used to avoid the
+    // update bounds animation of the windows in overview grid on overview mode
+    // ended.
     kWindowDragged
   };
 
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index 57d40578..eb1d82b 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -38,6 +38,7 @@
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/splitview/split_view_divider.h"
 #include "ash/wm/splitview/split_view_drag_indicators.h"
+#include "ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -3187,6 +3188,31 @@
   EXPECT_TRUE(observer.last_animation_was_slide());
 }
 
+// Tests that overview mode is entered with kWindowDragged mode when an app is
+// dragged from the top of the screen.
+TEST_F(WindowSelectorTest, DraggingFromTopAnimation) {
+  // Ensure calls to EnableTabletModeWindowManager complete.
+  base::RunLoop().RunUntilIdle();
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+  base::RunLoop().RunUntilIdle();
+
+  const gfx::Rect bounds(200, 200);
+  std::unique_ptr<aura::Window> window(CreateWindow(bounds));
+  std::unique_ptr<views::Widget> widget(CreateWindowWidget(bounds));
+
+  // Drag from the the top of the app to enter overview.
+  auto drag_controller = std::make_unique<TabletModeAppWindowDragController>();
+  ui::GestureEvent event(0, 0, 0, base::TimeTicks(),
+                         ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
+  ui::Event::DispatcherApi dispatch_helper(&event);
+  dispatch_helper.set_target(widget->GetNativeWindow());
+  drag_controller->DragWindowFromTop(&event);
+
+  ASSERT_TRUE(IsSelecting());
+  EXPECT_EQ(WindowSelector::EnterExitOverviewType::kWindowDragged,
+            window_selector()->enter_exit_overview_type());
+}
+
 class SplitViewWindowSelectorTest : public WindowSelectorTest {
  public:
   SplitViewWindowSelectorTest() = default;
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
index 8e82f3f..8e2f68e 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
@@ -102,8 +102,10 @@
   // might open overview in the dragged window side of the screen.
   split_view_controller_->OnWindowDragStarted(dragged_window_);
 
-  if (ShouldOpenOverviewWhenDragStarts() && !controller->IsSelecting())
-    controller->ToggleOverview();
+  if (ShouldOpenOverviewWhenDragStarts() && !controller->IsSelecting()) {
+    controller->ToggleOverview(
+        WindowSelector::EnterExitOverviewType::kWindowDragged);
+  }
 
   if (controller->IsSelecting()) {
     // Only do animation if overview was open before the drag started. If the
diff --git a/ash/wm/workspace/multi_window_resize_controller.cc b/ash/wm/workspace/multi_window_resize_controller.cc
index b4db01e..9212e6e 100644
--- a/ash/wm/workspace/multi_window_resize_controller.cc
+++ b/ash/wm/workspace/multi_window_resize_controller.cc
@@ -201,8 +201,7 @@
 MultiWindowResizeController::MultiWindowResizeController() = default;
 
 MultiWindowResizeController::~MultiWindowResizeController() {
-  window_resizer_.reset();
-  Hide();
+  ResetResizer();
 }
 
 void MultiWindowResizeController::Show(aura::Window* window,
@@ -235,39 +234,19 @@
                     &MultiWindowResizeController::ShowIfValidMouseLocation);
 }
 
-void MultiWindowResizeController::Hide() {
-  if (window_resizer_)
-    return;  // Ignore hides while actively resizing.
-
-  if (windows_.window1) {
-    StopObserving(windows_.window1);
-    windows_.window1 = nullptr;
-  }
-  if (windows_.window2) {
-    StopObserving(windows_.window2);
-    windows_.window2 = nullptr;
-  }
-
-  show_timer_.Stop();
-
-  if (!resize_widget_)
-    return;
-
-  for (size_t i = 0; i < windows_.other_windows.size(); ++i)
-    StopObserving(windows_.other_windows[i]);
-  mouse_watcher_.reset();
-  resize_widget_.reset();
-  windows_ = ResizeWindows();
-}
-
 void MultiWindowResizeController::MouseMovedOutOfHost() {
   Hide();
 }
 
+void MultiWindowResizeController::OnWindowVisibilityChanged(
+    aura::Window* window,
+    bool visible) {
+  if (!visible)
+    ResetResizer();
+}
+
 void MultiWindowResizeController::OnWindowDestroying(aura::Window* window) {
-  // Have to explicitly reset the WindowResizer, otherwise Hide() does nothing.
-  window_resizer_.reset();
-  Hide();
+  ResetResizer();
 }
 
 void MultiWindowResizeController::OnPostWindowStateTypeChange(
@@ -275,8 +254,7 @@
     mojom::WindowStateType old_type) {
   if (window_state->IsMaximized() || window_state->IsFullscreen() ||
       window_state->IsMinimized()) {
-    window_resizer_.reset();
-    Hide();
+    ResetResizer();
   }
 }
 
@@ -476,6 +454,37 @@
   return resize_widget_.get() || show_timer_.IsRunning();
 }
 
+void MultiWindowResizeController::Hide() {
+  if (window_resizer_)
+    return;  // Ignore hides while actively resizing.
+
+  if (windows_.window1) {
+    StopObserving(windows_.window1);
+    windows_.window1 = nullptr;
+  }
+  if (windows_.window2) {
+    StopObserving(windows_.window2);
+    windows_.window2 = nullptr;
+  }
+
+  show_timer_.Stop();
+
+  if (!resize_widget_)
+    return;
+
+  for (auto* window : windows_.other_windows)
+    StopObserving(window);
+  mouse_watcher_.reset();
+  resize_widget_.reset();
+  windows_ = ResizeWindows();
+}
+
+void MultiWindowResizeController::ResetResizer() {
+  // Have to explicitly reset the WindowResizer, otherwise Hide() does nothing.
+  window_resizer_.reset();
+  Hide();
+}
+
 void MultiWindowResizeController::StartResize(
     const gfx::Point& location_in_screen) {
   DCHECK(!window_resizer_.get());
@@ -543,8 +552,7 @@
     return;  // Happens if window was destroyed and we nuked the WindowResizer.
   window_resizer_->RevertDrag();
   wm::GetWindowState(window_resizer_->GetTarget())->DeleteDragDetails();
-  window_resizer_.reset();
-  Hide();
+  ResetResizer();
 }
 
 gfx::Rect MultiWindowResizeController::CalculateResizeWidgetBounds(
diff --git a/ash/wm/workspace/multi_window_resize_controller.h b/ash/wm/workspace/multi_window_resize_controller.h
index 43c47ea..5f46ddf 100644
--- a/ash/wm/workspace/multi_window_resize_controller.h
+++ b/ash/wm/workspace/multi_window_resize_controller.h
@@ -45,13 +45,11 @@
   // is over, |component| the edge and |point| the location of the mouse.
   void Show(aura::Window* window, int component, const gfx::Point& point);
 
-  // Hides the resize widget.
-  void Hide();
-
   // MouseWatcherListener:
   void MouseMovedOutOfHost() override;
 
   // WindowObserver:
+  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
   void OnWindowDestroying(aura::Window* window) override;
 
   // wm::WindowStateObserver:
@@ -132,6 +130,12 @@
   // Returns true if the widget is showing.
   bool IsShowing() const;
 
+  // Hides the resize widget.
+  void Hide();
+
+  // Resets the window resizer and hides the resize widget.
+  void ResetResizer();
+
   // Initiates a resize.
   void StartResize(const gfx::Point& location_in_screen);
 
diff --git a/ash/wm/workspace/multi_window_resize_controller_unittest.cc b/ash/wm/workspace/multi_window_resize_controller_unittest.cc
index df238d1..5d14ea5 100644
--- a/ash/wm/workspace/multi_window_resize_controller_unittest.cc
+++ b/ash/wm/workspace/multi_window_resize_controller_unittest.cc
@@ -406,6 +406,29 @@
   EXPECT_FALSE(IsShowing());
 }
 
+// Tests that if one of the resized windows visibility changes to hidden, the
+// resize widget should be dismissed.
+TEST_F(MultiWindowResizeControllerTest, HideWindowTest) {
+  aura::test::TestWindowDelegate delegate1;
+  std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
+      &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
+  delegate1.set_window_component(HTRIGHT);
+  aura::test::TestWindowDelegate delegate2;
+  std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
+      &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
+  delegate2.set_window_component(HTLEFT);
+
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  gfx::Point w1_center_in_screen = w1->GetBoundsInScreen().CenterPoint();
+  generator->MoveMouseTo(w1_center_in_screen);
+  ShowNow();
+  EXPECT_TRUE(IsShowing());
+
+  // Hide one window should dimiss the resizer.
+  w1->Hide();
+  EXPECT_FALSE(IsShowing());
+}
+
 namespace {
 
 class TestWindowStateDelegate : public wm::WindowStateDelegate {
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index a9bf314..cbb00b6a 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -559,11 +559,7 @@
   if (event->type() == ui::ET_SCROLL_FLING_START) {
     CompleteDrag();
 
-    // TODO(pkotwicz): Fix tests which inadvertently start flings and check
-    // window_resizer_->IsMove() instead of the hittest component at |event|'s
-    // location.
-    if (wm::GetNonClientComponent(GetTarget(), event->location()) !=
-            HTCAPTION ||
+    if (details().bounds_change != WindowResizer::kBoundsChange_Repositions ||
         !wm::GetWindowState(GetTarget())->IsNormalOrSnapped()) {
       return;
     }
diff --git a/base/allocator/partition_allocator/address_space_randomization.cc b/base/allocator/partition_allocator/address_space_randomization.cc
index 3e3bf13..6a47e6c 100644
--- a/base/allocator/partition_allocator/address_space_randomization.cc
+++ b/base/allocator/partition_allocator/address_space_randomization.cc
@@ -6,8 +6,8 @@
 
 #include "base/allocator/partition_allocator/page_allocator.h"
 #include "base/allocator/partition_allocator/spin_lock.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "build/build_config.h"
 
@@ -32,8 +32,10 @@
   uint32_t d;
 };
 
-static LazyInstance<RandomContext>::Leaky s_RandomContext =
-    LAZY_INSTANCE_INITIALIZER;
+RandomContext* GetRandomContext() {
+  static NoDestructor<RandomContext> s_RandomContext;
+  return s_RandomContext.get();
+}
 
 #define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
 
@@ -68,7 +70,7 @@
 }  // namespace
 
 void SetRandomPageBaseSeed(int64_t seed) {
-  RandomContext* x = s_RandomContext.Pointer();
+  RandomContext* x = GetRandomContext();
   subtle::SpinLock::Guard guard(x->lock);
   // Set RNG to initial state.
   x->initialized = true;
@@ -77,12 +79,11 @@
 }
 
 void* GetRandomPageBase() {
-  uintptr_t random =
-      static_cast<uintptr_t>(RandomValue(s_RandomContext.Pointer()));
+  uintptr_t random = static_cast<uintptr_t>(RandomValue(GetRandomContext()));
 
 #if defined(ARCH_CPU_64_BITS)
   random <<= 32ULL;
-  random |= static_cast<uintptr_t>(RandomValue(s_RandomContext.Pointer()));
+  random |= static_cast<uintptr_t>(RandomValue(GetRandomContext()));
 
 // The kASLRMask and kASLROffset constants will be suitable for the
 // OS and build configuration.
diff --git a/base/allocator/partition_allocator/oom_callback.cc b/base/allocator/partition_allocator/oom_callback.cc
index 980c45f..2e22e109 100644
--- a/base/allocator/partition_allocator/oom_callback.cc
+++ b/base/allocator/partition_allocator/oom_callback.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/allocator/partition_allocator/oom_callback.h"
+
 #include "base/logging.h"
 
 namespace base {
diff --git a/base/allocator/partition_allocator/page_allocator.cc b/base/allocator/partition_allocator/page_allocator.cc
index 4a28550a..f3fd1e6 100644
--- a/base/allocator/partition_allocator/page_allocator.cc
+++ b/base/allocator/partition_allocator/page_allocator.cc
@@ -11,8 +11,8 @@
 #include "base/allocator/partition_allocator/address_space_randomization.h"
 #include "base/allocator/partition_allocator/page_allocator_internal.h"
 #include "base/allocator/partition_allocator/spin_lock.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/no_destructor.h"
 #include "base/numerics/checked_math.h"
 #include "build/build_config.h"
 
@@ -33,7 +33,10 @@
 namespace {
 
 // We may reserve/release address space on different threads.
-LazyInstance<subtle::SpinLock>::Leaky s_reserveLock = LAZY_INSTANCE_INITIALIZER;
+subtle::SpinLock& GetReserveLock() {
+  static NoDestructor<subtle::SpinLock> s_reserveLock;
+  return *s_reserveLock;
+}
 
 // We only support a single block of reserved address space.
 void* s_reservation_address = nullptr;
@@ -223,7 +226,7 @@
 
 bool ReserveAddressSpace(size_t size) {
   // To avoid deadlock, call only SystemAllocPages.
-  subtle::SpinLock::Guard guard(s_reserveLock.Get());
+  subtle::SpinLock::Guard guard(GetReserveLock());
   if (s_reservation_address == nullptr) {
     void* mem = SystemAllocPages(nullptr, size, PageInaccessible,
                                  PageTag::kChromium, false);
@@ -241,7 +244,7 @@
 
 void ReleaseReservation() {
   // To avoid deadlock, call only FreePages.
-  subtle::SpinLock::Guard guard(s_reserveLock.Get());
+  subtle::SpinLock::Guard guard(GetReserveLock());
   if (s_reservation_address != nullptr) {
     FreePages(s_reservation_address, s_reservation_size);
     s_reservation_address = nullptr;
diff --git a/base/allocator/partition_allocator/page_allocator_internals_win.h b/base/allocator/partition_allocator/page_allocator_internals_win.h
index d5dcf375..22940d6 100644
--- a/base/allocator/partition_allocator/page_allocator_internals_win.h
+++ b/base/allocator/partition_allocator/page_allocator_internals_win.h
@@ -69,12 +69,10 @@
     void* address,
     size_t length,
     PageAccessibilityConfiguration accessibility) {
-  if (accessibility == PageInaccessible) {
+  if (accessibility == PageInaccessible)
     return VirtualFree(address, length, MEM_DECOMMIT) != 0;
-  } else {
-    return nullptr != VirtualAlloc(address, length, MEM_COMMIT,
-                                   GetAccessFlags(accessibility));
-  }
+  return nullptr != VirtualAlloc(address, length, MEM_COMMIT,
+                                 GetAccessFlags(accessibility));
 }
 
 void FreePagesInternal(void* address, size_t length) {
diff --git a/base/allocator/partition_allocator/partition_alloc.cc b/base/allocator/partition_allocator/partition_alloc.cc
index 0326d0b..6ef8bfe 100644
--- a/base/allocator/partition_allocator/partition_alloc.cc
+++ b/base/allocator/partition_allocator/partition_alloc.cc
@@ -13,8 +13,8 @@
 #include "base/allocator/partition_allocator/partition_oom.h"
 #include "base/allocator/partition_allocator/partition_page.h"
 #include "base/allocator/partition_allocator/spin_lock.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/no_destructor.h"
 
 namespace base {
 
@@ -57,8 +57,10 @@
 PartitionAllocatorGeneric::PartitionAllocatorGeneric() = default;
 PartitionAllocatorGeneric::~PartitionAllocatorGeneric() = default;
 
-static LazyInstance<subtle::SpinLock>::Leaky g_initialized_lock =
-    LAZY_INSTANCE_INITIALIZER;
+subtle::SpinLock& GetLock() {
+  static NoDestructor<subtle::SpinLock> s_initialized_lock;
+  return *s_initialized_lock;
+}
 static bool g_initialized = false;
 
 void (*internal::PartitionRootBase::gOomHandlingFunction)() = nullptr;
@@ -69,7 +71,7 @@
 static void PartitionAllocBaseInit(internal::PartitionRootBase* root) {
   DCHECK(!root->initialized);
   {
-    subtle::SpinLock::Guard guard(g_initialized_lock.Get());
+    subtle::SpinLock::Guard guard(GetLock());
     if (!g_initialized) {
       g_initialized = true;
       // We mark the sentinel bucket/page as free to make sure it is skipped by
diff --git a/base/allocator/partition_allocator/spin_lock.cc b/base/allocator/partition_allocator/spin_lock.cc
index 752889bd..5cc04168 100644
--- a/base/allocator/partition_allocator/spin_lock.cc
+++ b/base/allocator/partition_allocator/spin_lock.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/allocator/partition_allocator/spin_lock.h"
+
 #include "base/threading/platform_thread.h"
 #include "build/build_config.h"
 
diff --git a/base/logging.cc b/base/logging.cc
index 9e69d5e..826a4bd 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -95,6 +95,7 @@
 #include "base/lazy_instance.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
@@ -770,8 +771,17 @@
         priority = ANDROID_LOG_FATAL;
         break;
     }
+#if DCHECK_IS_ON()
+    // Split the output by new lines to prevent the Android system from
+    // truncating the log.
+    for (const auto& line : base::SplitString(
+             str_newline, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL))
+      __android_log_write(priority, "chromium", line.c_str());
+#else
+    // The Android system may truncate the string if it's too long.
     __android_log_write(priority, "chromium", str_newline.c_str());
 #endif
+#endif  // OS_ANDROID
     ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr));
     fflush(stderr);
   } else if (severity_ >= kAlwaysPrintErrorLevel) {
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc
index 0db05e921..a35895c 100644
--- a/base/profiler/stack_sampling_profiler.cc
+++ b/base/profiler/stack_sampling_profiler.cc
@@ -19,6 +19,7 @@
 #include "base/memory/singleton.h"
 #include "base/profiler/native_stack_sampler.h"
 #include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -201,15 +202,19 @@
   // Thread API (Start, Stop, StopSoon, & DetachFromSequence) so that
   // multiple threads may make those calls.
   Lock thread_execution_state_lock_;  // Protects all thread_execution_state_*
-  ThreadExecutionState thread_execution_state_ = NOT_STARTED;
-  scoped_refptr<SingleThreadTaskRunner> thread_execution_state_task_runner_;
-  bool thread_execution_state_disable_idle_shutdown_for_testing_ = false;
+  ThreadExecutionState thread_execution_state_
+      GUARDED_BY(thread_execution_state_lock_) = NOT_STARTED;
+  scoped_refptr<SingleThreadTaskRunner> thread_execution_state_task_runner_
+      GUARDED_BY(thread_execution_state_lock_);
+  bool thread_execution_state_disable_idle_shutdown_for_testing_
+      GUARDED_BY(thread_execution_state_lock_) = false;
 
   // A counter that notes adds of new collection requests. It is incremented
   // when changes occur so that delayed shutdown tasks are able to detect if
   // something new has happened while it was waiting. Like all "execution_state"
   // vars, this must be accessed while holding |thread_execution_state_lock_|.
-  int thread_execution_state_add_events_ = 0;
+  int thread_execution_state_add_events_
+      GUARDED_BY(thread_execution_state_lock_) = 0;
 
   DISALLOW_COPY_AND_ASSIGN(SamplingThread);
 };
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index c2a62de3..051a355 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -252,6 +252,7 @@
 
   // Handled by the launcher process.
   switches.erase(kGTestRepeatFlag);
+  switches.erase(kIsolatedScriptTestRepeatFlag);
   switches.erase(kGTestShuffleFlag);
   switches.erase(kGTestRandomSeedFlag);
 
@@ -568,6 +569,20 @@
                output_file_contents));
 }
 
+std::vector<std::string> ExtractTestsFromFilter(const std::string& filter,
+                                                bool double_colon_supported) {
+  std::vector<std::string> tests;
+  if (double_colon_supported) {
+    tests =
+        SplitString(filter, "::", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  }
+  if (tests.size() <= 1) {
+    tests =
+        SplitString(filter, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  }
+  return tests;
+}
+
 }  // namespace
 
 const char kGTestBreakOnFailure[] = "gtest_break_on_failure";
@@ -580,6 +595,10 @@
 const char kGTestOutputFlag[] = "gtest_output";
 const char kGTestShuffleFlag[] = "gtest_shuffle";
 const char kGTestRandomSeedFlag[] = "gtest_random_seed";
+const char kIsolatedScriptRunDisabledTestsFlag[] =
+    "isolated-script-test-also-run-disabled-tests";
+const char kIsolatedScriptTestFilterFlag[] = "isolated-script-test-filter";
+const char kIsolatedScriptTestRepeatFlag[] = "isolated-script-test-repeat";
 
 TestLauncherDelegate::~TestLauncherDelegate() = default;
 
@@ -946,6 +965,13 @@
     LOG(ERROR) << "Invalid value for " << kGTestRepeatFlag;
     return false;
   }
+  if (command_line->HasSwitch(kIsolatedScriptTestRepeatFlag) &&
+      !StringToInt(
+          command_line->GetSwitchValueASCII(kIsolatedScriptTestRepeatFlag),
+          &cycles_)) {
+    LOG(ERROR) << "Invalid value for " << kIsolatedScriptTestRepeatFlag;
+    return false;
+  }
 
   if (command_line->HasSwitch(switches::kTestLauncherRetryLimit)) {
     int retry_limit = -1;
@@ -957,7 +983,22 @@
     }
 
     retry_limit_ = retry_limit;
-  } else if (!command_line->HasSwitch(kGTestFilterFlag) || BotModeEnabled()) {
+  } else if (command_line->HasSwitch(
+                 switches::kIsolatedScriptTestLauncherRetryLimit)) {
+    int retry_limit = -1;
+    if (!StringToInt(command_line->GetSwitchValueASCII(
+                         switches::kIsolatedScriptTestLauncherRetryLimit),
+                     &retry_limit) ||
+        retry_limit < 0) {
+      LOG(ERROR) << "Invalid value for "
+                 << switches::kIsolatedScriptTestLauncherRetryLimit;
+      return false;
+    }
+
+    retry_limit_ = retry_limit;
+  } else if (BotModeEnabled() ||
+             !(command_line->HasSwitch(kGTestFilterFlag) ||
+               command_line->HasSwitch(kIsolatedScriptTestFilterFlag))) {
     // Retry failures 3 times by default if we are running all of the tests or
     // in bot mode.
     retry_limit_ = 3;
@@ -1022,21 +1063,22 @@
 
   // Split --gtest_filter at '-', if there is one, to separate into
   // positive filter and negative filter portions.
-  std::string filter = command_line->GetSwitchValueASCII(kGTestFilterFlag);
+  bool double_colon_supported = !command_line->HasSwitch(kGTestFilterFlag);
+  std::string filter = command_line->GetSwitchValueASCII(
+      double_colon_supported ? kIsolatedScriptTestFilterFlag
+                             : kGTestFilterFlag);
   size_t dash_pos = filter.find('-');
   if (dash_pos == std::string::npos) {
     positive_gtest_filter =
-        SplitString(filter, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+        ExtractTestsFromFilter(filter, double_colon_supported);
   } else {
     // Everything up to the dash.
-    positive_gtest_filter =
-        SplitString(filter.substr(0, dash_pos), ":", base::TRIM_WHITESPACE,
-                    base::SPLIT_WANT_ALL);
+    positive_gtest_filter = ExtractTestsFromFilter(filter.substr(0, dash_pos),
+                                                   double_colon_supported);
 
     // Everything after the dash.
-    for (std::string pattern :
-         SplitString(filter.substr(dash_pos + 1), ":", base::TRIM_WHITESPACE,
-                     base::SPLIT_WANT_ALL)) {
+    for (std::string pattern : ExtractTestsFromFilter(
+             filter.substr(dash_pos + 1), double_colon_supported)) {
       negative_test_filter_.push_back(pattern);
     }
   }
@@ -1168,7 +1210,8 @@
       results_tracker_.AddDisabledTest(test_name);
 
       // Skip disabled tests unless explicitly requested.
-      if (!command_line->HasSwitch(kGTestRunDisabledTestsFlag))
+      if (!command_line->HasSwitch(kGTestRunDisabledTestsFlag) &&
+          !command_line->HasSwitch(kIsolatedScriptRunDisabledTestsFlag))
         continue;
     }
 
@@ -1366,7 +1409,9 @@
     }
     return jobs;
   }
-  if (command_line->HasSwitch(kGTestFilterFlag) && !BotModeEnabled()) {
+  if (!BotModeEnabled() &&
+      (command_line->HasSwitch(kGTestFilterFlag) ||
+       command_line->HasSwitch(kIsolatedScriptTestFilterFlag))) {
     // Do not run jobs in parallel by default if we are running a subset of
     // the tests and if bot mode is off.
     return 1U;
diff --git a/base/test/launcher/test_launcher.h b/base/test/launcher/test_launcher.h
index 9ac45ba..ef8cbeb 100644
--- a/base/test/launcher/test_launcher.h
+++ b/base/test/launcher/test_launcher.h
@@ -40,6 +40,9 @@
 extern const char kGTestOutputFlag[];
 extern const char kGTestShuffleFlag[];
 extern const char kGTestRandomSeedFlag[];
+extern const char kIsolatedScriptRunDisabledTestsFlag[];
+extern const char kIsolatedScriptTestFilterFlag[];
+extern const char kIsolatedScriptTestRepeatFlag[];
 
 // Interface for use with LaunchTests that abstracts away exact details
 // which tests and how are run.
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index 50a46325..32265ee 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -5,6 +5,7 @@
 #include "base/test/scoped_task_environment.h"
 
 #include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
@@ -16,6 +17,7 @@
 #include "base/task/task_scheduler/task_scheduler_impl.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/sequence_local_storage_map.h"
+#include "base/threading/thread_local.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -29,6 +31,9 @@
 
 namespace {
 
+LazyInstance<ThreadLocalPointer<ScopedTaskEnvironment::LifetimeObserver>>::Leaky
+    environment_lifetime_observer;
+
 std::unique_ptr<MessageLoop> CreateMessageLoopForMainThreadType(
     ScopedTaskEnvironment::MainThreadType main_thread_type) {
   switch (main_thread_type) {
@@ -146,6 +151,12 @@
 
   if (execution_control_mode_ == ExecutionMode::QUEUED)
     CHECK(task_tracker_->DisallowRunTasks());
+
+  LifetimeObserver* observer = environment_lifetime_observer.Get().Get();
+  if (observer) {
+    observer->OnScopedTaskEnvironmentCreated(main_thread_type,
+                                             GetMainThreadTaskRunner());
+  }
 }
 
 ScopedTaskEnvironment::~ScopedTaskEnvironment() {
@@ -165,6 +176,16 @@
   // on their main thread.
   ScopedAllowBaseSyncPrimitivesForTesting allow_waits_to_destroy_task_tracker;
   TaskScheduler::SetInstance(nullptr);
+
+  LifetimeObserver* observer = environment_lifetime_observer.Get().Get();
+  if (observer)
+    observer->OnScopedTaskEnvironmentDestroyed();
+}
+
+void ScopedTaskEnvironment::SetLifetimeObserver(
+    ScopedTaskEnvironment::LifetimeObserver* lifetime_observer) {
+  DCHECK_NE(!!environment_lifetime_observer.Get().Get(), !!lifetime_observer);
+  environment_lifetime_observer.Get().Set(lifetime_observer);
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/base/test/scoped_task_environment.h b/base/test/scoped_task_environment.h
index 2e46073..9c4ce59 100644
--- a/base/test/scoped_task_environment.h
+++ b/base/test/scoped_task_environment.h
@@ -98,6 +98,22 @@
   // TaskScheduler and the (Thread|Sequenced)TaskRunnerHandle.
   ~ScopedTaskEnvironment();
 
+  class LifetimeObserver {
+   public:
+    virtual ~LifetimeObserver() = default;
+
+    virtual void OnScopedTaskEnvironmentCreated(
+        MainThreadType main_thread_type,
+        scoped_refptr<SingleThreadTaskRunner> task_runner) = 0;
+    virtual void OnScopedTaskEnvironmentDestroyed() = 0;
+  };
+
+  // Set a thread-local observer which will get notifications when
+  // a new ScopedTaskEnvironment is created or destroyed.
+  // This is needed due to peculiarities of Blink initialisation
+  // (Blink is per-test suite and ScopedTaskEnvironment is per-test).
+  static void SetLifetimeObserver(LifetimeObserver* lifetime_observer);
+
   // Returns a TaskRunner that schedules tasks on the main thread.
   scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner();
 
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc
index f32e93b..d8e76c5 100644
--- a/base/test/scoped_task_environment_unittest.cc
+++ b/base/test/scoped_task_environment_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/tick_clock.h"
 #include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_POSIX)
@@ -303,6 +304,36 @@
   EXPECT_EQ(kLongTaskDelay * 2, tick_clock->NowTicks() - tick_clock_ref);
 }
 
+namespace {
+
+class MockLifetimeObserver : public ScopedTaskEnvironment::LifetimeObserver {
+ public:
+  MockLifetimeObserver() = default;
+  ~MockLifetimeObserver() override = default;
+
+  MOCK_METHOD2(OnScopedTaskEnvironmentCreated,
+               void(ScopedTaskEnvironment::MainThreadType,
+                    scoped_refptr<SingleThreadTaskRunner>));
+  MOCK_METHOD0(OnScopedTaskEnvironmentDestroyed, void());
+};
+
+}  // namespace
+
+TEST_F(ScopedTaskEnvironmentTest, LifetimeObserver) {
+  testing::StrictMock<MockLifetimeObserver> lifetime_observer;
+  ScopedTaskEnvironment::SetLifetimeObserver(&lifetime_observer);
+
+  EXPECT_CALL(lifetime_observer,
+              OnScopedTaskEnvironmentCreated(testing::_, testing::_));
+  std::unique_ptr<ScopedTaskEnvironment> task_environment(
+      std::make_unique<ScopedTaskEnvironment>());
+  testing::Mock::VerifyAndClearExpectations(&lifetime_observer);
+
+  EXPECT_CALL(lifetime_observer, OnScopedTaskEnvironmentDestroyed());
+  task_environment.reset();
+  testing::Mock::VerifyAndClearExpectations(&lifetime_observer);
+}
+
 INSTANTIATE_TEST_CASE_P(
     MainThreadDefault,
     ScopedTaskEnvironmentTest,
diff --git a/base/test/test_switches.cc b/base/test/test_switches.cc
index 5e4f9cf..ecdf493 100644
--- a/base/test/test_switches.cc
+++ b/base/test/test_switches.cc
@@ -40,8 +40,12 @@
 // Path to test results file in our custom test launcher format.
 const char switches::kTestLauncherOutput[] = "test-launcher-output";
 
+// These two flags has the same effect, but don't use them at the same time.
+// And isolated-script-test-launcher-retry-limit is preferred in the future.
 // Maximum number of times to retry a test after failure.
 const char switches::kTestLauncherRetryLimit[] = "test-launcher-retry-limit";
+const char switches::kIsolatedScriptTestLauncherRetryLimit[] =
+    "isolated-script-test-launcher-retry-limit";
 
 // Path to test results file with all the info from the test launcher.
 const char switches::kTestLauncherSummaryOutput[] =
diff --git a/base/test/test_switches.h b/base/test/test_switches.h
index 6baba308..d3857ba 100644
--- a/base/test/test_switches.h
+++ b/base/test/test_switches.h
@@ -19,6 +19,7 @@
 extern const char kTestLauncherListTests[];
 extern const char kTestLauncherOutput[];
 extern const char kTestLauncherRetryLimit[];
+extern const char kIsolatedScriptTestLauncherRetryLimit[];
 extern const char kTestLauncherSummaryOutput[];
 extern const char kTestLauncherPrintTestStdio[];
 extern const char kTestLauncherPrintWritablePath[];
diff --git a/build/build_config.h b/build/build_config.h
index c7b02664..4d1ba77f 100644
--- a/build/build_config.h
+++ b/build/build_config.h
@@ -140,7 +140,7 @@
 #define ARCH_CPU_ARMEL 1
 #define ARCH_CPU_32_BITS 1
 #define ARCH_CPU_LITTLE_ENDIAN 1
-#elif defined(__aarch64__)
+#elif defined(__aarch64__) || defined(_M_ARM64)
 #define ARCH_CPU_ARM_FAMILY 1
 #define ARCH_CPU_ARM64 1
 #define ARCH_CPU_64_BITS 1
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 387fdb2..f33cba1 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -203,6 +203,7 @@
   } else if (host_os == "win") {
     # On Windows always use the target CPU for host builds for x86/x64. On the
     # configurations we support this will always work and it saves build steps.
+    # Windows ARM64 targets require an x64 host for cross build.
     if (target_cpu == "x86" || target_cpu == "x64") {
       if (is_clang) {
         host_toolchain = "//build/toolchain/win:win_clang_$target_cpu"
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 148e913..88a9c1b3 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1772,7 +1772,7 @@
 # Code that currently generates warnings for this can include this
 # config to disable them.
 config("no_shorten_64_warnings") {
-  if (current_cpu == "x64") {
+  if (current_cpu == "x64" || current_cpu == "arm64") {
     if (is_clang) {
       cflags = [ "-Wno-shorten-64-to-32" ]
     } else {
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index debc64c..50427222 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -390,6 +390,12 @@
   }
 }
 
+config("cfi_icall_disable") {
+  if (is_clang && is_cfi && use_cfi_icall) {
+    cflags = [ "-fno-sanitize=cfi-icall" ]
+  }
+}
+
 config("coverage_flags") {
   cflags = []
   if (use_sanitizer_coverage) {
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index e605040..3c8f327 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -479,6 +479,9 @@
   # The number after the comma is the minimum required OS version.
   # 5.02 = Windows Server 2003.
   subsystem_version_suffix = ",5.02"
+} else if (current_cpu == "arm64") {
+  # Windows ARM64 requires Windows 10.
+  subsystem_version_suffix = ",10.0"
 } else {
   # 5.01 = Windows XP.
   subsystem_version_suffix = ",5.01"
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index 3c6c9d0..5fcda742 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -49,6 +49,12 @@
               ])
 }
 
+if (host_os == "win") {
+  clang_cl = "clang-cl.exe"
+} else {
+  clang_cl = "clang-cl"
+}
+
 # Parameters:
 #   environment: File name of environment file.
 #
@@ -203,10 +209,18 @@
     tool("asm") {
       if (toolchain_args.current_cpu == "x64") {
         ml = "ml64.exe"
+      } else if (toolchain_args.current_cpu == "arm64") {
+        prefix = rebase_path("$clang_base_path/bin", root_build_dir)
+        ml = "${goma_prefix}${prefix}/${clang_cl} --target=arm64-windows"
       } else {
         ml = "ml.exe"
       }
-      command = "$python_path $tool_wrapper_path asm-wrapper $env $ml {{defines}} {{include_dirs}} {{asmflags}} /c /Fo{{output}} {{source}}"
+      command = "$python_path $tool_wrapper_path asm-wrapper $env $ml {{defines}} {{include_dirs}} {{asmflags}} "
+      if (toolchain_args.current_cpu == "arm64") {
+        command += "-c -o{{output}} {{source}}"
+      } else {
+        command += "/c /Fo{{output}} {{source}}"
+      }
       description = "ASM {{output}}"
       outputs = [
         "$object_subdir/{{source_name_part}}.obj",
@@ -332,12 +346,6 @@
   }
 }
 
-if (host_os == "win") {
-  clang_cl = "clang-cl.exe"
-} else {
-  clang_cl = "clang-cl"
-}
-
 if (target_cpu == "x86" || target_cpu == "x64") {
   win_build_host_cpu = target_cpu
 } else {
@@ -392,25 +400,28 @@
   }
 }
 
-# 64-bit toolchains.
-x64_toolchain_data = exec_script("setup_toolchain.py",
-                                 [
-                                   visual_studio_path,
-                                   windows_sdk_path,
-                                   visual_studio_runtime_dirs,
-                                   "win",
-                                   "x64",
-                                   "environment.x64",
-                                 ],
-                                 "scope")
+# 64-bit toolchains, including x64 and arm64.
+template("win_64bit_toolchains") {
+  assert(defined(invoker.toolchain_arch))
+  toolchain_arch = invoker.toolchain_arch
 
-template("win_x64_toolchains") {
+  win_64bit_toolchain_data = exec_script("setup_toolchain.py",
+                                         [
+                                           visual_studio_path,
+                                           windows_sdk_path,
+                                           visual_studio_runtime_dirs,
+                                           "win",
+                                           toolchain_arch,
+                                           "environment." + toolchain_arch,
+                                         ],
+                                         "scope")
+
   msvc_toolchain(target_name) {
-    environment = "environment.x64"
-    cl = "${goma_prefix}\"${x64_toolchain_data.vc_bin_dir}/cl.exe\""
+    environment = "environment." + toolchain_arch
+    cl = "${goma_prefix}\"${win_64bit_toolchain_data.vc_bin_dir}/cl.exe\""
     if (host_os != "win") {
       # For win cross build
-      sys_lib_flags = "${x64_toolchain_data.libpath_flags}"
+      sys_lib_flags = "${win_64bit_toolchain_data.libpath_flags}"
     }
 
     toolchain_args = {
@@ -419,18 +430,18 @@
       }
       is_clang = false
       current_os = "win"
-      current_cpu = "x64"
+      current_cpu = toolchain_arch
     }
   }
 
   msvc_toolchain("win_clang_" + target_name) {
-    environment = "environment.x64"
+    environment = "environment." + toolchain_arch
     prefix = rebase_path("$clang_base_path/bin", root_build_dir)
     cl = "${goma_prefix}$prefix/${clang_cl}"
-    sys_include_flags = "${x64_toolchain_data.include_flags_imsvc}"
+    sys_include_flags = "${win_64bit_toolchain_data.include_flags_imsvc}"
     if (host_os != "win") {
       # For win cross build
-      sys_lib_flags = "${x64_toolchain_data.libpath_flags}"
+      sys_lib_flags = "${win_64bit_toolchain_data.libpath_flags}"
     }
 
     toolchain_args = {
@@ -439,23 +450,34 @@
       }
       is_clang = true
       current_os = "win"
-      current_cpu = "x64"
+      current_cpu = toolchain_arch
     }
   }
 }
 
-win_x64_toolchains("x64") {
+win_64bit_toolchains("x64") {
+  toolchain_arch = "x64"
   toolchain_args = {
     # Use the defaults.
   }
 }
 
+if (target_cpu == "arm64") {
+  win_64bit_toolchains("arm64") {
+    toolchain_arch = "arm64"
+    toolchain_args = {
+      # Use the defaults.
+    }
+  }
+}
+
 # The nacl_win64 toolchain is nearly identical to the plain x64 toolchain.
 # It's used solely for building nacl64.exe (//components/nacl/broker:nacl64).
 # The only reason it's a separate toolchain is so that it can force
 # is_component_build to false in the toolchain_args() block, because
 # building nacl64.exe in component style does not work.
-win_x64_toolchains("nacl_win64") {
+win_64bit_toolchains("nacl_win64") {
+  toolchain_arch = "x64"
   toolchain_args = {
     is_component_build = false
   }
diff --git a/build/toolchain/win/midl.gni b/build/toolchain/win/midl.gni
index 9ff29c67..b46f4cd 100644
--- a/build/toolchain/win/midl.gni
+++ b/build/toolchain/win/midl.gni
@@ -77,6 +77,9 @@
     } else if (current_cpu == "x64") {
       win_tool_arch = "environment.x64"
       idl_target_platform = "x64"
+    } else if (current_cpu == "arm64") {
+      win_tool_arch = "environment.arm64"
+      idl_target_platform = "arm64"
     } else {
       assert(false, "Need environment for this arch")
     }
diff --git a/build/toolchain/win/tool_wrapper.py b/build/toolchain/win/tool_wrapper.py
index cb0393e..6479440 100644
--- a/build/toolchain/win/tool_wrapper.py
+++ b/build/toolchain/win/tool_wrapper.py
@@ -168,6 +168,11 @@
   def ExecAsmWrapper(self, arch, *args):
     """Filter logo banner from invocations of asm.exe."""
     env = self._GetEnv(arch)
+    if sys.platform == 'win32':
+        # Windows ARM64 uses clang-cl as assembler which has '/' as path
+        # separator, convert it to '\\' when running on Windows.
+        args = list(args) # *args is a tuple by default, which is read-only
+        args[0] = args[0].replace('/', '\\')
     popen = subprocess.Popen(args, shell=True, env=env,
                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     out, _ = popen.communicate()
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 32bad7f4..6a2723e2 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -238,6 +238,7 @@
     # from HostX86/x86.
     pgo_x86_runtime_dir = os.path.join(pgo_runtime_root, 'HostX86', 'x86')
     pgo_x64_runtime_dir = os.path.join(pgo_runtime_root, 'HostX64', 'x64')
+    pgo_arm64_runtime_dir = os.path.join(pgo_runtime_root, 'arm64')
   else:
     raise Exception('Unexpected toolchain version: %s.' % env_version)
 
@@ -250,6 +251,8 @@
       source = os.path.join(pgo_x86_runtime_dir, runtime)
     elif target_cpu == 'x64':
       source = os.path.join(pgo_x64_runtime_dir, runtime)
+    elif target_cpu == 'arm64':
+      source = os.path.join(pgo_arm64_runtime_dir, runtime)
     else:
       raise NotImplementedError("Unexpected target_cpu value: " + target_cpu)
     if not os.path.exists(source):
diff --git a/build/win/reorder-imports.py b/build/win/reorder-imports.py
index c4b294d..ee27ed1 100755
--- a/build/win/reorder-imports.py
+++ b/build/win/reorder-imports.py
@@ -36,7 +36,7 @@
   # through the Structure, while other data must bet set through
   # the set_bytes_*() methods.
   pe = pefile.PE(input_image, fast_load=True)
-  if architecture == 'x64':
+  if architecture == 'x64' or architecture == 'arm64':
     assert pe.PE_TYPE == pefile.OPTIONAL_HEADER_MAGIC_PE_PLUS
   else:
     assert pe.PE_TYPE == pefile.OPTIONAL_HEADER_MAGIC_PE
diff --git a/cc/animation/animation_host_unittest.cc b/cc/animation/animation_host_unittest.cc
index 104b16c7..e21a0c9 100644
--- a/cc/animation/animation_host_unittest.cc
+++ b/cc/animation/animation_host_unittest.cc
@@ -50,7 +50,8 @@
   }
 
   void SetOutputState(base::TimeDelta local_time) {
-    MutatorOutputState::AnimationState state(worklet_animation_id_, local_time);
+    MutatorOutputState::AnimationState state(worklet_animation_id_);
+    state.local_times.push_back(local_time);
     worklet_animation_impl_->SetOutputState(state);
   }
 
diff --git a/cc/animation/worklet_animation.cc b/cc/animation/worklet_animation.cc
index 34a9746..0c95d0a 100644
--- a/cc/animation/worklet_animation.cc
+++ b/cc/animation/worklet_animation.cc
@@ -112,8 +112,12 @@
 
   switch (state_) {
     case State::PENDING:
-      input_state->Add(
-          {worklet_animation_id(), name(), current_time, CloneOptions()});
+      // TODO(yigu): cc side WorkletAnimation is only capable of handling single
+      // keyframe effect at the moment. We should pass in the number of effects
+      // once Worklet Group Effect is fully implemented in cc.
+      // https://crbug.com/767043.
+      input_state->Add({worklet_animation_id(), name(), current_time,
+                        CloneOptions(), 1 /* num_effects */});
       state_ = State::RUNNING;
       break;
     case State::RUNNING:
@@ -127,7 +131,10 @@
 
 void WorkletAnimation::SetOutputState(
     const MutatorOutputState::AnimationState& state) {
-  local_time_ = state.local_time;
+  // TODO(yigu): cc side WorkletAnimation is only capable of handling single
+  // keyframe effect at the moment. https://crbug.com/767043.
+  DCHECK_EQ(state.local_times.size(), 1u);
+  local_time_ = state.local_times[0];
 }
 
 // TODO(crbug.com/780151): Multiply the result by the play back rate.
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc
index 673c2cc..fe1bdf55 100644
--- a/cc/animation/worklet_animation_unittest.cc
+++ b/cc/animation/worklet_animation_unittest.cc
@@ -64,8 +64,10 @@
           false /* not impl instance*/, std::move(effect)));
 
   EXPECT_CALL(*mock_effect, Tick(_)).Times(0);
-  worklet_animation->SetOutputState(
-      {worklet_animation_id_, base::TimeDelta::FromSecondsD(1)});
+
+  MutatorOutputState::AnimationState state(worklet_animation_id_);
+  state.local_times.push_back(base::TimeDelta::FromSecondsD(1));
+  worklet_animation->SetOutputState(state);
   worklet_animation->Tick(base::TimeTicks());
 }
 
@@ -87,7 +89,9 @@
   keyframe_model->set_needs_synchronized_start_time(false);
 
   base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2);
-  worklet_animation_->SetOutputState({worklet_animation_id_, local_time});
+  MutatorOutputState::AnimationState state(worklet_animation_id_);
+  state.local_times.push_back(local_time);
+  worklet_animation_->SetOutputState(state);
 
   worklet_animation_->Tick(base::TimeTicks());
 
diff --git a/cc/raster/bitmap_raster_buffer_provider.cc b/cc/raster/bitmap_raster_buffer_provider.cc
index 12eaae7..1e4e21bb 100644
--- a/cc/raster/bitmap_raster_buffer_provider.cc
+++ b/cc/raster/bitmap_raster_buffer_provider.cc
@@ -164,4 +164,8 @@
 
 void BitmapRasterBufferProvider::Shutdown() {}
 
+bool BitmapRasterBufferProvider::CheckRasterFinishedQueries() {
+  return false;
+}
+
 }  // namespace cc
diff --git a/cc/raster/bitmap_raster_buffer_provider.h b/cc/raster/bitmap_raster_buffer_provider.h
index 5174176..9ceb0012 100644
--- a/cc/raster/bitmap_raster_buffer_provider.h
+++ b/cc/raster/bitmap_raster_buffer_provider.h
@@ -43,6 +43,7 @@
       const base::Closure& callback,
       uint64_t pending_callback_id) const override;
   void Shutdown() override;
+  bool CheckRasterFinishedQueries() override;
 
  private:
   std::unique_ptr<base::trace_event::ConvertableToTraceFormat> StateAsValue()
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index 6f3c7602..9c516e3 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -154,6 +154,7 @@
   float recording_to_raster_scale =
       transform.scale() / raster_source->recording_scale_factor();
   gfx::Size content_size = raster_source->GetContentSize(transform.scale());
+
   // TODO(enne): could skip the clear on new textures, as the service side has
   // to do that anyway.  resource_has_previous_content implies that the texture
   // is not new, but the reverse does not hold, so more plumbing is needed.
@@ -481,23 +482,36 @@
         100.0f * fraction_saved);
   }
 
-  if (enable_oop_rasterization_) {
-    RasterizeSourceOOP(raster_source, resource_has_previous_content, mailbox,
-                       sync_token, texture_target, texture_is_overlay_candidate,
-                       resource_size, resource_format, color_space,
-                       raster_full_rect, playback_rect, transform,
-                       playback_settings, worker_context_provider_,
-                       msaa_sample_count_);
-  } else {
-    RasterizeSource(
-        raster_source, resource_has_previous_content, mailbox, sync_token,
-        texture_target, texture_is_overlay_candidate, resource_size,
-        resource_format, color_space, raster_full_rect, playback_rect,
-        transform, playback_settings, worker_context_provider_,
-        msaa_sample_count_,
-        ShouldUnpremultiplyAndDitherResource(resource_format), max_tile_size_);
+  // Use a query to time the GPU side work for rasterizing this tile.
+  pending_raster_queries_.emplace_back();
+  auto& query = pending_raster_queries_.back();
+  ri->GenQueriesEXT(1, &query.query_id);
+  ri->BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, query.query_id);
+
+  {
+    base::ElapsedTimer timer;
+    if (enable_oop_rasterization_) {
+      RasterizeSourceOOP(raster_source, resource_has_previous_content, mailbox,
+                         sync_token, texture_target,
+                         texture_is_overlay_candidate, resource_size,
+                         resource_format, color_space, raster_full_rect,
+                         playback_rect, transform, playback_settings,
+                         worker_context_provider_, msaa_sample_count_);
+    } else {
+      RasterizeSource(raster_source, resource_has_previous_content, mailbox,
+                      sync_token, texture_target, texture_is_overlay_candidate,
+                      resource_size, resource_format, color_space,
+                      raster_full_rect, playback_rect, transform,
+                      playback_settings, worker_context_provider_,
+                      msaa_sample_count_,
+                      ShouldUnpremultiplyAndDitherResource(resource_format),
+                      max_tile_size_);
+    }
+    query.worker_duration = timer.Elapsed();
   }
 
+  ri->EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
+
   // Generate sync token for cross context synchronization.
   return viz::ClientResourceProvider::GenerateSyncTokenHelper(ri);
 }
@@ -512,4 +526,52 @@
   }
 }
 
+#define UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS(name, total_time) \
+  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(                              \
+      name, total_time, base::TimeDelta::FromMicroseconds(1),           \
+      base::TimeDelta::FromMilliseconds(100), 100);
+
+bool GpuRasterBufferProvider::CheckRasterFinishedQueries() {
+  viz::RasterContextProvider::ScopedRasterContextLock scoped_context(
+      worker_context_provider_);
+  auto* ri = scoped_context.RasterInterface();
+
+  auto it = pending_raster_queries_.begin();
+  while (it != pending_raster_queries_.end()) {
+    GLuint complete = 1;
+    ri->GetQueryObjectuivEXT(it->query_id,
+                             GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT,
+                             &complete);
+    if (!complete)
+      break;
+
+    GLuint gpu_duration = 0u;
+    ri->GetQueryObjectuivEXT(it->query_id, GL_QUERY_RESULT_EXT, &gpu_duration);
+    ri->DeleteQueriesEXT(1, &it->query_id);
+
+    base::TimeDelta total_time =
+        it->worker_duration + base::TimeDelta::FromMicroseconds(gpu_duration);
+
+    // It is safe to use the UMA macros here with runtime generated strings
+    // because the client name should be initialized once in the process, before
+    // recording any metrics here.
+    const char* client_name = GetClientNameForMetrics();
+    if (enable_oop_rasterization_) {
+      UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS(
+          base::StringPrintf("Renderer4.%s.RasterTaskTotalDuration.Oop",
+                             client_name),
+          total_time);
+    } else {
+      UMA_HISTOGRAM_RASTER_TIME_CUSTOM_MICROSECONDS(
+          base::StringPrintf("Renderer4.%s.RasterTaskTotalDuration.Gpu",
+                             client_name),
+          total_time);
+    }
+
+    it = pending_raster_queries_.erase(it);
+  }
+
+  return pending_raster_queries_.size() > 0u;
+}
+
 }  // namespace cc
diff --git a/cc/raster/gpu_raster_buffer_provider.h b/cc/raster/gpu_raster_buffer_provider.h
index 44bc6249..2012687 100644
--- a/cc/raster/gpu_raster_buffer_provider.h
+++ b/cc/raster/gpu_raster_buffer_provider.h
@@ -11,6 +11,12 @@
 #include "cc/raster/raster_buffer_provider.h"
 #include "gpu/command_buffer/common/sync_token.h"
 
+namespace gpu {
+namespace raster {
+class RasterInterface;
+}  // namespace raster
+}  // namespace gpu
+
 namespace viz {
 class ContextProvider;
 class RasterContextProvider;
@@ -47,6 +53,7 @@
       const base::Closure& callback,
       uint64_t pending_callback_id) const override;
   void Shutdown() override;
+  bool CheckRasterFinishedQueries() override;
 
   gpu::SyncToken PlaybackOnWorkerThread(
       gpu::Mailbox* mailbox,
@@ -118,6 +125,16 @@
   const bool unpremultiply_and_dither_low_bit_depth_tiles_;
   const bool enable_oop_rasterization_;
 
+  struct PendingRasterQuery {
+    // The id for querying the duration in executing the GPU side work.
+    GLuint query_id = 0u;
+
+    // The duration for executing the work on the raster worker thread.
+    base::TimeDelta worker_duration;
+  };
+  // This should only be accessed with the context lock acquired.
+  base::circular_deque<PendingRasterQuery> pending_raster_queries_;
+
   DISALLOW_COPY_AND_ASSIGN(GpuRasterBufferProvider);
 };
 
diff --git a/cc/raster/one_copy_raster_buffer_provider.cc b/cc/raster/one_copy_raster_buffer_provider.cc
index 5764e17..562d2e2 100644
--- a/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/cc/raster/one_copy_raster_buffer_provider.cc
@@ -497,4 +497,8 @@
              : gfx::BufferUsage::GPU_READ_CPU_READ_WRITE;
 }
 
+bool OneCopyRasterBufferProvider::CheckRasterFinishedQueries() {
+  return false;
+}
+
 }  // namespace cc
diff --git a/cc/raster/one_copy_raster_buffer_provider.h b/cc/raster/one_copy_raster_buffer_provider.h
index 98bb90e..6766cae7 100644
--- a/cc/raster/one_copy_raster_buffer_provider.h
+++ b/cc/raster/one_copy_raster_buffer_provider.h
@@ -58,6 +58,7 @@
       const base::Closure& callback,
       uint64_t pending_callback_id) const override;
   void Shutdown() override;
+  bool CheckRasterFinishedQueries() override;
 
   // Playback raster source and copy result into |resource|.
   gpu::SyncToken PlaybackAndCopyOnWorkerThread(
diff --git a/cc/raster/raster_buffer_provider.h b/cc/raster/raster_buffer_provider.h
index 3e2b1aae..74354c7 100644
--- a/cc/raster/raster_buffer_provider.h
+++ b/cc/raster/raster_buffer_provider.h
@@ -88,6 +88,14 @@
 
   // Shutdown for doing cleanup.
   virtual void Shutdown() = 0;
+
+  // Checks whether GPU side queries issued for previous raster work have been
+  // finished. Note that this will acquire the worker context lock so it can be
+  // used from any thread. But usage from the compositor thread should be
+  // avoided to prevent contention with worker threads.
+  // Returns true if there are pending queries that could not be completed in
+  // this check.
+  virtual bool CheckRasterFinishedQueries() = 0;
 };
 
 }  // namespace cc
diff --git a/cc/raster/raster_buffer_provider_unittest.cc b/cc/raster/raster_buffer_provider_unittest.cc
index cbe334d..b18343a 100644
--- a/cc/raster/raster_buffer_provider_unittest.cc
+++ b/cc/raster/raster_buffer_provider_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "cc/base/unique_notifier.h"
 #include "cc/paint/draw_image.h"
@@ -487,6 +488,37 @@
   EXPECT_FALSE(completed_tasks()[1].canceled);
 }
 
+TEST_P(RasterBufferProviderTest, MeasureGpuRasterDuration) {
+  if (GetParam() != RASTER_BUFFER_PROVIDER_TYPE_GPU)
+    return;
+
+  // Schedule a task.
+  AppendTask(0u);
+  ScheduleTasks();
+  RunMessageLoopUntilAllTasksHaveCompleted();
+
+  // Wait for the GPU side work to finish.
+  base::RunLoop run_loop;
+  std::vector<const ResourcePool::InUsePoolResource*> array;
+  for (const auto& resource : resources_)
+    array.push_back(&resource);
+  uint64_t callback_id = raster_buffer_provider_->SetReadyToDrawCallback(
+      array,
+      base::Bind([](base::RunLoop* run_loop) { run_loop->Quit(); }, &run_loop),
+      0);
+  ASSERT_TRUE(callback_id);
+  run_loop.Run();
+
+  // Poll the task and make sure a histogram is logged.
+  base::HistogramTester histogram_tester;
+  std::string histogram("Renderer4.Renderer.RasterTaskTotalDuration.Gpu");
+  histogram_tester.ExpectTotalCount(histogram, 0);
+  bool has_pending_queries =
+      raster_buffer_provider_->CheckRasterFinishedQueries();
+  EXPECT_FALSE(has_pending_queries);
+  histogram_tester.ExpectTotalCount(histogram, 1);
+}
+
 INSTANTIATE_TEST_CASE_P(
     RasterBufferProviderTests,
     RasterBufferProviderTest,
diff --git a/cc/raster/zero_copy_raster_buffer_provider.cc b/cc/raster/zero_copy_raster_buffer_provider.cc
index 037e8a36..21010d7 100644
--- a/cc/raster/zero_copy_raster_buffer_provider.cc
+++ b/cc/raster/zero_copy_raster_buffer_provider.cc
@@ -271,4 +271,8 @@
 
 void ZeroCopyRasterBufferProvider::Shutdown() {}
 
+bool ZeroCopyRasterBufferProvider::CheckRasterFinishedQueries() {
+  return false;
+}
+
 }  // namespace cc
diff --git a/cc/raster/zero_copy_raster_buffer_provider.h b/cc/raster/zero_copy_raster_buffer_provider.h
index 68f78bf..106f7c9 100644
--- a/cc/raster/zero_copy_raster_buffer_provider.h
+++ b/cc/raster/zero_copy_raster_buffer_provider.h
@@ -49,6 +49,7 @@
       const base::Closure& callback,
       uint64_t pending_callback_id) const override;
   void Shutdown() override;
+  bool CheckRasterFinishedQueries() override;
 
  private:
   std::unique_ptr<base::trace_event::ConvertableToTraceFormat> StateAsValue()
diff --git a/cc/test/cc_test_suite.cc b/cc/test/cc_test_suite.cc
index ee7ae53f..53f2531 100644
--- a/cc/test/cc_test_suite.cc
+++ b/cc/test/cc_test_suite.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/message_loop/message_loop.h"
 #include "base/threading/thread_id_name_manager.h"
+#include "cc/base/histograms.h"
 #include "components/viz/test/paths.h"
 #include "gpu/config/gpu_info_collector.h"
 #include "gpu/config/gpu_preferences.h"
@@ -44,6 +45,8 @@
   base::ThreadIdNameManager::GetInstance()->SetName("Main");
 
   base::DiscardableMemoryAllocator::SetInstance(&discardable_memory_allocator_);
+
+  SetClientNameForMetrics("Renderer");
 }
 
 void CCTestSuite::Shutdown() {
diff --git a/cc/test/fake_raster_buffer_provider.cc b/cc/test/fake_raster_buffer_provider.cc
index a203f47..9b91296 100644
--- a/cc/test/fake_raster_buffer_provider.cc
+++ b/cc/test/fake_raster_buffer_provider.cc
@@ -65,4 +65,8 @@
 
 void FakeRasterBufferProviderImpl::Shutdown() {}
 
+bool FakeRasterBufferProviderImpl::CheckRasterFinishedQueries() {
+  return false;
+}
+
 }  // namespace cc
diff --git a/cc/test/fake_raster_buffer_provider.h b/cc/test/fake_raster_buffer_provider.h
index b7e8e8d..a93e9c8 100644
--- a/cc/test/fake_raster_buffer_provider.h
+++ b/cc/test/fake_raster_buffer_provider.h
@@ -32,6 +32,7 @@
       const base::Callback<void()>& callback,
       uint64_t pending_callback_id) const override;
   void Shutdown() override;
+  bool CheckRasterFinishedQueries() override;
 };
 
 }  // namespace cc
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 7dc254a..8f07672 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -326,6 +326,36 @@
   DISALLOW_COPY_AND_ASSIGN(TaskSetFinishedTaskImpl);
 };
 
+class DidFinishRunningAllTilesTask : public TileTask {
+ public:
+  using CompletionCb = base::OnceCallback<void(bool has_pending_queries)>;
+  DidFinishRunningAllTilesTask(base::SequencedTaskRunner* task_runner,
+                               RasterBufferProvider* raster_buffer_provider,
+                               CompletionCb completion_cb)
+      : TileTask(false /* supports_concurrent_execution */),
+        task_runner_(task_runner),
+        raster_buffer_provider_(raster_buffer_provider),
+        completion_cb_(std::move(completion_cb)) {}
+
+  void RunOnWorkerThread() override {
+    TRACE_EVENT0("cc", "TaskSetFinishedTaskImpl::RunOnWorkerThread");
+    bool has_pending_queries =
+        raster_buffer_provider_->CheckRasterFinishedQueries();
+    task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(completion_cb_),
+                                                     has_pending_queries));
+  }
+
+  void OnTaskCompleted() override {}
+
+ protected:
+  ~DidFinishRunningAllTilesTask() override = default;
+
+ private:
+  base::SequencedTaskRunner* task_runner_;
+  RasterBufferProvider* raster_buffer_provider_;
+  CompletionCb completion_cb_;
+};
+
 }  // namespace
 
 RasterTaskCompletionStats::RasterTaskCompletionStats()
@@ -402,6 +432,7 @@
   signals_check_notifier_.Cancel();
   task_set_finished_weak_ptr_factory_.InvalidateWeakPtrs();
   ready_to_draw_callback_weak_ptr_factory_.InvalidateWeakPtrs();
+  check_pending_tile_queries_callback_.Cancel();
   raster_buffer_provider_ = nullptr;
 
   // Ask the tracker to drop any locked decodes since we will be destroying the
@@ -457,13 +488,14 @@
   signals_check_notifier_.Schedule();
 }
 
-void TileManager::DidFinishRunningAllTileTasks() {
+void TileManager::DidFinishRunningAllTileTasks(bool has_pending_queries) {
   TRACE_EVENT0("cc", "TileManager::DidFinishRunningAllTileTasks");
   TRACE_EVENT_ASYNC_END0("cc", "ScheduledTasks", this);
   DCHECK(resource_pool_);
   DCHECK(tile_task_manager_);
 
   has_scheduled_tile_tasks_ = false;
+  has_pending_queries_ = has_pending_queries;
 
   if (all_tiles_that_need_to_be_rasterized_are_scheduled_ &&
       !resource_pool_->ResourceUsageTooHigh()) {
@@ -958,8 +990,13 @@
   scoped_refptr<TileTask> required_for_draw_done_task =
       CreateTaskSetFinishedTask(
           &TileManager::DidFinishRunningTileTasksRequiredForDraw);
+
+  auto all_done_cb =
+      base::BindOnce(&TileManager::DidFinishRunningAllTileTasks,
+                     task_set_finished_weak_ptr_factory_.GetWeakPtr());
   scoped_refptr<TileTask> all_done_task =
-      CreateTaskSetFinishedTask(&TileManager::DidFinishRunningAllTileTasks);
+      base::MakeRefCounted<DidFinishRunningAllTilesTask>(
+          task_runner_, raster_buffer_provider_, std::move(all_done_cb));
 
   // Build a new task queue containing all task currently needed. Tasks
   // are added in order of priority, highest priority task first.
@@ -1350,6 +1387,34 @@
              RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
 }
 
+void TileManager::ScheduleCheckRasterFinishedQueries() {
+  DCHECK(has_pending_queries_);
+
+  if (!check_pending_tile_queries_callback_.IsCancelled())
+    return;
+
+  check_pending_tile_queries_callback_.Reset(base::Bind(
+      &TileManager::CheckRasterFinishedQueries, base::Unretained(this)));
+  task_runner_->PostDelayedTask(FROM_HERE,
+                                check_pending_tile_queries_callback_.callback(),
+                                base::TimeDelta::FromMilliseconds(100));
+}
+
+void TileManager::CheckRasterFinishedQueries() {
+  check_pending_tile_queries_callback_.Cancel();
+
+  if (!has_pending_queries_)
+    return;
+
+  // Raster tasks are in progress. The queries will be polled once they finish.
+  if (has_scheduled_tile_tasks_ || !signals_.all_tile_tasks_completed)
+    return;
+
+  has_pending_queries_ = raster_buffer_provider_->CheckRasterFinishedQueries();
+  if (has_pending_queries_)
+    ScheduleCheckRasterFinishedQueries();
+}
+
 void TileManager::FlushAndIssueSignals() {
   TRACE_EVENT0("cc", "TileManager::FlushAndIssueSignals");
   tile_task_manager_->CheckForCompletedTasks();
@@ -1389,6 +1454,10 @@
     if (!has_scheduled_tile_tasks_) {
       TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
                    "TileManager::IssueSignals - all tile tasks completed");
+
+      if (has_pending_queries_)
+        ScheduleCheckRasterFinishedQueries();
+
       signals_.did_notify_all_tile_tasks_completed = true;
       client_->NotifyAllTileTasksCompleted();
     }
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index 469b21f..e475cdf 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -369,7 +369,7 @@
 
   void DidFinishRunningTileTasksRequiredForActivation();
   void DidFinishRunningTileTasksRequiredForDraw();
-  void DidFinishRunningAllTileTasks();
+  void DidFinishRunningAllTileTasks(bool has_pending_queries);
 
   scoped_refptr<TileTask> CreateTaskSetFinishedTask(
       void (TileManager::*callback)());
@@ -397,6 +397,8 @@
   void FlushAndIssueSignals();
   void CheckPendingGpuWorkAndIssueSignals();
   void IssueSignals();
+  void ScheduleCheckRasterFinishedQueries();
+  void CheckRasterFinishedQueries();
 
   TileManagerClient* client_;
   base::SequencedTaskRunner* task_runner_;
@@ -452,6 +454,11 @@
 
   GURL active_url_;
 
+  // The callback scheduled to poll whether the GPU side work for pending tiles
+  // has completed.
+  bool has_pending_queries_ = false;
+  base::CancelableClosure check_pending_tile_queries_callback_;
+
   // We need two WeakPtrFactory objects as the invalidation pattern of each is
   // different. The |task_set_finished_weak_ptr_factory_| is invalidated any
   // time new tasks are scheduled, preventing a race when the callback has
diff --git a/cc/tiles/tile_manager_unittest.cc b/cc/tiles/tile_manager_unittest.cc
index 1925b06..6c7fe877 100644
--- a/cc/tiles/tile_manager_unittest.cc
+++ b/cc/tiles/tile_manager_unittest.cc
@@ -3344,5 +3344,34 @@
                     .NumLockedImagesForTesting());
 }
 
+class TileManagerCheckRasterQueriesTest : public TileManagerTest {
+ public:
+  void SetUp() override {
+    TileManagerTest::SetUp();
+    host_impl()->tile_manager()->SetRasterBufferProviderForTesting(
+        &raster_buffer_provider_);
+  }
+
+ protected:
+  class MockRasterBufferProvider : public FakeRasterBufferProviderImpl {
+   public:
+    MOCK_METHOD0(CheckRasterFinishedQueries, bool());
+  };
+
+  MockRasterBufferProvider raster_buffer_provider_;
+};
+
+TEST_F(TileManagerCheckRasterQueriesTest,
+       ChecksRasterQueriesInAllTilesDoneTask) {
+  base::RunLoop run_loop;
+  EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
+  EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
+      .WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
+  EXPECT_CALL(raster_buffer_provider_, CheckRasterFinishedQueries()).Times(1);
+  host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
+  EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
+  run_loop.Run();
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index a3a93bbb..4b30c972 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -376,22 +376,14 @@
   // Dump property trees and layers if run with:
   //   --vmodule=layer_tree_host=3
   if (VLOG_IS_ON(3)) {
-    VLOG(3) << "After finishing commit on impl, the sync tree:";
-    // Because the property tree and layer list output can be verbose, the VLOG
-    // output is split by line to avoid line buffer limits on android.
-    VLOG(3) << "property trees:";
     std::string property_trees;
     base::JSONWriter::WriteWithOptions(
         *sync_tree->property_trees()->AsTracedValue()->ToBaseValue(),
         base::JSONWriter::OPTIONS_PRETTY_PRINT, &property_trees);
-    std::stringstream property_trees_stream(property_trees);
-    for (std::string line; std::getline(property_trees_stream, line);)
-      VLOG(3) << line;
-
-    VLOG(3) << "layers:";
-    std::stringstream layers_stream(host_impl->LayerListAsJson());
-    for (std::string line; std::getline(layers_stream, line);)
-      VLOG(3) << line;
+    VLOG(3) << "After finishing commit on impl, the sync tree:"
+            << "\nproperty_trees:\n"
+            << property_trees << "\nlayers:\n"
+            << host_impl->LayerListAsJson();
   }
 }
 
@@ -813,33 +805,27 @@
   //   --vmodule=layer_tree_host=3
   // This only prints output for the renderer.
   if (VLOG_IS_ON(3) && GetClientNameForMetrics() == std::string("Renderer")) {
-    VLOG(3) << "After updating layers on the main thread:";
-    // Because the property tree and layer list output can be verbose, the VLOG
-    // output is split by line to avoid line buffer limits on android.
-    VLOG(3) << "property trees:";
     std::string property_trees;
     base::JSONWriter::WriteWithOptions(
         *property_trees_.AsTracedValue()->ToBaseValue(),
         base::JSONWriter::OPTIONS_PRETTY_PRINT, &property_trees);
-    std::stringstream property_trees_stream(property_trees);
-    for (std::string line; std::getline(property_trees_stream, line);)
-      VLOG(3) << line;
-
-    VLOG(3) << "layers:";
+    std::ostringstream layers;
     for (auto* layer : *this) {
-      VLOG(3) << "  layer id " << layer->id();
-      VLOG(3) << "    element_id: " << layer->element_id();
-      VLOG(3) << "    bounds: " << layer->bounds().ToString();
-      VLOG(3) << "    opacity: " << layer->opacity();
-      VLOG(3) << "    position: " << layer->position().ToString();
-      VLOG(3) << "    draws_content: " << layer->DrawsContent();
-      VLOG(3) << "    scrollable: " << layer->scrollable();
-      VLOG(3) << "    contents_opaque: " << layer->contents_opaque();
-      VLOG(3) << "    transform_tree_index: " << layer->transform_tree_index();
-      VLOG(3) << "    clip_tree_index: " << layer->clip_tree_index();
-      VLOG(3) << "    effect_tree_index: " << layer->effect_tree_index();
-      VLOG(3) << "    scroll_tree_index: " << layer->scroll_tree_index();
+      layers << "\n  layer id " << layer->id();
+      layers << "\n    element_id: " << layer->element_id();
+      layers << "\n    bounds: " << layer->bounds().ToString();
+      layers << "\n    opacity: " << layer->opacity();
+      layers << "\n    position: " << layer->position().ToString();
+      layers << "\n    draws_content: " << layer->DrawsContent();
+      layers << "\n    scrollable: " << layer->scrollable();
+      layers << "\n    contents_opaque: " << layer->contents_opaque();
+      layers << "\n    transform_tree_index: " << layer->transform_tree_index();
+      layers << "\n    clip_tree_index: " << layer->clip_tree_index();
+      layers << "\n    effect_tree_index: " << layer->effect_tree_index();
+      layers << "\n    scroll_tree_index: " << layer->scroll_tree_index();
     }
+    VLOG(3) << "After updating layers on the main thread:\nproperty trees:\n"
+            << property_trees << "\nlayers:" << layers.str();
   }
 
   bool painted_content_has_slow_paths = false;
diff --git a/cc/trees/layer_tree_mutator.cc b/cc/trees/layer_tree_mutator.cc
index 845ed7d..491eec5 100644
--- a/cc/trees/layer_tree_mutator.cc
+++ b/cc/trees/layer_tree_mutator.cc
@@ -12,11 +12,13 @@
     WorkletAnimationId worklet_animation_id,
     std::string name,
     double current_time,
-    std::unique_ptr<AnimationOptions> options)
+    std::unique_ptr<AnimationOptions> options,
+    int num_effects)
     : worklet_animation_id(worklet_animation_id),
       name(name),
       current_time(current_time),
-      options(std::move(options)) {}
+      options(std::move(options)),
+      num_effects(num_effects) {}
 AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState(
     AddAndUpdateState&&) = default;
 AnimationWorkletInput::AddAndUpdateState::~AddAndUpdateState() = default;
@@ -97,11 +99,10 @@
 AnimationWorkletOutput::AnimationWorkletOutput() = default;
 AnimationWorkletOutput::~AnimationWorkletOutput() = default;
 
-AnimationWorkletOutput::AnimationState::AnimationState(
-    WorkletAnimationId id,
-    base::Optional<base::TimeDelta> time)
-    : worklet_animation_id(id), local_time(time) {}
+AnimationWorkletOutput::AnimationState::AnimationState(WorkletAnimationId id)
+    : worklet_animation_id(id) {}
 AnimationWorkletOutput::AnimationState::AnimationState(const AnimationState&) =
     default;
+AnimationWorkletOutput::AnimationState::~AnimationState() = default;
 
 }  // namespace cc
diff --git a/cc/trees/layer_tree_mutator.h b/cc/trees/layer_tree_mutator.h
index dd702c4..42cc3177 100644
--- a/cc/trees/layer_tree_mutator.h
+++ b/cc/trees/layer_tree_mutator.h
@@ -40,11 +40,13 @@
     // Worklet animation's current time, from its associated timeline.
     double current_time;
     std::unique_ptr<AnimationOptions> options;
+    int num_effects;
 
     AddAndUpdateState(WorkletAnimationId worklet_animation_id,
                       std::string name,
                       double current_time,
-                      std::unique_ptr<AnimationOptions> options);
+                      std::unique_ptr<AnimationOptions> options,
+                      int num_effects);
 
     AddAndUpdateState(AddAndUpdateState&&);
     ~AddAndUpdateState();
@@ -109,16 +111,12 @@
 
 struct CC_EXPORT AnimationWorkletOutput {
   struct CC_EXPORT AnimationState {
-    AnimationState(WorkletAnimationId,
-                   base::Optional<base::TimeDelta> local_time);
+    explicit AnimationState(WorkletAnimationId);
     AnimationState(const AnimationState&);
+    ~AnimationState();
 
     WorkletAnimationId worklet_animation_id;
-    // The animator effect's local time.
-    // TODO(majidvp): This assumes each animator has a single output effect
-    // which does not hold once we state support group effects.
-    // http://crbug.com/767043
-    base::Optional<base::TimeDelta> local_time;
+    std::vector<base::Optional<base::TimeDelta>> local_times;
   };
 
   AnimationWorkletOutput();
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index d61e686b..b413506 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1547,10 +1547,14 @@
   use_chromium_linker = false
   uncompress_shared_libraries = true
 
-  _native_lib_file =
-      rebase_path("$root_gen_dir/CHROME_VERSION.json", root_out_dir)
-  native_lib_version_arg = "@FileArg($_native_lib_file:full-quoted)"
-  native_lib_version_rule = "//build/util:chrome_version_json"
+  # Only try to generate the native library version in configurations that
+  # include a native library.
+  if (!android_64bit_target_cpu || build_apk_secondary_abi) {
+    _native_lib_file =
+        rebase_path("$root_gen_dir/CHROME_VERSION.json", root_out_dir)
+    native_lib_version_arg = "@FileArg($_native_lib_file:full-quoted)"
+    native_lib_version_rule = "//build/util:chrome_version_json"
+  }
 
   if (android_64bit_target_cpu) {
     # Include a 64-bit placeholder library to ensure that the library is treated
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index 5faf2c6..299fae0 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -425,7 +425,7 @@
      *                   the FeedProcessScope will be reset.
      */
     @VisibleForTesting
-    public static void setInTestMode(boolean inTestMode) {
+    public static void setInTestMode(boolean inTestMode, TestNetworkClient networkClient) {
         if (inTestMode) {
             FeedScheduler feedScheduler = new TestFeedScheduler();
             FeedAppLifecycleListener lifecycleListener =
@@ -433,9 +433,8 @@
             Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
             FeedAppLifecycle feedAppLifecycle = new FeedAppLifecycle(
                     lifecycleListener, new FeedLifecycleBridge(profile), feedScheduler);
-            FeedProcessScopeFactory.createFeedProcessScopeForTesting(feedScheduler,
-                    new TestNetworkClient(), new TestFeedOfflineIndicator(), feedAppLifecycle,
-                    lifecycleListener);
+            FeedProcessScopeFactory.createFeedProcessScopeForTesting(feedScheduler, networkClient,
+                    new TestFeedOfflineIndicator(), feedAppLifecycle, lifecycleListener);
         } else {
             FeedProcessScopeFactory.clearFeedProcessScopeForTesting();
         }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
index cd843ce1..3d304607f 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -33,7 +33,6 @@
     private static FeedProcessScope sFeedProcessScope;
     private static FeedScheduler sFeedScheduler;
     private static FeedOfflineIndicator sFeedOfflineIndicator;
-    private static NetworkClient sTestNetworkClient;
     private static FeedLoggingBridge sFeedLoggingBridge;
 
     /** @return The shared {@link FeedProcessScope} instance. Null if the Feed is disabled. */
@@ -111,17 +110,16 @@
                 new FeedAppLifecycleListener(new ThreadUtils());
         FeedContentStorage contentStorage = new FeedContentStorage(profile);
         FeedJournalStorage journalStorage = new FeedJournalStorage(profile);
-        NetworkClient networkClient = sTestNetworkClient == null ?
-            new FeedNetworkBridge(profile) : sTestNetworkClient;
         sFeedLoggingBridge = new FeedLoggingBridge(profile);
-        sFeedProcessScope = new FeedProcessScope
-                                    .Builder(configHostApi, Executors.newSingleThreadExecutor(),
-                                            new LoggingApiImpl(), networkClient, schedulerBridge,
-                                            lifecycleListener, DebugBehavior.SILENT,
-                                            ContextUtils.getApplicationContext(), applicationInfo)
-                                    .setContentStorage(contentStorage)
-                                    .setJournalStorage(journalStorage)
-                                    .build();
+        sFeedProcessScope =
+                new FeedProcessScope
+                        .Builder(configHostApi, Executors.newSingleThreadExecutor(),
+                                new LoggingApiImpl(), new FeedNetworkBridge(profile),
+                                schedulerBridge, lifecycleListener, DebugBehavior.SILENT,
+                                ContextUtils.getApplicationContext(), applicationInfo)
+                        .setContentStorage(contentStorage)
+                        .setJournalStorage(journalStorage)
+                        .build();
         schedulerBridge.initializeFeedDependencies(
                 sFeedProcessScope.getRequestManager(), sFeedProcessScope.getSessionManager());
 
@@ -159,19 +157,6 @@
         sFeedAppLifecycle = feedAppLifecycle;
     }
 
-    /** Use supplied NetworkClient instead of real one, for tests. */
-    @VisibleForTesting
-    public static void setTestNetworkClient(NetworkClient client) {
-        if (client == null) {
-            sTestNetworkClient = null;
-        } else if (sFeedProcessScope == null) {
-            sTestNetworkClient = client;
-        } else {
-            throw(new IllegalStateException(
-                    "TestNetworkClient can not be set after FeedProcessScope has initialized."));
-        }
-    }
-
     /** Resets the FeedProcessScope after testing is complete. */
     @VisibleForTesting
     static void clearFeedProcessScopeForTesting() {
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/TestNetworkClient.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/TestNetworkClient.java
index cc71bd9..56e1a9dd 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/TestNetworkClient.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/TestNetworkClient.java
@@ -59,6 +59,7 @@
             mMockServer = MockServer.getDefaultInstance();
         } else {
             mMockServer = MockServer.parseFrom(fs);
+            fs.close();
         }
     }
 
diff --git a/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
index a1177a9..535f5ace 100644
--- a/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
+++ b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
@@ -76,19 +76,10 @@
                 android:contentDescription="@string/close" />
         </LinearLayout>
 
-        <org.chromium.chrome.browser.widget.MaterialProgressBar
-            android:id="@+id/progress_bar"
-            android:layout_width="match_parent"
-            android:layout_height="4dp"
-            app:colorBackground="@color/modern_grey_300"
-            app:colorProgress="@color/modern_blue_600"
-            android:visibility="invisible" />
-
         <RelativeLayout
             android:id="@+id/details"
             android:layout_width="match_parent"
             android:layout_height="@dimen/autofill_assistant_details_image_size"
-            android:layout_marginTop="8dp"
             android:layout_marginStart="24dp"
             android:layout_marginEnd="24dp"
             android:layout_marginBottom="8dp"
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 2f38f8b..bef70fe1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -270,6 +270,7 @@
     public static final String PWA_PERSISTENT_NOTIFICATION = "PwaPersistentNotification";
     public static final String READER_MODE_IN_CCT = "ReaderModeInCCT";
     public static final String REMOVE_NAVIGATION_HISTORY = "RemoveNavigationHistory";
+    public static final String SERVICE_MANAGER_FOR_DOWNLOAD = "ServiceManagerForDownload";
     public static final String SERVICE_WORKER_PAYMENT_APPS = "ServiceWorkerPaymentApps";
     public static final String SHOW_TRUSTED_PUBLISHER_URL = "ShowTrustedPublisherURL";
     public static final String SIMPLIFIED_NTP = "SimplifiedNTP";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index c9e9d75..3bfd415 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -237,16 +237,6 @@
                 new AutofillAssistantUiDelegate.Details(title, url, date, description));
     }
 
-    @CalledByNative
-    private void onShowProgressBar(int progress, String message) {
-        mUiDelegate.showProgressBar(progress, message);
-    }
-
-    @CalledByNative
-    private void onHideProgressBar() {
-        mUiDelegate.hideProgressBar();
-    }
-
     // native methods.
     private native long nativeInit(
             WebContents webContents, String[] parameterNames, String[] parameterValues);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
index 9e949dc..c996577 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
@@ -28,7 +28,6 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.widget.MaterialProgressBar;
 
 import java.io.InputStream;
 import java.net.URL;
@@ -56,7 +55,6 @@
     private final LinearLayout mBottomBar;
     private final ViewGroup mChipsViewContainer;
     private final TextView mStatusMessageView;
-    private final MaterialProgressBar mProgressBar;
 
     private final ViewGroup mDetails;
     private final AppCompatImageView mDetailsImage;
@@ -191,7 +189,6 @@
                                 FEEDBACK_CATEGORY_TAG));
         mChipsViewContainer = mBottomBar.findViewById(R.id.carousel);
         mStatusMessageView = mBottomBar.findViewById(R.id.status_message);
-        mProgressBar = mBottomBar.findViewById(R.id.progress_bar);
 
         mDetails = (ViewGroup) mBottomBar.findViewById(R.id.details);
         mDetailsImage = (AppCompatImageView) mDetails.findViewById(R.id.details_image);
@@ -332,21 +329,6 @@
         return roundedBitmap;
     }
 
-    public void showProgressBar(int progress, String message) {
-        ensureFullContainerIsShown();
-        // TODO(crbug.com/806868): Animate the progress change.
-        mProgressBar.setProgress(progress);
-        mProgressBar.setVisibility(View.VISIBLE);
-
-        if (!message.isEmpty()) {
-            mStatusMessageView.setText(message);
-        }
-    }
-
-    public void hideProgressBar() {
-        mProgressBar.setVisibility(View.INVISIBLE);
-    }
-
     /**
      * Shuts down the Autofill Assistant. The UI disappears and any associated state goes away.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
index 6edf424..1c08137a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
@@ -33,10 +33,12 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.chrome.browser.ChromeApplication;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorNotificationBridgeUiFactory;
 import org.chromium.chrome.browser.init.BrowserParts;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.EmptyBrowserParts;
+import org.chromium.chrome.browser.init.ServiceManagerStartupUtils;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
@@ -179,9 +181,9 @@
 
             @Override
             public boolean startServiceManagerOnly() {
-                // TODO(qinmin): change this to return true once ServiceManager can be started
-                // without launching full browser.
-                return false;
+                return ServiceManagerStartupUtils.canStartServiceManager(
+                               ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD)
+                        && !ACTION_DOWNLOAD_OPEN.equals(intent.getAction());
             }
         };
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 22c6d2a..6d92e63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -46,10 +46,12 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.ChromeApplication;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorNotificationBridgeUiFactory;
 import org.chromium.chrome.browser.init.BrowserParts;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.EmptyBrowserParts;
+import org.chromium.chrome.browser.init.ServiceManagerStartupUtils;
 import org.chromium.chrome.browser.media.MediaViewerUtils;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
@@ -1249,6 +1251,13 @@
                                 ? entry.notificationId
                                 : -1);
             }
+
+            @Override
+            public boolean startServiceManagerOnly() {
+                return ServiceManagerStartupUtils.canStartServiceManager(
+                               ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD)
+                        && !ACTION_DOWNLOAD_OPEN.equals(intent.getAction());
+            }
         };
         try {
             ChromeBrowserInitializer.getInstance(ContextUtils.getApplicationContext())
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageSummaryProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageSummaryProvider.java
index dcd89797..f657ce3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageSummaryProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageSummaryProvider.java
@@ -96,7 +96,7 @@
     public void onItemUpdated(OfflineItem oldItem, OfflineItem item) {
         // Computes the delta of storage used by downloads.
         mTotalDownloadSize -= oldItem.receivedBytes;
-        mTotalDownloadSize += oldItem.receivedBytes;
+        mTotalDownloadSize += item.receivedBytes;
 
         if (item.state != OfflineItemState.IN_PROGRESS) update();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
index 729afb6..01f35103 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
@@ -123,7 +123,8 @@
         }
         if (key == ExploreSitesPage.SCROLL_TO_CATEGORY_KEY) {
             int pos = mCategoryModel.get(ExploreSitesPage.SCROLL_TO_CATEGORY_KEY);
-            mLayoutManager.scrollToPosition(pos);
+            // Add 1 for title.
+            mLayoutManager.scrollToPosition(pos + 1);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
index e09339f3..9045074 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
@@ -40,6 +40,7 @@
 public class ExploreSitesPage extends BasicNativePage {
     private static final String TAG = "ExploreSitesPage";
     private static final String CONTEXT_MENU_USER_ACTION_PREFIX = "ExploreSites";
+    private static final int INITIAL_SCROLL_POSITION = 3;
     static final PropertyModel.WritableIntPropertyKey STATUS_KEY =
             new PropertyModel.WritableIntPropertyKey();
     static final PropertyModel.WritableIntPropertyKey SCROLL_TO_CATEGORY_KEY =
@@ -144,6 +145,9 @@
         categoryListModel.set(categoryList);
         if (mNavFragment != null) {
             lookupCategoryAndScroll(mNavFragment);
+        } else {
+            mModel.set(SCROLL_TO_CATEGORY_KEY,
+                    Math.min(categoryListModel.size() - 1, INITIAL_SCROLL_POSITION));
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index 5292665e..9ecd8c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -422,6 +422,7 @@
 
         mNativeInitializationComplete = true;
         ContentUriUtils.setFileProviderUtil(new FileProviderHelper());
+        ServiceManagerStartupUtils.registerEnabledFeatures();
 
         // When a minidump is detected, extract and append a logcat to it, then upload it to the
         // crash server. Note that the logcat extraction might fail. This is ok; in that case, the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ServiceManagerStartupUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ServiceManagerStartupUtils.java
new file mode 100644
index 0000000..16d5723e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ServiceManagerStartupUtils.java
@@ -0,0 +1,57 @@
+// Copyright 2018 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.init;
+
+import android.content.SharedPreferences;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.chrome.browser.ChromeFeatureList;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Helper class for features to query the SharedPreferences to learn whether they can start
+ * ServiceManager instead of full browser on startup. Because Java code can only access the feature
+ * list after native library is loaded, this class stores the feature values in the
+ * SharedPreferences so that Java classes can access them on next startup.
+ */
+public class ServiceManagerStartupUtils {
+    // List of features that supports starting ServiceManager on startup.
+    private static final String[] SERVICE_MANAGER_FEATURES = {
+            ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD};
+    // Key in the SharedPreferences for storing all features that will start ServiceManager.
+    private static final String SERVICE_MANAGER_FEATURES_KEY = "ServiceManagerFeatures";
+
+    /**
+     *  Check whether ServiceManager can be started for |featureName|.
+     *  @param featureName Feature to query.
+     *  @return Whether the feature can start service manager.
+     */
+    public static boolean canStartServiceManager(String featureName) {
+        Set<String> features = ContextUtils.getAppSharedPreferences().getStringSet(
+                SERVICE_MANAGER_FEATURES_KEY, null);
+        return features != null && features.contains(featureName);
+    }
+
+    /**
+     *  Register all the service manager startup features with SharedPreferences.
+     */
+    public static void registerEnabledFeatures() {
+        SharedPreferences.Editor editor = ContextUtils.getAppSharedPreferences().edit();
+        Set<String> features = new HashSet<String>();
+        for (String feature : SERVICE_MANAGER_FEATURES) {
+            if (ChromeFeatureList.isEnabled(feature)) {
+                features.add(feature);
+            }
+        }
+        if (features.isEmpty()) {
+            editor.remove(SERVICE_MANAGER_FEATURES_KEY);
+        } else {
+            editor.putStringSet(SERVICE_MANAGER_FEATURES_KEY, features);
+        }
+        editor.apply();
+    }
+}
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 cbdfb9f..6a0c7c2 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
@@ -325,7 +325,16 @@
         updateSearchProviderHasLogo();
 
         initializeMainView(activity);
-        updateMargins(mTab.getBrowserControlsStateConstraints());
+        getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View view) {
+                updateMargins(mTab.getBrowserControlsStateConstraints());
+                getView().removeOnAttachStateChangeListener(this);
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View view) {}
+        });
 
         eventReporter.onSurfaceOpened();
 
@@ -384,12 +393,7 @@
         View view = getView();
         ViewGroup.MarginLayoutParams layoutParams =
                 ((ViewGroup.MarginLayoutParams) view.getLayoutParams());
-        if (layoutParams == null) {
-            // We could be updating the margin before the root view is attached to window.
-            layoutParams = new ViewGroup.MarginLayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-            view.setLayoutParams(layoutParams);
-        }
+        if (layoutParams == null) return;
 
         int bottomMargin = 0;
         if (FeatureUtilities.isBottomToolbarEnabled()
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 108acac..fc4ca9b7 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -760,6 +760,7 @@
   "java/src/org/chromium/chrome/browser/init/InvalidStartupDialog.java",
   "java/src/org/chromium/chrome/browser/init/NativeInitializationController.java",
   "java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java",
+  "java/src/org/chromium/chrome/browser/init/ServiceManagerStartupUtils.java",
   "java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderFactory.java",
   "java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderImpl.java",
   "java/src/org/chromium/chrome/browser/installedapp/PackageHash.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
index 0b1a072..cb2c506a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
@@ -30,6 +30,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -49,8 +50,11 @@
 import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils;
 import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.net.NetworkChangeNotifier;
 import org.chromium.net.test.EmbeddedTestServer;
 
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -61,6 +65,13 @@
 @CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
 @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)
 public class FeedNewTabPageTest {
+    private static final String FILEPATH = "chrome/test/data/android/feed/feed_large.gcl.bin";
+    private static final String FIRST_CARD_URL = "http://profootballtalk.nbcsports.com/2017/11/10/"
+            + "jerry-jones-owners-should-approve-of-roger-goodells-decisions/";
+    private static final int SIGNIN_PROMO_POSITION = 1;
+    private static final int ARTICLE_SECTION_HEADER_POSITION = 2;
+    private static final int FIRST_CARD_POSITION = 3;
+
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
@@ -73,11 +84,22 @@
     private FakeMostVisitedSites mMostVisitedSites;
     private EmbeddedTestServer mTestServer;
     private List<SiteSuggestion> mSiteSuggestions;
+    private TestNetworkClient mTestNetworkClient;
 
     @Before
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityWithURL("about:blank");
-        ThreadUtils.runOnUiThreadBlocking(() -> FeedNewTabPage.setInTestMode(true));
+        // Ensure we start in an online state.
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            if (!NetworkChangeNotifier.isInitialized()) {
+                NetworkChangeNotifier.init();
+            }
+            NetworkChangeNotifier.forceConnectivityState(true);
+        });
+
+        mTestNetworkClient = new TestNetworkClient();
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> FeedNewTabPage.setInTestMode(true, mTestNetworkClient));
 
         mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
         mSiteSuggestions = NewTabPageTestUtils.createFakeSiteSuggestions(mTestServer);
@@ -98,7 +120,7 @@
     @After
     public void tearDown() {
         mTestServer.stopAndDestroyServer();
-        ThreadUtils.runOnUiThreadBlocking(() -> FeedNewTabPage.setInTestMode(false));
+        ThreadUtils.runOnUiThreadBlocking(() -> FeedNewTabPage.setInTestMode(false, null));
     }
 
     @Test
@@ -114,18 +136,21 @@
         // that sign-in promo is not shown.
         ThreadUtils.runOnUiThreadBlocking(signinObserver::onSignedIn);
         RecyclerViewTestUtils.waitForStableRecyclerView(recyclerView);
-        onView(instanceOf(RecyclerView.class)).perform(RecyclerViewActions.scrollToPosition(1));
+        onView(instanceOf(RecyclerView.class))
+                .perform(RecyclerViewActions.scrollToPosition(SIGNIN_PROMO_POSITION));
         onView(withId(R.id.signin_promo_view_container)).check(doesNotExist());
 
         // Simulate sign out, scroll to the position where sign-in promo could be placed, and verify
         // that sign-in promo is shown.
         ThreadUtils.runOnUiThreadBlocking(signinObserver::onSignedOut);
         RecyclerViewTestUtils.waitForStableRecyclerView(recyclerView);
-        onView(instanceOf(RecyclerView.class)).perform(RecyclerViewActions.scrollToPosition(1));
+        onView(instanceOf(RecyclerView.class))
+                .perform(RecyclerViewActions.scrollToPosition(SIGNIN_PROMO_POSITION));
         onView(withId(R.id.signin_promo_view_container)).check(matches(isDisplayed()));
 
         // Scroll to the article section header in case it is not visible.
-        onView(instanceOf(RecyclerView.class)).perform(RecyclerViewActions.scrollToPosition(2));
+        onView(instanceOf(RecyclerView.class))
+                .perform(RecyclerViewActions.scrollToPosition(ARTICLE_SECTION_HEADER_POSITION));
 
         // Hide articles and verify that the sign-in promo is not shown.
         onView(withId(R.id.header_title)).perform(click());
@@ -256,6 +281,23 @@
                 Pref.NTP_ARTICLES_SECTION_ENABLED, pref));
     }
 
+    @Test
+    @MediumTest
+    @Feature({"FeedNewTabPage"})
+    public void testClickSuggestion() throws InterruptedException, IOException {
+        FileInputStream is = new FileInputStream(UrlUtils.getIsolatedTestFilePath(FILEPATH));
+        mTestNetworkClient.setResponseData(is);
+        ThreadUtils.runOnUiThreadBlocking(() -> mNtp.getStream().triggerRefresh());
+
+        ChromeTabUtils.waitForTabPageLoaded(mTab, () -> {
+            onView(instanceOf(RecyclerView.class))
+                    .perform(RecyclerViewActions.scrollToPosition(FIRST_CARD_POSITION),
+                            RecyclerViewActions.actionOnItemAtPosition(
+                                    FIRST_CARD_POSITION, click()));
+        });
+        Assert.assertEquals(FIRST_CARD_URL, mTab.getUrl());
+    }
+
     private boolean getPreferenceForArticleSectionHeader() throws Exception {
         return ThreadUtils.runOnUiThreadBlocking(
                 () -> PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE));
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 6c174325..376e9fb0 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
@@ -47,6 +47,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.feed.FeedNewTabPage;
+import org.chromium.chrome.browser.feed.TestNetworkClient;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView;
@@ -145,7 +146,8 @@
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityWithURL("about:blank");
         if (mInterestFeedEnabled) {
-            ThreadUtils.runOnUiThreadBlocking(() -> FeedNewTabPage.setInTestMode(true));
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> FeedNewTabPage.setInTestMode(true, new TestNetworkClient()));
         }
 
         mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
@@ -170,7 +172,7 @@
     public void tearDown() throws Exception {
         mTestServer.stopAndDestroyServer();
         if (mInterestFeedEnabled) {
-            ThreadUtils.runOnUiThreadBlocking(() -> FeedNewTabPage.setInTestMode(false));
+            ThreadUtils.runOnUiThreadBlocking(() -> FeedNewTabPage.setInTestMode(false, null));
         }
     }
 
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 066e59bc..1005969 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 64
+current_shell_apk_version = 65
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserLauncher.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserLauncher.java
index aba712f6..d561899 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserLauncher.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserLauncher.java
@@ -35,7 +35,8 @@
     public static void launch(Context context, HostBrowserLauncherParams params) {
         Log.v(TAG, "WebAPK Launch URL: " + params.getStartUrl());
 
-        if (HostBrowserUtils.shouldLaunchInTab(params.getHostBrowserMajorChromiumVersion())) {
+        if (HostBrowserUtils.shouldLaunchInTab(params.getHostBrowserPackageName(),
+                    params.getHostBrowserMajorChromiumVersion())) {
             launchInTab(context, params);
             return;
         }
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
index c76a74b..29d3153 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
@@ -29,6 +29,8 @@
 
     private static final int MINIMUM_REQUIRED_CHROME_VERSION = 57;
 
+    private static final int MINIMUM_REQUIRED_INTENT_HELPER_VERSION = 2;
+
     private static final String TAG = "cr_HostBrowserUtils";
 
     /**
@@ -212,7 +214,16 @@
     }
 
     /** Returns whether a WebAPK should be launched as a tab. See crbug.com/772398. */
-    public static boolean shouldLaunchInTab(int hostBrowserChromiumMajorVersion) {
+    public static boolean shouldLaunchInTab(
+            String hostBrowserPackageName, int hostBrowserChromiumMajorVersion) {
+        if (!sBrowsersSupportingWebApk.contains(hostBrowserPackageName)) {
+            return true;
+        }
+
+        if (TextUtils.equals(hostBrowserPackageName, "org.chromium.arc.intent_helper")) {
+            return hostBrowserChromiumMajorVersion < MINIMUM_REQUIRED_INTENT_HELPER_VERSION;
+        }
+
         return hostBrowserChromiumMajorVersion < MINIMUM_REQUIRED_CHROME_VERSION;
     }
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 29b71b9..0d406e83 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -244,6 +244,8 @@
     "browsing_data/site_data_size_collector.h",
     "cache_stats_recorder.cc",
     "cache_stats_recorder.h",
+    "cached_image_fetcher/cached_image_fetcher_service_factory.cc",
+    "cached_image_fetcher/cached_image_fetcher_service_factory.h",
     "chooser_controller/chooser_controller.cc",
     "chooser_controller/chooser_controller.h",
     "chrome_browser_application_mac.h",
@@ -803,8 +805,6 @@
     "native_window_notification_source.h",
     "navigation_predictor/navigation_predictor.cc",
     "navigation_predictor/navigation_predictor.h",
-    "net/chrome_accept_language_settings.cc",
-    "net/chrome_accept_language_settings.h",
     "net/chrome_cookie_notification_details.h",
     "net/chrome_extensions_network_delegate.cc",
     "net/chrome_extensions_network_delegate.h",
@@ -1781,6 +1781,7 @@
     "//components/history/content/browser",
     "//components/history/core/browser",
     "//components/history/core/common",
+    "//components/image_fetcher/core",
     "//components/infobars/core",
     "//components/invalidation/impl",
     "//components/keyed_service/content",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index cbb8dac..12cda67 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4466,6 +4466,12 @@
      kOsAndroid, FEATURE_VALUE_TYPE(safe_browsing::kUseLocalBlacklistsV2)},
 #endif  // defined(OS_ANDROID)
 
+#if defined(OS_CHROMEOS)
+    {"enable-native-google-assistant",
+     flag_descriptions::kEnableGoogleAssistantName,
+     flag_descriptions::kEnableGoogleAssistantDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::switches::kAssistantFeature)},
+#endif  // defined(OS_ANDROID)
 };
 
 class FlagsStateSingleton {
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 349a7744..8dfa01d 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -239,20 +239,6 @@
       month, day, hour, minute, second);
 }
 
-void UiControllerAndroid::ShowProgressBar(int progress,
-                                          const std::string& message) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_AutofillAssistantUiController_onShowProgressBar(
-      env, java_autofill_assistant_ui_controller_, progress,
-      base::android::ConvertUTF8ToJavaString(env, message));
-}
-
-void UiControllerAndroid::HideProgressBar() {
-  JNIEnv* env = AttachCurrentThread();
-  Java_AutofillAssistantUiController_onHideProgressBar(
-      env, java_autofill_assistant_ui_controller_);
-}
-
 std::string UiControllerAndroid::GetApiKey() {
   std::string api_key;
   if (google_apis::IsGoogleChromeAPIKeyUsed()) {
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 73769c69..707702e 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -42,8 +42,6 @@
       override;
   void HideDetails() override;
   void ShowDetails(const DetailsProto& details) override;
-  void ShowProgressBar(int progress, const std::string& message) override;
-  void HideProgressBar() override;
 
   // Overrides Client:
   std::string GetApiKey() override;
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index ede7ea9..2f31c8c 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -136,6 +136,7 @@
     &kReaderModeInCCT,
     &kSearchEnginePromoExistingDevice,
     &kSearchEnginePromoNewDevice,
+    &kServiceManagerForDownload,
     &kSoleIntegration,
     &kSpannableInlineAutocomplete,
     &kSpecialLocaleFeature,
@@ -384,6 +385,9 @@
 const base::Feature kReaderModeInCCT{"ReaderModeInCCT",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kServiceManagerForDownload{
+    "ServiceManagerForDownload", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kSimplifiedNTP{"SimplifiedNTP",
                                    base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 0dea05a..f9104a9 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -76,6 +76,7 @@
 extern const base::Feature kPwaImprovedSplashScreen;
 extern const base::Feature kPwaPersistentNotification;
 extern const base::Feature kReaderModeInCCT;
+extern const base::Feature kServiceManagerForDownload;
 extern const base::Feature kSimplifiedNTP;
 extern const base::Feature kSoleIntegration;
 extern const base::Feature kSpannableInlineAutocomplete;
diff --git a/chrome/browser/android/explore_sites/explore_sites_fetcher.cc b/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
index 1446f979..d1431d66 100644
--- a/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/android/explore_sites/explore_sites_types.h"
 #include "chrome/browser/android/explore_sites/url_util.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/net/chrome_accept_language_settings.h"
 #include "chrome/common/channel_info.h"
 #include "components/variations/service/variations_service.h"
 #include "components/version_info/version_info.h"
diff --git a/chrome/browser/android/explore_sites/explore_sites_service_impl.cc b/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
index c39835a..8e2f63c 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
@@ -23,9 +23,6 @@
 using chrome::android::explore_sites::GetExploreSitesVariation;
 
 namespace explore_sites {
-namespace {
-const int kFaviconsPerCategoryImage = 4;
-}
 
 ExploreSitesServiceImpl::ExploreSitesServiceImpl(
     std::unique_ptr<ExploreSitesStore> store,
diff --git a/chrome/browser/android/explore_sites/explore_sites_types.h b/chrome/browser/android/explore_sites/explore_sites_types.h
index 88a147f..f9bc42d 100644
--- a/chrome/browser/android/explore_sites/explore_sites_types.h
+++ b/chrome/browser/android/explore_sites/explore_sites_types.h
@@ -16,6 +16,8 @@
 #include "url/gurl.h"
 
 namespace explore_sites {
+constexpr int kFaviconsPerCategoryImage = 4;
+
 // The in-memory representation of a site in the ExploreSitesStore.
 // Image data is not represented here because it is requested separately from
 // the UI layer.
diff --git a/chrome/browser/android/explore_sites/image_helper.cc b/chrome/browser/android/explore_sites/image_helper.cc
index d85887c7..95af464 100644
--- a/chrome/browser/android/explore_sites/image_helper.cc
+++ b/chrome/browser/android/explore_sites/image_helper.cc
@@ -19,8 +19,6 @@
 
 namespace explore_sites {
 namespace {
-const int kFaviconsPerCategoryImage = 4;
-
 // Ratio of icon size to the amount of padding between the icons.
 const int kIconPaddingScale = 8;
 }  // namespace
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_impl.cc b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
index 2b7c5a6..04e30297 100644
--- a/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
+++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
@@ -77,10 +77,12 @@
     std::unique_ptr<content::BackgroundFetchDescription> fetch_description,
     const std::string& provider_namespace,
     bool is_off_the_record)
-    : paused(fetch_description->start_paused),
-      offline_item(offline_items_collection::ContentId(
+    : offline_item(offline_items_collection::ContentId(
           provider_namespace,
           fetch_description->job_unique_id)),
+      job_state(fetch_description->start_paused
+                    ? State::kPendingWillStartPaused
+                    : State::kPendingWillStartDownloading),
       fetch_description(std::move(fetch_description)) {
   offline_item.is_off_the_record = is_off_the_record;
   offline_item.original_url = this->fetch_description->origin.GetURL();
@@ -89,6 +91,13 @@
 
 BackgroundFetchDelegateImpl::JobDetails::~JobDetails() = default;
 
+void BackgroundFetchDelegateImpl::JobDetails::MarkJobAsStarted() {
+  if (job_state == State::kPendingWillStartDownloading)
+    job_state = State::kStartedAndDownloading;
+  else if (job_state == State::kPendingWillStartPaused)
+    job_state = State::kStartedButPaused;
+}
+
 void BackgroundFetchDelegateImpl::JobDetails::UpdateOfflineItem() {
   DCHECK_GT(fetch_description->total_parts, 0);
 
@@ -115,7 +124,7 @@
   offline_item.is_resumable = true;
 
   using OfflineItemState = offline_items_collection::OfflineItemState;
-  if (cancelled) {
+  if (job_state == State::kCancelled) {
     offline_item.state = OfflineItemState::CANCELLED;
   } else if (fetch_description->completed_parts ==
              fetch_description->total_parts) {
@@ -123,7 +132,7 @@
     // response was an HTTP error, e.g. 404.
     offline_item.state = OfflineItemState::COMPLETE;
     offline_item.is_openable = true;
-  } else if (started && paused) {
+  } else if (job_state == State::kStartedButPaused) {
     offline_item.state = OfflineItemState::PAUSED;
   } else {
     offline_item.state = OfflineItemState::IN_PROGRESS;
@@ -263,14 +272,16 @@
 
   JobDetails& job_details = job_details_map_.find(job_unique_id)->second;
 
-  if (!job_details.started) {
+  if (job_details.job_state == JobDetails::State::kPendingWillStartPaused ||
+      job_details.job_state ==
+          JobDetails::State::kPendingWillStartDownloading) {
     // Create a notification.
     for (auto* observer : observers_)
       observer->OnItemsAdded({job_details.offline_item});
-    job_details.started = true;
+    job_details.MarkJobAsStarted();
   }
 
-  if (job_details.paused) {
+  if (job_details.job_state == JobDetails::State::kStartedButPaused) {
     job_details.on_resume =
         base::BindOnce(&BackgroundFetchDelegateImpl::StartDownload,
                        GetWeakPtr(), job_unique_id, params);
@@ -298,7 +309,7 @@
     return;
 
   JobDetails& job_details = job_details_iter->second;
-  job_details.cancelled = true;
+  job_details.job_state = JobDetails::State::kCancelled;
 
   for (const auto& download_guid : job_details.current_download_guids) {
     GetDownloadService()->CancelDownload(download_guid);
@@ -331,6 +342,10 @@
   }
 
   UpdateOfflineItemAndUpdateObservers(&job_details);
+
+  // UpdateUI() can only be called once, and only when the background fetch
+  // has succeeded or failed, so we can delete |job_details| now.
+  job_details_map_.erase(job_details_iter);
 }
 
 void BackgroundFetchDelegateImpl::OnDownloadStarted(
@@ -541,7 +556,7 @@
     return;
 
   JobDetails& job_details = job_details_iter->second;
-  job_details.paused = true;
+  job_details.job_state = JobDetails::State::kStartedButPaused;
   for (auto& download_guid : job_details.current_download_guids)
     GetDownloadService()->PauseDownload(download_guid);
 }
@@ -554,7 +569,7 @@
     return;
 
   JobDetails& job_details = job_details_iter->second;
-  job_details.paused = false;
+  job_details.job_state = JobDetails::State::kStartedAndDownloading;
   for (auto& download_guid : job_details.current_download_guids)
     GetDownloadService()->ResumeDownload(download_guid);
 
@@ -645,7 +660,7 @@
 
   DCHECK(job_details_map_.find(unique_id) != job_details_map_.end());
   JobDetails& job_details = job_details_map_.find(unique_id)->second;
-  job_details.paused = true;
+  job_details.job_state = JobDetails::State::kStartedButPaused;
 
   UpdateOfflineItemAndUpdateObservers(&job_details);
 }
@@ -657,7 +672,7 @@
 
     // If the job is loaded at this point, then it already started
     // in a previous session.
-    job_details.started = true;
+    job_details.MarkJobAsStarted();
 
     std::vector<std::string>& job_outstanding_guids =
         job_details.fetch_description->outstanding_guids;
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_impl.h b/chrome/browser/background_fetch/background_fetch_delegate_impl.h
index 15aed432..ab79b32 100644
--- a/chrome/browser/background_fetch/background_fetch_delegate_impl.h
+++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.h
@@ -123,6 +123,16 @@
 
  private:
   struct JobDetails {
+    // If a job is part of the |job_details_map_|, it will have one of these
+    // states.
+    enum class State {
+      kPendingWillStartPaused,
+      kPendingWillStartDownloading,
+      kStartedButPaused,
+      kStartedAndDownloading,
+      kCancelled,
+    };
+
     JobDetails(JobDetails&&);
     JobDetails(
         std::unique_ptr<content::BackgroundFetchDescription> fetch_description,
@@ -131,10 +141,7 @@
     ~JobDetails();
 
     void UpdateOfflineItem();
-
-    bool started = false;
-    bool cancelled = false;
-    bool paused = false;
+    void MarkJobAsStarted();
 
     // Set of DownloadService GUIDs that are currently downloading. They are
     // added by DownloadUrl and are removed when the download completes, fails
@@ -142,6 +149,7 @@
     base::flat_set<std::string> current_download_guids;
 
     offline_items_collection::OfflineItem offline_item;
+    State job_state;
     std::unique_ptr<content::BackgroundFetchDescription> fetch_description;
 
     base::OnceClosure on_resume;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 39860bc..523debb 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -65,6 +65,8 @@
         <structure name="IDR_WELCOME_ONBOARDING_WELCOME_LANDING_VIEW_JS" file="resources\welcome\onboarding_welcome\landing_view.js" type="chrome_html" preprocess="true"/>
         <structure name="IDR_WELCOME_ONBOARDING_WELCOME_NAVIGATION_BEHAVIOR_HTML" file="resources\welcome\onboarding_welcome\navigation_behavior.html" type="chrome_html" preprocess="true"/>
         <structure name="IDR_WELCOME_ONBOARDING_WELCOME_NAVIGATION_BEHAVIOR_JS" file="resources\welcome\onboarding_welcome\navigation_behavior.js" type="chrome_html" preprocess="true"/>
+        <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SIGNIN_VIEW_HTML" file="resources\welcome\onboarding_welcome\signin_view.html" type="chrome_html" preprocess="true"/>
+        <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SIGNIN_VIEW_JS" file="resources\welcome\onboarding_welcome\signin_view.js" type="chrome_html" preprocess="true"/>
         <structure name="IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_APP_HTML" file="resources\welcome\onboarding_welcome\welcome_app.html" type="chrome_html" preprocess="true"/>
         <structure name="IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_APP_JS" file="resources\welcome\onboarding_welcome\welcome_app.js" type="chrome_html" preprocess="true"/>
         <structure name="IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_BROWSER_PROXY_HTML" file="resources\welcome\onboarding_welcome\welcome_browser_proxy.html" type="chrome_html"/>
@@ -88,7 +90,11 @@
         <structure name="IDR_NUX_SET_AS_DEFAULT_JS" file="resources\welcome\onboarding_welcome\set_as_default\nux_set_as_default.js" type="chrome_html" />
         <structure name="IDR_NUX_SET_AS_DEFAULT_PROXY_HTML" file="resources\welcome\onboarding_welcome\set_as_default\nux_set_as_default_proxy.html" type="chrome_html" />
         <structure name="IDR_NUX_SET_AS_DEFAULT_PROXY_JS" file="resources\welcome\onboarding_welcome\set_as_default\nux_set_as_default_proxy.js" type="chrome_html" />
-        <structure name="IDR_NUX_CHOOSER_SHARED_CSS" file="resources\welcome\onboarding_welcome\shared\chooser_shared_css.html" type="chrome_html" />
+        <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_JS" file="resources\welcome\onboarding_welcome\shared\action_link_style.js" type="chrome_html" />
+        <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_CSS_HTML" file="resources\welcome\onboarding_welcome\shared\action_link_style_css.html" type="chrome_html" />
+        <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ONBOARDING_BACKGROUND_HTML" file="resources\welcome\onboarding_welcome\shared\onboarding_background.html" type="chrome_html" />
+        <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ONBOARDING_BACKGROUND_JS" file="resources\welcome\onboarding_welcome\shared\onboarding_background.js" type="chrome_html" />
+        <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_CHOOSER_SHARED_CSS" file="resources\welcome\onboarding_welcome\shared\chooser_shared_css.html" type="chrome_html" />
         <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_I18N_SETUP_HTML" file="resources\welcome\onboarding_welcome\shared\i18n_setup.html" type="chrome_html" />
       </if>
       <if expr="is_win">
@@ -715,6 +721,13 @@
         <include name="IDR_NUX_GOOGLE_APPS_TRANSLATE_2X" file="resources\welcome\onboarding_welcome\images\translate_2x.png" type="BINDATA" />
         <include name="IDR_NUX_GOOGLE_APPS_YOUTUBE_1X" file="resources\welcome\onboarding_welcome\images\youtube_1x.png" type="BINDATA" />
         <include name="IDR_NUX_GOOGLE_APPS_YOUTUBE_2X" file="resources\welcome\onboarding_welcome\images\youtube_2x.png" type="BINDATA" />
+        <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_BLUE_CIRCLE_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\blue_circle.svg" type="BINDATA" />
+        <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_GREEN_RECTANGLE_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\green_rectangle.svg" type="BINDATA" />
+        <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_GREY_OVAL_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\grey_oval.svg" type="BINDATA" />
+        <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_GREY_ROUNDED_RECTANGLE_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\grey_rounded_rectangle.svg" type="BINDATA" />
+        <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_RED_TRIANGLE_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\red_triangle.svg" type="BINDATA" />
+        <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_YELLOW_DOTS_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\yellow_dots.svg" type="BINDATA" />
+        <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_YELLOW_SEMICIRCLE_SVG" file="resources\welcome\onboarding_welcome\images\background_svgs\yellow_semicircle.svg" type="BINDATA" />
       </if>
       <if expr="is_win">
         <include name="IDR_WELCOME_WIN10_DEFAULT_WEBP" file="resources\welcome\default.webp" type="BINDATA" />
diff --git a/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.cc b/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.cc
new file mode 100644
index 0000000..449628b
--- /dev/null
+++ b/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.cc
@@ -0,0 +1,109 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/time/default_clock.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/suggestions/image_decoder_impl.h"
+#include "components/image_fetcher/core/cached_image_fetcher_service.h"
+#include "components/image_fetcher/core/storage/image_cache.h"
+#include "components/image_fetcher/core/storage/image_data_store_disk.h"
+#include "components/image_fetcher/core/storage/image_metadata_store_leveldb.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+
+#if defined(OS_ANDROID)
+#include "base/path_service.h"
+#endif
+
+namespace image_fetcher {
+
+namespace {
+
+// The path under the browser context's data directory which the image_cache
+// will be stored.
+const base::FilePath::CharType kImageCacheSubdir[] =
+    FILE_PATH_LITERAL("image_cache");
+
+std::unique_ptr<ImageDecoder> CreateImageDecoderImpl() {
+  return std::make_unique<suggestions::ImageDecoderImpl>();
+}
+
+}  // namespace
+
+CachedImageFetcherService*
+CachedImageFetcherServiceFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<CachedImageFetcherService*>(
+      GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+CachedImageFetcherServiceFactory*
+CachedImageFetcherServiceFactory::GetInstance() {
+  return base::Singleton<CachedImageFetcherServiceFactory>::get();
+}
+
+CachedImageFetcherServiceFactory::CachedImageFetcherServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "CachedImageFetcherService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+CachedImageFetcherServiceFactory::~CachedImageFetcherServiceFactory() = default;
+
+KeyedService* CachedImageFetcherServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  base::FilePath cache_path;
+#if defined(OS_ANDROID)
+  // On Android, get a special cache directory that is cleared under pressure.
+  // The subdirectory under needs to be registered file_paths.xml as well.
+  if (base::PathService::Get(base::DIR_CACHE, &cache_path)) {
+    cache_path = cache_path.Append(kImageCacheSubdir);
+  }
+#else
+  // On other platforms, GetCachePath can be cleared by the user.
+  cache_path = context->GetCachePath().Append(kImageCacheSubdir);
+#endif
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
+      base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
+  base::DefaultClock* clock = base::DefaultClock::GetInstance();
+
+  auto metadata_store = std::make_unique<ImageMetadataStoreLevelDB>(
+      cache_path, task_runner, clock);
+  auto data_store =
+      std::make_unique<ImageDataStoreDisk>(cache_path, task_runner);
+
+  Profile* profile = Profile::FromBrowserContext(context);
+  // TODO(wylieb): Start in read-only mode if user is incognito.
+  scoped_refptr<ImageCache> image_cache = base::MakeRefCounted<ImageCache>(
+      std::move(data_store), std::move(metadata_store), profile->GetPrefs(),
+      clock, task_runner);
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
+      content::BrowserContext::GetDefaultStoragePartition(profile)
+          ->GetURLLoaderFactoryForBrowserProcess();
+
+  return new CachedImageFetcherService(
+      base::BindRepeating(CreateImageDecoderImpl),
+      std::move(url_loader_factory), std::move(image_cache));
+}
+
+content::BrowserContext*
+CachedImageFetcherServiceFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  // Return different BrowserContexts for regular/incognito.
+  return context;
+}
+
+}  // namespace image_fetcher
diff --git a/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h b/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h
new file mode 100644
index 0000000..90c95f5
--- /dev/null
+++ b/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h
@@ -0,0 +1,45 @@
+// Copyright 2018 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_CACHED_IMAGE_FETCHER_CACHED_IMAGE_FETCHER_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_CACHED_IMAGE_FETCHER_CACHED_IMAGE_FETCHER_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace image_fetcher {
+
+class CachedImageFetcherService;
+
+// Factory to create one CachedImageFetcherService per browser context.
+class CachedImageFetcherServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static CachedImageFetcherService* GetForBrowserContext(
+      content::BrowserContext* context);
+  static CachedImageFetcherServiceFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<CachedImageFetcherServiceFactory>;
+
+  CachedImageFetcherServiceFactory();
+  ~CachedImageFetcherServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(CachedImageFetcherServiceFactory);
+};
+
+}  // namespace image_fetcher
+
+#endif  // CHROME_BROWSER_CACHED_IMAGE_FETCHER_CACHED_IMAGE_FETCHER_SERVICE_FACTORY_H_
diff --git a/chrome/browser/chrome_main_browsertest.cc b/chrome/browser/chrome_main_browsertest.cc
index 6b9a6cc675..532a766 100644
--- a/chrome/browser/chrome_main_browsertest.cc
+++ b/chrome/browser/chrome_main_browsertest.cc
@@ -88,17 +88,18 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ChromeMainTest, SecondLaunchFromIncognitoWithNormalUrl) {
+  Profile* const profile = browser()->profile();
+
   // We should start with one normal window.
-  ASSERT_EQ(1u, chrome::GetTabbedBrowserCount(browser()->profile()));
+  ASSERT_EQ(1u, chrome::GetTabbedBrowserCount(profile));
 
   // Create an incognito window.
-  chrome::NewIncognitoWindow(browser());
+  chrome::NewIncognitoWindow(profile);
 
   ASSERT_EQ(2u, chrome::GetTotalBrowserCount());
-  ASSERT_EQ(1u, chrome::GetTabbedBrowserCount(browser()->profile()));
+  ASSERT_EQ(1u, chrome::GetTabbedBrowserCount(profile));
 
   // Close the first window.
-  Profile* profile = browser()->profile();
   CloseBrowserSynchronously(browser());
 
   // There should only be the incognito window open now.
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.cc
index 7e45325..4f36cdd 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.cc
@@ -77,9 +77,9 @@
       std::move(event));
 }
 
-void SpokenFeedbackEventRewriterDelegate::HandleKeyboardEvent(
+bool SpokenFeedbackEventRewriterDelegate::HandleKeyboardEvent(
     content::WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
-  OnUnhandledSpokenFeedbackEvent(
-      ui::Event::Clone(*static_cast<ui::Event*>(event.os_event)));
+  OnUnhandledSpokenFeedbackEvent(ui::Event::Clone(*event.os_event));
+  return true;
 }
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h
index c05d51f12..5faa894 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h
@@ -35,7 +35,7 @@
   void OnUnhandledSpokenFeedbackEvent(std::unique_ptr<ui::Event> event) const;
 
   // WebContentsDelegate:
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
 
diff --git a/chrome/browser/chromeos/arc/arc_util.cc b/chrome/browser/chromeos/arc/arc_util.cc
index 81dc64b..66a4d9a 100644
--- a/chrome/browser/chromeos/arc/arc_util.cc
+++ b/chrome/browser/chromeos/arc/arc_util.cc
@@ -708,9 +708,15 @@
 }
 
 bool IsPlayStoreAvailable() {
-  return (!IsRobotOrOfflineDemoAccountMode() ||
-          chromeos::DemoSession::IsDeviceInDemoMode()) &&
-         !ShouldArcAlwaysStartWithNoPlayStore();
+  if (ShouldArcAlwaysStartWithNoPlayStore())
+    return false;
+
+  if (!IsRobotOrOfflineDemoAccountMode())
+    return true;
+
+  // Demo Mode is the only public session scenario that can launch Play.
+  return chromeos::DemoSession::IsDeviceInDemoMode() &&
+         chromeos::switches::ShouldShowPlayStoreInDemoMode();
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/arc_util_unittest.cc b/chrome/browser/chromeos/arc/arc_util_unittest.cc
index c92da713..9c1c989 100644
--- a/chrome/browser/chromeos/arc/arc_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_util_unittest.cc
@@ -861,6 +861,18 @@
   ScopedLogIn login(GetFakeUserManager(),
                     AccountId::FromUserEmail("public_user@gmail.com"),
                     user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+  EXPECT_FALSE(IsPlayStoreAvailable());
+}
+
+TEST_F(ChromeArcUtilTest, ArcStartModeDefaultDemoModeWithPlayStore) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(
+      {"", "--arc-availability=installed", "--show-play-in-demo-mode"});
+  chromeos::DemoSession::SetDemoConfigForTesting(
+      chromeos::DemoSession::DemoModeConfig::kOnline);
+  ScopedLogIn login(GetFakeUserManager(),
+                    AccountId::FromUserEmail("public_user@gmail.com"),
+                    user_manager::USER_TYPE_PUBLIC_ACCOUNT);
   EXPECT_TRUE(IsPlayStoreAvailable());
 }
 
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.cc b/chrome/browser/chromeos/drive/drive_integration_service.cc
index 42726e5..7403b5ed 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.cc
+++ b/chrome/browser/chromeos/drive/drive_integration_service.cc
@@ -422,9 +422,7 @@
 
   // network::NetworkConnectionTracker::NetworkConnectionObserver
   void OnConnectionChanged(network::mojom::ConnectionType type) override {
-    bool is_offline =
-        type == network::mojom::ConnectionType::CONNECTION_UNKNOWN ||
-        type == network::mojom::ConnectionType::CONNECTION_NONE;
+    bool is_offline = type == network::mojom::ConnectionType::CONNECTION_NONE;
     // Check for a pending remount first. In this case, DriveFS will not be
     // mounted and thus its interface will be null.
     if (integration_service_->drivefs_holder_ &&
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
index 05676ac..772d7bb 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
@@ -363,7 +363,7 @@
   if (state == ScreenlockState::AUTHENTICATED) {
     if (power_monitor_)
       power_monitor_->RecordStartUpTime();
-  } else if (auth_attempt_.get()) {
+  } else if (auth_attempt_) {
     // Clean up existing auth attempt if we can no longer authenticate the
     // remote device.
     auth_attempt_.reset();
@@ -381,8 +381,13 @@
   const EasyUnlockAuthAttempt::Type auth_attempt_type =
       GetType() == TYPE_REGULAR ? EasyUnlockAuthAttempt::TYPE_UNLOCK
                                 : EasyUnlockAuthAttempt::TYPE_SIGNIN;
+  if (auth_attempt_) {
+    PA_LOG(INFO) << "Already attempting auth, skipping this request.";
+    return;
+  }
+
   if (!GetAccountId().is_valid()) {
-    LOG(ERROR) << "Empty user account. Refresh token might go bad.";
+    PA_LOG(ERROR) << "Empty user account. Auth attempt failed.";
     return;
   }
 
@@ -403,7 +408,7 @@
 }
 
 void EasyUnlockService::FinalizeUnlock(bool success) {
-  if (!auth_attempt_.get())
+  if (!auth_attempt_)
     return;
 
   this->OnWillFinalizeUnlock(success);
@@ -421,8 +426,9 @@
 }
 
 void EasyUnlockService::FinalizeSignin(const std::string& key) {
-  if (!auth_attempt_.get())
+  if (!auth_attempt_)
     return;
+
   std::string wrapped_secret = GetWrappedSecret();
   if (!wrapped_secret.empty())
     auth_attempt_->FinalizeSignin(GetAccountId(), wrapped_secret, key);
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
index b3d542d..5645cbba 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -321,10 +321,10 @@
   return true;
 }
 
-void OobeUIDialogDelegate::HandleKeyboardEvent(
+bool OobeUIDialogDelegate::HandleKeyboardEvent(
     content::WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
-  unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+  return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
       event, dialog_widget_->GetFocusManager());
 }
 
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
index 37627e3..9fecf38b 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
@@ -94,7 +94,7 @@
 
   // content::WebContentsDelegate:
   bool TakeFocus(content::WebContents* source, bool reverse) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
 
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.cc b/chrome/browser/chromeos/login/ui/webui_login_view.cc
index 78a826a7..1d80fca 100644
--- a/chrome/browser/chromeos/login/ui/webui_login_view.cc
+++ b/chrome/browser/chromeos/login/ui/webui_login_view.cc
@@ -453,15 +453,16 @@
 #endif
 }
 
-void WebUILoginView::HandleKeyboardEvent(content::WebContents* source,
+bool WebUILoginView::HandleKeyboardEvent(content::WebContents* source,
                                          const NativeWebKeyboardEvent& event) {
+  bool handled = false;
   if (forward_keyboard_event_) {
     // Disable arrow key traversal because arrow keys are handled via
     // accelerator when this view has focus.
     ScopedArrowKeyTraversal arrow_key_traversal(false);
 
-    unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
-                                                          GetFocusManager());
+    handled = unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+        event, GetFocusManager());
   }
 
   // Make sure error bubble is cleared on keyboard event. This is needed
@@ -472,6 +473,7 @@
     if (web_ui)
       web_ui->CallJavascriptFunctionUnsafe("cr.ui.Oobe.clearErrors");
   }
+  return handled;
 }
 
 bool WebUILoginView::TakeFocus(content::WebContents* source, bool reverse) {
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.h b/chrome/browser/chromeos/login/ui/webui_login_view.h
index 887b23c..9e803fa 100644
--- a/chrome/browser/chromeos/login/ui/webui_login_view.h
+++ b/chrome/browser/chromeos/login/ui/webui_login_view.h
@@ -150,7 +150,7 @@
 
   // Overridden from content::WebContentsDelegate.
   bool HandleContextMenu(const content::ContextMenuParams& params) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   bool TakeFocus(content::WebContents* source, bool reverse) override;
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller.h b/chrome/browser/chromeos/power/auto_screen_brightness/modeller.h
index b61fada1..07070f9 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller.h
@@ -6,7 +6,8 @@
 #define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_MODELLER_H_
 
 #include "base/observer_list_types.h"
-#include "chrome/browser/chromeos/power/auto_screen_brightness/trainer.h"
+#include "base/optional.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.h"
 
 namespace chromeos {
 namespace power {
@@ -32,14 +33,15 @@
     ~Observer() override = default;
 
     // Called when a new curve (|brightness_curve|) is trained.
-    virtual void OnModelTrained(const BrightnessCurve& brightness_curve) = 0;
+    virtual void OnModelTrained(
+        const MonotoneCubicSpline& brightness_curve) = 0;
 
     // Called when the model is initialized. Observers will be notified about
     // both model status and initial curve (|brightness_curve|). If model status
-    // is |kDisabled|, the |brightness_curve| will be empty.
+    // is |kDisabled|, the |brightness_curve| will be a nullopt.
     virtual void OnModelInitialized(
         Status model_status,
-        const BrightnessCurve& brightness_curve) = 0;
+        const base::Optional<MonotoneCubicSpline>& brightness_curve) = 0;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(Observer);
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
index 071bac68..0c46567c 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
@@ -26,33 +26,48 @@
 namespace auto_screen_brightness {
 
 namespace {
-// Loads data from a specified location on disk. This should run in another
+
+// Creates a global/default brightness curve.
+// TODO(crbug.com/881215): add actual default curve and then add unit test too.
+// TODO(crbug.com/881215): add param flag to allow for experiments.
+MonotoneCubicSpline CreateGlobalCurve() {
+  const std::vector<double> default_log_lux = {0, 100};
+  const std::vector<double> default_brightness = {50, 100};
+  return MonotoneCubicSpline(default_log_lux, default_brightness);
+}
+
+// Loads curve from a specified location on disk. This should run in another
 // thread to be non-blocking to the main thread (if |is_testing| is false).
-std::string LoadDataFromDisk(const base::FilePath& path, bool is_testing) {
+// The ambient values read from disk should be in the log-domain already.
+base::Optional<MonotoneCubicSpline> LoadCurveFromDisk(
+    const base::FilePath& path,
+    bool is_testing) {
   DCHECK(is_testing ||
          !content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   if (!PathExists(path)) {
-    return "";
+    return base::nullopt;
   }
 
   std::string content;
-  if (!base::ReadFileToString(path, &content)) {
-    return "";
+  if (!base::ReadFileToString(path, &content) || content.empty()) {
+    return base::nullopt;
   }
-  return content;
+
+  return MonotoneCubicSpline::FromString(content);
 }
 
-// Saves data to disk. This should run in another thread to be non-blocking to
-// the main thread (if |is_testing| is false).
+// Saves |curve| to disk. This should run in another thread to be non-blocking
+// to the main thread (if |is_testing| is false).
 // TODO(jiameng): alternative to WriteFile is WriteFileAtomically, but the
 // latter is very slow. Investigate whether we need to change to
 // WriteFileAtomically.
 void SaveCurveToDisk(const base::FilePath& path,
-                     const BrightnessCurve& curve,
+                     const MonotoneCubicSpline& curve,
                      bool is_testing) {
   DCHECK(is_testing ||
          !content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  const std::string data = CurveToString(curve);
+  const std::string data = curve.ToString();
+  DCHECK(!data.empty());
   const int bytes_written = base::WriteFile(path, data.data(), data.size());
   if (bytes_written != static_cast<int>(data.size())) {
     LOG(ERROR) << "Wrote " << bytes_written << " byte(s) instead of "
@@ -60,22 +75,24 @@
   }
 }
 
-// Trains a new curve using the current |curve| and training |data|. Returns the
-// new curve. This should run in another thread to be non-blocking to the main
+// Trains a new curve using training |data| and returns the new curve. This
+// should only be called after trainer has been initialized with a global curve
+// and a latest curve.
+// This should run in another thread to be non-blocking to the main
 // thread (if |is_testing| is false).
-BrightnessCurve TrainModel(Trainer* trainer,
-                           const BrightnessCurve& curve,
-                           const std::vector<TrainingDataPoint>& data,
-                           bool is_testing) {
+MonotoneCubicSpline TrainModel(Trainer* trainer,
+                               const std::vector<TrainingDataPoint>& data,
+                               bool is_testing) {
   DCHECK(is_testing ||
          !content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  return trainer->Train(curve, data);
+  return trainer->Train(data);
 }
 
 }  // namespace
 
 constexpr base::TimeDelta ModellerImpl::kTrainingDelay;
-constexpr int ModellerImpl::kMaxTrainingDataPoints;
+constexpr size_t ModellerImpl::kMaxTrainingDataPoints;
+constexpr size_t ModellerImpl::kMinTrainingDataPoints;
 constexpr int ModellerImpl::kAmbientLightHorizonSeconds;
 constexpr base::TimeDelta ModellerImpl::kAmbientLightHorizon;
 constexpr int ModellerImpl::kNumberAmbientValuesToTrack;
@@ -103,7 +120,7 @@
   DCHECK(observer);
   observers_.AddObserver(observer);
   if (model_status_ != Modeller::Status::kInitializing) {
-    observer->OnModelInitialized(model_status_, curve_);
+    observer->OnModelInitialized(model_status_, current_curve_);
   }
 }
 
@@ -148,7 +165,8 @@
 
   const double average_ambient_lux = AverageAmbient(ambient_light_values_, -1);
   data_cache_.push_back({old_brightness_percent, new_brightness_percent,
-                         average_ambient_lux, tick_clock_->NowTicks()});
+                         ConvertToLog(average_ambient_lux),
+                         tick_clock_->NowTicks()});
 
   if (data_cache_.size() == kMaxTrainingDataPoints) {
     model_timer_.Stop();
@@ -181,7 +199,7 @@
   return AverageAmbient(ambient_light_values_, -1);
 }
 
-int ModellerImpl::NumberTrainingDataPointsForTesting() const {
+size_t ModellerImpl::NumberTrainingDataPointsForTesting() const {
   return data_cache_.size();
 }
 
@@ -189,6 +207,10 @@
   return GetCurvePathFromProfile(profile);
 }
 
+MonotoneCubicSpline ModellerImpl::GetGlobalCurveForTesting() const {
+  return global_curve_;
+}
+
 ModellerImpl::ModellerImpl(
     Profile* profile,
     AlsReader* als_reader,
@@ -207,6 +229,7 @@
                base::OnTaskRunnerDeleter(model_task_runner_)),
       tick_clock_(tick_clock),
       model_timer_(tick_clock_),
+      global_curve_(CreateGlobalCurve()),
       weak_ptr_factory_(this) {
   DCHECK(als_reader);
   DCHECK(brightness_monitor);
@@ -274,7 +297,7 @@
 
   base::PostTaskAndReplyWithResult(
       model_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&LoadDataFromDisk, curve_path_, is_testing_),
+      base::BindOnce(&LoadCurveFromDisk, curve_path_, is_testing_),
       base::BindOnce(&ModellerImpl::OnCurveLoadedFromDisk,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -282,25 +305,22 @@
 void ModellerImpl::OnInitializationComplete() {
   DCHECK_NE(model_status_, Status::kInitializing);
   for (auto& observer : observers_)
-    observer.OnModelInitialized(model_status_, curve_);
+    observer.OnModelInitialized(model_status_, current_curve_);
 }
 
-// TODO(crbug.com/881215): add actual default curve and then add unit test too.
-void ModellerImpl::InitWithDefaultCurve() {
-  curve_.clear();
-  return;
-}
-
-void ModellerImpl::OnCurveLoadedFromDisk(const std::string& content) {
-  if (content.empty() || !CurveFromString(content, &curve_)) {
-    InitWithDefaultCurve();
+void ModellerImpl::OnCurveLoadedFromDisk(
+    const base::Optional<MonotoneCubicSpline>& curve) {
+  if (!curve.has_value()) {
+    current_curve_.emplace(global_curve_);
     model_status_ = Status::kGlobal;
   } else {
+    current_curve_.emplace(curve.value());
     model_status_ = Status::kPersonal;
   }
 
   OnInitializationComplete();
 
+  trainer_->SetInitialCurves(global_curve_, current_curve_.value());
   ScheduleTrainerStart();
 }
 
@@ -311,28 +331,28 @@
 }
 
 void ModellerImpl::StartTraining() {
-  if (data_cache_.empty()) {
+  if (data_cache_.size() < kMinTrainingDataPoints) {
     ScheduleTrainerStart();
     return;
   }
 
   base::PostTaskAndReplyWithResult(
       model_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&TrainModel, trainer_.get(), curve_,
-                     std::move(data_cache_), is_testing_),
+      base::BindOnce(&TrainModel, trainer_.get(), std::move(data_cache_),
+                     is_testing_),
       base::BindOnce(&ModellerImpl::OnTrainingFinished,
                      weak_ptr_factory_.GetWeakPtr()));
   data_cache_.clear();
 }
 
-void ModellerImpl::OnTrainingFinished(const BrightnessCurve& curve) {
-  curve_ = curve;
+void ModellerImpl::OnTrainingFinished(const MonotoneCubicSpline& curve) {
+  current_curve_.emplace(curve);
   for (auto& observer : observers_)
     observer.OnModelTrained(curve);
 
   model_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&SaveCurveToDisk, curve_path_, curve_, is_testing_));
+      base::BindOnce(&SaveCurveToDisk, curve_path_, curve, is_testing_));
   ScheduleTrainerStart();
 }
 
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h
index 50d3a16..35c4443 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h
@@ -44,7 +44,11 @@
   // If number of recorded training data has reached |kMaxTrainingDataPoints| we
   // start training immediately, without waiting for user to become idle for
   // |kTrainingDelay|.
-  static constexpr int kMaxTrainingDataPoints = 100;
+  static constexpr size_t kMaxTrainingDataPoints = 100;
+
+  // Only train when there are at least |kMinTrainingDataPoints| training data
+  // points.
+  static constexpr size_t kMinTrainingDataPoints = 10;
 
   // TODO(jiameng): we currently use past 10 seconds of ambient values to
   // calculate average. May revise.
@@ -99,11 +103,13 @@
 
   // Current number of training data points stored, which will be used for next
   // training.
-  int NumberTrainingDataPointsForTesting() const;
+  size_t NumberTrainingDataPointsForTesting() const;
 
   // Calls GetCurvePathFromProfile directly.
   base::FilePath GetCurvePathForTesting(Profile* profile) const;
 
+  MonotoneCubicSpline GetGlobalCurveForTesting() const;
+
  private:
   // ModellerImpl has weak dependencies on all parameters except |trainer|.
   ModellerImpl(Profile* profile,
@@ -138,12 +144,11 @@
   // |model_status_| is not |kInitializing|.
   void OnInitializationComplete();
 
-  // Called when there is no saved curve from the disk.
-  void InitWithDefaultCurve();
-
-  // Called after reading from disk is complete. |content| may be empty, in that
-  // case we'll construct a default curve.
-  void OnCurveLoadedFromDisk(const std::string& content);
+  // Called after we've attempted to construct a |curve| from data saved on
+  // disk. |curve| will be assigned to |current_curve_| if |curve| is not
+  // nullopt. Otherwise, |current_curve_| will have the same value as
+  // |global_curve_|.
+  void OnCurveLoadedFromDisk(const base::Optional<MonotoneCubicSpline>& curve);
 
   // Starts |model_timer_| to start training after certain inactivity period.
   void ScheduleTrainerStart();
@@ -153,7 +158,7 @@
   void StartTraining();
 
   // Called after training is complete with a new curve.
-  void OnTrainingFinished(const BrightnessCurve& curve);
+  void OnTrainingFinished(const MonotoneCubicSpline& curve);
 
   // If |is_testing_| is false, we check curve saving/loading and training jobs
   // are running on non-UI thread.
@@ -182,7 +187,10 @@
   base::FilePath curve_path_;
 
   // Latest trained curve.
-  BrightnessCurve curve_;
+  base::Optional<MonotoneCubicSpline> current_curve_;
+
+  // Global curve constructed from predefined params.
+  const MonotoneCubicSpline global_curve_;
 
   // Recent |kNumberAmbientValuesToTrack| ambient values.
   base::RingBuffer<AmbientLightSample, kNumberAmbientValuesToTrack>
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
index b84454c..970a76a 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/fake_brightness_monitor.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/trainer.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h"
 #include "chrome/test/base/testing_profile.h"
@@ -29,18 +30,23 @@
 
 namespace {
 
-void CompareBrightnessCurve(const BrightnessCurve& expected,
-                            const BrightnessCurve& actual) {
-  EXPECT_EQ(expected.size(), actual.size());
-  for (size_t i = 0; i < expected.size(); ++i) {
-    EXPECT_DOUBLE_EQ(expected[i].first, actual[i].first);
-    EXPECT_DOUBLE_EQ(expected[i].second, actual[i].second);
-  }
-}
+MonotoneCubicSpline CreateTestCurveFromTrainingData(
+    const std::vector<TrainingDataPoint>& data) {
+  CHECK_GT(data.size(), 1u);
 
-std::pair<double, double> DataToCurvePoint(const TrainingDataPoint& data) {
-  return std::make_pair(data.ambient_lux,
-                        (data.brightness_old + data.brightness_new) / 2);
+  std::vector<double> xs;
+  std::vector<double> ys;
+
+  const auto& data_point = data[0];
+  xs.push_back(data_point.ambient_log_lux);
+  ys.push_back((data_point.brightness_old + data_point.brightness_new) / 2);
+
+  for (size_t i = 1; i < data.size(); ++i) {
+    xs.push_back(xs[i - 1] + 1);
+    ys.push_back(ys[i - 1] + 1);
+  }
+
+  return MonotoneCubicSpline(xs, ys);
 }
 
 // Testing trainer.
@@ -50,16 +56,26 @@
   ~FakeTrainer() override = default;
 
   // Trainer overrides:
-  BrightnessCurve Train(const BrightnessCurve& /* curve */,
-                        const std::vector<TrainingDataPoint>& data) override {
-    BrightnessCurve result_curve;
-    for (const auto& training_data_point : data) {
-      result_curve.push_back(DataToCurvePoint(training_data_point));
-    }
-    return result_curve;
+  void SetInitialCurves(const MonotoneCubicSpline& global_curve,
+                        const MonotoneCubicSpline& current_curve) override {
+    CHECK(!global_curve_.has_value());
+    CHECK(!current_curve_.has_value());
+
+    global_curve_.emplace(global_curve);
+    current_curve_.emplace(current_curve);
+  }
+
+  MonotoneCubicSpline Train(
+      const std::vector<TrainingDataPoint>& data) override {
+    CHECK(current_curve_.has_value());
+    current_curve_.emplace(CreateTestCurveFromTrainingData(data));
+    return current_curve_.value();
   }
 
  private:
+  base::Optional<MonotoneCubicSpline> global_curve_;
+  base::Optional<MonotoneCubicSpline> current_curve_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeTrainer);
 };
 
@@ -69,32 +85,38 @@
   ~TestObserver() override = default;
 
   // Modeller::Observer overrides:
-  void OnModelTrained(const BrightnessCurve& brightness_curve) override {
-    brightness_curve_ = base::Optional<BrightnessCurve>(brightness_curve);
+  void OnModelTrained(const MonotoneCubicSpline& brightness_curve) override {
+    brightness_curve_.emplace(brightness_curve);
+    trained_curve_received_ = true;
   }
 
-  void OnModelInitialized(const Modeller::Status model_status,
-                          const BrightnessCurve& brightness_curve) override {
+  void OnModelInitialized(
+      const Modeller::Status model_status,
+      const base::Optional<MonotoneCubicSpline>& brightness_curve) override {
     model_status_ = base::Optional<Modeller::Status>(model_status);
-    brightness_curve_ = base::Optional<BrightnessCurve>(brightness_curve);
+    if (brightness_curve.has_value()) {
+      brightness_curve_.emplace(brightness_curve.value());
+    }
   }
 
   Modeller::Status model_status() const {
-    DCHECK(model_status_.has_value());
+    CHECK(model_status_.has_value());
     return model_status_.value();
   }
 
-  BrightnessCurve brightness_curve() const {
-    DCHECK(brightness_curve_.has_value());
+  MonotoneCubicSpline brightness_curve() const {
+    CHECK(brightness_curve_.has_value());
     return brightness_curve_.value();
   }
 
   bool HasModelStatus() { return model_status_.has_value(); }
   bool HasBrightnessCurve() { return brightness_curve_.has_value(); }
+  bool trained_curve_received() { return trained_curve_received_; }
 
  private:
   base::Optional<Modeller::Status> model_status_;
-  base::Optional<BrightnessCurve> brightness_curve_;
+  base::Optional<MonotoneCubicSpline> brightness_curve_;
+  bool trained_curve_received_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestObserver);
 };
@@ -130,12 +152,12 @@
   }
 
  protected:
-  void WriteCurveToFile(const BrightnessCurve& curve) {
+  void WriteCurveToFile(const MonotoneCubicSpline& curve) {
     const base::FilePath curve_path =
         modeller_->GetCurvePathForTesting(profile_.get());
     CHECK(!curve_path.empty());
 
-    const std::string& data = CurveToString(curve);
+    const std::string data = curve.ToString();
     const int bytes_written =
         base::WriteFile(curve_path, data.data(), data.size());
     ASSERT_EQ(bytes_written, static_cast<int>(data.size()))
@@ -168,6 +190,7 @@
   SetUpModeller();
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(Modeller::Status::kDisabled, test_observer_->model_status());
+  EXPECT_FALSE(test_observer_->HasBrightnessCurve());
 }
 
 // BrightnessMonitor is |kDisabled| when Modeller is created.
@@ -177,6 +200,7 @@
   SetUpModeller();
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(Modeller::Status::kDisabled, test_observer_->model_status());
+  EXPECT_FALSE(test_observer_->HasBrightnessCurve());
 }
 
 // AlsReader is |kDisabled| on later notification.
@@ -191,6 +215,7 @@
   fake_als_reader_.set_als_init_status(AlsReader::AlsInitStatus::kDisabled);
   fake_als_reader_.ReportReaderInitialized();
   EXPECT_EQ(Modeller::Status::kDisabled, test_observer_->model_status());
+  EXPECT_FALSE(test_observer_->HasBrightnessCurve());
 }
 
 // AlsReader is |kSuccess| on later notification.
@@ -206,7 +231,8 @@
   fake_als_reader_.ReportReaderInitialized();
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(Modeller::Status::kGlobal, test_observer_->model_status());
-  EXPECT_TRUE(test_observer_->brightness_curve().empty());
+  EXPECT_EQ(test_observer_->brightness_curve(),
+            modeller_->GetGlobalCurveForTesting());
 }
 
 // BrightnessMonitor is |kDisabled| on later notification.
@@ -221,6 +247,7 @@
   fake_brightness_monitor_.set_status(BrightnessMonitor::Status::kDisabled);
   fake_brightness_monitor_.ReportBrightnessMonitorInitialized();
   EXPECT_EQ(Modeller::Status::kDisabled, test_observer_->model_status());
+  EXPECT_FALSE(test_observer_->HasBrightnessCurve());
 }
 
 // BrightnessMonitor is |kSuccess| on later notification.
@@ -236,7 +263,8 @@
   fake_brightness_monitor_.ReportBrightnessMonitorInitialized();
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(Modeller::Status::kGlobal, test_observer_->model_status());
-  EXPECT_TRUE(test_observer_->brightness_curve().empty());
+  EXPECT_EQ(test_observer_->brightness_curve(),
+            modeller_->GetGlobalCurveForTesting());
 }
 
 // There is no saved curve, hence a global curve is created.
@@ -246,13 +274,16 @@
   SetUpModeller();
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(Modeller::Status::kGlobal, test_observer_->model_status());
-  EXPECT_TRUE(test_observer_->brightness_curve().empty());
+  EXPECT_EQ(test_observer_->brightness_curve(),
+            modeller_->GetGlobalCurveForTesting());
 }
 
 // A curve is loaded from disk, this is a personal curve.
 TEST_F(ModellerImplTest, CurveLoadedFromProfilePath) {
-  const BrightnessCurve curve = {{10, 15}, {20, 25}, {30, 35}, {40, 45},
-                                 {50, 55}, {60, 65}, {70, 75}, {80, 85}};
+  const std::vector<double> xs = {0, 10, 20, 40, 60, 80, 90, 100};
+  const std::vector<double> ys = {0, 5, 10, 15, 20, 25, 30, 40};
+  MonotoneCubicSpline curve(xs, ys);
+
   WriteCurveToFile(curve);
 
   scoped_task_environment_.RunUntilIdle();
@@ -303,7 +334,7 @@
   ASSERT_EQ(Modeller::Status::kGlobal, test_observer_->model_status());
 
   std::vector<TrainingDataPoint> expected_data;
-  for (int i = 0; i < ModellerImpl::kMaxTrainingDataPoints - 1; ++i) {
+  for (size_t i = 0; i < ModellerImpl::kMaxTrainingDataPoints - 1; ++i) {
     EXPECT_EQ(i, modeller_->NumberTrainingDataPointsForTesting());
     scoped_task_environment_.FastForwardBy(
         base::TimeDelta::FromMilliseconds(1));
@@ -314,8 +345,9 @@
     const double brightness_old = 10.0 + i;
     const double brightness_new = 20.0 + i;
     modeller_->OnUserBrightnessChanged(brightness_old, brightness_new);
-    expected_data.push_back({brightness_old, brightness_new,
-                             modeller_->AverageAmbientForTesting(), now});
+    expected_data.push_back(
+        {brightness_old, brightness_new,
+         ConvertToLog(modeller_->AverageAmbientForTesting()), now});
   }
 
   // Training should not have started.
@@ -330,16 +362,16 @@
   const double brightness_new = 95;
   modeller_->OnUserBrightnessChanged(brightness_old, brightness_new);
   expected_data.push_back({brightness_old, brightness_new,
-                           modeller_->AverageAmbientForTesting(), now});
+                           ConvertToLog(modeller_->AverageAmbientForTesting()),
+                           now});
   scoped_task_environment_.RunUntilIdle();
 
-  EXPECT_EQ(0, modeller_->NumberTrainingDataPointsForTesting());
-  const BrightnessCurve result_curve = test_observer_->brightness_curve();
-  BrightnessCurve expected_curve;
-  for (auto& training_data_point : expected_data) {
-    expected_curve.push_back(DataToCurvePoint(training_data_point));
-  }
-  CompareBrightnessCurve(expected_curve, result_curve);
+  EXPECT_EQ(0u, modeller_->NumberTrainingDataPointsForTesting());
+  const MonotoneCubicSpline& result_curve = test_observer_->brightness_curve();
+
+  const MonotoneCubicSpline expected_curve =
+      CreateTestCurveFromTrainingData(expected_data);
+  EXPECT_EQ(expected_curve, result_curve);
 }
 
 // User activities resets timer used to start training.
@@ -351,10 +383,25 @@
   ASSERT_EQ(Modeller::Status::kGlobal, test_observer_->model_status());
 
   fake_als_reader_.ReportAmbientLightUpdate(30);
-  modeller_->OnUserBrightnessChanged(10, 20);
-  const TrainingDataPoint expected_data = {
-      10, 20, 30, scoped_task_environment_.GetMockTickClock()->NowTicks()};
-  EXPECT_EQ(1, modeller_->NumberTrainingDataPointsForTesting());
+  std::vector<TrainingDataPoint> expected_data;
+  for (size_t i = 0; i < ModellerImpl::kMinTrainingDataPoints; ++i) {
+    EXPECT_EQ(i, modeller_->NumberTrainingDataPointsForTesting());
+    scoped_task_environment_.FastForwardBy(
+        base::TimeDelta::FromMilliseconds(1));
+    const base::TimeTicks now =
+        scoped_task_environment_.GetMockTickClock()->NowTicks();
+    const int lux = i * 20;
+    fake_als_reader_.ReportAmbientLightUpdate(lux);
+    const double brightness_old = 10.0 + i;
+    const double brightness_new = 20.0 + i;
+    modeller_->OnUserBrightnessChanged(brightness_old, brightness_new);
+    expected_data.push_back(
+        {brightness_old, brightness_new,
+         ConvertToLog(modeller_->AverageAmbientForTesting()), now});
+  }
+
+  EXPECT_EQ(ModellerImpl::kMinTrainingDataPoints,
+            modeller_->NumberTrainingDataPointsForTesting());
 
   scoped_task_environment_.FastForwardBy(ModellerImpl::kTrainingDelay -
                                          base::TimeDelta::FromSeconds(10));
@@ -365,7 +412,8 @@
 
   scoped_task_environment_.FastForwardBy(ModellerImpl::kTrainingDelay -
                                          base::TimeDelta::FromSeconds(2));
-  EXPECT_EQ(1, modeller_->NumberTrainingDataPointsForTesting());
+  EXPECT_EQ(ModellerImpl::kMinTrainingDataPoints,
+            modeller_->NumberTrainingDataPointsForTesting());
 
   // Another user event is received.
   modeller_->OnUserActivity(&mouse_event);
@@ -374,16 +422,40 @@
   scoped_task_environment_.FastForwardBy(ModellerImpl::kTrainingDelay -
                                          base::TimeDelta::FromSeconds(2));
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_EQ(1, modeller_->NumberTrainingDataPointsForTesting());
+  EXPECT_EQ(ModellerImpl::kMinTrainingDataPoints,
+            modeller_->NumberTrainingDataPointsForTesting());
 
   // After another 2 seconds, training is scheduled.
   scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(2));
   scoped_task_environment_.RunUntilIdle();
 
-  EXPECT_EQ(0, modeller_->NumberTrainingDataPointsForTesting());
-  const BrightnessCurve result_curve = test_observer_->brightness_curve();
-  BrightnessCurve expected_curve = {DataToCurvePoint(expected_data)};
-  CompareBrightnessCurve(expected_curve, result_curve);
+  EXPECT_EQ(0u, modeller_->NumberTrainingDataPointsForTesting());
+  const MonotoneCubicSpline& result_curve = test_observer_->brightness_curve();
+
+  const MonotoneCubicSpline expected_curve =
+      CreateTestCurveFromTrainingData(expected_data);
+  EXPECT_EQ(expected_curve, result_curve);
+}
+
+// No training is done because number of training data points is less than
+// |kMinTrainingDataPoints|.
+TEST_F(ModellerImplTest, MinTrainingDataPointsRequired) {
+  fake_als_reader_.set_als_init_status(AlsReader::AlsInitStatus::kSuccess);
+  fake_brightness_monitor_.set_status(BrightnessMonitor::Status::kSuccess);
+  SetUpModeller();
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_EQ(Modeller::Status::kGlobal, test_observer_->model_status());
+
+  fake_als_reader_.ReportAmbientLightUpdate(30);
+  modeller_->OnUserBrightnessChanged(10, 20);
+
+  // No training is done because we have too few training data points.
+  scoped_task_environment_.FastForwardBy(ModellerImpl::kTrainingDelay +
+                                         base::TimeDelta::FromSeconds(10));
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ(1u, modeller_->NumberTrainingDataPointsForTesting());
+
+  EXPECT_FALSE(test_observer_->trained_curve_received());
 }
 
 }  // namespace auto_screen_brightness
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.cc b/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.cc
index 80d6b80..34166830 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.cc
@@ -8,6 +8,9 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 
 namespace chromeos {
 namespace power {
@@ -90,6 +93,58 @@
 
 MonotoneCubicSpline::~MonotoneCubicSpline() = default;
 
+base::Optional<MonotoneCubicSpline> MonotoneCubicSpline::FromString(
+    const std::string& data) {
+  std::vector<double> xs;
+  std::vector<double> ys;
+
+  if (data.empty())
+    return base::nullopt;
+
+  base::StringPairs key_value_pairs;
+  if (!base::SplitStringIntoKeyValuePairs(data, ',', '\n', &key_value_pairs)) {
+    LOG(ERROR) << "Ill-formatted spline";
+    return base::nullopt;
+  }
+
+  for (base::StringPairs::iterator it = key_value_pairs.begin();
+       it != key_value_pairs.end(); ++it) {
+    double x;
+    if (!base::StringToDouble(it->first, &x)) {
+      LOG(ERROR) << "Ill-formatted xs";
+      return base::nullopt;
+    }
+
+    double y;
+    if (!base::StringToDouble(it->second, &y)) {
+      LOG(ERROR) << "Ill-formatted ys";
+      return base::nullopt;
+    }
+    xs.push_back(x);
+    ys.push_back(y);
+  }
+
+  if (xs.size() < 2)
+    return base::nullopt;
+
+  return MonotoneCubicSpline(xs, ys);
+}
+
+bool MonotoneCubicSpline::operator==(const MonotoneCubicSpline& spline) const {
+  if (xs_.size() != spline.xs_.size()) {
+    return false;
+  }
+
+  for (size_t i = 0; i < xs_.size(); ++i) {
+    if (std::abs(xs_[i] - spline.xs_[i]) >= kTol ||
+        std::abs(ys_[i] - spline.ys_[i]) >= kTol) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 double MonotoneCubicSpline::Interpolate(double x) const {
   DCHECK_GT(num_points_, 1u);
 
@@ -136,6 +191,16 @@
   return ys_;
 }
 
+std::string MonotoneCubicSpline::ToString() const {
+  std::vector<std::string> rows;
+  for (size_t i = 0; i < num_points_; ++i) {
+    rows.push_back(base::JoinString(
+        {base::NumberToString(xs_[i]), base::NumberToString(ys_[i])}, ","));
+  }
+
+  return base::JoinString(rows, "\n");
+}
+
 }  // namespace auto_screen_brightness
 }  // namespace power
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.h b/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.h
index b2c4a8d..804d54b 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.h
@@ -28,6 +28,15 @@
 
   ~MonotoneCubicSpline();
 
+  // Parses and returns a MonotoneCubicSpline from input |data| or nullopt if
+  // parsing fails. Correct formatting in |data| should be 1 row per
+  // (<x>, <y>) mapping, and values of xs should strictly increase per row and
+  // ys should be non-decreasing.
+  static base::Optional<MonotoneCubicSpline> FromString(
+      const std::string& data);
+
+  bool operator==(const MonotoneCubicSpline& spline) const;
+
   // Returns interpolated value for |x|. If |x| is smaller|greater than
   // smallest|largest value in |xs_|, then smallest|largest value in |ys_| will
   // be returned.
@@ -37,6 +46,10 @@
 
   std::vector<double> GetControlPointsY() const;
 
+  // Converts to a string. Each (x, y) point in this curve will be converted to
+  // 1 row and each (x, y) point will converted to x:y format.
+  std::string ToString() const;
+
  private:
   const std::vector<double> xs_;
   const std::vector<double> ys_;
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline_unittest.cc
index 08505ec..2de10fa 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline_unittest.cc
@@ -77,6 +77,34 @@
   }
 }
 
+TEST(MonotoneCubicSpline, FromStringCorrectFormat) {
+  const std::string data("1,10\n2,20\n3,30");
+  const base::Optional<MonotoneCubicSpline> spline_from_string =
+      MonotoneCubicSpline::FromString(data);
+  const std::vector<double> xs = {1, 2, 3};
+  const std::vector<double> ys = {10, 20, 30};
+  const MonotoneCubicSpline expected_spline(xs, ys);
+  EXPECT_EQ(expected_spline, spline_from_string);
+}
+
+TEST(MonotoneCubicSpline, FromStringTooFewRows) {
+  const std::string data("1,10");
+  const base::Optional<MonotoneCubicSpline> spline_from_string =
+      MonotoneCubicSpline::FromString(data);
+  EXPECT_FALSE(spline_from_string.has_value());
+}
+
+TEST(MonotoneCubicSpline, ToString) {
+  const std::vector<double> xs = {1, 2, 3};
+  const std::vector<double> ys = {10, 20, 30};
+  const MonotoneCubicSpline spline(xs, ys);
+  const std::string string_from_spline = spline.ToString();
+
+  const std::string expected_string("1,10\n2,20\n3,30");
+
+  EXPECT_EQ(expected_string, string_from_spline);
+}
+
 }  // namespace auto_screen_brightness
 }  // namespace power
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/trainer.h b/chrome/browser/chromeos/power/auto_screen_brightness/trainer.h
index 0ed5c89..b1d271fe 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/trainer.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/trainer.h
@@ -5,18 +5,17 @@
 #ifndef CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_TRAINER_H_
 #define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_TRAINER_H_
 
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.h"
+
 namespace chromeos {
 namespace power {
 namespace auto_screen_brightness {
 
-// It is a mapping from ambient light (in lux) to brightness percent. It should
-// be sorted in the ascending order in lux.
-using BrightnessCurve = std::vector<std::pair<double, double>>;
-
 struct TrainingDataPoint {
   double brightness_old;
   double brightness_new;
-  double ambient_lux;
+  double ambient_log_lux;
   base::TimeTicks sample_time;
 };
 
@@ -25,8 +24,13 @@
  public:
   virtual ~Trainer() = default;
 
-  virtual BrightnessCurve Train(const BrightnessCurve& curve,
-                                const std::vector<TrainingDataPoint>& data) = 0;
+  virtual void SetInitialCurves(const MonotoneCubicSpline& global_curve,
+                                const MonotoneCubicSpline& current_curve) = 0;
+
+  // Updates current curve stored in trainer with |data|. This function should
+  // only be called after |SetInitialCurves|.
+  virtual MonotoneCubicSpline Train(
+      const std::vector<TrainingDataPoint>& data) = 0;
 };
 
 }  // namespace auto_screen_brightness
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/utils.cc b/chrome/browser/chromeos/power/auto_screen_brightness/utils.cc
index fc9b208..a0f62869 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/utils.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/utils.cc
@@ -4,59 +4,12 @@
 
 #include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h"
 
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-
 namespace chromeos {
 namespace power {
 namespace auto_screen_brightness {
 
-bool CurveFromString(const std::string& data, BrightnessCurve* const curve) {
-  DCHECK(curve);
-  curve->clear();
-  if (data.empty())
-    return true;
-
-  base::StringPairs key_value_pairs;
-  if (!base::SplitStringIntoKeyValuePairs(data, ',', '\n', &key_value_pairs)) {
-    LOG(ERROR) << "Ill-formatted curve";
-    return false;
-  }
-
-  for (base::StringPairs::iterator it = key_value_pairs.begin();
-       it != key_value_pairs.end(); ++it) {
-    double lux;
-    if (!base::StringToDouble(it->first, &lux)) {
-      LOG(ERROR) << "Ill-formatted lux";
-      curve->clear();
-      return false;
-    }
-
-    double brightness;
-    if (!base::StringToDouble(it->second, &brightness)) {
-      LOG(ERROR) << "Ill-formatted brightness";
-      curve->clear();
-      return false;
-    }
-
-    curve->push_back(std::make_pair(lux, brightness));
-  }
-  return true;
-}
-
-std::string CurveToString(const BrightnessCurve& curve) {
-  if (curve.empty())
-    return "";
-
-  std::vector<std::string> rows;
-  for (const auto& kv : curve) {
-    rows.push_back(base::JoinString(
-        {base::NumberToString(kv.first), base::NumberToString(kv.second)},
-        ","));
-  }
-
-  return base::JoinString(rows, "\n");
+double ConvertToLog(double value) {
+  return std::log(1 + value);
 }
 
 }  // namespace auto_screen_brightness
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/utils.h b/chrome/browser/chromeos/power/auto_screen_brightness/utils.h
index a5b6cbe..96a2a295 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/utils.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/utils.h
@@ -10,21 +10,13 @@
 
 #include "base/containers/ring_buffer.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/power/auto_screen_brightness/trainer.h"
 
 namespace chromeos {
 namespace power {
 namespace auto_screen_brightness {
 
-// Replaces |curve| with the parsed curve data from |data|, returning true if
-// successful. Correct formatting in |data| should be 1 row per
-// (<ambient_light>, <brightness>) mapping. Ambient light should be a double and
-// brightness should be a double.
-bool CurveFromString(const std::string& data, BrightnessCurve* const curve);
-
-// Converts |curve| to a string. An empty string will be returned if |curve| is
-// empty.
-std::string CurveToString(const BrightnessCurve& curve);
+// Returns natural log of 1+|value|.
+double ConvertToLog(double value);
 
 struct AmbientLightSample {
   int lux;
diff --git a/chrome/browser/chromeos/tether/tether_service.cc b/chrome/browser/chromeos/tether/tether_service.cc
index 54162577..2f0707a1 100644
--- a/chrome/browser/chromeos/tether/tether_service.cc
+++ b/chrome/browser/chromeos/tether/tether_service.cc
@@ -685,6 +685,15 @@
           kUnavailableSuiteDisabled:
         return BETTER_TOGETHER_SUITE_DISABLED;
       case chromeos::multidevice_setup::mojom::FeatureState::
+          kUnavailableNoVerifiedHost:
+        // Note that because of the early return above after
+        // !HasSyncedTetherHosts, if this point is hit, there are synced tether
+        // hosts available, but the multidevice state is unverified. This switch
+        // case can only occur for legacy Magic Tether hosts, in which case the
+        // service should be enabled.
+        // TODO(crbug.com/894585): Remove this legacy special case after M71.
+        return ENABLED;
+      case chromeos::multidevice_setup::mojom::FeatureState::
           kNotSupportedByChromebook:
         // CryptAuth may not yet know that this device supports
         // MAGIC_TETHER_CLIENT (and the local device metadata is reflecting
@@ -694,10 +703,6 @@
         FALLTHROUGH;
       case chromeos::multidevice_setup::mojom::FeatureState::
           kNotSupportedByPhone:
-        FALLTHROUGH;
-      case chromeos::multidevice_setup::mojom::FeatureState::
-          kUnavailableNoVerifiedHost:
-        no_available_hosts_false_positive_encountered_ = true;
         return NO_AVAILABLE_HOSTS;
       default:
         // Other FeatureStates:
diff --git a/chrome/browser/chromeos/tether/tether_service_unittest.cc b/chrome/browser/chromeos/tether/tether_service_unittest.cc
index 0c7e5d5c9..6c958d6d 100644
--- a/chrome/browser/chromeos/tether/tether_service_unittest.cc
+++ b/chrome/browser/chromeos/tether/tether_service_unittest.cc
@@ -77,6 +77,9 @@
   for (size_t i = 0; i < kNumTestDevices; ++i) {
     list.push_back(cryptauth::RemoteDeviceRefBuilder()
                        .SetSupportsMobileHotspot(true)
+                       .SetSoftwareFeatureState(
+                           cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST,
+                           cryptauth::SoftwareFeatureState::kSupported)
                        .Build());
   }
   return list;
@@ -727,6 +730,7 @@
 
 TEST_F(TetherServiceTest,
        TestMultiDeviceSetupClientInitiallyHasNoVerifiedHost) {
+  fake_tether_host_fetcher_factory_->SetNoInitialDevices();
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
       {chromeos::features::kMultiDeviceApi,
@@ -745,6 +749,8 @@
           chromeos::NetworkTypePattern::Tether()));
   VerifyTetherActiveStatus(false /* expected_active */);
 
+  fake_tether_host_fetcher_factory_->last_created()->set_tether_hosts(
+      test_devices_);
   fake_multidevice_setup_client_->SetFeatureState(
       chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
       chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
@@ -767,6 +773,7 @@
                 chromeos::NetworkTypePattern::Tether()));
   VerifyTetherActiveStatus(true /* expected_active */);
 
+  fake_tether_host_fetcher_factory_->last_created()->set_tether_hosts({});
   fake_multidevice_setup_client_->SetFeatureState(
       chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
       chromeos::multidevice_setup::mojom::FeatureState::
@@ -778,6 +785,7 @@
           chromeos::NetworkTypePattern::Tether()));
   VerifyTetherActiveStatus(false /* expected_active */);
 
+  mock_timer_->Fire();
   ShutdownTetherService();
   VerifyTetherFeatureStateRecorded(
       TetherService::TetherFeatureState::NO_AVAILABLE_HOSTS,
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index e99d8ad8..79675fe 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -144,7 +144,7 @@
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   void WebContentsDestroyed() override;
@@ -186,16 +186,15 @@
   return content::KeyboardEventProcessingResult::NOT_HANDLED;
 }
 
-void DevToolsToolboxDelegate::HandleKeyboardEvent(
+bool DevToolsToolboxDelegate::HandleKeyboardEvent(
     content::WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
   if (event.windows_key_code == 0x08) {
     // Do not navigate back in history on Windows (http://crbug.com/74156).
-    return;
+    return false;
   }
   BrowserWindow* window = GetInspectedBrowserWindow();
-  if (window)
-    window->HandleKeyboardEvent(event);
+  return window && window->HandleKeyboardEvent(event);
 }
 
 void DevToolsToolboxDelegate::WebContentsDestroyed() {
@@ -1249,16 +1248,15 @@
   return content::KeyboardEventProcessingResult::NOT_HANDLED;
 }
 
-void DevToolsWindow::HandleKeyboardEvent(
+bool DevToolsWindow::HandleKeyboardEvent(
     WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
   if (event.windows_key_code == 0x08) {
     // Do not navigate back in history on Windows (http://crbug.com/74156).
-    return;
+    return true;
   }
   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
-  if (inspected_window)
-    inspected_window->HandleKeyboardEvent(event);
+  return inspected_window && inspected_window->HandleKeyboardEvent(event);
 }
 
 content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager(
diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h
index fc742cf..09808999 100644
--- a/chrome/browser/devtools/devtools_window.h
+++ b/chrome/browser/devtools/devtools_window.h
@@ -319,7 +319,7 @@
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   content::JavaScriptDialogManager* GetJavaScriptDialogManager(
diff --git a/chrome/browser/devtools/protocol_string.h b/chrome/browser/devtools/protocol_string.h
index 4a9c533..af77145 100644
--- a/chrome/browser/devtools/protocol_string.h
+++ b/chrome/browser/devtools/protocol_string.h
@@ -79,6 +79,20 @@
   static std::unique_ptr<protocol::Value> parseJSON(const String&);
 };
 
+// A read-only sequence of uninterpreted bytes with reference-counted storage.
+// Though the templates for generating the protocol bindings reference
+// this type, thus far it's not used in the Chrome layer, so we provide no
+// implementation here and rely on the linker optimizing it away. If this
+// changes, look to content/browser/devtools/protocol_string{.h,.cc} for
+// inspiration.
+class Binary {
+ public:
+  const uint8_t* data() const;
+  size_t size() const;
+  String toBase64() const;
+  static Binary fromBase64(const String& base64, bool* success);
+};
+
 std::unique_ptr<protocol::Value> toProtocolValue(const base::Value* value,
                                                  int depth);
 std::unique_ptr<base::Value> toBaseValue(protocol::Value* value, int depth);
diff --git a/chrome/browser/extensions/extension_keybinding_apitest.cc b/chrome/browser/extensions/extension_keybinding_apitest.cc
index 8bbdaa2..29fb452 100644
--- a/chrome/browser/extensions/extension_keybinding_apitest.cc
+++ b/chrome/browser/extensions/extension_keybinding_apitest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/browser_action_test_util.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -350,6 +351,8 @@
                                               control_is_modifier, false, false,
                                               command_is_modifier));
 
+  ui_test_utils::WaitUntilViewFocused(browser(), VIEW_ID_OMNIBOX);
+
   // Activate the shortcut.
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_P,
                                               control_is_modifier, false, false,
diff --git a/chrome/browser/extensions/extension_view.h b/chrome/browser/extensions/extension_view.h
index 9018f5c..41e89b5d 100644
--- a/chrome/browser/extensions/extension_view.h
+++ b/chrome/browser/extensions/extension_view.h
@@ -42,7 +42,7 @@
   virtual void RenderViewCreated(content::RenderViewHost* render_view_host) = 0;
 
   // Handles unhandled keyboard messages coming back from the renderer process.
-  virtual void HandleKeyboardEvent(
+  virtual bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) = 0;
 
diff --git a/chrome/browser/extensions/extension_view_host.cc b/chrome/browser/extensions/extension_view_host.cc
index a8ef1d9..56573ba 100644
--- a/chrome/browser/extensions/extension_view_host.cc
+++ b/chrome/browser/extensions/extension_view_host.cc
@@ -193,17 +193,18 @@
   return content::KeyboardEventProcessingResult::NOT_HANDLED;
 }
 
-void ExtensionViewHost::HandleKeyboardEvent(
+bool ExtensionViewHost::HandleKeyboardEvent(
     WebContents* source,
     const NativeWebKeyboardEvent& event) {
   if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP) {
     if (event.GetType() == NativeWebKeyboardEvent::kRawKeyDown &&
         event.windows_key_code == ui::VKEY_ESCAPE) {
       Close();
-      return;
+      return true;
     }
   }
   UnhandledKeyboardEvent(source, event);
+  return true;
 }
 
 bool ExtensionViewHost::PreHandleGestureEvent(
diff --git a/chrome/browser/extensions/extension_view_host.h b/chrome/browser/extensions/extension_view_host.h
index d16c9f7..fb034c1 100644
--- a/chrome/browser/extensions/extension_view_host.h
+++ b/chrome/browser/extensions/extension_view_host.h
@@ -72,7 +72,7 @@
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   bool PreHandleGestureEvent(content::WebContents* source,
diff --git a/chrome/browser/feedback/show_feedback_page.cc b/chrome/browser/feedback/show_feedback_page.cc
index fff878b3..8b3a682 100644
--- a/chrome/browser/feedback/show_feedback_page.cc
+++ b/chrome/browser/feedback/show_feedback_page.cc
@@ -16,8 +16,8 @@
 
 #if defined(OS_CHROMEOS)
 #include "base/sys_info.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "components/signin/core/browser/signin_manager.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #endif
 
 namespace feedback_private = extensions::api::feedback_private;
@@ -85,10 +85,9 @@
           : feedback_private::FeedbackFlow::FEEDBACK_FLOW_REGULAR;
 
 #if defined(OS_CHROMEOS)
-  SigninManagerBase* signin_manager =
-      SigninManagerFactory::GetForProfile(profile);
-  if (signin_manager &&
-      base::EndsWith(signin_manager->GetAuthenticatedAccountInfo().email,
+  auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
+  if (identity_manager &&
+      base::EndsWith(identity_manager->GetPrimaryAccountInfo().email,
                      kGoogleDotCom, base::CompareCase::INSENSITIVE_ASCII) &&
       IsFromUserInteraction(source) && IsBluetoothLoggingAllowedByBoard()) {
     flow = feedback_private::FeedbackFlow::FEEDBACK_FLOW_GOOGLEINTERNAL;
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index aad4c470..95e94cd4 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3257,6 +3257,11 @@
     "If enabled, the handwriting virtual keyboard will allow user to write "
     "anywhere on the screen";
 
+const char kEnableGoogleAssistantName[] = "Enable Google Assistant";
+const char kEnableGoogleAssistantDescription[] =
+    "Enable an experimental Assistant implementation that will work on all "
+    "Chromebooks.";
+
 const char kEnableHomeLauncherName[] = "Enable home launcher";
 const char kEnableHomeLauncherDescription[] =
     "Enable home launcher in tablet mode.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 5b27f50..44a2b75 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1984,6 +1984,9 @@
 extern const char kEnableFullscreenHandwritingVirtualKeyboardName[];
 extern const char kEnableFullscreenHandwritingVirtualKeyboardDescription[];
 
+extern const char kEnableGoogleAssistantName[];
+extern const char kEnableGoogleAssistantDescription[];
+
 extern const char kEnableHomeLauncherName[];
 extern const char kEnableHomeLauncherDescription[];
 
diff --git a/chrome/browser/net/chrome_accept_language_settings.cc b/chrome/browser/net/chrome_accept_language_settings.cc
deleted file mode 100644
index cf96988..0000000
--- a/chrome/browser/net/chrome_accept_language_settings.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/net/chrome_accept_language_settings.h"
-
-#include <unordered_set>
-
-#include "base/feature_list.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "chrome/common/chrome_features.h"
-#include "net/http/http_util.h"
-
-namespace chrome_accept_language_settings {
-namespace {
-
-// Helper class that builds the list of languages for the Accept-Language
-// headers.
-// The output is a comma-separated list of languages as string.
-// Duplicates are removed.
-class AcceptLanguageBuilder {
- public:
-  // Adds a language to the string.
-  // Duplicates are ignored.
-  void AddLanguageCode(const std::string& language) {
-    if (seen_.find(language) == seen_.end()) {
-      if (str_.empty()) {
-        base::StringAppendF(&str_, "%s", language.c_str());
-      } else {
-        base::StringAppendF(&str_, ",%s", language.c_str());
-      }
-      seen_.insert(language);
-    }
-  }
-
-  // Returns the string constructed up to this point.
-  std::string GetString() const { return str_; }
-
- private:
-  // The string that contains the list of languages, comma-separated.
-  std::string str_;
-  // Set the remove duplicates.
-  std::unordered_set<std::string> seen_;
-};
-
-// Extract the base language code from a language code.
-// If there is no '-' in the code, the original code is returned.
-std::string GetBaseLanguageCode(const std::string& language_code) {
-  const std::vector<std::string> tokens = base::SplitString(
-      language_code, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  return tokens.empty() ? "" : tokens[0];
-}
-
-}  // namespace
-
-std::string ComputeAcceptLanguageFromPref(const std::string& language_pref) {
-  std::string accept_languages_str =
-      base::FeatureList::IsEnabled(features::kUseNewAcceptLanguageHeader)
-          ? ExpandLanguageList(language_pref)
-          : language_pref;
-  return net::HttpUtil::GenerateAcceptLanguageHeader(accept_languages_str);
-}
-
-std::string ExpandLanguageList(const std::string& language_prefs) {
-  const std::vector<std::string> languages = base::SplitString(
-      language_prefs, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-  if (languages.empty())
-    return "";
-
-  AcceptLanguageBuilder builder;
-
-  const int size = languages.size();
-  for (int i = 0; i < size; ++i) {
-    const std::string& language = languages[i];
-    builder.AddLanguageCode(language);
-
-    // Extract the base language
-    const std::string& base_language = GetBaseLanguageCode(language);
-
-    // Look ahead and add the base language if the next language is not part
-    // of the same family.
-    const int j = i + 1;
-    if (j >= size || GetBaseLanguageCode(languages[j]) != base_language) {
-      builder.AddLanguageCode(base_language);
-    }
-  }
-
-  return builder.GetString();
-}
-
-}  // namespace chrome_accept_language_settings
diff --git a/chrome/browser/net/chrome_accept_language_settings.h b/chrome/browser/net/chrome_accept_language_settings.h
deleted file mode 100644
index 470d1575..0000000
--- a/chrome/browser/net/chrome_accept_language_settings.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_NET_CHROME_ACCEPT_LANGUAGE_SETTINGS_H_
-#define CHROME_BROWSER_NET_CHROME_ACCEPT_LANGUAGE_SETTINGS_H_
-
-#include <string>
-
-namespace chrome_accept_language_settings {
-
-// Given value of prefs::kAcceptLanguages pref, computes the corresponding
-// Accept-Language header to send.
-std::string ComputeAcceptLanguageFromPref(const std::string& language_pref);
-
-// Adds the base language if a corresponding language+region code is present.
-std::string ExpandLanguageList(const std::string& language_prefs);
-
-}  // namespace chrome_accept_language_settings
-
-#endif  // CHROME_BROWSER_NET_CHROME_ACCEPT_LANGUAGE_SETTINGS_H_
diff --git a/chrome/browser/net/chrome_accept_language_settings_unittest.cc b/chrome/browser/net/chrome_accept_language_settings_unittest.cc
deleted file mode 100644
index 53ab1f30..0000000
--- a/chrome/browser/net/chrome_accept_language_settings_unittest.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/net/chrome_accept_language_settings.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-// Test the expansion of the Language List.
-TEST(ChromeAcceptLanguageSettings, ExpandLanguageList) {
-  std::string output = chrome_accept_language_settings::ExpandLanguageList("");
-  EXPECT_EQ("", output);
-
-  output = chrome_accept_language_settings::ExpandLanguageList("en-US");
-  EXPECT_EQ("en-US,en", output);
-
-  output = chrome_accept_language_settings::ExpandLanguageList("fr");
-  EXPECT_EQ("fr", output);
-
-  // The base language is added after all regional codes...
-  output = chrome_accept_language_settings::ExpandLanguageList("en-US,en-CA");
-  EXPECT_EQ("en-US,en-CA,en", output);
-
-  // ... but before other language families.
-  output =
-      chrome_accept_language_settings::ExpandLanguageList("en-US,en-CA,fr");
-  EXPECT_EQ("en-US,en-CA,en,fr", output);
-
-  output = chrome_accept_language_settings::ExpandLanguageList(
-      "en-US,en-CA,fr,en-AU");
-  EXPECT_EQ("en-US,en-CA,en,fr,en-AU", output);
-
-  output =
-      chrome_accept_language_settings::ExpandLanguageList("en-US,en-CA,fr-CA");
-  EXPECT_EQ("en-US,en-CA,en,fr-CA,fr", output);
-
-  // Add a base language even if it's already in the list.
-  output = chrome_accept_language_settings::ExpandLanguageList(
-      "en-US,fr-CA,it,fr,es-AR,it-IT");
-  EXPECT_EQ("en-US,en,fr-CA,fr,it,es-AR,es,it-IT", output);
-}
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 109cf2899..dd9f903 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -13,13 +13,13 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/net/chrome_accept_language_settings.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_content_client.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths_internal.h"
 #include "chrome/common/pref_names.h"
 #include "components/certificate_transparency/pref_names.h"
@@ -35,6 +35,7 @@
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/common/url_constants.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "net/http/http_util.h"
 #include "net/net_buildflags.h"
 #include "services/network/public/cpp/features.h"
 
@@ -53,6 +54,14 @@
   return strings;
 }
 
+std::string ComputeAcceptLanguageFromPref(const std::string& language_pref) {
+  std::string accept_languages_str =
+      base::FeatureList::IsEnabled(features::kUseNewAcceptLanguageHeader)
+          ? net::HttpUtil::ExpandLanguageList(language_pref)
+          : language_pref;
+  return net::HttpUtil::GenerateAcceptLanguageHeader(accept_languages_str);
+}
+
 }  // namespace
 
 ProfileNetworkContextService::ProfileNetworkContextService(Profile* profile)
@@ -218,8 +227,7 @@
 }
 
 std::string ProfileNetworkContextService::ComputeAcceptLanguage() const {
-  return chrome_accept_language_settings::ComputeAcceptLanguageFromPref(
-      pref_accept_language_.GetValue());
+  return ComputeAcceptLanguageFromPref(pref_accept_language_.GetValue());
 }
 
 void ProfileNetworkContextService::UpdateReferrersEnabled() {
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.css b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.css
index 0a1c639b..9925627a 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.css
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.css
@@ -10,3 +10,38 @@
 #footer-text {
   padding-top: 24px;
 }
+
+.oobe-popup {
+  width: 640px;
+}
+
+#webview-container {
+  width: 100%;
+}
+
+#webview-container.overlay-loading > webview {
+  visibility: hidden;
+}
+
+#overlay-webview {
+  border: 1px solid #d9d9d9;
+  box-sizing: border-box;
+  display: block;
+  height: 300px;
+  margin: 16px;
+  width: 608px;
+}
+
+#overlay-close-button {
+  margin-right: 0;
+}
+
+#close-button-text {
+  padding: 0 16px 0 16px;
+}
+
+.button-strip {
+  display: flex;
+  justify-content: flex-end;
+  margin: 16px;
+}
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.html
index 2f63a66..d0f650b5 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.html
@@ -5,6 +5,7 @@
 <dom-module id="assistant-third-party">
   <template>
     <link rel="stylesheet" href="../login/oobe_flex_layout.css">
+    <link rel="stylesheet" href="../login/oobe_popup_overlay.css">
     <link rel="stylesheet" href="assistant_shared_styles.css">
     <link rel="stylesheet" href="assistant_third_party.css">
     <oobe-dialog id="third-party-dialog" role="dialog" has-buttons hide-shadow
@@ -26,5 +27,19 @@
         </oobe-next-button>
       </div>
     </oobe-dialog>
+
+    <div id="third-party-overlay" class="popup-overlay" hidden>
+      <div id="overlay-container" class="oobe-popup not-resizable">
+        <div id="webview-container">
+          <webview id="overlay-webview"></webview>
+        </div>
+        <div class="button-strip">
+          <oobe-text-button inverse id="overlay-close-button">
+            <div i18n-content="assistantOptinOKButton" id="close-button-text">
+            </div>
+          </oobe-text-button>
+        </div>
+      </div>
+    </div>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
index 9a9aa00e..2c75ab9 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_third_party.js
@@ -69,6 +69,37 @@
   },
 
   /**
+   * Click event handler for information links.
+   * @param {MouseEvent} e click event.
+   */
+  urlClickHandler: function(e) {
+    if (!e.target.localName == 'a') {
+      return;
+    }
+    e.preventDefault();
+    this.showThirdPartyOverlay(e.target.href);
+  },
+
+  /**
+   * Shows third party information links in overlay dialog.
+   * @param {string} url URL to show.
+   */
+  showThirdPartyOverlay: function(url) {
+    this.$['webview-container'].classList.add('overlay-loading');
+    this.$['overlay-webview'].src = url;
+
+    var overlay = this.$['third-party-overlay'];
+    overlay.hidden = false;
+  },
+
+  /**
+   * Hides overlay dialog.
+   */
+  hideOverlay: function() {
+    this.$['third-party-overlay'].hidden = true;
+  },
+
+  /**
    * Reloads the page.
    */
   reloadPage: function() {
@@ -85,6 +116,8 @@
     this.$['footer-text'].innerHTML =
         this.sanitizer_.sanitizeHtml(data['thirdPartyFooter']);
 
+    this.$['footer-text'].onclick = this.urlClickHandler.bind(this);
+
     this.consentStringLoaded_ = true;
     if (this.settingZippyLoaded_) {
       this.onPageLoaded();
@@ -120,6 +153,8 @@
           this.sanitizer_.sanitizeHtml(data['additionalInfo']);
       zippy.appendChild(additional);
 
+      additional.onclick = this.urlClickHandler.bind(this);
+
       this.$['insertion-point'].appendChild(zippy);
     }
 
@@ -147,6 +182,13 @@
    * Signal from host to show the screen.
    */
   onShow: function() {
+    this.$['overlay-close-button'].addEventListener(
+        'click', this.hideOverlay.bind(this));
+    var webviewContainer = this.$['webview-container'];
+    this.$['overlay-webview'].addEventListener('contentload', function() {
+      webviewContainer.classList.remove('overlay-loading');
+    });
+
     if (!this.settingZippyLoaded_ || !this.consentStringLoaded_) {
       this.reloadPage();
     } else {
diff --git a/chrome/browser/resources/chromeos/login/demo_setup.html b/chrome/browser/resources/chromeos/login/demo_setup.html
index fab9a8e..6116e9d 100644
--- a/chrome/browser/resources/chromeos/login/demo_setup.html
+++ b/chrome/browser/resources/chromeos/login/demo_setup.html
@@ -34,7 +34,7 @@
       <h1 slot="title">
         [[i18nDynamic(locale, 'demoSetupProgressScreenTitle')]]
       </h1>
-      <div slot="footer" class="flex layout vertical center">
+      <div slot="footer" class="flex layout vertical center center-justified">
         <paper-spinner-lite id="spinner" dir="ltr" active></paper-spinner-lite>
       </div>
     </oobe-dialog>
@@ -48,7 +48,7 @@
       <div slot="subtitle">
         [[i18nDynamic(locale, 'demoSetupErrorScreenSubtitle')]]
       </div>
-      <div slot="footer" class="flex layout vertical center">
+      <div slot="footer" class="flex layout vertical center center-justified">
         <img srcset="images/alert-illustration_1x.svg 1x,
             images/alert-illustration_2x.svg 2x">
       </div>
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_card.html b/chrome/browser/resources/chromeos/login/discover/discover_card.html
index f84f896..bd8cc64e 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_card.html
+++ b/chrome/browser/resources/chromeos/login/discover/discover_card.html
@@ -14,6 +14,7 @@
 <dom-module id="discover-card">
   <template>
     <style include="iron-flex iron-flex-alignment iron-positioning"></style>
+    <link rel="stylesheet" href="../oobe_iron_flex_layout_fix.css">
     <link rel="stylesheet" href="../oobe_fonts.css">
     <link rel="stylesheet" href="discover_card.css">
     <div class="fit layout vertical center end-justified">
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_ui.html b/chrome/browser/resources/chromeos/login/discover/discover_ui.html
index 53cc2cd..228abb8 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_ui.html
+++ b/chrome/browser/resources/chromeos/login/discover/discover_ui.html
@@ -1,5 +1,4 @@
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 
 <!--
   Discover App object. Manages all Discover UI behavior.
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_welcome.html b/chrome/browser/resources/chromeos/login/discover/discover_welcome.html
index 6f02b947..a325e9c 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_welcome.html
+++ b/chrome/browser/resources/chromeos/login/discover/discover_welcome.html
@@ -10,6 +10,7 @@
     <style
         include="iron-flex iron-flex-alignment iron-positioning paper-button-style cr-icons cr-shared-style">
     </style>
+    <link rel="stylesheet" href="../oobe_iron_flex_layout_fix.css">
     <oobe-dialog id="discoverWelcome" role="dialog" no-header no-footer-padding
         aria-label$="[[i18nDynamic(locale, 'discoverWelcomeTitle')]]">
       <div slot="footer" class="flex layout vertical center">
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_launch_help_app.html b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_launch_help_app.html
index e7f92d17..ae4a5a0 100644
--- a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_launch_help_app.html
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_launch_help_app.html
@@ -1,4 +1,3 @@
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html
index 7883666..2ee8f569 100644
--- a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html
@@ -62,6 +62,7 @@
     <style include="iron-flex iron-flex-alignment iron-positioning
                     cr-shared-style">
     </style>
+    <link rel="stylesheet" href="../../oobe_iron_flex_layout_fix.css">
     <style include="settings-shared"></style>
     <link rel="stylesheet" href="discover_module_pin_setup.css">
     <oobe-dialog id="loading" role="dialog" no-header no-footer-padding
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_redeem_offers.html b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_redeem_offers.html
index 6ea4cd5b..17d32caa 100644
--- a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_redeem_offers.html
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_redeem_offers.html
@@ -1,4 +1,3 @@
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_sync_files.html b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_sync_files.html
index bbbcff51..50de0a6 100644
--- a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_sync_files.html
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_sync_files.html
@@ -1,4 +1,3 @@
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
diff --git a/chrome/browser/resources/chromeos/login/enrollment_license_card.html b/chrome/browser/resources/chromeos/login/enrollment_license_card.html
index 58ec66dd..28da411 100644
--- a/chrome/browser/resources/chromeos/login/enrollment_license_card.html
+++ b/chrome/browser/resources/chromeos/login/enrollment_license_card.html
@@ -29,6 +29,7 @@
     <style
         include="shared-style iron-flex iron-flex-alignment iron-positioning">
     </style>
+    <link rel="stylesheet" href="oobe_iron_flex_layout_fix.css">
 
     <oobe-dialog id="license-selection-prompt-card" has-buttons>
       <hd-iron-icon slot="oobe-icon"
diff --git a/chrome/browser/resources/chromeos/login/hd-iron-icon.html b/chrome/browser/resources/chromeos/login/hd-iron-icon.html
index d858cb9..b9a670a 100644
--- a/chrome/browser/resources/chromeos/login/hd-iron-icon.html
+++ b/chrome/browser/resources/chromeos/login/hd-iron-icon.html
@@ -20,6 +20,7 @@
 <dom-module id="hd-iron-icon">
   <template>
     <style include="iron-flex iron-flex-alignment iron-positioning"></style>
+    <link rel="stylesheet" href="oobe_iron_flex_layout_fix.css">
     <link rel="stylesheet" href="hd-iron-icon.css">
     <div class="flex layout vertical">
       <iron-icon id="icon1x" icon="[[icon1x]]"></iron-icon>
diff --git a/chrome/browser/resources/chromeos/login/offline_ad_login.html b/chrome/browser/resources/chromeos/login/offline_ad_login.html
index c64c401..7cc780f 100644
--- a/chrome/browser/resources/chromeos/login/offline_ad_login.html
+++ b/chrome/browser/resources/chromeos/login/offline_ad_login.html
@@ -48,6 +48,7 @@
   <template>
     <style include="md-select iron-flex iron-flex-alignment iron-positioning">
     </style>
+    <link rel="stylesheet" href="oobe_iron_flex_layout_fix.css">
     <link rel="stylesheet" href="offline_ad_login.css">
     <oobe-dialog id="unlockStep" hidden="[[!unlockPasswordStep]]"
         aria-label$="[[adWelcomeMessage]]" has-buttons>
diff --git a/chrome/browser/resources/chromeos/login/oobe_a11y_option.html b/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
index 19d60ed..cbb41e3b 100644
--- a/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
+++ b/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
@@ -9,6 +9,7 @@
     <style
         include="shared-style iron-flex iron-flex-alignment iron-positioning">
     </style>
+    <link rel="stylesheet" href="oobe_iron_flex_layout_fix.css">
     <div id="elementBox" class="layout horizontal">
       <div class="flex layout vertical center-justified">
         <div id="titleContainer">
diff --git a/chrome/browser/resources/chromeos/login/oobe_buttons.html b/chrome/browser/resources/chromeos/login/oobe_buttons.html
index e6714e1..91ecf3a72 100644
--- a/chrome/browser/resources/chromeos/login/oobe_buttons.html
+++ b/chrome/browser/resources/chromeos/login/oobe_buttons.html
@@ -53,6 +53,7 @@
 <dom-module id="oobe-text-button">
   <template>
     <style include="iron-flex iron-flex-alignment iron-positioning"></style>
+    <link rel="stylesheet" href="oobe_iron_flex_layout_fix.css">
     <style include="paper-button-style cr-icons cr-shared-style"></style>
     <link rel="stylesheet" href="oobe_fonts.css">
     <link rel="stylesheet" href="oobe_text_buttons.css">
@@ -88,6 +89,7 @@
 <dom-module id="oobe-back-button">
   <template>
     <style include="iron-flex iron-flex-alignment iron-positioning"></style>
+    <link rel="stylesheet" href="oobe_iron_flex_layout_fix.css">
     <style include="paper-button-style cr-icons cr-shared-style"></style>
     <link rel="stylesheet" href="oobe_fonts.css">
     <link rel="stylesheet" href="oobe_text_buttons.css">
@@ -110,6 +112,7 @@
 <dom-module id="oobe-next-button">
   <template>
     <style include="iron-flex iron-flex-alignment iron-positioning"></style>
+    <link rel="stylesheet" href="oobe_iron_flex_layout_fix.css">
     <style include="paper-button-style cr-icons cr-shared-style"></style>
     <link rel="stylesheet" href="oobe_fonts.css">
     <link rel="stylesheet" href="oobe_text_buttons.css">
@@ -146,6 +149,7 @@
 <dom-module id="oobe-welcome-secondary-button">
   <template>
     <style include="iron-flex iron-flex-alignment iron-positioning"></style>
+    <link rel="stylesheet" href="oobe_iron_flex_layout_fix.css">
     <style include="paper-button-style cr-icons cr-shared-style"></style>
     <link rel="stylesheet" href="oobe_fonts.css">
     <link rel="stylesheet" href="oobe_text_buttons.css">
diff --git a/chrome/browser/resources/chromeos/login/oobe_iron_flex_layout_fix.css b/chrome/browser/resources/chromeos/login/oobe_iron_flex_layout_fix.css
new file mode 100644
index 0000000..cd8db9d
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/oobe_iron_flex_layout_fix.css
@@ -0,0 +1,16 @@
+/* Copyright 2018 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. */
+
+/**
+ * This fixes the issue with polymer 1.0 layout classes.
+ * (see https://crbug.com/892757)
+ */
+
+.layout.horizontal,
+.layout.horizontal-reverse,
+.layout.inline,
+.layout.vertical,
+.layout.vertical-reverse {
+  min-height: 0;
+}
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/BUILD.gn b/chrome/browser/resources/chromeos/zip_archiver/cpp/BUILD.gn
index 5cb01cea..fd662fe 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/BUILD.gn
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/BUILD.gn
@@ -45,6 +45,7 @@
 
 test("ziparchiver_unittests") {
   sources = [
+    "compressor_archive_minizip_unittest.cc",
     "test_module.cc",
     "volume_archive_minizip_unittest.cc",
   ]
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.cc
index aee6265..7995e74d 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.cc
@@ -32,100 +32,89 @@
          exploded.minute << 5 | exploded.second >> 1;
 }
 
-};  // namespace
-
-namespace compressor_archive_functions {
-
-// Called when minizip tries to open a zip archive file. We do nothing here
-// because JavaScript takes care of file opening operation.
-void* CustomArchiveOpen(void* compressor,
-                        const char* /*filename*/,
-                        int /*mode*/) {
+void* MinizipOpen(void* compressor, const char* /*filename*/, int /*mode*/) {
   return compressor;
 }
 
-// This function is not called because we don't unpack zip files here.
-uint32_t CustomArchiveRead(void* /*compressor*/,
-                           void* /*stream*/,
-                           void* /*buffur*/,
-                           uint32_t /*size*/) {
-  return 0 /* Success */;
+uint32_t MinizipRead(void* /*compressor*/,
+                     void* /*stream*/,
+                     void* /*buffur*/,
+                     uint32_t /*size*/) {
+  NOTREACHED();
+  return 0;
 }
 
+int MinizipClose(void* /*compressor*/, void* /*stream*/) {
+  return 0;
+}
+
+int MinizipError(void* /*compressor*/, void* /*stream*/) {
+  return 0;
+}
+
+};  // namespace
+
 // Called when data chunk must be written on the archive. It copies data
 // from the given buffer processed by minizip to an array buffer and passes
 // it to compressor_stream.
-uint32_t CustomArchiveWrite(void* compressor,
-                            void* /*stream*/,
-                            const void* zip_buffer,
-                            uint32_t zip_length) {
-  CompressorArchiveMinizip* compressor_minizip =
-      static_cast<CompressorArchiveMinizip*>(compressor);
+uint32_t CompressorArchiveMinizip::MinizipWrite(void* compressor,
+                                                void* /*stream*/,
+                                                const void* zip_buffer,
+                                                uint32_t zip_length) {
+  return static_cast<CompressorArchiveMinizip*>(compressor)
+      ->StreamWrite(zip_buffer, zip_length);
+}
 
-  int64_t written_bytes = compressor_minizip->compressor_stream()->Write(
-      compressor_minizip->offset(), zip_length,
-      static_cast<const char*>(zip_buffer));
+uint32_t CompressorArchiveMinizip::StreamWrite(const void* zip_buffer,
+                                               uint32_t zip_length) {
+  int64_t written_bytes = compressor_stream()->Write(
+      offset_, zip_length, static_cast<const char*>(zip_buffer));
 
   if (written_bytes != zip_length)
     return 0 /* Error */;
 
   // Update offset_ and length_.
-  compressor_minizip->set_offset(compressor_minizip->offset() + written_bytes);
-  if (compressor_minizip->offset() > compressor_minizip->length())
-    compressor_minizip->set_length(compressor_minizip->offset());
-  return static_cast<uLong>(written_bytes);
+  offset_ += written_bytes;
+  if (offset_ > length_)
+    length_ = offset_;
+  return static_cast<uint32_t>(written_bytes);
 }
 
 // Returns the offset from the beginning of the data.
-long CustomArchiveTell(void* compressor, void* /*stream*/) {
-  CompressorArchiveMinizip* compressor_minizip =
-      static_cast<CompressorArchiveMinizip*>(compressor);
-  return static_cast<long>(compressor_minizip->offset_);
+long CompressorArchiveMinizip::MinizipTell(void* compressor, void* /*stream*/) {
+  return static_cast<CompressorArchiveMinizip*>(compressor)->StreamTell();
+}
+
+long CompressorArchiveMinizip::StreamTell() {
+  return static_cast<long>(offset_);
 }
 
 // Moves the current offset to the specified position.
-long CustomArchiveSeek(void* compressor,
-                       void* /*stream*/,
-                       uint32_t offset,
-                       int origin) {
-  CompressorArchiveMinizip* compressor_minizip =
-      static_cast<CompressorArchiveMinizip*>(compressor);
+long CompressorArchiveMinizip::MinizipSeek(void* compressor,
+                                           void* /*stream*/,
+                                           uint32_t offset,
+                                           int origin) {
+  return static_cast<CompressorArchiveMinizip*>(compressor)
+      ->StreamSeek(offset, origin);
+}
 
+long CompressorArchiveMinizip::StreamSeek(uint32_t offset, int origin) {
   if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
-    compressor_minizip->set_offset(
-        std::min(compressor_minizip->offset() + static_cast<int64_t>(offset),
-                 compressor_minizip->length()));
+    offset_ = std::min(offset_ + static_cast<int64_t>(offset), length_);
     return 0 /* Success */;
   }
   if (origin == ZLIB_FILEFUNC_SEEK_END) {
-    compressor_minizip->set_offset(
-        std::max(compressor_minizip->length() - static_cast<int64_t>(offset),
-                 static_cast<int64_t>(0)));
+    offset_ = std::max(length_ - static_cast<int64_t>(offset),
+                       static_cast<int64_t>(0));
     return 0 /* Success */;
   }
   if (origin == ZLIB_FILEFUNC_SEEK_SET) {
-    compressor_minizip->set_offset(
-        std::min(static_cast<int64_t>(offset), compressor_minizip->length()));
+    offset_ = std::min(static_cast<int64_t>(offset), length_);
     return 0 /* Success */;
   }
   return -1 /* Error */;
 }
 
-// Releases all used resources. compressor points to compressor_minizip and
-// it is deleted in the destructor of Compressor, so we don't need to delete
-// it here.
-int CustomArchiveClose(void* /*compressor*/, void* /*stream*/) {
-  return 0 /* Success */;
-}
-
-// Returns the last error that happened when writing data. This function always
-// returns zero, which means there are no errors.
-int CustomArchiveError(void* /*compressor*/, void* /*stream*/) {
-  return 0 /* Success */;
-}
-
-}  // namespace compressor_archive_functions
-
 CompressorArchiveMinizip::CompressorArchiveMinizip(
     CompressorStream* compressor_stream)
     : CompressorArchive(compressor_stream),
@@ -140,13 +129,13 @@
 bool CompressorArchiveMinizip::CreateArchive() {
   // Set up archive object.
   zlib_filefunc_def zip_funcs;
-  zip_funcs.zopen_file = compressor_archive_functions::CustomArchiveOpen;
-  zip_funcs.zread_file = compressor_archive_functions::CustomArchiveRead;
-  zip_funcs.zwrite_file = compressor_archive_functions::CustomArchiveWrite;
-  zip_funcs.ztell_file = compressor_archive_functions::CustomArchiveTell;
-  zip_funcs.zseek_file = compressor_archive_functions::CustomArchiveSeek;
-  zip_funcs.zclose_file = compressor_archive_functions::CustomArchiveClose;
-  zip_funcs.zerror_file = compressor_archive_functions::CustomArchiveError;
+  zip_funcs.zopen_file = MinizipOpen;
+  zip_funcs.zread_file = MinizipRead;
+  zip_funcs.zwrite_file = MinizipWrite;
+  zip_funcs.ztell_file = MinizipTell;
+  zip_funcs.zseek_file = MinizipSeek;
+  zip_funcs.zclose_file = MinizipClose;
+  zip_funcs.zerror_file = MinizipError;
   zip_funcs.opaque = this;
 
   zip_file_ = zipOpen2(nullptr /* pathname */, APPEND_STATUS_CREATE,
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h
index 1fd1da9..d48036a3 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h
@@ -14,23 +14,6 @@
 
 class CompressorStream;
 
-// A name space with custom functions passed to minizip.
-namespace compressor_archive_functions {
-
-uLong CustomArchiveWrite(void* compressor,
-                         void* stream,
-                         const void* buffer,
-                         uLong length);
-
-long CustomArchiveTell(void* compressor, void* stream);
-
-long CustomArchiveSeek(void* compressor,
-                       void* stream,
-                       uLong offset,
-                       int origin);
-
-}  // namespace compressor_archive_functions
-
 class CompressorArchiveMinizip : public CompressorArchive {
  public:
   explicit CompressorArchiveMinizip(CompressorStream* compressor_stream);
@@ -52,37 +35,24 @@
                     base::Time modification_time,
                     bool is_directory) override;
 
-  // A getter function for zip_file_.
-  zipFile zip_file() const { return zip_file_; }
-
-  // Getter and setter for offset_.
-  int64_t offset() const { return offset_; }
-  void set_offset(int64_t value) { offset_ = value; }
-
-  // Getter and setter for length_.
-  int64_t length() const { return length_; }
-  void set_length(int64_t value) { length_ = value; }
-
-  // A getter function for compressor_stream.
-  CompressorStream* compressor_stream() const { return compressor_stream_; }
-
-  // Custom functions need to access private variables of
-  // CompressorArchiveMinizip frequently.
-  friend uLong compressor_archive_functions::CustomArchiveWrite(
-      void* compressor,
-      void* stream,
-      const void* buffer,
-      uLong length);
-
-  friend long compressor_archive_functions::CustomArchiveTell(void* compressor,
-                                                              void* stream);
-
-  friend long compressor_archive_functions::CustomArchiveSeek(void* compressor,
-                                                              void* stream,
-                                                              uLong offset,
-                                                              int origin);
-
  private:
+  // Stream functions used by minizip. In all cases, |compressor| points to
+  // |this|.
+  static uint32_t MinizipWrite(void* compressor,
+                               void* stream,
+                               const void* buffer,
+                               uint32_t length);
+  static long MinizipTell(void* compressor, void* stream);
+  static long MinizipSeek(void* compressor,
+                          void* stream,
+                          uint32_t offset,
+                          int origin);
+
+  // Implementation of stream functions used by minizip.
+  uint32_t StreamWrite(const void* buffer, uint32_t length);
+  long StreamTell();
+  long StreamSeek(uint32_t offset, int origin);
+
   // An instance that takes care of all IO operations.
   CompressorStream* compressor_stream_;
 
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip_unittest.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip_unittest.cc
new file mode 100644
index 0000000..d07d3316
--- /dev/null
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_stream.h"
+#include "chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestFileName[] = "test.txt";
+const char kLargeTestFileName[] = "large.file";
+const char kTestDirName[] = "foo";
+const char kTestFileContent[] = "Hello, World!";
+const std::string kLargeTestFileContent(1234567, 'a');
+
+class TestCompressorStream : public CompressorStream {
+ public:
+  TestCompressorStream() = default;
+
+  int64_t Flush() override { return 0; }
+
+  int64_t Write(int64_t zip_offset,
+                int64_t zip_length,
+                const char* zip_buffer) override {
+    CHECK_EQ(zip_offset, write_offset_);
+    CHECK_GT(zip_length, 0);
+    CHECK_NE(zip_buffer, nullptr);
+
+    if (write_error_)
+      return -1;
+
+    write_buffer_.append(zip_buffer, zip_length);
+    write_offset_ += zip_length;
+    return zip_length;
+  }
+
+  int64_t WriteChunkDone(int64_t write_bytes) override {
+    NOTREACHED();
+    return -1;
+  }
+
+  int64_t Read(int64_t bytes_to_read, char* destination_buffer) override {
+    if (read_buffer_.empty())
+      return 0;
+
+    int64_t read_length =
+        std::min(bytes_to_read, static_cast<int64_t>(read_buffer_.size()));
+    memcpy(destination_buffer, read_buffer_.data(), read_length);
+    read_buffer_ = read_buffer_.substr(read_length);
+    return read_length;
+  }
+
+  int64_t ReadFileChunkDone(int64_t read_bytes,
+                            pp::VarArrayBuffer* buffer) override {
+    NOTREACHED();
+    return -1;
+  }
+
+  void SetReadBuffer(const std::string& buffer) { read_buffer_ = buffer; }
+  void SetWriteError() { write_error_ = true; }
+
+  const std::string& write_buffer() { return write_buffer_; }
+
+ private:
+  std::string write_buffer_;
+  int64_t write_offset_ = 0;
+
+  bool write_error_ = false;
+
+  std::string read_buffer_;
+};
+
+class InMemoryVolumeReader : public VolumeReader {
+ public:
+  explicit InMemoryVolumeReader(const std::string& file) : file_(file) {}
+
+  int64_t Read(int64_t bytes_to_read,
+               const void** destination_buffer) override {
+    if (file_offset_ >= static_cast<int64_t>(file_.size()))
+      return 0;
+
+    int64_t read_length = std::min(
+        bytes_to_read, static_cast<int64_t>(file_.size()) - file_offset_);
+    *destination_buffer = static_cast<const void*>(file_.data() + file_offset_);
+    file_offset_ += read_length;
+    return read_length;
+  }
+
+  int64_t Seek(int64_t offset, base::File::Whence whence) override {
+    switch (whence) {
+      case base::File::FROM_BEGIN:
+        file_offset_ = offset;
+        break;
+      case base::File::FROM_CURRENT:
+        file_offset_ += offset;
+        break;
+      case base::File::FROM_END:
+        file_offset_ = file_.size() + offset;
+        break;
+    }
+    return file_offset_;
+  }
+
+  std::unique_ptr<std::string> Passphrase() override { return nullptr; }
+
+  int64_t offset() override { return file_offset_; }
+
+  int64_t archive_size() override { return file_.size(); }
+
+ private:
+  const std::string file_;
+  int64_t file_offset_ = 0;
+};
+
+class CompressorArchiveMinizipTest : public testing::Test {
+ public:
+  CompressorArchiveMinizipTest() = default;
+
+  void CheckZipContents(const std::string& volume,
+                        const std::string& path,
+                        const std::string& contents) {
+    std::unique_ptr<InMemoryVolumeReader> reader =
+        std::make_unique<InMemoryVolumeReader>(volume);
+    VolumeArchiveMinizip archive(std::move(reader));
+    ASSERT_TRUE(archive.Init(""));
+
+    EXPECT_TRUE(archive.SeekHeader(path));
+    const char* buffer = nullptr;
+    int64_t offset = 0;
+    while (offset < static_cast<int64_t>(contents.size())) {
+      int64_t read =
+          archive.ReadData(offset, contents.size() - offset, &buffer);
+      ASSERT_GT(read, 0);
+      EXPECT_EQ(contents.substr(offset, read), base::StringPiece(buffer, read));
+      offset += read;
+    }
+    EXPECT_EQ(offset, static_cast<int64_t>(contents.size()));
+  }
+
+  void CheckZipMetadata(const std::string& volume,
+                        const std::string& path,
+                        int64_t size,
+                        base::Time mod_time,
+                        bool is_directory) {
+    std::unique_ptr<InMemoryVolumeReader> reader =
+        std::make_unique<InMemoryVolumeReader>(volume);
+    VolumeArchiveMinizip archive(std::move(reader));
+    ASSERT_TRUE(archive.Init(""));
+
+    EXPECT_TRUE(archive.SeekHeader(path));
+    std::string volume_file_path;
+    bool volume_is_utf8 = false;
+    int64_t volume_size = -1;
+    bool volume_is_directory = false;
+    time_t volume_mod_time = 0;
+    auto result = archive.GetCurrentFileInfo(&volume_file_path, &volume_is_utf8,
+                                             &volume_size, &volume_is_directory,
+                                             &volume_mod_time);
+    EXPECT_EQ(result, VolumeArchive::RESULT_SUCCESS);
+    EXPECT_EQ(size, volume_size);
+    EXPECT_EQ(mod_time.ToTimeT(), volume_mod_time);
+    EXPECT_EQ(is_directory, volume_is_directory);
+  }
+
+ private:
+  std::unique_ptr<CompressorArchiveMinizip> archive_;
+};
+
+TEST_F(CompressorArchiveMinizipTest, Create) {
+  TestCompressorStream stream;
+  CompressorArchiveMinizip archive(&stream);
+
+  const base::Time add_time = base::Time::Now();
+  EXPECT_TRUE(archive.CreateArchive());
+  stream.SetReadBuffer(kTestFileContent);
+  EXPECT_TRUE(archive.AddToArchive(kTestFileName, sizeof(kTestFileContent) - 1,
+                                   add_time, false));
+  stream.SetReadBuffer(kLargeTestFileContent);
+  EXPECT_TRUE(archive.AddToArchive(
+      kLargeTestFileName, kLargeTestFileContent.size(), add_time, false));
+  EXPECT_TRUE(archive.AddToArchive(kTestDirName, 0, add_time, true));
+
+  EXPECT_TRUE(archive.CloseArchive(false));
+  EXPECT_FALSE(stream.write_buffer().empty());
+
+  CheckZipMetadata(stream.write_buffer(), kTestFileName,
+                   sizeof(kTestFileContent) - 1, add_time, false);
+  CheckZipMetadata(stream.write_buffer(), kLargeTestFileName,
+                   kLargeTestFileContent.size(), add_time, false);
+  CheckZipMetadata(stream.write_buffer(), std::string(kTestDirName) + "/", 0,
+                   add_time, true);
+
+  CheckZipContents(stream.write_buffer(), kTestFileName, kTestFileContent);
+  CheckZipContents(stream.write_buffer(), kLargeTestFileName,
+                   kLargeTestFileContent);
+}
+
+TEST_F(CompressorArchiveMinizipTest, Create_WriteError) {
+  TestCompressorStream stream;
+  CompressorArchiveMinizip archive(&stream);
+
+  const base::Time add_time = base::Time::Now();
+  EXPECT_TRUE(archive.CreateArchive());
+  stream.SetReadBuffer(kTestFileContent);
+  EXPECT_TRUE(archive.AddToArchive(kTestFileName, sizeof(kTestFileContent) - 1,
+                                   add_time, false));
+  stream.SetReadBuffer(kLargeTestFileContent);
+  stream.SetWriteError();
+  EXPECT_FALSE(archive.AddToArchive(
+      kLargeTestFileName, kLargeTestFileContent.size(), add_time, false));
+  EXPECT_FALSE(archive.error_message().empty());
+}
+
+TEST_F(CompressorArchiveMinizipTest, CreateAndCancel) {
+  TestCompressorStream stream;
+  CompressorArchiveMinizip archive(&stream);
+
+  const base::Time add_time = base::Time::Now();
+  EXPECT_TRUE(archive.CreateArchive());
+  stream.SetReadBuffer(kTestFileContent);
+  EXPECT_TRUE(archive.AddToArchive(kTestFileName, sizeof(kTestFileContent) - 1,
+                                   add_time, false));
+  stream.SetReadBuffer(kLargeTestFileContent);
+  archive.CancelArchive();
+  EXPECT_FALSE(archive.AddToArchive(
+      kLargeTestFileName, kLargeTestFileContent.size(), add_time, false));
+  EXPECT_TRUE(archive.error_message().empty());
+}
+
+}  // namespace
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
index 146e873..5b32737d 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
@@ -62,32 +62,96 @@
   return exploded_time;
 }
 
-};  // namespace
-
-namespace volume_archive_functions {
-void* CustomArchiveOpen(void* archive,
-                        const char* /* filename */,
-                        int /* mode */) {
+void* MinizipOpen(void* archive, const char* /* filename */, int /* mode */) {
   return archive;
 }
 
-int64_t DynamicCache(VolumeArchiveMinizip* archive, int64_t unzip_size) {
-  int64_t offset = archive->reader()->offset();
-  if (archive->reader()->Seek(static_cast<int64_t>(offset),
-                              base::File::FROM_BEGIN) < 0) {
+uint32_t MinizipWrite(void* /*archive*/,
+                      void* /*stream*/,
+                      const void* /*buffer*/,
+                      uint32_t /*length*/) {
+  NOTREACHED();
+  return 0;
+}
+
+int MinizipClose(void* /*archive*/, void* /*stream*/) {
+  return 0;
+}
+
+int MinizipError(void* /*archive*/, void* /*stream*/) {
+  return 0;
+}
+
+};  // namespace
+
+uint32_t VolumeArchiveMinizip::MinizipRead(void* archive,
+                                           void* /* stream */,
+                                           void* buffer,
+                                           uint32_t size) {
+  return static_cast<VolumeArchiveMinizip*>(archive)->StreamRead(buffer, size);
+}
+
+uint32_t VolumeArchiveMinizip::StreamRead(void* buffer, uint32_t size) {
+  int64_t offset = reader()->offset();
+
+  // When minizip requests a chunk in static_cache_.
+  if (offset >= static_cache_offset_) {
+    // Relative offset in the central directory.
+    int64_t offset_in_cache = offset - static_cache_offset_;
+    memcpy(buffer, static_cache_.get() + offset_in_cache, size);
+    if (reader()->Seek(static_cast<int64_t>(size), base::File::FROM_CURRENT) <
+        0) {
+      return -1 /* Error */;
+    }
+    return size;
+  }
+
+  char* unzip_buffer_pointer = static_cast<char*>(buffer);
+  int64_t left_length = static_cast<int64_t>(size);
+
+  do {
+    offset = reader()->offset();
+    // If dynamic_cache_ is empty or it cannot be reused, update the cache so
+    // that it contains the chunk required by minizip.
+    if (dynamic_cache_size_ == 0 || offset < dynamic_cache_offset_ ||
+        dynamic_cache_offset_ + dynamic_cache_size_ < offset + size) {
+      if (DynamicCache(size) < 0)
+        return -1 /* Error */;
+    }
+
+    // Just copy the required data from the cache.
+    int64_t offset_in_cache = offset - dynamic_cache_offset_;
+    int64_t copy_length =
+        std::min(left_length, dynamic_cache_size_ - offset_in_cache);
+    memcpy(unzip_buffer_pointer, dynamic_cache_.get() + offset_in_cache,
+           copy_length);
+    unzip_buffer_pointer += copy_length;
+    left_length -= copy_length;
+    if (reader()->Seek(static_cast<int64_t>(copy_length),
+                       base::File::FROM_CURRENT) < 0) {
+      return -1 /* Error */;
+    }
+  } while (left_length > 0);
+
+  return size;
+}
+
+int64_t VolumeArchiveMinizip::DynamicCache(int64_t unzip_size) {
+  int64_t offset = reader()->offset();
+  if (reader()->Seek(static_cast<int64_t>(offset), base::File::FROM_BEGIN) <
+      0) {
     return -1 /* Error */;
   }
 
-  int64_t bytes_to_read = std::min(kMaximumDataChunkSize,
-                                   archive->reader()->archive_size() - offset);
+  int64_t bytes_to_read =
+      std::min(kMaximumDataChunkSize, reader()->archive_size() - offset);
   DCHECK_GT(bytes_to_read, 0);
   int64_t left_length = bytes_to_read;
-  char* buffer_pointer = archive->dynamic_cache_.get();
+  char* buffer_pointer = dynamic_cache_.get();
   const void* destination_buffer;
 
   do {
-    int64_t read_bytes =
-        archive->reader()->Read(left_length, &destination_buffer);
+    int64_t read_bytes = reader()->Read(left_length, &destination_buffer);
     // End of the zip file.
     if (read_bytes == 0)
       break;
@@ -98,91 +162,33 @@
     buffer_pointer += read_bytes;
   } while (left_length > 0);
 
-  if (archive->reader()->Seek(static_cast<int64_t>(offset),
-                              base::File::FROM_BEGIN) < 0) {
+  if (reader()->Seek(static_cast<int64_t>(offset), base::File::FROM_BEGIN) <
+      0) {
     return -1 /* Error */;
   }
-  archive->dynamic_cache_size_ = bytes_to_read - left_length;
-  archive->dynamic_cache_offset_ = offset;
+  dynamic_cache_size_ = bytes_to_read - left_length;
+  dynamic_cache_offset_ = offset;
 
   return unzip_size - left_length;
 }
 
-uint32_t CustomArchiveRead(void* archive,
-                           void* /* stream */,
-                           void* buffer,
-                           uint32_t size) {
-  VolumeArchiveMinizip* archive_minizip =
-      static_cast<VolumeArchiveMinizip*>(archive);
-  int64_t offset = archive_minizip->reader()->offset();
-
-  // When minizip requests a chunk in static_cache_.
-  if (offset >= archive_minizip->static_cache_offset_) {
-    // Relative offset in the central directory.
-    int64_t offset_in_cache = offset - archive_minizip->static_cache_offset_;
-    memcpy(buffer, archive_minizip->static_cache_.get() + offset_in_cache,
-           size);
-    if (archive_minizip->reader()->Seek(static_cast<int64_t>(size),
-                                        base::File::FROM_CURRENT) < 0) {
-      return -1 /* Error */;
-    }
-    return size;
-  }
-
-  char* unzip_buffer_pointer = static_cast<char*>(buffer);
-  int64_t left_length = static_cast<int64_t>(size);
-
-  do {
-    offset = archive_minizip->reader()->offset();
-    // If dynamic_cache_ is empty or it cannot be reused, update the cache so
-    // that it contains the chunk required by minizip.
-    if (archive_minizip->dynamic_cache_size_ == 0 ||
-        offset < archive_minizip->dynamic_cache_offset_ ||
-        archive_minizip->dynamic_cache_offset_ +
-                archive_minizip->dynamic_cache_size_ <
-            offset + size) {
-      if (volume_archive_functions::DynamicCache(archive_minizip, size) < 0)
-        return -1 /* Error */;
-    }
-
-    // Just copy the required data from the cache.
-    int64_t offset_in_cache = offset - archive_minizip->dynamic_cache_offset_;
-    int64_t copy_length = std::min(
-        left_length, archive_minizip->dynamic_cache_size_ - offset_in_cache);
-    memcpy(unzip_buffer_pointer,
-           archive_minizip->dynamic_cache_.get() + offset_in_cache,
-           copy_length);
-    unzip_buffer_pointer += copy_length;
-    left_length -= copy_length;
-    if (archive_minizip->reader()->Seek(static_cast<int64_t>(copy_length),
-                                        base::File::FROM_CURRENT) < 0) {
-      return -1 /* Error */;
-    }
-  } while (left_length > 0);
-
-  return size;
+long VolumeArchiveMinizip::MinizipTell(void* archive, void* /*stream*/) {
+  return static_cast<VolumeArchiveMinizip*>(archive)->StreamTell();
 }
 
-uint32_t CustomArchiveWrite(void* /*archive*/,
-                            void* /*stream*/,
-                            const void* /*buffer*/,
-                            uint32_t /*length*/) {
-  return 0 /* Success */;
+long VolumeArchiveMinizip::StreamTell() {
+  return static_cast<long>(reader()->offset());
 }
 
-long CustomArchiveTell(void* archive, void* /*stream*/) {
-  VolumeArchiveMinizip* archive_minizip =
-      static_cast<VolumeArchiveMinizip*>(archive);
-  return static_cast<long>(archive_minizip->reader()->offset());
+long VolumeArchiveMinizip::MinizipSeek(void* archive,
+                                       void* /*stream*/,
+                                       uint32_t offset,
+                                       int origin) {
+  return static_cast<VolumeArchiveMinizip*>(archive)->StreamSeek(offset,
+                                                                 origin);
 }
 
-long CustomArchiveSeek(void* archive,
-                       void* /*stream*/,
-                       uint32_t offset,
-                       int origin) {
-  VolumeArchiveMinizip* archive_minizip =
-      static_cast<VolumeArchiveMinizip*>(archive);
-
+long VolumeArchiveMinizip::StreamSeek(uint32_t offset, int origin) {
   base::File::Whence whence;
   switch (origin) {
     case ZLIB_FILEFUNC_SEEK_SET:
@@ -199,28 +205,13 @@
       return -1;
   }
 
-  long return_value = static_cast<long>(
-      archive_minizip->reader()->Seek(static_cast<int64_t>(offset), whence));
+  long return_value =
+      static_cast<long>(reader()->Seek(static_cast<int64_t>(offset), whence));
   if (return_value >= 0)
     return 0 /* Success */;
   return -1 /* Error */;
 }
 
-int CustomArchiveClose(void* /*opaque*/, void* /*stream*/) {
-  return 0;
-}
-
-int CustomArchiveError(void* /*opaque*/, void* /*stream*/) {
-  return 0;
-}
-
-std::unique_ptr<std::string> GetPassphrase(
-    VolumeArchiveMinizip* archive_minizip) {
-  return archive_minizip->reader()->Passphrase();
-}
-
-}  // namespace volume_archive_functions
-
 VolumeArchiveMinizip::VolumeArchiveMinizip(std::unique_ptr<VolumeReader> reader)
     : VolumeArchive(std::move(reader)),
       reader_data_size_(kMinimumDataChunkSize),
@@ -251,13 +242,13 @@
 bool VolumeArchiveMinizip::Init(const std::string& encoding) {
   // Set up minizip object.
   zlib_filefunc_def zip_funcs;
-  zip_funcs.zopen_file = volume_archive_functions::CustomArchiveOpen;
-  zip_funcs.zread_file = volume_archive_functions::CustomArchiveRead;
-  zip_funcs.zwrite_file = volume_archive_functions::CustomArchiveWrite;
-  zip_funcs.ztell_file = volume_archive_functions::CustomArchiveTell;
-  zip_funcs.zseek_file = volume_archive_functions::CustomArchiveSeek;
-  zip_funcs.zclose_file = volume_archive_functions::CustomArchiveClose;
-  zip_funcs.zerror_file = volume_archive_functions::CustomArchiveError;
+  zip_funcs.zopen_file = MinizipOpen;
+  zip_funcs.zread_file = MinizipRead;
+  zip_funcs.zwrite_file = MinizipWrite;
+  zip_funcs.ztell_file = MinizipTell;
+  zip_funcs.zseek_file = MinizipSeek;
+  zip_funcs.zclose_file = MinizipClose;
+  zip_funcs.zerror_file = MinizipError;
   zip_funcs.opaque = static_cast<void*>(this);
 
   // Load maximum static_cache_size_ bytes from the end of the archive to
@@ -400,7 +391,7 @@
     do {
       if (password_cache_ == nullptr) {
         // Save passphrase for upcoming file requests.
-        password_cache_ = volume_archive_functions::GetPassphrase(this);
+        password_cache_ = reader()->Passphrase();
         // check if |password_cache_| is nullptr in case when user clicks Cancel
         if (password_cache_ == nullptr) {
           return false;
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
index 7196fae..be871270 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
@@ -14,31 +14,6 @@
 
 #include "chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h"
 
-class VolumeArchiveMinizip;
-
-// A namespace with custom functions passed to minizip.
-namespace volume_archive_functions {
-
-int64_t DynamicCache(VolumeArchiveMinizip* archive, int64_t unz_size);
-
-uint32_t CustomArchiveRead(void* archive,
-                           void* stream,
-                           void* buf,
-                           uint32_t size);
-
-// Returns the offset from the beginning of the data.
-long CustomArchiveTell(void* archive, void* stream);
-
-// Moves the current offset to the specified position.
-long CustomArchiveSeek(void* archive,
-                       void* stream,
-                       uint32_t offset,
-                       int origin);
-
-}  // namespace volume_archive_functions
-
-class VolumeArchiveMinizip;
-
 // Defines an implementation of VolumeArchive that wraps all minizip
 // operations.
 class VolumeArchiveMinizip : public VolumeArchive {
@@ -70,28 +45,26 @@
   // See volume_archive_interface.h.
   void MaybeDecompressAhead() override;
 
-  int64_t reader_data_size() const { return reader_data_size_; }
-
-  // Custom functions need to access private variables of
-  // CompressorArchiveMinizip frequently.
-  friend int64_t volume_archive_functions::DynamicCache(
-      VolumeArchiveMinizip* va,
-      int64_t unz_size);
-
-  friend uint32_t volume_archive_functions::CustomArchiveRead(void* archive,
-                                                              void* stream,
-                                                              void* buf,
-                                                              uint32_t size);
-
-  friend long volume_archive_functions::CustomArchiveTell(void* archive,
-                                                          void* stream);
-
-  friend long volume_archive_functions::CustomArchiveSeek(void* archive,
-                                                          void* stream,
-                                                          uint32_t offset,
-                                                          int origin);
-
  private:
+  // Stream functions used by minizip. In all cases, |archive| points to |this|.
+  static uint32_t MinizipRead(void* archive,
+                              void* stream,
+                              void* buf,
+                              uint32_t size);
+  static long MinizipTell(void* archive, void* stream);
+  static long MinizipSeek(void* archive,
+                          void* stream,
+                          uint32_t offset,
+                          int origin);
+
+  // Implementation of stream functions used by minizip.
+  uint32_t StreamRead(void* buf, uint32_t size);
+  long StreamTell();
+  long StreamSeek(uint32_t offset, int origin);
+
+  // Read cache.
+  int64_t DynamicCache(int64_t unz_size);
+
   // Decompress length bytes of data starting from offset.
   void DecompressData(int64_t offset, int64_t length);
 
diff --git a/chrome/browser/resources/print_preview/new/advanced_settings_dialog.js b/chrome/browser/resources/print_preview/new/advanced_settings_dialog.js
index af92c9a7..9ea1f68 100644
--- a/chrome/browser/resources/print_preview/new/advanced_settings_dialog.js
+++ b/chrome/browser/resources/print_preview/new/advanced_settings_dialog.js
@@ -42,12 +42,7 @@
   attached: function() {
     this.metrics_.record(print_preview.Metrics.PrintSettingsUiBucket
                              .ADVANCED_SETTINGS_DIALOG_SHOWN);
-    // This async() call is a workaround to prevent a DCHECK - see
-    // https://crbug.com/804047.
-    // TODO(rbpotter): Remove after Polymer2 migration is complete.
-    this.async(() => {
-      this.$.dialog.showModal();
-    }, 1);
+    this.$.dialog.showModal();
   },
 
   /**
diff --git a/chrome/browser/resources/print_preview/new/destination_list.js b/chrome/browser/resources/print_preview/new/destination_list.js
index 0b89769..0bf39f1 100644
--- a/chrome/browser/resources/print_preview/new/destination_list.js
+++ b/chrome/browser/resources/print_preview/new/destination_list.js
@@ -27,6 +27,7 @@
     loadingDestinations: {
       type: Boolean,
       value: false,
+      observer: 'forceIronResize',
     },
 
     listName: String,
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.js b/chrome/browser/resources/print_preview/new/destination_settings.js
index f635138..e3477e1 100644
--- a/chrome/browser/resources/print_preview/new/destination_settings.js
+++ b/chrome/browser/resources/print_preview/new/destination_settings.js
@@ -87,12 +87,7 @@
     this.destinationStore.startLoadAllDestinations();
     this.invitationStore.startLoadingInvitations();
     const dialog = this.$.destinationDialog.get();
-    // This async() call is a workaround to prevent a DCHECK - see
-    // https://crbug.com/804047.
-    // TODO(rbpotter): Remove after Polymer2 migration is complete.
-    this.async(() => {
-      dialog.show();
-    }, 1);
+    dialog.show();
   },
 
   showCloudPrintPromo: function() {
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
index 7de44a61..5177fbd1 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
@@ -16,6 +16,7 @@
 js_type_check("welcome_files") {
   deps = [
     ":landing_view",
+    ":signin_view",
     ":welcome_app",
   ]
 }
@@ -27,6 +28,13 @@
   ]
 }
 
+js_library("signin_view") {
+  deps = [
+    ":navigation_behavior",
+    ":welcome_browser_proxy",
+  ]
+}
+
 js_library("navigation_behavior") {
   deps = [
     "//ui/webui/resources/js:cr",
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
index f2c5261..b24bc99b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
@@ -20,6 +20,7 @@
 js_library("email_chooser") {
   deps = [
     ":nux_email_proxy",
+    "../:navigation_behavior",
     "//third_party/polymer/v1_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer-extracted",
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:i18n_behavior",
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
index d588831..f014194 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
@@ -8,7 +8,8 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://welcome/shared/chooser_shared_css.html">
+<link rel="import" href="../navigation_behavior.html">
+<link rel="import" href="../shared/chooser_shared_css.html">
 <link rel="import" href="../shared/i18n_setup.html">
 <link rel="import" href="nux_email_proxy.html">
 
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js
index 01bed2c..b9891cc 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js
@@ -70,7 +70,7 @@
 
       if (this.selectedEmailProvider_) {
         this.browserProxy_.recordProviderSelected(
-            this.selectedEmailProvider_.id);
+            this.selectedEmailProvider_.id, this.emailList_.length);
       }
 
       this.browserProxy_.recordFinalize();
@@ -170,15 +170,18 @@
     this.revertBookmark_();
     this.browserProxy_.toggleBookmarkBar(this.bookmarkBarWasShown_);
     this.browserProxy_.recordNoThanks();
-    window.location.replace('chrome://newtab');
+    welcome.navigateToNextStep();
   },
 
   /** @private */
   onGetStartedClicked_: function() {
     this.finalized_ = true;
-    this.browserProxy_.recordProviderSelected(this.selectedEmailProvider_.id);
+    this.browserProxy_.recordProviderSelected(
+        this.selectedEmailProvider_.id, this.emailList_.length);
     this.browserProxy_.recordGetStarted();
-    window.location.replace(this.selectedEmailProvider_.url);
+    // TODO(scottchen): store the selected email provider URL somewhere to
+    //     redirect to at the end.
+    welcome.navigateToNextStep();
   },
 
   /** @private */
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
index 83b6458..4652da4 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
@@ -1,77 +1,63 @@
-<!DOCTYPE html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
-<head>
-  <meta charset="utf-8">
-  <title>$i18n{headerText}</title>
-  <link rel="import" href="chrome://resources/html/polymer.html">
-  <link rel="import" href="email_chooser.html">
+<link rel="import" href="chrome://resources/html/polymer.html">
 
-  <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-  <style>
-    body {
-      margin: 0;
-    }
-  </style>
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="email_chooser.html">
 
-  <dom-module id="nux-email">
-    <template>
-      <style>
-        :host {
-          align-items: center;
-          display: flex;
-          height: fit-content;
-          margin: auto;
-          min-height: 100vh;
-          width: fit-content;
-        }
+<dom-module id="nux-email">
+  <template>
+    <style>
+      :host-context(#viewManager):host([slot='view']) {
+        /* Unsets cr-view-manager's styling to make all views full-page, and
+           allows the content to be centered horizontally/vertically on the
+           page. */
+        bottom: initial;
+        left: initial;
+        right: initial;
+        top: initial;
+      }
 
-        .email-ask {
-          text-align: center;
-        }
+      .email-ask {
+        text-align: center;
+      }
 
-        .email-logo {
-          content: -webkit-image-set(
-            url(chrome://welcome/logo.png) 1x,
-            url(chrome://welcome/logo2x.png) 2x);
-          height: 48px;
-          margin: auto;
-          margin-bottom: 16px;
-          width: 48px;
-        }
+      .email-logo {
+        content: -webkit-image-set(
+          url(chrome://welcome/logo.png) 1x,
+          url(chrome://welcome/logo2x.png) 2x);
+        height: 48px;
+        margin: auto;
+        margin-bottom: 16px;
+        width: 48px;
+      }
 
-        h1 {
-          color: #202124;
-          font-size: 1.5rem;
-          font-weight: 500;
-          line-height: 2.5rem;
-          margin: 0;
-        }
+      h1 {
+        color: var(--google-grey-900);
+        font-size: 1.5rem;
+        font-weight: 500;
+        line-height: 2.5rem;
+        margin: 0;
+      }
 
-        h2 {
-          color: #202124;
-          font-size: 1.125rem;
-          font-weight: unset;
-          line-height: 2rem;
-          margin: 0;
-          margin-bottom: 48px;
-        }
+      h2 {
+        color: var(--google-grey-900);
+        font-size: 1.125rem;
+        font-weight: unset;
+        line-height: 2rem;
+        margin: 0;
+        margin-bottom: 48px;
+      }
 
-        #emailChooser {
-          color: #202124;
-          margin-bottom: 48px;
-        }
-      </style>
-      <div class="email-ask">
-        <div class="email-logo" alt=""></div>
-        <h1>$i18n{welcomeTitle}</h1>
-        <h2>$i18n{emailPrompt}</h2>
-        <email-chooser id="emailChooser"></email-chooser>
-      </div>
-    </template>
-    <script src="nux_email.js"></script>
-  </dom-module>
-</head>
-<body>
-  <nux-email></nux-email>
-</body>
-</html>
\ No newline at end of file
+      #emailChooser {
+        color: var(--google-grey-900);
+        margin-bottom: 48px;
+      }
+    </style>
+    <div class="email-ask">
+      <div class="email-logo" alt=""></div>
+      <h1>$i18n{welcomeTitle}</h1>
+      <h2>$i18n{emailPrompt}</h2>
+      <email-chooser id="emailChooser"></email-chooser>
+    </div>
+  </template>
+  <script src="nux_email.js"></script>
+</dom-module>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js
index bab40557..b7cc4b1 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js
@@ -76,8 +76,9 @@
     /**
      * @param {number} providerId This should match one of the histogram enum
      *     value for NuxEmailProvidersSelections.
+     * @param {number} length
      */
-    recordProviderSelected(providerId) {}
+    recordProviderSelected(providerId, length) {}
 
     recordNoThanks() {}
 
@@ -145,10 +146,11 @@
     }
 
     /** @override */
-    recordProviderSelected(providerId) {
+    recordProviderSelected(providerId, length) {
+      // TODO(hcarmona): get enum's max from loadTimeData instead, since length
+      //     might not be accurate once we start localizing.
       chrome.metricsPrivate.recordEnumerationValue(
-          SELECTION_METRIC_NAME, providerId,
-          loadTimeData.getInteger('email_count'));
+          SELECTION_METRIC_NAME, providerId, length);
     }
 
     /** @override */
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
index c4e96fa3..d4c820e 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
@@ -21,6 +21,7 @@
   deps = [
     ":apps_chooser",
     ":nux_google_apps_proxy",
+    "../:navigation_behavior",
     "../shared:nux_types",
   ]
 }
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
index fe8a9322..43f6569 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
@@ -1,27 +1,28 @@
-<!DOCTYPE html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
-<meta charset="utf-8">
-<title>$i18n{headerText}</title>
-
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="../navigation_behavior.html">
 <link rel="import" href="apps_chooser.html">
 <link rel="import" href="nux_google_apps_proxy.html">
 
 <dom-module id="nux-google-apps">
   <template>
     <style include="paper-button-style">
-      body {
-        margin: 0;
+      :host-context(#viewManager):host([slot='view']) {
+        /* Unsets cr-view-manager's styling to make all views full-page, and
+           allows the content to be centered horizontally/vertically on the
+           page. */
+        bottom: initial;
+        left: initial;
+        right: initial;
+        top: initial;
       }
 
       .apps-ask {
         margin-left: auto;
         margin-right: auto;
-        margin-top: 120px;
         width: fit-content;
       }
 
@@ -75,8 +76,3 @@
   </template>
   <script src="nux_google_apps.js"></script>
 </dom-module>
-
-<nux-google-apps></nux-google-apps>
-<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-
-</html>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
index 911ec744..90c0370 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
@@ -13,13 +13,13 @@
   /** @private */
   onNoThanksClicked_: function() {
     chrome.send('rejectGoogleApps');
-    window.location.replace('chrome://newtab');
+    welcome.navigateToNextStep();
   },
 
   /** @private */
   onGetStartedClicked_: function() {
     let selectedApps = this.$.appChooser.getSelectedAppList();
     nux.NuxGoogleAppsProxyImpl.getInstance().addGoogleApps(selectedApps);
-    window.location.replace('chrome://newtab');
+    welcome.navigateToNextStep();
   },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/blue_circle.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/blue_circle.svg
new file mode 100644
index 0000000..3f34976
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/blue_circle.svg
@@ -0,0 +1,3 @@
+<svg width="43px" height="43px" viewBox="0 0 43 43" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <circle fill="#1A73E8" fill-rule="evenodd" cx="21.5" cy="21.5" r="21.5"></circle>
+</svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/green_rectangle.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/green_rectangle.svg
new file mode 100644
index 0000000..8cb0471
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/green_rectangle.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="371" height="371" viewBox="0 0 371 371">
+  <path fill="#31A753" fill-rule="evenodd" d="M7.61078518,166.895209 L166.895209,7.61078518 C177.042923,-2.53692839 193.495617,-2.53692839 203.643331,7.61078518 L362.935748,166.903202 C373.083461,177.050916 373.083461,193.50361 362.935748,203.651324 L203.651324,362.935748 C193.50361,373.083461 177.050916,373.083461 166.903202,362.935748 L7.61078518,203.643331 C-2.53692839,193.495617 -2.53692839,177.042923 7.61078518,166.895209 Z"/>
+</svg>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/grey_oval.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/grey_oval.svg
new file mode 100644
index 0000000..cf75ceb
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/grey_oval.svg
@@ -0,0 +1,3 @@
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <circle fill-rule="evenodd" fill="#F1F3F4" cx="50" cy="50" r="50"></circle>
+</svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/grey_rounded_rectangle.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/grey_rounded_rectangle.svg
new file mode 100644
index 0000000..71f9fd6
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/grey_rounded_rectangle.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="132" height="132" viewBox="0 0 132 132">
+  <path fill="#F1F3F4" fill-rule="evenodd" d="M81.6025226,14.0007794 L117.999221,50.3974774 C136.666926,69.0651833 136.666926,99.3315147 117.999221,117.999221 C99.3315147,136.666926 69.0651833,136.666926 50.3974774,117.999221 L14.0007794,81.6025226 C-4.66692648,62.9348167 -4.66692648,32.6684853 14.0007794,14.0007794 C32.6684853,-4.66692648 62.9348167,-4.66692648 81.6025226,14.0007794 Z"/>
+</svg>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/red_triangle.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/red_triangle.svg
new file mode 100644
index 0000000..d89d1ae
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/red_triangle.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="65" height="74" viewBox="0 0 65 74">
+  <path fill="#E94235" fill-rule="evenodd" d="M64.5429228,7.29310524 L64.3644898,67.9344347 C64.1624511,71.340818 61.2707769,73.9365373 57.9057548,73.732136 C56.9598601,73.6746796 56.0401784,73.3950765 55.2195428,72.9154694 L3.60676683,42.7512281 C0.687353928,41.0450251 -0.312855086,37.266089 1.37273747,34.3107382 C1.84655098,33.4800006 2.50492275,32.7723367 3.29571384,32.2437891 L55.0869229,1.76670102 C57.9001639,-0.113607947 61.6864522,0.670655917 63.5438345,3.51840338 C64.2715067,4.63407375 64.6220761,5.95857646 64.5429228,7.29310524 Z"/>
+</svg>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/yellow_dots.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/yellow_dots.svg
new file mode 100644
index 0000000..41ce568b
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/yellow_dots.svg
@@ -0,0 +1,46 @@
+<svg width="76px" height="57px" viewBox="0 0 76 57" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <g fill-rule="evenodd" fill="#FDD663">
+    <circle cx="14" cy="13" r="2"></circle>
+    <circle cx="14" cy="2" r="2"></circle>
+    <circle cx="14" cy="23" r="2"></circle>
+    <circle cx="14" cy="34" r="2"></circle>
+    <circle cx="14" cy="45" r="2"></circle>
+    <circle cx="14" cy="55" r="2"></circle>
+    <circle cx="2" cy="13" r="2"></circle>
+    <circle cx="2" cy="2" r="2"></circle>
+    <circle cx="2" cy="23" r="2"></circle>
+    <circle cx="2" cy="34" r="2"></circle>
+    <circle cx="2" cy="45" r="2"></circle>
+    <circle cx="2" cy="55" r="2"></circle>
+    <circle cx="26" cy="13" r="2"></circle>
+    <circle cx="26" cy="2" r="2"></circle>
+    <circle cx="26" cy="23" r="2"></circle>
+    <circle cx="26" cy="34" r="2"></circle>
+    <circle cx="26" cy="45" r="2"></circle>
+    <circle cx="26" cy="55" r="2"></circle>
+    <circle cx="38" cy="13" r="2"></circle>
+    <circle cx="38" cy="2" r="2"></circle>
+    <circle cx="38" cy="23" r="2"></circle>
+    <circle cx="38" cy="34" r="2"></circle>
+    <circle cx="38" cy="45" r="2"></circle>
+    <circle cx="38" cy="55" r="2"></circle>
+    <circle cx="50" cy="13" r="2"></circle>
+    <circle cx="50" cy="2" r="2"></circle>
+    <circle cx="50" cy="23" r="2"></circle>
+    <circle cx="50" cy="34" r="2"></circle>
+    <circle cx="50" cy="45" r="2"></circle>
+    <circle cx="50" cy="55" r="2"></circle>
+    <circle cx="62" cy="13" r="2"></circle>
+    <circle cx="62" cy="2" r="2"></circle>
+    <circle cx="62" cy="23" r="2"></circle>
+    <circle cx="62" cy="34" r="2"></circle>
+    <circle cx="62" cy="45" r="2"></circle>
+    <circle cx="62" cy="55" r="2"></circle>
+    <circle cx="74" cy="13" r="2"></circle>
+    <circle cx="74" cy="2" r="2"></circle>
+    <circle cx="74" cy="23" r="2"></circle>
+    <circle cx="74" cy="34" r="2"></circle>
+    <circle cx="74" cy="45" r="2"></circle>
+    <circle cx="74" cy="55" r="2"></circle>
+  </g>
+</svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/yellow_semicircle.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/yellow_semicircle.svg
new file mode 100644
index 0000000..3fe924d
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/images/background_svgs/yellow_semicircle.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="211" height="171" viewBox="0 0 211 171">
+  <path fill="#FACF4C" fill-rule="evenodd" d="M152.95531,155.322875 C102.461259,184.681893 39.1648672,171.482614 4.01772623,126.776523 C0.528185501,122.337934 -5.16163709,112.29754 9.88388926,103.549542 C48.9517191,80.8341309 106.527836,47.3573508 182.61224,3.1192014 C196.248192,-4.80922088 200.05639,4.35165919 201.923,8.80779391 C224.340646,62.3251755 204.196032,125.529716 152.95531,155.322875 Z"/>
+</svg>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/landing_view.html b/chrome/browser/resources/welcome/onboarding_welcome/landing_view.html
index e5046308..1d58cd1 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/landing_view.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/landing_view.html
@@ -4,11 +4,13 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="navigation_behavior.html">
+<link rel="import" href="shared/action_link_style_css.html">
+<link rel="import" href="shared/onboarding_background.html">
 <link rel="import" href="welcome_browser_proxy.html">
 
 <dom-module id="landing-view">
   <template>
-    <style include="paper-button-style">
+    <style include="paper-button-style action-link-style">
       #container {
         align-items: center;
         display: flex;
@@ -16,37 +18,50 @@
         height: 100%;
         justify-content: center;
         margin: auto;
-        width: 800px;
+        min-width: 800px;
       }
 
       h1 {
         font-size: 4rem;
-        margin-bottom: 20px;
+        margin-bottom: 40px;
+        margin-top: 16px;
       }
 
       h2 {
         color: darkgrey;
         font-size: 1.5rem;
+        font-weight: 500;
+        line-height: 2.25rem;
+        margin: 0;
       }
 
       paper-button {
         font-size: 1rem;
-        height: 2.5rem;
+        height: 3rem;
+        padding-bottom: 12px;
+        padding-top: 12px;
         text-align: center;
         white-space: nowrap;
-        width: 220px;
+        width: 256px;
+      }
+
+      .action-link {
+        font-size: 1rem;
+        font-weight: 500;
+        margin-top: 24px;
       }
     </style>
     <!-- TODO(scottchen): localize -->
+    <onboarding-background></onboarding-background>
     <div id="container">
       <h2>Set up your browser in a few simple steps</h2>
       <h1>Make Chrome your own</h1>
       <paper-button class="action-button" on-click="onNewUserClick_">
         Get Started
       </paper-button>
-      <paper-button on-click="onExistingUserClick_">
-        Already set up? Sign in
-      </paper-button>
+      <button class="action-link" on-click="onExistingUserClick_">
+        Already a Chrome user? Sign in
+      </button>
     </div>
   </template>
   <script src="landing_view.js"></script>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/landing_view.js b/chrome/browser/resources/welcome/onboarding_welcome/landing_view.js
index 925b6fd..5e3f4f3 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/landing_view.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/landing_view.js
@@ -5,8 +5,6 @@
 Polymer({
   is: 'landing-view',
 
-  behaviors: [welcome.NavigationBehavior],
-
   /** @private */
   onExistingUserClick_: function() {
     welcome.WelcomeBrowserProxyImpl.getInstance().handleActivateSignIn(
@@ -15,6 +13,6 @@
 
   /** @private */
   onNewUserClick_: function() {
-    this.navigateTo(welcome.Routes.NEW_USER, 1);
+    welcome.navigateTo(welcome.Routes.NEW_USER, 1);
   }
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.js b/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.js
index c1cdfa6..baf9bbbc 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.js
@@ -57,13 +57,44 @@
     const route = /** @type {!welcome.Routes} */ (history.state.route);
     const step = history.state.step;
     routeObservers.forEach((observer) => {
-      observer.onRouteChange(route, step);
+      (/** @type {{onRouteChange: Function}} */ (observer))
+          .onRouteChange(route, step);
     });
   }
 
   // Notifies all elements when browser history is popped.
   window.addEventListener('popstate', notifyObservers);
 
+  function navigateToNextStep() {
+    history.pushState(
+        {
+          route: history.state.route,
+          step: history.state.step + 1,
+        },
+        '', `/${history.state.route}`);
+    notifyObservers();
+  }
+
+  /**
+   * @param {!welcome.Routes} route
+   * @param {number} step
+   */
+  function navigateTo(route, step) {
+    assert([
+      Routes.LANDING,
+      Routes.NEW_USER,
+      Routes.RETURNING_USER,
+    ].includes(route));
+
+    history.pushState(
+        {
+          route: route,
+          step: step,
+        },
+        '', '/' + (route === Routes.LANDING ? '' : route));
+    notifyObservers();
+  }
+
   /** @polymerBehavior */
   const NavigationBehavior = {
     /** @override */
@@ -85,40 +116,12 @@
 
     /** Elements can override onRouteChange to handle route changes. */
     onRouteChange: function() {},
-
-    navigateToNextStep: function() {
-      history.pushState(
-          {
-            route: history.state.route,
-            step: history.state.step + 1,
-          },
-          '', `/${history.state.route}`);
-      notifyObservers();
-    },
-
-    /**
-     * @param {!welcome.Routes} route
-     * @param {number} step
-     */
-    navigateTo: function(route, step) {
-      assert([
-        Routes.LANDING,
-        Routes.NEW_USER,
-        Routes.RETURNING_USER,
-      ].includes(route));
-
-      history.pushState(
-          {
-            route: route,
-            step: step,
-          },
-          '', '/' + (route === Routes.LANDING ? '' : route));
-      notifyObservers();
-    },
   };
 
   return {
     NavigationBehavior: NavigationBehavior,
+    navigateTo: navigateTo,
+    navigateToNextStep: navigateToNextStep,
     Routes: Routes,
   };
 });
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/BUILD.gn
index cf61b6c..cb4851f 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/BUILD.gn
@@ -13,6 +13,7 @@
 js_library("nux_set_as_default") {
   deps = [
     ":nux_set_as_default_proxy",
+    "../:navigation_behavior",
     "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.html b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.html
index 0e29e03..60125a1 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.html
@@ -1,97 +1,87 @@
-<!DOCTYPE html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
-<head>
-  <meta charset="utf-8">
-  <title>$i18n{headerText}</title>
-  <link rel="import" href="chrome://resources/html/polymer.html">
-  <link rel="import" href="chrome://resources/cr_elements/icons.html">
-  <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
-  <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-  <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-  <link rel="import" href="nux_set_as_default_proxy.html">
-  <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-  <style>
-    body {
-      margin: 0;
-    }
-  </style>
+<link rel="import" href="chrome://resources/html/polymer.html">
 
-  <dom-module id="nux-set-as-default">
-    <template>
-      <style include="paper-button-style">
-        :host {
-          align-items: center;
-          display: flex;
-          height: 100vh;
-          justify-content: space-around;
-        }
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="../navigation_behavior.html">
+<link rel="import" href="nux_set_as_default_proxy.html">
 
-        .container {
-          text-align: center;
-          width: 800px;
-        }
+<dom-module id="nux-set-as-default">
+  <template>
+    <style include="paper-button-style">
+      :host-context(#viewManager):host([slot='view']) {
+        /* Unsets cr-view-manager's styling to make all views full-page, and
+           allows the content to be centered horizontally/vertically on the
+           page. */
+        bottom: initial;
+        left: initial;
+        right: initial;
+        top: initial;
+      }
 
-        .logo {
-          margin-bottom: 16px;
-          /* TODO(scottchen): placeholder; replace when asset is available. */
-          width: 32px;
-          height: 32px;
-          display: inline-block;
-          background: red;
-        }
+      .container {
+        text-align: center;
+        width: 800px;
+      }
 
-        h1 {
-          color: #202124;
-          font-weight: 500;
-          font-size: 1.5rem;
-          line-height: 2.5rem;
-          margin: 0;
-        }
+      .logo {
+        /* TODO(scottchen): placeholder; replace when asset is available. */
+        background: red;
+        display: inline-block;
+        height: 32px;
+        margin-bottom: 16px;
+        width: 32px;
+      }
 
-        h2 {
-          color: #202124;
-          font-weight: unset;
-          font-size: 1.25rem;
-          line-height: 1.875rem;
-          margin: 0;
-          margin-top: 16px;
-          margin-bottom: 48px;
-        }
+      h1 {
+        color: var(--google-grey-900);
+        font-size: 1.5rem;
+        font-weight: 500;
+        line-height: 2.5rem;
+        margin: 0;
+      }
 
-        img {
-          /* TODO(scottchen): placeholder; replace when asset is available. */
-          width: 454px;
-          height: 186px;
-          display: inline-block;
-          background: red;
-        }
+      h2 {
+        color: var(--google-grey-900);
+        font-size: 1.25rem;
+        font-weight: unset;
+        line-height: 1.875rem;
+        margin: 0;
+        margin-bottom: 48px;
+        margin-top: 16px;
+      }
 
-        .button-bar {
-          display: flex;
-          margin-top: 64px;
-          justify-content: space-between;
-        }
-      </style>
-      <div class="container">
-        <div class="logo"></div>
-        <h1>TODO_HEADER</h1>
-        <h2>TODO_SUBHEADER</h2>
-        <!-- TODO(scottchen): WIP behind feature flag, add src later. -->
-        <img>
-        <div class="button-bar">
-          <paper-button on-click="onDeclineClick_">
-            TODO_NO_THANKS
-          </paper-button>
-          <paper-button class="action-button" on-click="onSetDefaultClick_">
-            TODO_SET_AS_DEFAULT
-          </paper-button>
-        </div>
+      img {
+        /* TODO(scottchen): placeholder; replace when asset is available. */
+        background: red;
+        display: inline-block;
+        height: 186px;
+        width: 454px;
+      }
+
+      .button-bar {
+        display: flex;
+        justify-content: space-between;
+        margin-top: 64px;
+      }
+    </style>
+    <div class="container">
+      <div class="logo"></div>
+      <h1>TODO_HEADER</h1>
+      <h2>TODO_SUBHEADER</h2>
+      <!-- TODO(scottchen): WIP behind feature flag, add src later. -->
+      <img>
+      <div class="button-bar">
+        <paper-button on-click="onDeclineClick_">
+          TODO_NO_THANKS
+        </paper-button>
+        <paper-button class="action-button" on-click="onSetDefaultClick_">
+          TODO_SET_AS_DEFAULT
+        </paper-button>
       </div>
-    </template>
-    <script src="nux_set_as_default.js"></script>
-  </dom-module>
-</head>
-<body>
-  <nux-set-as-default></nux-set-as-default>
-</body>
-</html>
\ No newline at end of file
+    </div>
+  </template>
+  <script src="nux_set_as_default.js"></script>
+</dom-module>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.js b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.js
index 97e443a..4e22f8e 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/set_as_default/nux_set_as_default.js
@@ -45,7 +45,6 @@
 
   /** @private */
   finished_: function() {
-    // TODO(scottchen): use navigation behavior to go to next step once this
-    //     module is integrated with onboarding-welcome's welcome-app.
+    welcome.navigateToNextStep();
   },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/action_link_style.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/action_link_style.js
new file mode 100644
index 0000000..b4e52c4b
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/action_link_style.js
@@ -0,0 +1,9 @@
+// Copyright 2018 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.
+
+/**
+ * @fileoverview Initiates focus-outline-manager for this document so that
+ * action-link style can take advantage of it.
+ */
+cr.ui.FocusOutlineManager.forDocument(document);
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/action_link_style_css.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/action_link_style_css.html
new file mode 100644
index 0000000..b4fb1e7
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/action_link_style_css.html
@@ -0,0 +1,34 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/cr/ui/focus_outline_manager.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+
+<dom-module id="action-link-style">
+  <template>
+    <style>
+      button.action-link {
+        @apply --cr-actionable;
+        -webkit-appearance: none;
+        background: none;
+        border: none;
+        color: var(--google-blue-700);
+        display: inline-block;
+        font-family: inherit;
+        text-decoration: none;
+      }
+
+      button.action-link[disabled] {
+        color: var(--paper-grey-600);
+        cursor: default;
+        opacity: 0.65;
+      }
+
+      :host-context(html:not(.focus-outline-visible)) button.action-link {
+        outline: none;
+      }
+    </style>
+  </template>
+</dom-module>
+
+<script src="action_link_style.js"></script>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/i18n_setup.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/i18n_setup.html
index fa9610d..c505b2c13 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/i18n_setup.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/i18n_setup.html
@@ -1,2 +1,2 @@
 <script src="chrome://resources/js/load_time_data.js"></script>
-<script src="../strings.js"></script>
\ No newline at end of file
+<script src="../strings.js"></script>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/onboarding_background.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/onboarding_background.html
new file mode 100644
index 0000000..d16b04a
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/onboarding_background.html
@@ -0,0 +1,80 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<dom-module id="onboarding-background">
+  <template>
+    <style>
+      :host {
+        position: absolute;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        top: 0;
+        overflow: hidden;
+        max-width: 1280px;
+        margin: auto;
+        z-index: -1;
+      }
+
+      /* The entire container is anchored at the center of the page, and each
+         is positioned relative to the center. */
+      #container {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+      }
+
+      img {
+        position: absolute;
+      }
+
+      #blue-circle {
+        right: 611px;
+        bottom: 255px;
+      }
+
+      #yellow-dots {
+        right: 397px;
+        bottom: 194px;
+      }
+
+      #grey-rounded-rectangle {
+        right: 573px;
+        top: -43px;
+      }
+
+      #red-triangle {
+        top: 187px;
+        right: 405px;
+      }
+
+      #yellow-semicircle {
+        bottom: 282px;
+        left: 60px;
+      }
+
+      #green-rectangle {
+        left: 483px;
+        top: -77px;
+      }
+
+      #grey-oval {
+        left: 420px;
+        top: 130px;
+        mix-blend-mode: multiply;
+      }
+    </style>
+    <div id="container">
+      <img id="blue-circle" src="../images/background_svgs/blue_circle.svg">
+      <img id="green-rectangle"
+          src="../images/background_svgs/green_rectangle.svg">
+      <img id="grey-oval" src="../images/background_svgs/grey_oval.svg">
+      <img id="grey-rounded-rectangle"
+          src="../images/background_svgs/grey_rounded_rectangle.svg">
+      <img id="red-triangle" src="../images/background_svgs/red_triangle.svg">
+      <img id="yellow-dots" src="../images/background_svgs/yellow_dots.svg">
+      <img id="yellow-semicircle"
+          src="../images/background_svgs/yellow_semicircle.svg">
+    </div>
+  </template>
+  <script src="onboarding_background.js"></script>
+</dom-module>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/onboarding_background.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/onboarding_background.js
new file mode 100644
index 0000000..12358fa
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/onboarding_background.js
@@ -0,0 +1,11 @@
+// Copyright 2018 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.
+
+/**
+ * @fileoverview This element contains a set of SVGs that together acts as an
+ * animated and responsive background for any page that contains it.
+ */
+Polymer({
+  is: 'onboarding-background',
+});
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/signin_view.html b/chrome/browser/resources/welcome/onboarding_welcome/signin_view.html
new file mode 100644
index 0000000..a03cb4f
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/signin_view.html
@@ -0,0 +1,53 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+<link rel="import" href="navigation_behavior.html">
+<link rel="import" href="welcome_browser_proxy.html">
+
+<dom-module id="signin-view">
+  <template>
+    <style include="paper-button-style">
+      #container {
+        align-items: center;
+        display: flex;
+        flex-direction: column;
+        height: 100%;
+        justify-content: center;
+        margin: auto;
+        width: 800px;
+      }
+
+      h1 {
+        font-size: 4rem;
+        margin-bottom: 20px;
+      }
+
+      h2 {
+        color: darkgrey;
+        font-size: 1.5rem;
+      }
+
+      paper-button {
+        font-size: 1rem;
+        height: 2.5rem;
+        text-align: center;
+        white-space: nowrap;
+        width: 220px;
+      }
+    </style>
+    <!-- TODO(scottchen): localize -->
+    <div id="container">
+      <h2>TODO Sign in to get your bookmarks on all devices</h2>
+      <h1>TODO Save your progress</h1>
+      <paper-button class="action-button" on-click="onSignInClick_">
+        Sign in
+      </paper-button>
+      <paper-button on-click="onNoThanksClick_">
+        No thanks
+      </paper-button>
+    </div>
+  </template>
+  <script src="signin_view.js"></script>
+</dom-module>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/signin_view.js b/chrome/browser/resources/welcome/onboarding_welcome/signin_view.js
new file mode 100644
index 0000000..ea50c42d
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/signin_view.js
@@ -0,0 +1,22 @@
+// Copyright 2018 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.
+
+Polymer({
+  is: 'signin-view',
+
+  behaviors: [welcome.NavigationBehavior],
+
+  /** @private */
+  onSignInClick_: function() {
+    // TODO(scottchen): create a feature flag to direct part of the users to
+    //     chrome://welcome/email-interstitial instead of NTP.
+    // TODO(scottchen): implement chrome://welcome/email-interstitial.
+    welcome.WelcomeBrowserProxyImpl.getInstance().handleActivateSignIn();
+  },
+
+  /** @private */
+  onNoThanksClick_: function() {
+    welcome.navigateToNextStep();
+  }
+});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.html b/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.html
index 31388710..2b7f48c8 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.html
@@ -1,8 +1,12 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_view_manager/cr_view_manager.html">
-<link rel="import" href="navigation_behavior.html">
+<link rel="import" href="apps/nux_google_apps.html">
+<link rel="import" href="email/nux_email.html">
 <link rel="import" href="landing_view.html">
+<link rel="import" href="navigation_behavior.html">
+<link rel="import" href="set_as_default/nux_set_as_default.html">
+<link rel="import" href="signin_view.html">
 
 <dom-module id="welcome-app">
   <template>
@@ -16,16 +20,6 @@
         margin: 0;
         min-height: 100vh;
       }
-
-      [slot='view'] {
-        /* Each view should be centered instead of taking full page. */
-        --cr-view-manager-view: {
-          bottom: initial;
-          top: initial;
-          right: initial;
-          left: initial;
-        };
-      }
     </style>
     <cr-view-manager id="viewManager">
       <landing-view id="step-landing" slot="view" class="active"></landing-view>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.js b/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.js
index 4122b2e..395f63a 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/welcome_app.js
@@ -22,8 +22,9 @@
   // TODO(scottchen): instead of dummy, get data from finch/load time data.
   /** @private {NuxOnboardingModules} */
   modules_: {
-    'new-user': ['h1', 'h1', 'h1'],
-    'returning-user': ['h3', 'h3'],
+    'new-user':
+        ['nux-email', 'nux-google-apps', 'nux-set-as-default', 'signin-view'],
+    'returning-user': ['nux-set-as-default'],
   },
 
   /**
@@ -65,13 +66,6 @@
       element.id = 'step-' + (index + 1);
       element.setAttribute('slot', 'view');
       this.$.viewManager.appendChild(element);
-
-      // TODO(scottchen): this is just to test routing works. Actual elements
-      //     will have buttons that are responsible for navigation.
-      element.textContent = index + 1;
-      element.addEventListener('click', () => {
-        this.navigateToNextStep();
-      });
     });
   },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/welcome_browser_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/welcome_browser_proxy.js
index 679fe655..0f4b33b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/welcome_browser_proxy.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/welcome_browser_proxy.js
@@ -11,7 +11,7 @@
 
   /** @interface */
   class WelcomeBrowserProxy {
-    /** @param {string} redirectUrl the URL to redirect to, after signing in. */
+    /** @param {string=} redirectUrl the URL to go to, after signing in. */
     handleActivateSignIn(redirectUrl) {}
     goToNewTabPage() {}
   }
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.cc b/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.cc
index d43cce3..c01db6f 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.cc
@@ -5,16 +5,18 @@
 #include "chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.h"
 
 #include <algorithm>
+#include <string>
 #include <utility>
 
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "components/crx_file/id_util.h"
 #include "content/public/browser/browser_thread.h"
-#include "extensions/browser/disable_reason.h"
+#include "extensions/browser/uninstall_reason.h"
 
 namespace safe_browsing {
 
@@ -59,6 +61,9 @@
         extension_ids
             ? ExtensionCollection(extension_ids->begin(), extension_ids->end())
             : ExtensionCollection());
+    if (extension_ids.has_value()) {
+      extension_ids_ = extension_ids;
+    }
     std::move(on_prompt_user_)
         .Run(std::move(scanner_results), std::move(callback));
   }
@@ -67,25 +72,40 @@
 void ChromePromptImpl::DisableExtensions(
     const std::vector<base::string16>& extension_ids,
     ChromePrompt::DisableExtensionsCallback callback) {
-  if (extension_service_ == nullptr) {
+  if (extension_service_ == nullptr || !extension_ids_.has_value()) {
     std::move(callback).Run(false);
     return;
   }
+  // Clear the stored extension_ids by moving it onto this stack frame,
+  // so subsequent calls will fail.
+  base::Optional<std::vector<base::string16>> optional_verified_extension_ids{};
+  extension_ids_.swap(optional_verified_extension_ids);
+  std::vector<base::string16> verified_extension_ids =
+      optional_verified_extension_ids.value();
   bool ids_are_valid = std::all_of(
-      extension_ids.begin(), extension_ids.end(), [](const base::string16& id) {
-        return crx_file::id_util::IdIsValid(base::UTF16ToUTF8(id));
+      extension_ids.begin(), extension_ids.end(),
+      [this, &verified_extension_ids](const base::string16& id) {
+        std::string id_utf8 = base::UTF16ToUTF8(id);
+        return crx_file::id_util::IdIsValid(id_utf8) &&
+               base::ContainsValue(verified_extension_ids, id) &&
+               extension_service_->GetInstalledExtension(id_utf8) != nullptr;
       });
   if (!ids_are_valid) {
     std::move(callback).Run(false);
     return;
   }
 
-  int reason = extensions::disable_reason::DISABLE_EXTERNAL_EXTENSION;
+  // This only uninstalls extensions that have been displayed to the user on
+  // the cleanup page.
+  extensions::UninstallReason reason =
+      extensions::UNINSTALL_REASON_USER_INITIATED;
+  bool result = true;
   for (const base::string16& extension_id : extension_ids) {
-    extension_service_->DisableExtension(base::UTF16ToUTF8(extension_id),
-                                         reason);
+    result = extension_service_->UninstallExtension(
+                 base::UTF16ToUTF8(extension_id), reason, nullptr) &&
+             result;
   }
-  std::move(callback).Run(true);
+  std::move(callback).Run(result);
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.h b/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.h
index 9c71ce4..843af0c 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_chrome_prompt_impl.h
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_scanner_results.h"
 #include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
@@ -49,6 +50,7 @@
   mojo::Binding<chrome_cleaner::mojom::ChromePrompt> binding_;
   extensions::ExtensionService* extension_service_;
   OnPromptUser on_prompt_user_;
+  base::Optional<std::vector<base::string16>> extension_ids_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromePromptImpl);
 };
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc
index e0b7e97..9252ef33 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/test_extension_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/mock_extension_system.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
@@ -47,38 +48,96 @@
   std::unique_ptr<ChromePromptImpl> chrome_prompt =
       std::make_unique<ChromePromptImpl>(extension_service, nullptr,
                                          base::DoNothing(), base::DoNothing());
+  chrome_prompt->PromptUser({}, {}, extension_ids, base::DoNothing());
   std::vector<base::string16> extensions_to_disable{extension_ids[0]};
   chrome_prompt->DisableExtensions(
       extensions_to_disable,
       base::BindOnce([](bool result) { EXPECT_TRUE(result); }));
-  EXPECT_FALSE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[0])));
-  EXPECT_TRUE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[1])));
-  EXPECT_TRUE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[2])));
+  EXPECT_EQ(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[0])),
+            nullptr);
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[1])),
+            nullptr);
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[2])),
+            nullptr);
 
-  extensions_to_disable.push_back(extension_ids[2]);
+  chrome_prompt = std::make_unique<ChromePromptImpl>(
+      extension_service, nullptr, base::DoNothing(), base::DoNothing());
+  chrome_prompt->PromptUser({}, {}, extension_ids, base::DoNothing());
+  extensions_to_disable = {extension_ids[2], extension_ids[1]};
   chrome_prompt->DisableExtensions(
       extensions_to_disable,
       base::BindOnce([](bool result) { EXPECT_TRUE(result); }));
-  EXPECT_FALSE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[0])));
-  EXPECT_TRUE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[1])));
-  EXPECT_FALSE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[2])));
+  EXPECT_EQ(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[0])),
+            nullptr);
+  EXPECT_EQ(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[1])),
+            nullptr);
+  EXPECT_EQ(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[2])),
+            nullptr);
+}
 
-  extensions_to_disable.push_back(extension_ids[1]);
+TEST_F(ExtensionDeletionTest, CantDeleteNonPromptedExtensions) {
+  std::vector<base::string16> extension_ids{};
+  extensions::ExtensionService* extension_service = this->service();
+  for (int i = 40; i < 43; i++) {
+    scoped_refptr<const extensions::Extension> extension =
+        extensions::ExtensionBuilder(base::NumberToString(i))
+            .SetManifestKey("version", "1")
+            .Build();
+    auto id = extension->id();
+    extension_ids.push_back(base::UTF8ToUTF16(id));
+    extension_service->AddExtension(extension.get());
+    extension_service->EnableExtension(id);
+  }
+  std::unique_ptr<ChromePromptImpl> chrome_prompt =
+      std::make_unique<ChromePromptImpl>(extension_service, nullptr,
+                                         base::DoNothing(), base::DoNothing());
+  std::vector<base::string16> extensions_to_disable{extension_ids[0]};
   chrome_prompt->DisableExtensions(
       extensions_to_disable,
-      base::BindOnce([](bool result) { EXPECT_TRUE(result); }));
-  EXPECT_FALSE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[0])));
-  EXPECT_FALSE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[1])));
-  EXPECT_FALSE(extension_service->IsExtensionEnabled(
-      base::UTF16ToUTF8(extension_ids[2])));
+      base::BindOnce([](bool result) { EXPECT_FALSE(result); }));
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[0])),
+            nullptr);
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[1])),
+            nullptr);
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[2])),
+            nullptr);
+
+  chrome_prompt = std::make_unique<ChromePromptImpl>(
+      extension_service, nullptr, base::DoNothing(), base::DoNothing());
+  chrome_prompt->DisableExtensions(
+      extension_ids, base::BindOnce([](bool result) { EXPECT_FALSE(result); }));
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[0])),
+            nullptr);
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[1])),
+            nullptr);
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[2])),
+            nullptr);
+
+  chrome_prompt->PromptUser({}, {}, {{extension_ids[2]}}, base::DoNothing());
+  chrome_prompt->DisableExtensions(
+      {extension_ids[0], extension_ids[1]},
+      base::BindOnce([](bool result) { EXPECT_FALSE(result); }));
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[0])),
+            nullptr);
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[1])),
+            nullptr);
+  EXPECT_NE(extension_service->GetInstalledExtension(
+                base::UTF16ToUTF8(extension_ids[2])),
+            nullptr);
 }
 
 TEST_F(ExtensionDeletionTest, EmptyDeletionTest) {
@@ -87,6 +146,7 @@
   std::unique_ptr<ChromePromptImpl> chrome_prompt =
       std::make_unique<ChromePromptImpl>(extension_service, nullptr,
                                          base::DoNothing(), base::DoNothing());
+  chrome_prompt->PromptUser({}, {}, extension_ids, base::DoNothing());
   for (int i = 40; i < 43; i++) {
     scoped_refptr<const extensions::Extension> extension =
         extensions::ExtensionBuilder(base::NumberToString(i))
@@ -149,7 +209,7 @@
     extension_ids.push_back(base::UTF8ToUTF16(id));
   }
   chrome_prompt->DisableExtensions(
-      extension_ids, base::BindOnce([](bool result) { EXPECT_TRUE(result); }));
+      extension_ids, base::BindOnce([](bool result) { EXPECT_FALSE(result); }));
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index 16831b6..8c8c53b 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -313,9 +313,13 @@
 class TestSafeBrowsingBlockingPageFactory
     : public SafeBrowsingBlockingPageFactory {
  public:
-  TestSafeBrowsingBlockingPageFactory() { }
+  TestSafeBrowsingBlockingPageFactory() : always_show_back_to_safety_(true) {}
   ~TestSafeBrowsingBlockingPageFactory() override {}
 
+  void SetAlwaysShowBackToSafety(bool value) {
+    always_show_back_to_safety_ = value;
+  }
+
   SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
       BaseUIManager* delegate,
       WebContents* web_contents,
@@ -340,13 +344,16 @@
         web_contents->GetBrowserContext()->IsOffTheRecord(),
         is_unified_consent_given, IsExtendedReportingEnabled(*prefs),
         IsExtendedReportingPolicyManaged(*prefs), is_proceed_anyway_disabled,
-        true,   // should_open_links_in_new_tab
-        false,  // check_can_go_back_to_safety
+        true,  // should_open_links_in_new_tab
+        always_show_back_to_safety_,
         "cpn_safe_browsing" /* help_center_article_link */);
     return new TestSafeBrowsingBlockingPage(delegate, web_contents,
                                             main_frame_url, unsafe_resources,
                                             display_options);
   }
+
+ private:
+  bool always_show_back_to_safety_;
 };
 
 // Tests the safe browsing blocking page in a browser.
@@ -427,6 +434,12 @@
         embedded_test_server()->GetURL(kEmptyPage), browser);
   }
 
+  // The basic version of this method, which uses an HTTP test URL.
+  GURL SetupWarningAndNavigateInNewTab(Browser* browser) {
+    return SetupWarningAndNavigateToURLInNewTab(
+        embedded_test_server()->GetURL(kEmptyPage), browser);
+  }
+
   // Navigates to a warning on a valid HTTPS website.
   GURL SetupWarningAndNavigateToValidHTTPS() {
     EXPECT_TRUE(https_server_.Start());
@@ -775,6 +788,10 @@
         https_server_.GetURL("/title1.html"));
   }
 
+  void SetAlwaysShowBackToSafety(bool val) {
+    blocking_page_factory_.SetAlwaysShowBackToSafety(val);
+  }
+
  protected:
   TestThreatDetailsFactory details_factory_;
 
@@ -788,6 +805,17 @@
     EXPECT_TRUE(WaitForReady(browser));
     return url;
   }
+  // Adds a safebrowsing result of the current test threat to the fake
+  // safebrowsing service, navigates to that page, and returns the url.
+  // The various wrappers supply different URLs.
+  GURL SetupWarningAndNavigateToURLInNewTab(GURL url, Browser* browser) {
+    SetURLThreatType(url, testing::get<0>(GetParam()));
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser, url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
+    EXPECT_TRUE(WaitForReady(browser));
+    return url;
+  }
 
   base::test::ScopedFeatureList scoped_feature_list_;
   TestSafeBrowsingServiceFactory factory_;
@@ -1149,6 +1177,20 @@
             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
 }
 
+IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, NoBackToSafety) {
+  SetAlwaysShowBackToSafety(false);
+  SetupWarningAndNavigateInNewTab(browser());
+
+  EXPECT_EQ(HIDDEN, GetVisibility("primary-button"));
+  EXPECT_EQ(HIDDEN, GetVisibility("details"));
+  EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
+  EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
+  EXPECT_TRUE(Click("details-button"));
+  EXPECT_EQ(VISIBLE, GetVisibility("details"));
+  EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
+  EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
+}
+
 // Verifies that the reporting checkbox is hidden when opt-in is
 // disabled by policy. However, reports can still be sent if extended
 // reporting is enabled (eg: by its own policy).
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index 711ea81..58fb1c6 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -190,7 +190,7 @@
     BrowserWindow::AvatarBubbleMode bubble_mode;
     switch (service_type) {
       case GAIA_SERVICE_TYPE_INCOGNITO:
-        chrome::NewIncognitoWindow(browser);
+        chrome::NewIncognitoWindow(profile);
         return;
       case GAIA_SERVICE_TYPE_ADDSESSION:
         bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_ADD_ACCOUNT;
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 9c9b41e..84c815a 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -663,56 +663,37 @@
 // static
 SkColor ThemeService::GetSeparatorColor(SkColor tab_color,
                                         SkColor frame_color) {
-  // We use this alpha value for the separator if possible.
-  const SkAlpha kAlpha = 0x40;
+  const float kContrastRatio = 2.f;
 
   // In most cases, if the tab is lighter than the frame, we darken the
   // frame; if the tab is darker than the frame, we lighten the frame.
   // However, if the frame is already very dark or very light, respectively,
   // this won't contrast sufficiently with the frame color, so we'll need to
   // reverse when we're lightening and darkening.
-  const float tab_luminance = color_utils::GetRelativeLuminance(tab_color);
-  const float frame_luminance = color_utils::GetRelativeLuminance(frame_color);
-  const bool lighten = tab_luminance < frame_luminance;
-  SkColor separator_color = lighten ? SK_ColorWHITE : SK_ColorBLACK;
-  float separator_luminance = color_utils::GetRelativeLuminance(
-      color_utils::AlphaBlend(separator_color, frame_color, kAlpha));
-  // The minimum contrast ratio here is just under the ~1.1469 in the default MD
-  // incognito theme.  We want the separator to still darken the frame in that
-  // theme, but that's about as low of contrast as we're willing to accept.
-  const float kMinContrastRatio = 1.1465f;
-  if (color_utils::GetContrastRatio(separator_luminance, frame_luminance) >=
-      kMinContrastRatio)
-    return SkColorSetA(separator_color, kAlpha);
+  const bool lighten = color_utils::GetRelativeLuminance(tab_color) <
+                       color_utils::GetRelativeLuminance(frame_color);
+  SkColor separator_color =
+      lighten ? SK_ColorWHITE : color_utils::GetDarkestColor();
 
-  // We need to reverse whether we're darkening or lightening.  We know the new
-  // separator color will contrast with the frame; check whether it also
-  // contrasts at least as well with the tab.
-  separator_color = color_utils::InvertColor(separator_color);
-  separator_luminance = color_utils::GetRelativeLuminance(
-      color_utils::AlphaBlend(separator_color, frame_color, kAlpha));
-  if (color_utils::GetContrastRatio(separator_luminance, tab_luminance) >=
-      color_utils::GetContrastRatio(separator_luminance, frame_luminance))
-    return SkColorSetA(separator_color, kAlpha);
-
-  // The reversed separator doesn't contrast enough with the tab.  Compute the
-  // resulting luminance from adjusting the tab color, instead of the frame
-  // color, by the separator color.
-  const float target_luminance = color_utils::GetRelativeLuminance(
-      color_utils::AlphaBlend(separator_color, tab_color, kAlpha));
-
-  // Now try to compute an alpha for the separator such that, when blended with
-  // the frame, it results in the above luminance.  Because the luminance
-  // computation is not easily invertible, we use a binary search over the
-  // possible range of alpha values.
-  SkAlpha alpha = 128;
-  for (int delta = lighten ? 64 : -64; delta != 0; delta /= 2) {
-    const float luminance = color_utils::GetRelativeLuminance(
-        color_utils::AlphaBlend(separator_color, frame_color, alpha));
-    if (luminance == target_luminance)
-      break;
-    alpha += (luminance < target_luminance) ? -delta : delta;
+  SkAlpha alpha = color_utils::FindBlendValueForContrastRatio(
+      frame_color, separator_color, frame_color, kContrastRatio, 0);
+  if (color_utils::GetContrastRatio(
+          color_utils::AlphaBlend(separator_color, frame_color, alpha),
+          frame_color) >= kContrastRatio) {
+    return SkColorSetA(separator_color, alpha);
   }
+
+  separator_color =
+      color_utils::BlendTowardOppositeLuma(separator_color, SK_AlphaOPAQUE);
+
+  // If the above call failed to create sufficient contrast, the frame color is
+  // already very dark or very light.  Since separators are only used when the
+  // tab has low contrast against the frame, the tab color is similarly very
+  // dark or very light, just not quite as much so as the frame color.  Blend
+  // towards the opposite separator color, and compute the contrast against the
+  // tab instead of the frame to ensure both contrasts hit the desired minimum.
+  alpha = color_utils::FindBlendValueForContrastRatio(
+      frame_color, separator_color, tab_color, kContrastRatio, 0);
   return SkColorSetA(separator_color, alpha);
 }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 25368f3..15e72dd 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1444,8 +1444,6 @@
       "views/frame/browser_non_client_frame_view_ash.h",
       "views/frame/immersive_context_mus.cc",
       "views/frame/immersive_context_mus.h",
-      "views/frame/immersive_focus_watcher_mus.cc",
-      "views/frame/immersive_focus_watcher_mus.h",
       "views/frame/immersive_handler_factory_mus.cc",
       "views/frame/immersive_handler_factory_mus.h",
       "views/frame/immersive_mode_controller_ash.cc",
@@ -3334,8 +3332,8 @@
     sources += [
       "in_product_help/active_tab_tracker.cc",
       "in_product_help/active_tab_tracker.h",
-      "in_product_help/reopen_tab_iph_trigger.cc",
-      "in_product_help/reopen_tab_iph_trigger.h",
+      "in_product_help/reopen_tab_in_product_help_trigger.cc",
+      "in_product_help/reopen_tab_in_product_help_trigger.h",
     ]
   }
 
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index a8419a1b..e88405c 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_package_syncable_service.h"
 #include "chrome/browser/ui/app_list/arc/arc_pai_starter.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_util.h"
@@ -1030,7 +1031,8 @@
   // TODO(khmel): Use show_in_launcher flag to hide the Play Store app.
   if (app_id == arc::kPlayStoreAppId &&
       arc::IsRobotOrOfflineDemoAccountMode() &&
-      !chromeos::DemoSession::IsDeviceInDemoMode()) {
+      !(chromeos::DemoSession::IsDeviceInDemoMode() &&
+        chromeos::switches::ShouldShowPlayStoreInDemoMode())) {
     return;
   }
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index b738d06..7c6f711 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -167,6 +167,12 @@
       ->GetShelfViewForTesting();
 }
 
+int64_t GetDisplayIdForBrowserWindow(BrowserWindow* window) {
+  return display::Screen::GetScreen()
+      ->GetDisplayNearestWindow(window->GetNativeWindow())
+      .id();
+}
+
 }  // namespace
 
 class LauncherPlatformAppBrowserTest
@@ -855,10 +861,11 @@
   // Ensures browser 2 is above browser 1 in display 1.
   browser_list->SetLastActive(browser2);
   browser_list->SetLastActive(browser0);
+  aura::test::WaitForAllChangesToComplete();
   EXPECT_EQ(browser_list->size(), 3U);
-  EXPECT_EQ(browser0->window()->GetNativeWindow()->GetRootWindow(), roots[0]);
-  EXPECT_EQ(browser1->window()->GetNativeWindow()->GetRootWindow(), roots[1]);
-  EXPECT_EQ(browser2->window()->GetNativeWindow()->GetRootWindow(), roots[1]);
+  EXPECT_EQ(displays[0].id(), GetDisplayIdForBrowserWindow(browser0->window()));
+  EXPECT_EQ(displays[1].id(), GetDisplayIdForBrowserWindow(browser1->window()));
+  EXPECT_EQ(displays[1].id(), GetDisplayIdForBrowserWindow(browser2->window()));
   EXPECT_EQ(browser0->tab_strip_model()->count(), 1);
   EXPECT_EQ(browser1->tab_strip_model()->count(), 1);
   EXPECT_EQ(browser2->tab_strip_model()->count(), 1);
@@ -897,8 +904,9 @@
   BrowserList* browser_list = BrowserList::GetInstance();
   Browser* browser0 = browser();
   browser0->window()->SetBounds(displays[0].work_area());
+  aura::test::WaitForAllChangesToComplete();
   EXPECT_EQ(browser_list->size(), 1U);
-  EXPECT_EQ(browser0->window()->GetNativeWindow()->GetRootWindow(), roots[0]);
+  EXPECT_EQ(displays[0].id(), GetDisplayIdForBrowserWindow(browser0->window()));
   EXPECT_EQ(browser0->tab_strip_model()->count(), 1);
 
   // Launches an app from the shelf of display 0 and expects a new browser with
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index f8f817f..30f4864 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1296,16 +1296,12 @@
   return window()->PreHandleKeyboardEvent(event);
 }
 
-void Browser::HandleKeyboardEvent(content::WebContents* source,
+bool Browser::HandleKeyboardEvent(content::WebContents* source,
                                   const NativeWebKeyboardEvent& event) {
   DevToolsWindow* devtools_window =
       DevToolsWindow::GetInstanceForInspectedWebContents(source);
-  bool handled = false;
-  if (devtools_window)
-    handled = devtools_window->ForwardKeyboardEvent(event);
-
-  if (!handled)
-    window()->HandleKeyboardEvent(event);
+  return (devtools_window && devtools_window->ForwardKeyboardEvent(event)) ||
+         window()->HandleKeyboardEvent(event);
 }
 
 bool Browser::TabsNeedBeforeUnloadFired() {
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 4bcc4ab5..875f5d8 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -482,7 +482,7 @@
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   bool PreHandleGestureEvent(content::WebContents* source,
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 0649a4c5..6dfff626 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -342,7 +342,7 @@
       NewWindow(browser_);
       break;
     case IDC_NEW_INCOGNITO_WINDOW:
-      NewIncognitoWindow(browser_);
+      NewIncognitoWindow(profile());
       break;
     case IDC_CLOSE_WINDOW:
       base::RecordAction(base::UserMetricsAction("CloseWindowByKey"));
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index fe98ecab..10b3d0e 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -577,13 +577,13 @@
   NewEmptyWindow(browser->profile()->GetOriginalProfile());
 }
 
-void NewIncognitoWindow(Browser* browser) {
+void NewIncognitoWindow(Profile* profile) {
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
   feature_engagement::IncognitoWindowTrackerFactory::GetInstance()
-      ->GetForProfile(browser->profile())
+      ->GetForProfile(profile)
       ->OnIncognitoWindowOpened();
 #endif
-  NewEmptyWindow(browser->profile()->GetOffTheRecordProfile());
+  NewEmptyWindow(profile->GetOffTheRecordProfile());
 }
 
 void CloseWindow(Browser* browser) {
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h
index 4b31e64..5c6b768 100644
--- a/chrome/browser/ui/browser_commands.h
+++ b/chrome/browser/ui/browser_commands.h
@@ -70,7 +70,7 @@
 void OpenCurrentURL(Browser* browser);
 void Stop(Browser* browser);
 void NewWindow(Browser* browser);
-void NewIncognitoWindow(Browser* browser);
+void NewIncognitoWindow(Profile* profile);
 void CloseWindow(Browser* browser);
 void NewTab(Browser* browser);
 void CloseTab(Browser* browser);
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 21ee7f04..19d4ce76 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -366,7 +366,7 @@
 
   // Allows the BrowserWindow object to handle the specified keyboard event,
   // if the renderer did not process it.
-  virtual void HandleKeyboardEvent(
+  virtual bool HandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) = 0;
 
   // Clipboard commands applied to the whole browser window.
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_iph_trigger.cc b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.cc
similarity index 70%
rename from chrome/browser/ui/in_product_help/reopen_tab_iph_trigger.cc
rename to chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.cc
index 0ae8154..d667c2d7 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_iph_trigger.cc
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/in_product_help/reopen_tab_iph_trigger.h"
+#include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h"
 
 #include <utility>
 
@@ -13,17 +13,18 @@
 namespace in_product_help {
 
 // static
-const base::TimeDelta ReopenTabIPHTrigger::kTabMinimumActiveDuration =
+const base::TimeDelta ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration =
     base::TimeDelta::FromSeconds(10);
 // static
-const base::TimeDelta ReopenTabIPHTrigger::kNewTabOpenedTimeout =
+const base::TimeDelta ReopenTabInProductHelpTrigger::kNewTabOpenedTimeout =
     base::TimeDelta::FromSeconds(10);
 // static
-const base::TimeDelta ReopenTabIPHTrigger::kOmniboxFocusedTimeout =
+const base::TimeDelta ReopenTabInProductHelpTrigger::kOmniboxFocusedTimeout =
     base::TimeDelta::FromSeconds(10);
 
-ReopenTabIPHTrigger::ReopenTabIPHTrigger(feature_engagement::Tracker* tracker,
-                                         const base::TickClock* clock)
+ReopenTabInProductHelpTrigger::ReopenTabInProductHelpTrigger(
+    feature_engagement::Tracker* tracker,
+    const base::TickClock* clock)
     : tracker_(tracker), clock_(clock), trigger_state_(NO_ACTIONS_SEEN) {
   DCHECK(tracker);
   DCHECK(clock);
@@ -33,14 +34,16 @@
   DCHECK(!kOmniboxFocusedTimeout.is_zero());
 }
 
-ReopenTabIPHTrigger::~ReopenTabIPHTrigger() = default;
+ReopenTabInProductHelpTrigger::~ReopenTabInProductHelpTrigger() = default;
 
-void ReopenTabIPHTrigger::SetShowHelpCallback(ShowHelpCallback callback) {
+void ReopenTabInProductHelpTrigger::SetShowHelpCallback(
+    ShowHelpCallback callback) {
   DCHECK(callback);
   cb_ = std::move(callback);
 }
 
-void ReopenTabIPHTrigger::ActiveTabClosed(base::TimeTicks activation_time) {
+void ReopenTabInProductHelpTrigger::ActiveTabClosed(
+    base::TimeTicks activation_time) {
   // Reset all flags at this point. We should only trigger IPH if the events
   // happen in the prescribed order.
   ResetTriggerState();
@@ -53,7 +56,7 @@
   }
 }
 
-void ReopenTabIPHTrigger::NewTabOpened() {
+void ReopenTabInProductHelpTrigger::NewTabOpened() {
   if (trigger_state_ != ACTIVE_TAB_CLOSED)
     return;
 
@@ -67,7 +70,7 @@
   }
 }
 
-void ReopenTabIPHTrigger::OmniboxFocused() {
+void ReopenTabInProductHelpTrigger::OmniboxFocused() {
   if (trigger_state_ != NEW_TAB_OPENED)
     return;
 
@@ -83,12 +86,12 @@
   }
 }
 
-void ReopenTabIPHTrigger::HelpDismissed() {
+void ReopenTabInProductHelpTrigger::HelpDismissed() {
   tracker_->Dismissed(feature_engagement::kIPHReopenTabFeature);
   ResetTriggerState();
 }
 
-void ReopenTabIPHTrigger::ResetTriggerState() {
+void ReopenTabInProductHelpTrigger::ResetTriggerState() {
   time_of_last_step_ = base::TimeTicks();
   trigger_state_ = NO_ACTIONS_SEEN;
 }
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_iph_trigger.h b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h
similarity index 81%
rename from chrome/browser/ui/in_product_help/reopen_tab_iph_trigger.h
rename to chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h
index 1fa1fc58..82a74c17 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_iph_trigger.h
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_IN_PRODUCT_HELP_REOPEN_TAB_IPH_TRIGGER_H_
-#define CHROME_BROWSER_UI_IN_PRODUCT_HELP_REOPEN_TAB_IPH_TRIGGER_H_
+#ifndef CHROME_BROWSER_UI_IN_PRODUCT_HELP_REOPEN_TAB_IN_PRODUCT_HELP_TRIGGER_H_
+#define CHROME_BROWSER_UI_IN_PRODUCT_HELP_REOPEN_TAB_IN_PRODUCT_HELP_TRIGGER_H_
 
 #include "base/callback.h"
 #include "base/time/tick_clock.h"
@@ -22,11 +22,11 @@
 //
 // Clients should listen for the relevant user events and pass them to this
 // class. Additionally, clients must display IPH when told by this class.
-class ReopenTabIPHTrigger {
+class ReopenTabInProductHelpTrigger {
  public:
-  ReopenTabIPHTrigger(feature_engagement::Tracker* tracker,
-                      const base::TickClock* clock);
-  ~ReopenTabIPHTrigger();
+  ReopenTabInProductHelpTrigger(feature_engagement::Tracker* tracker,
+                                const base::TickClock* clock);
+  ~ReopenTabInProductHelpTrigger();
 
   using ShowHelpCallback = base::RepeatingCallback<void()>;
 
@@ -74,9 +74,9 @@
 
   base::TimeTicks time_of_last_step_;
 
-  DISALLOW_COPY_AND_ASSIGN(ReopenTabIPHTrigger);
+  DISALLOW_COPY_AND_ASSIGN(ReopenTabInProductHelpTrigger);
 };
 
 }  // namespace in_product_help
 
-#endif  // CHROME_BROWSER_UI_IN_PRODUCT_HELP_REOPEN_TAB_IPH_TRIGGER_H_
+#endif  // CHROME_BROWSER_UI_IN_PRODUCT_HELP_REOPEN_TAB_IN_PRODUCT_HELP_TRIGGER_H_
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_iph_trigger_unittest.cc b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc
similarity index 71%
rename from chrome/browser/ui/in_product_help/reopen_tab_iph_trigger_unittest.cc
rename to chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc
index d18ae7e5..8d3517f 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_iph_trigger_unittest.cc
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/in_product_help/reopen_tab_iph_trigger.h"
+#include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h"
 
 #include "base/bind.h"
 #include "base/test/simple_test_tick_clock.h"
@@ -22,18 +22,19 @@
 
 namespace {
 
-void DismissImmediately(ReopenTabIPHTrigger* trigger) {
+void DismissImmediately(ReopenTabInProductHelpTrigger* trigger) {
   trigger->HelpDismissed();
 }
 
-void DismissAndSetFlag(ReopenTabIPHTrigger* trigger, bool* triggered) {
+void DismissAndSetFlag(ReopenTabInProductHelpTrigger* trigger,
+                       bool* triggered) {
   *triggered = true;
   trigger->HelpDismissed();
 }
 
 }  // namespace
 
-TEST(ReopenTabIPHTriggerTest, TriggersIPH) {
+TEST(ReopenTabInProductHelpTriggerTest, TriggersIPH) {
   NiceMock<MockTracker> mock_tracker;
 
   // We expect to send the backend our trigger event and ask if we should
@@ -50,19 +51,19 @@
 
   // Instantiate IPH and send sequence of user interactions.
   base::SimpleTestTickClock clock;
-  ReopenTabIPHTrigger reopen_tab_iph(&mock_tracker, &clock);
+  ReopenTabInProductHelpTrigger reopen_tab_iph(&mock_tracker, &clock);
 
   reopen_tab_iph.SetShowHelpCallback(
       base::BindRepeating(DismissImmediately, &reopen_tab_iph));
 
   auto activation_time = clock.NowTicks();
-  clock.Advance(ReopenTabIPHTrigger::kTabMinimumActiveDuration);
+  clock.Advance(ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.ActiveTabClosed(activation_time);
   reopen_tab_iph.NewTabOpened();
   reopen_tab_iph.OmniboxFocused();
 }
 
-TEST(ReopenTabIPHTriggerTest, RespectsBackendShouldTrigger) {
+TEST(ReopenTabInProductHelpTriggerTest, RespectsBackendShouldTrigger) {
   NiceMock<MockTracker> mock_tracker;
 
   EXPECT_CALL(mock_tracker, ShouldTriggerHelpUI(_))
@@ -71,62 +72,62 @@
   EXPECT_CALL(mock_tracker, Dismissed(_)).Times(0);
 
   base::SimpleTestTickClock clock;
-  ReopenTabIPHTrigger reopen_tab_iph(&mock_tracker, &clock);
+  ReopenTabInProductHelpTrigger reopen_tab_iph(&mock_tracker, &clock);
 
   reopen_tab_iph.SetShowHelpCallback(
       base::BindRepeating(DismissImmediately, &reopen_tab_iph));
 
   auto activation_time = clock.NowTicks();
-  clock.Advance(ReopenTabIPHTrigger::kTabMinimumActiveDuration);
+  clock.Advance(ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.ActiveTabClosed(activation_time);
   reopen_tab_iph.NewTabOpened();
   reopen_tab_iph.OmniboxFocused();
 }
 
-TEST(ReopenTabIPHTriggerTest, TabNotActiveLongEnough) {
+TEST(ReopenTabInProductHelpTriggerTest, TabNotActiveLongEnough) {
   NiceMock<MockTracker> mock_tracker;
 
   EXPECT_CALL(mock_tracker, NotifyEvent(_)).Times(0);
   EXPECT_CALL(mock_tracker, ShouldTriggerHelpUI(_)).Times(0);
 
   base::SimpleTestTickClock clock;
-  ReopenTabIPHTrigger reopen_tab_iph(&mock_tracker, &clock);
+  ReopenTabInProductHelpTrigger reopen_tab_iph(&mock_tracker, &clock);
 
   auto activation_time = clock.NowTicks();
-  clock.Advance(ReopenTabIPHTrigger::kTabMinimumActiveDuration / 2);
+  clock.Advance(ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration / 2);
   reopen_tab_iph.ActiveTabClosed(activation_time);
   reopen_tab_iph.NewTabOpened();
   reopen_tab_iph.OmniboxFocused();
 }
 
-TEST(ReopenTabIPHTriggerTest, RespectsTimeouts) {
+TEST(ReopenTabInProductHelpTriggerTest, RespectsTimeouts) {
   NiceMock<MockTracker> mock_tracker;
 
   EXPECT_CALL(mock_tracker, NotifyEvent(_)).Times(0);
   EXPECT_CALL(mock_tracker, ShouldTriggerHelpUI(_)).Times(0);
 
   base::SimpleTestTickClock clock;
-  ReopenTabIPHTrigger reopen_tab_iph(&mock_tracker, &clock);
+  ReopenTabInProductHelpTrigger reopen_tab_iph(&mock_tracker, &clock);
 
   reopen_tab_iph.SetShowHelpCallback(
       base::BindRepeating(DismissImmediately, &reopen_tab_iph));
 
   auto activation_time = clock.NowTicks();
-  clock.Advance(ReopenTabIPHTrigger::kTabMinimumActiveDuration);
+  clock.Advance(ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.ActiveTabClosed(activation_time);
-  clock.Advance(ReopenTabIPHTrigger::kNewTabOpenedTimeout);
+  clock.Advance(ReopenTabInProductHelpTrigger::kNewTabOpenedTimeout);
   reopen_tab_iph.NewTabOpened();
   reopen_tab_iph.OmniboxFocused();
 
   activation_time = clock.NowTicks();
-  clock.Advance(ReopenTabIPHTrigger::kTabMinimumActiveDuration);
+  clock.Advance(ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.ActiveTabClosed(activation_time);
   reopen_tab_iph.NewTabOpened();
-  clock.Advance(ReopenTabIPHTrigger::kOmniboxFocusedTimeout);
+  clock.Advance(ReopenTabInProductHelpTrigger::kOmniboxFocusedTimeout);
   reopen_tab_iph.OmniboxFocused();
 }
 
-TEST(ReopenTabIPHTriggerTest, TriggersTwice) {
+TEST(ReopenTabInProductHelpTriggerTest, TriggersTwice) {
   NiceMock<MockTracker> mock_tracker;
 
   EXPECT_CALL(
@@ -139,14 +140,14 @@
   EXPECT_CALL(mock_tracker, Dismissed(_)).Times(2);
 
   base::SimpleTestTickClock clock;
-  ReopenTabIPHTrigger reopen_tab_iph(&mock_tracker, &clock);
+  ReopenTabInProductHelpTrigger reopen_tab_iph(&mock_tracker, &clock);
 
   bool triggered = false;
   reopen_tab_iph.SetShowHelpCallback(
       base::BindRepeating(DismissAndSetFlag, &reopen_tab_iph, &triggered));
 
   auto activation_time = clock.NowTicks();
-  clock.Advance(ReopenTabIPHTrigger::kTabMinimumActiveDuration);
+  clock.Advance(ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.ActiveTabClosed(activation_time);
   reopen_tab_iph.NewTabOpened();
   reopen_tab_iph.OmniboxFocused();
@@ -154,7 +155,7 @@
   EXPECT_TRUE(triggered);
   triggered = false;
 
-  clock.Advance(ReopenTabIPHTrigger::kTabMinimumActiveDuration);
+  clock.Advance(ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.ActiveTabClosed(activation_time);
   reopen_tab_iph.NewTabOpened();
   reopen_tab_iph.OmniboxFocused();
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index 451a892..e5a50b0 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -327,57 +327,6 @@
   }
 }
 
-// COLOR_TOOLBAR_TOP_SEPARATOR represents the border between tabs and the
-// frame, as well as the border between tabs and the toolbar.  For this
-// reason, it is difficult to calculate the One True Color that works well on
-// all themes and is opaque.  However, we can cheat to get a good color that
-// works well for both borders.  The idea is we have two variables: alpha and
-// lightness.  And we have two constraints (on lightness):
-// 1. the border color, when painted on |header_bg|, should give |header_fg|
-// 2. the border color, when painted on |tab_bg|, should give |tab_fg|
-// This gives the equations:
-// alpha*lightness + (1 - alpha)*header_bg = header_fg
-// alpha*lightness + (1 - alpha)*tab_bg = tab_fg
-// The algorithm below is just a result of solving those equations for alpha
-// and lightness.  If a problem is encountered, like division by zero, or
-// |a| or |l| not in [0, 1], then fallback on |header_fg| or |tab_fg|.
-SkColor GetToolbarTopSeparatorColor(SkColor header_fg,
-                                    SkColor header_bg,
-                                    SkColor tab_fg,
-                                    SkColor tab_bg) {
-  using namespace color_utils;
-
-  SkColor default_color = SkColorGetA(header_fg) ? header_fg : tab_fg;
-  if (!SkColorGetA(default_color))
-    return SK_ColorTRANSPARENT;
-
-  auto get_lightness = [](SkColor color) {
-    HSL hsl;
-    SkColorToHSL(color, &hsl);
-    return hsl.l;
-  };
-
-  double f1 = get_lightness(GetResultingPaintColor(header_fg, header_bg));
-  double b1 = get_lightness(header_bg);
-  double f2 = get_lightness(GetResultingPaintColor(tab_fg, tab_bg));
-  double b2 = get_lightness(tab_bg);
-
-  if (b1 == b2)
-    return default_color;
-  double a = (f1 - f2 - b1 + b2) / (b2 - b1);
-  if (a == 0)
-    return default_color;
-  double l = (f1 - (1 - a) * b1) / a;
-  if (a < 0 || a > 1 || l < 0 || l > 1)
-    return default_color;
-  // Take the hue and saturation from |default_color|, but use the
-  // calculated lightness.
-  HSL border;
-  SkColorToHSL(default_color, &border);
-  border.l = l;
-  return HSLToSkColor(border, a * 0xff);
-}
-
 }  // namespace
 
 GtkUi::GtkUi() {
@@ -423,6 +372,8 @@
                          G_CALLBACK(OnThemeChanged), this);
   g_signal_connect_after(settings, "notify::gtk-icon-theme-name",
                          G_CALLBACK(OnThemeChanged), this);
+  g_signal_connect_after(settings, "notify::gtk-application-prefer-dark-theme",
+                         G_CALLBACK(OnThemeChanged), this);
 
   GdkScreen* screen = gdk_screen_get_default();
   // Listen for DPI changes.
@@ -1008,22 +959,36 @@
 
     // These colors represent the border drawn around tabs and between
     // the tabstrip and toolbar.
-    SkColor toolbar_top_separator =
-        GetBorderColor(header_selector + " GtkButton#button");
+    SkColor toolbar_top_separator = GetBorderColor(
+        header_selector + " GtkSeparator#separator.vertical.titlebutton");
     SkColor toolbar_top_separator_inactive =
-        GetBorderColor(header_selector + ":backdrop GtkButton#button");
-    if (!ui::MaterialDesignController::IsRefreshUi()) {
-      toolbar_top_separator = GetToolbarTopSeparatorColor(
-          toolbar_top_separator, frame_color, tab_border, tab_color);
-      toolbar_top_separator_inactive = GetToolbarTopSeparatorColor(
-          toolbar_top_separator_inactive, frame_color_inactive, tab_border,
-          tab_color);
+        GetBorderColor(header_selector +
+                       ":backdrop GtkSeparator#separator.vertical.titlebutton");
+
+    auto toolbar_top_separator_has_good_contrast = [&]() {
+      // This constant is copied from chrome/browser/themes/theme_service.cc.
+      const float kMinContrastRatio = 2.f;
+
+      SkColor active = color_utils::GetResultingPaintColor(
+          toolbar_top_separator, frame_color);
+      SkColor inactive = color_utils::GetResultingPaintColor(
+          toolbar_top_separator_inactive, frame_color_inactive);
+      return color_utils::GetContrastRatio(frame_color, active) >=
+                 kMinContrastRatio &&
+             color_utils::GetContrastRatio(frame_color_inactive, inactive) >=
+                 kMinContrastRatio;
+    };
+
+    if (!toolbar_top_separator_has_good_contrast()) {
+      toolbar_top_separator =
+          GetBorderColor(header_selector + " GtkButton#button");
+      toolbar_top_separator_inactive =
+          GetBorderColor(header_selector + ":backdrop GtkButton#button");
     }
 
-    // Unlike with toolbars, we always want a border around tabs, so let
-    // ThemeService choose the border color if the theme doesn't provide one.
-    if (SkColorGetA(toolbar_top_separator) &&
-        SkColorGetA(toolbar_top_separator_inactive)) {
+    // If we can't get a contrasting stroke from the theme, have ThemeService
+    // provide a stroke color for us.
+    if (toolbar_top_separator_has_good_contrast()) {
       color_map[ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR] =
           toolbar_top_separator;
       color_map[ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR_INACTIVE] =
@@ -1095,6 +1060,9 @@
 }
 
 void GtkUi::ResetStyle() {
+  colors_.clear();
+  custom_frame_colors_.clear();
+  native_frame_colors_.clear();
   LoadGtkValues();
   native_theme_->NotifyObservers();
 }
diff --git a/chrome/browser/ui/login/OWNERS b/chrome/browser/ui/login/OWNERS
index 6cbbda3..6e808f5 100644
--- a/chrome/browser/ui/login/OWNERS
+++ b/chrome/browser/ui/login/OWNERS
@@ -1,5 +1,5 @@
+carlosil@chromium.org
 davidben@chromium.org
-meacer@chromium.org
 vabr@chromium.org
 
 # COMPONENT: Internals>Network>Auth
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index 9182362..c0c47c5 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -37,6 +37,8 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.h"
 #include "chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.h"
@@ -483,6 +485,10 @@
   controller_->GetWebContents()->GetController().DiscardNonCommittedEntries();
 }
 
+void ChromeOmniboxClient::NewIncognitoWindow() {
+  chrome::NewIncognitoWindow(profile_);
+}
+
 void ChromeOmniboxClient::PromptPageTranslation() {
   content::WebContents* contents = controller_->GetWebContents();
   if (contents) {
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.h b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
index b791ab6..8372fac 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.h
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
@@ -80,6 +80,7 @@
   void OnURLOpenedFromOmnibox(OmniboxLog* log) override;
   void OnBookmarkLaunched() override;
   void DiscardNonCommittedNavigations() override;
+  void NewIncognitoWindow() override;
   void PromptPageTranslation() override;
 
  private:
diff --git a/chrome/browser/ui/signin_view_controller_delegate.cc b/chrome/browser/ui/signin_view_controller_delegate.cc
index 23499a80..9312b16 100644
--- a/chrome/browser/ui/signin_view_controller_delegate.cc
+++ b/chrome/browser/ui/signin_view_controller_delegate.cc
@@ -95,10 +95,11 @@
   }
 }
 
-void SigninViewControllerDelegate::HandleKeyboardEvent(
+bool SigninViewControllerDelegate::HandleKeyboardEvent(
     content::WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
   NOTREACHED();
+  return false;
 }
 
 bool SigninViewControllerDelegate::CanGoBack(
diff --git a/chrome/browser/ui/signin_view_controller_delegate.h b/chrome/browser/ui/signin_view_controller_delegate.h
index 967dc4eb..475247b 100644
--- a/chrome/browser/ui/signin_view_controller_delegate.h
+++ b/chrome/browser/ui/signin_view_controller_delegate.h
@@ -98,7 +98,7 @@
                            bool to_different_document) override;
 
   // Subclasses must override this method to correctly handle accelerators.
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
 
diff --git a/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc b/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
index a2f5b76..ac8b2f0 100644
--- a/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
+++ b/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
@@ -117,7 +117,7 @@
   ~WebDialogWebContentsDelegateViews() override {}
 
   // ui::WebDialogWebContentsDelegate:
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override {
     // Forward shortcut keys in dialog to our initiator's delegate.
@@ -125,12 +125,15 @@
     // Disabled on Mac due to http://crbug.com/112173
 #if !defined(OS_MACOSX)
     if (!initiator_observer_->web_contents())
-      return;
+      return false;
 
     auto* delegate = initiator_observer_->web_contents()->GetDelegate();
     if (!delegate)
-      return;
-    delegate->HandleKeyboardEvent(initiator_observer_->web_contents(), event);
+      return false;
+    return delegate->HandleKeyboardEvent(initiator_observer_->web_contents(),
+                                         event);
+#else
+    return false;
 #endif
   }
 
@@ -191,10 +194,10 @@
   }
 
   // contents::WebContentsDelegate:
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override {
-    unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+    return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
         event, view_->GetFocusManager());
   }
 
diff --git a/chrome/browser/ui/views/extensions/extension_view_views.cc b/chrome/browser/ui/views/extensions/extension_view_views.cc
index c2ddada..4f3f949 100644
--- a/chrome/browser/ui/views/extensions/extension_view_views.cc
+++ b/chrome/browser/ui/views/extensions/extension_view_views.cc
@@ -89,11 +89,11 @@
   WebView::RenderViewCreated(render_view_host);
 }
 
-void ExtensionViewViews::HandleKeyboardEvent(
+bool ExtensionViewViews::HandleKeyboardEvent(
     content::WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
-  unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
-                                                        GetFocusManager());
+  return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+      event, GetFocusManager());
 }
 
 void ExtensionViewViews::OnLoaded() {
diff --git a/chrome/browser/ui/views/extensions/extension_view_views.h b/chrome/browser/ui/views/extensions/extension_view_views.h
index 880fd8d..976bc4b 100644
--- a/chrome/browser/ui/views/extensions/extension_view_views.h
+++ b/chrome/browser/ui/views/extensions/extension_view_views.h
@@ -57,7 +57,7 @@
   void ResizeDueToAutoResize(content::WebContents* web_contents,
                              const gfx::Size& new_size) override;
   void RenderViewCreated(content::RenderViewHost* render_view_host) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   void OnLoaded() override;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 4f4dd58..d057bd18 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1523,12 +1523,12 @@
   return content::KeyboardEventProcessingResult::NOT_HANDLED_IS_SHORTCUT;
 }
 
-void BrowserView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
+bool BrowserView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
   if (frame_->HandleKeyboardEvent(event))
-    return;
+    return true;
 
-  unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
-                                                        GetFocusManager());
+  return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+      event, GetFocusManager());
 }
 
 // TODO(devint): http://b/issue?id=1117225 Cut, Copy, and Paste are always
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index d6bafcb..2499f3c 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -385,7 +385,7 @@
   void ShowAppMenu() override;
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) override;
   void CutCopyPaste(int command_id) override;
   FindBar* CreateFindBar() override;
diff --git a/chrome/browser/ui/views/frame/immersive_focus_watcher_mus.h b/chrome/browser/ui/views/frame/immersive_focus_watcher_mus.h
deleted file mode 100644
index 69e32e1..0000000
--- a/chrome/browser/ui/views/frame/immersive_focus_watcher_mus.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2018 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_FRAME_IMMERSIVE_FOCUS_WATCHER_MUS_H_
-#define CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_FOCUS_WATCHER_MUS_H_
-
-#include "ash/public/cpp/immersive/immersive_focus_watcher.h"
-#include "ui/aura/client/focus_change_observer.h"
-#include "ui/aura/client/transient_window_client_observer.h"
-#include "ui/views/focus/focus_manager.h"
-#include "ui/wm/public/activation_change_observer.h"
-
-namespace ash {
-class ImmersiveFullscreenController;
-class ImmersiveRevealedLock;
-}  // namespace ash
-
-// ImmersiveFocusWatcherMus is responsible for grabbing a reveal lock based on
-// activation and/or focus. This implementation grabs a lock if views focus is
-// in the top view, a bubble is showing that is anchored to the top view, or
-// the focused window is a transient child of the top view's widget.
-class ImmersiveFocusWatcherMus
-    : public ash::ImmersiveFocusWatcher,
-      public views::FocusChangeListener,
-      public aura::client::TransientWindowClientObserver,
-      public ::wm::ActivationChangeObserver {
- public:
-  explicit ImmersiveFocusWatcherMus(
-      ash::ImmersiveFullscreenController* controller);
-  ~ImmersiveFocusWatcherMus() override;
-
-  // ImmersiveFocusWatcher:
-  void UpdateFocusRevealedLock() override;
-  void ReleaseLock() override;
-
- private:
-  class BubbleObserver;
-
-  views::Widget* GetWidget();
-  aura::Window* GetWidgetWindow();
-
-  // Recreate |bubble_observer_| and start observing any bubbles anchored to a
-  // child of |top_container_|.
-  void RecreateBubbleObserver();
-
-  // views::FocusChangeListener overrides:
-  void OnWillChangeFocus(views::View* focused_before,
-                         views::View* focused_now) override;
-  void OnDidChangeFocus(views::View* focused_before,
-                        views::View* focused_now) override;
-
-  // aura::client::TransientWindowClientObserver overrides:
-  void OnTransientChildWindowAdded(aura::Window* window,
-                                   aura::Window* transient) override;
-  void OnTransientChildWindowRemoved(aura::Window* window,
-                                     aura::Window* transient) override;
-
-  // ::wm::ActivationChangeObserver:
-  void OnWindowActivated(
-      ::wm::ActivationChangeObserver::ActivationReason reason,
-      aura::Window* gaining_active,
-      aura::Window* losing_active) override;
-
-  ash::ImmersiveFullscreenController* immersive_fullscreen_controller_;
-
-  // Lock which keeps the top-of-window views revealed based on the focused view
-  // and the active widget. Acquiring the lock never triggers a reveal because
-  // a view is not focusable till a reveal has made it visible.
-  std::unique_ptr<ash::ImmersiveRevealedLock> lock_;
-
-  // Manages bubbles which are anchored to a child of
-  // |ImmersiveFullscreenController::top_container_|.
-  std::unique_ptr<BubbleObserver> bubble_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImmersiveFocusWatcherMus);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_FOCUS_WATCHER_MUS_H_
diff --git a/chrome/browser/ui/views/frame/immersive_handler_factory_mus.cc b/chrome/browser/ui/views/frame/immersive_handler_factory_mus.cc
index 70def3f2..eabd98f 100644
--- a/chrome/browser/ui/views/frame/immersive_handler_factory_mus.cc
+++ b/chrome/browser/ui/views/frame/immersive_handler_factory_mus.cc
@@ -4,21 +4,13 @@
 
 #include "chrome/browser/ui/views/frame/immersive_handler_factory_mus.h"
 
-#include "ash/public/cpp/immersive/immersive_focus_watcher.h"
 #include "ash/public/cpp/immersive/immersive_gesture_handler.h"
 #include "base/logging.h"
-#include "chrome/browser/ui/views/frame/immersive_focus_watcher_mus.h"
 
 ImmersiveHandlerFactoryMus::ImmersiveHandlerFactoryMus() {}
 
 ImmersiveHandlerFactoryMus::~ImmersiveHandlerFactoryMus() {}
 
-std::unique_ptr<ash::ImmersiveFocusWatcher>
-ImmersiveHandlerFactoryMus::CreateFocusWatcher(
-    ash::ImmersiveFullscreenController* controller) {
-  return std::make_unique<ImmersiveFocusWatcherMus>(controller);
-}
-
 std::unique_ptr<ash::ImmersiveGestureHandler>
 ImmersiveHandlerFactoryMus::CreateGestureHandler(
     ash::ImmersiveFullscreenController* controller) {
diff --git a/chrome/browser/ui/views/frame/immersive_handler_factory_mus.h b/chrome/browser/ui/views/frame/immersive_handler_factory_mus.h
index 2504868..ef55a5c 100644
--- a/chrome/browser/ui/views/frame/immersive_handler_factory_mus.h
+++ b/chrome/browser/ui/views/frame/immersive_handler_factory_mus.h
@@ -14,8 +14,6 @@
   ~ImmersiveHandlerFactoryMus() override;
 
   // ImmersiveHandlerFactory:
-  std::unique_ptr<ash::ImmersiveFocusWatcher> CreateFocusWatcher(
-      ash::ImmersiveFullscreenController* controller) override;
   std::unique_ptr<ash::ImmersiveGestureHandler> CreateGestureHandler(
       ash::ImmersiveFullscreenController* controller) override;
 
diff --git a/chrome/browser/ui/views/frame/test_with_browser_view.cc b/chrome/browser/ui/views/frame/test_with_browser_view.cc
index b930cbb1..45cf73a 100644
--- a/chrome/browser/ui/views/frame/test_with_browser_view.cc
+++ b/chrome/browser/ui/views/frame/test_with_browser_view.cc
@@ -103,11 +103,11 @@
   // TemplateURLService is normally null during testing. Instant extended
   // needs this service so set a custom factory function.
   TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
-      profile, &CreateTemplateURLService);
+      profile, base::BindRepeating(&CreateTemplateURLService));
   // TODO(jamescook): Eliminate this by introducing a mock toolbar or mock
   // location bar.
   AutocompleteClassifierFactory::GetInstance()->SetTestingFactory(
-      profile, &CreateAutocompleteClassifier);
+      profile, base::BindRepeating(&CreateAutocompleteClassifier));
 
   // Configure the GaiaCookieManagerService to return no accounts.
   FakeGaiaCookieManagerService* gcms =
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
index 4b60650f..d84ee939 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
@@ -319,10 +319,6 @@
   return zoom_view->GetTextForTooltipAndAccessibleName();
 }
 
-views::View* ZoomBubbleView::GetInitiallyFocusedView() {
-  return reset_button_;
-}
-
 int ZoomBubbleView::GetDialogButtons() const {
   return ui::DIALOG_BUTTON_NONE;
 }
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.h b/chrome/browser/ui/views/location_bar/zoom_bubble_view.h
index 5fb7f558..45f2fbd3 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.h
@@ -93,7 +93,6 @@
   ~ZoomBubbleView() override;
 
   // LocationBarBubbleDelegateView:
-  View* GetInitiallyFocusedView() override;
   base::string16 GetAccessibleWindowTitle() const override;
   int GetDialogButtons() const override;
   void OnFocus() override;
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
index acaa3ac..214f9d8 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -360,7 +360,7 @@
 
   // Focus is usually gained via a key combination like alt+shift+a. The test
   // simulates this by focusing the bubble and then sending an empty KeyEvent.
-  focus_manager->SetFocusedView(bubble->GetInitiallyFocusedView());
+  focus_manager->SetFocusedView(bubble->reset_button_);
   bubble->OnKeyEvent(nullptr);
   // |auto_close_timer_| should not be running since focus should prevent the
   // bubble from closing.
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index a744523..ef3bbc0 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -650,7 +650,7 @@
     PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_OPEN_USER_MANAGER);
   } else if (sender == go_incognito_button_) {
     DCHECK(ShouldShowGoIncognito());
-    chrome::NewIncognitoWindow(browser_);
+    chrome::NewIncognitoWindow(browser_->profile());
     PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_GO_INCOGNITO);
   } else if (sender == lock_button_) {
     profiles::LockProfile(browser_->profile());
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index 1cd9951..5bfd0b6 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -121,7 +121,7 @@
   }
 }
 
-void SigninViewControllerDelegateViews::HandleKeyboardEvent(
+bool SigninViewControllerDelegateViews::HandleKeyboardEvent(
     content::WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
   // If this is a MODAL_TYPE_CHILD, then GetFocusManager() will return the focus
@@ -129,8 +129,8 @@
   // accelerators will fire. If this is a MODAL_TYPE_WINDOW, then this will have
   // no effect, since no accelerators have been registered for this standalone
   // window.
-  unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
-                                                        GetFocusManager());
+  return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+      event, GetFocusManager());
 }
 
 void SigninViewControllerDelegateViews::DisplayModal() {
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
index 71c00179..9fae456 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
@@ -75,7 +75,7 @@
   void ResizeNativeView(int height) override;
 
   // content::WebContentsDelegate:
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
 
diff --git a/chrome/browser/ui/views/select_file_dialog_extension.cc b/chrome/browser/ui/views/select_file_dialog_extension.cc
index 60efdab..05abd2a 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension.cc
@@ -294,6 +294,11 @@
   return NULL;
 }
 
+bool SelectFileDialogExtension::IsResizeable() const {
+  DCHECK(extension_dialog_.get());
+  return extension_dialog_->CanResize();
+}
+
 void SelectFileDialogExtension::NotifyListener() {
   if (!listener_)
     return;
diff --git a/chrome/browser/ui/views/select_file_dialog_extension.h b/chrome/browser/ui/views/select_file_dialog_extension.h
index 7779bae..7e112bf 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension.h
+++ b/chrome/browser/ui/views/select_file_dialog_extension.h
@@ -97,6 +97,10 @@
   // Returns true if the dialog has multiple file type choices.
   bool HasMultipleFileTypeChoicesImpl() override;
 
+  // Returns true if |extension_dialog_| is resizable; the dialog must be
+  // non-null at the time of this call.
+  bool IsResizeable() const;
+
   bool has_multiple_file_type_choices_;
 
   // Host for the extension that implements this dialog.
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 108c206..f1ce561 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -80,6 +80,7 @@
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/display/manager/display_manager.h"
+#include "ui/events/gesture_detection/gesture_configuration.h"
 #include "ui/events/test/event_generator.h"
 #endif
 
@@ -425,6 +426,10 @@
 #if defined(OS_CHROMEOS)
     root_ = browser()->window()->GetNativeWindow()->GetRootWindow();
     event_generator_ = std::make_unique<ui::test::EventGenerator>(root_);
+    // Disable flings which might otherwise inadvertently be generated from
+    // tests' touch events.
+    ui::GestureConfiguration::GetInstance()->set_min_fling_velocity(
+        std::numeric_limits<float>::max());
 #endif
 #if defined(OS_MACOSX)
     // Currently MacViews' browser windows are shown in the background and could
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
index aa3c784e..ccb4c42 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -88,13 +88,6 @@
   // TODO(pbos): Consolidate with ToolbarButton::OnBoundsChanged.
   SetProperty(views::kHighlightPathKey,
               CreateToolbarHighlightPath(this, gfx::Insets()).release());
-  if (focus_ring()) {
-    // For extensions we can't use a circular focus ring path since it may
-    // obscure the extension icon.
-    gfx::Path path;
-    path.addRect(RectToSkRect(GetLocalBounds()));
-    focus_ring()->SetPath(path);
-  }
 
   MenuButton::OnBoundsChanged(previous_bounds);
 }
@@ -307,9 +300,6 @@
 
   DCHECK(visible());  // We should never show a context menu for a hidden item.
 
-  gfx::Point screen_loc;
-  ConvertPointToScreen(this, &screen_loc);
-
   int run_types =
       views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU;
   if (delegate_->ShownInsideMenu())
@@ -330,7 +320,7 @@
   menu_ = menu_adapter_->CreateMenu();
   menu_runner_.reset(new views::MenuRunner(menu_, run_types));
 
-  menu_runner_->RunMenuAt(parent, this, gfx::Rect(screen_loc, size()),
+  menu_runner_->RunMenuAt(parent, this, GetAnchorBoundsInScreen(),
                           views::MENU_ANCHOR_TOPLEFT, source_type);
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.cc
index bfbed7e9..0647fc5 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.cc
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.cc
@@ -6,7 +6,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/webui/chromeos/user_image_source.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
@@ -14,6 +14,7 @@
 #include "components/consent_auditor/consent_auditor.h"
 #include "components/signin/core/browser/signin_manager_base.h"
 #include "components/user_manager/user_manager.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
 
@@ -208,10 +209,9 @@
 void RecordActivityControlConsent(Profile* profile,
                                   std::string ui_audit_key,
                                   bool opted_in) {
-  SigninManagerBase* signin_manager =
-      SigninManagerFactory::GetForProfile(profile);
-  DCHECK(signin_manager->IsAuthenticated());
-  std::string account_id = signin_manager->GetAuthenticatedAccountId();
+  auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
+  DCHECK(identity_manager->HasPrimaryAccount());
+  const std::string account_id = identity_manager->GetPrimaryAccountId();
 
   UserConsentTypes::AssistantActivityControlConsent consent;
   consent.set_ui_audit_key(ui_audit_key);
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.h b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.h
index 01f918c..f5013d2 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.h
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.h
@@ -20,17 +20,17 @@
 // histogram and should be treated as append-only.
 enum AssistantOptInFlowStatus {
   FLOW_STARTED = 0,
-  ACTIVITY_CONTROL_SHOWN,
-  ACTIVITY_CONTROL_ACCEPTED,
-  ACTIVITY_CONTROL_SKIPPED,
-  THIRD_PARTY_SHOWN,
-  THIRD_PARTY_CONTINUED,
-  GET_MORE_SHOWN,
-  EMAIL_OPTED_IN,
-  EMAIL_OPTED_OUT,
-  GET_MORE_CONTINUED,
-  READY_SCREEN_SHOWN,
-  READY_SCREEN_CONTINUED,
+  ACTIVITY_CONTROL_SHOWN = 1,
+  ACTIVITY_CONTROL_ACCEPTED = 2,
+  ACTIVITY_CONTROL_SKIPPED = 3,
+  THIRD_PARTY_SHOWN = 4,
+  THIRD_PARTY_CONTINUED = 5,
+  GET_MORE_SHOWN = 6,
+  EMAIL_OPTED_IN = 7,
+  EMAIL_OPTED_OUT = 8,
+  GET_MORE_CONTINUED = 9,
+  READY_SCREEN_SHOWN = 10,
+  READY_SCREEN_CONTINUED = 11,
   // Magic constant used by the histogram macros.
   kMaxValue = READY_SCREEN_CONTINUED
 };
diff --git a/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc b/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc
index 5a36e498..8a1efd47 100644
--- a/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc
+++ b/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc
@@ -100,9 +100,10 @@
   return web_contents_;
 }
 
-void ConstrainedWebDialogDelegateBase::HandleKeyboardEvent(
+bool ConstrainedWebDialogDelegateBase::HandleKeyboardEvent(
     content::WebContents* source,
     const NativeWebKeyboardEvent& event) {
+  return false;
 }
 
 gfx::Size ConstrainedWebDialogDelegateBase::GetConstrainedWebDialogMinimumSize()
diff --git a/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h b/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h
index 56111eb2..bea210c 100644
--- a/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h
+++ b/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h
@@ -51,7 +51,7 @@
   void WebContentsDestroyed() override;
 
   // WebDialogWebContentsDelegate interface.
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
 
diff --git a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
index 6b410ca..f1768b4 100644
--- a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
@@ -115,10 +115,13 @@
 //  DICT
 //    "event_listeners": DICT
 //      "count": INT
-//      "events": LIST
+//      "listeners": LIST
 //        DICT
-//          "name": STRING
+//          "event_name": STRING
 //          "filter": DICT
+//          "is_for_service_worker": STRING
+//          "is_lazy": STRING
+//          "url": STRING
 //    "id": STRING
 //    "keepalive": DICT
 //      "activities": LIST
@@ -135,14 +138,18 @@
 
 constexpr base::StringPiece kActivitesKey = "activites";
 constexpr base::StringPiece kCountKey = "count";
-constexpr base::StringPiece kEventsKey = "events";
+constexpr base::StringPiece kEventNameKey = "event_name";
 constexpr base::StringPiece kEventsListenersKey = "event_listeners";
 constexpr base::StringPiece kExtraDataKey = "extra_data";
 constexpr base::StringPiece kFilterKey = "filter";
 constexpr base::StringPiece kInternalsIdKey = "id";
 constexpr base::StringPiece kInternalsNameKey = "name";
 constexpr base::StringPiece kInternalsVersionKey = "version";
+constexpr base::StringPiece kIsForServiceWorkerKey = "is_for_service_worker";
+constexpr base::StringPiece kIsLazyKey = "is_lazy";
+constexpr base::StringPiece kListenersKey = "listeners";
 constexpr base::StringPiece kKeepaliveKey = "keepalive";
+constexpr base::StringPiece kListenerUrlKey = "url";
 constexpr base::StringPiece kLocationKey = "location";
 constexpr base::StringPiece kManifestVersionKey = "manifest_version";
 constexpr base::StringPiece kPathKey = "path";
@@ -172,30 +179,35 @@
 void AddEventListenerData(extensions::EventRouter* event_router,
                           base::Value* data) {
   CHECK(data->is_list());
-  // A map of extension ID to the event data for that extension,
+  // A map of extension ID to the listener data for that extension,
   // which is of type LIST of DICTIONARY.
   std::unordered_map<base::StringPiece, base::Value, base::StringPieceHash>
-      events_map;
+      listeners_map;
 
   // Build the map of extension IDs to the list of events.
   for (const auto& entry : event_router->listeners().listeners()) {
     for (const auto& listener_entry : entry.second) {
-      auto& events_list = events_map[listener_entry->extension_id()];
-      if (events_list.is_none()) {
+      auto& listeners_list = listeners_map[listener_entry->extension_id()];
+      if (listeners_list.is_none()) {
         // Not there, so make it a LIST.
-        events_list = base::Value(base::Value::Type::LIST);
+        listeners_list = base::Value(base::Value::Type::LIST);
       }
-      // The data for each event is a dictionary, with a name and a
-      // filter.
-      base::Value event_data(base::Value::Type::DICTIONARY);
-      event_data.SetKey(kInternalsNameKey,
-                        base::Value(listener_entry->event_name()));
+      // The data for each listener is a dictionary.
+      base::Value listener_data(base::Value::Type::DICTIONARY);
+      listener_data.SetKey(kEventNameKey,
+                           base::Value(listener_entry->event_name()));
+      listener_data.SetKey(
+          kIsForServiceWorkerKey,
+          base::Value(listener_entry->is_for_service_worker()));
+      listener_data.SetKey(kIsLazyKey, base::Value(listener_entry->IsLazy()));
+      listener_data.SetKey(kListenerUrlKey,
+                           base::Value(listener_entry->listener_url().spec()));
       // Add the filter if one exists.
       base::Value* const filter = listener_entry->filter();
       if (filter != nullptr) {
-        event_data.SetKey(kFilterKey, filter->Clone());
+        listener_data.SetKey(kFilterKey, filter->Clone());
       }
-      events_list.GetList().push_back(std::move(event_data));
+      listeners_list.GetList().push_back(std::move(listener_data));
     }
   }
 
@@ -203,20 +215,21 @@
   for (auto& output_entry : data->GetList()) {
     const base::Value* const value = output_entry.FindKey(kInternalsIdKey);
     CHECK(value && value->is_string());
-    const auto it = events_map.find(value->GetString());
-    base::Value listeners(base::Value::Type::DICTIONARY);
-    if (it == events_map.end()) {
+    const auto it = listeners_map.find(value->GetString());
+    base::Value event_listeners(base::Value::Type::DICTIONARY);
+    if (it == listeners_map.end()) {
       // We didn't find any events, so initialize an empty dictionary.
-      listeners.SetKey(kCountKey, base::Value(0));
-      listeners.SetKey(kEventsKey, base::Value(base::Value::Type::LIST));
+      event_listeners.SetKey(kCountKey, base::Value(0));
+      event_listeners.SetKey(kListenersKey,
+                             base::Value(base::Value::Type::LIST));
     } else {
       // Set the count and the events values.
-      listeners.SetKey(
+      event_listeners.SetKey(
           kCountKey,
           base::Value(base::checked_cast<int>(it->second.GetList().size())));
-      listeners.SetKey(kEventsKey, std::move(it->second));
+      event_listeners.SetKey(kListenersKey, std::move(it->second));
     }
-    output_entry.SetKey(kEventsListenersKey, std::move(listeners));
+    output_entry.SetKey(kEventsListenersKey, std::move(event_listeners));
   }
 }
 
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.cc b/chrome/browser/ui/webui/welcome/welcome_ui.cc
index 45010722..baf359e 100644
--- a/chrome/browser/ui/webui/welcome/welcome_ui.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_ui.cc
@@ -81,6 +81,10 @@
     html_source->AddResourcePath(
         "navigation_behavior.js",
         IDR_WELCOME_ONBOARDING_WELCOME_NAVIGATION_BEHAVIOR_JS);
+    html_source->AddResourcePath(
+        "signin_view.html", IDR_WELCOME_ONBOARDING_WELCOME_SIGNIN_VIEW_HTML);
+    html_source->AddResourcePath("signin_view.js",
+                                 IDR_WELCOME_ONBOARDING_WELCOME_SIGNIN_VIEW_JS);
     html_source->AddResourcePath("welcome.css",
                                  IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_CSS);
     html_source->AddResourcePath(
@@ -95,11 +99,45 @@
         IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_BROWSER_PROXY_JS);
 
     // Add resources shared by the NUX modules.
-    html_source->AddResourcePath("shared/chooser_shared_css.html",
-                                 IDR_NUX_CHOOSER_SHARED_CSS);
+    html_source->AddResourcePath(
+        "shared/action_link_style_css.html",
+        IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_CSS_HTML);
+    html_source->AddResourcePath(
+        "shared/action_link_style.js",
+        IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_JS);
+    html_source->AddResourcePath(
+        "shared/chooser_shared_css.html",
+        IDR_WELCOME_ONBOARDING_WELCOME_SHARED_CHOOSER_SHARED_CSS);
+    html_source->AddResourcePath(
+        "shared/onboarding_background.html",
+        IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ONBOARDING_BACKGROUND_HTML);
+    html_source->AddResourcePath(
+        "shared/onboarding_background.js",
+        IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ONBOARDING_BACKGROUND_JS);
     html_source->AddResourcePath(
         "shared/i18n_setup.html",
         IDR_WELCOME_ONBOARDING_WELCOME_SHARED_I18N_SETUP_HTML);
+    html_source->AddResourcePath(
+        "images/background_svgs/blue_circle.svg",
+        IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_BLUE_CIRCLE_SVG);
+    html_source->AddResourcePath(
+        "images/background_svgs/green_rectangle.svg",
+        IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_GREEN_RECTANGLE_SVG);
+    html_source->AddResourcePath(
+        "images/background_svgs/grey_oval.svg",
+        IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_GREY_OVAL_SVG);
+    html_source->AddResourcePath(
+        "images/background_svgs/grey_rounded_rectangle.svg",
+        IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_GREY_ROUNDED_RECTANGLE_SVG);
+    html_source->AddResourcePath(
+        "images/background_svgs/red_triangle.svg",
+        IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_RED_TRIANGLE_SVG);
+    html_source->AddResourcePath(
+        "images/background_svgs/yellow_dots.svg",
+        IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_YELLOW_DOTS_SVG);
+    html_source->AddResourcePath(
+        "images/background_svgs/yellow_semicircle.svg",
+        IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_YELLOW_SEMICIRCLE_SVG);
 
     // Add email provider bookmarking onboarding module.
     web_ui->AddMessageHandler(std::make_unique<nux::EmailHandler>(
diff --git a/chrome/credential_provider/gaiacp/BUILD.gn b/chrome/credential_provider/gaiacp/BUILD.gn
index 4d8d07f..771abce 100644
--- a/chrome/credential_provider/gaiacp/BUILD.gn
+++ b/chrome/credential_provider/gaiacp/BUILD.gn
@@ -115,11 +115,37 @@
   output = "$root_out_dir/gaia_credential_provider.rc"
 }
 
+if (is_chrome_branded) {
+  gaia_credential_provider_clsid = "0b5bfdf0-4594-47ac-940a-cfc69abc561c"
+} else {
+  gaia_credential_provider_clsid = "89adae71-aee5-4ee2-bffb-e8424e06f519"
+}
+
+action("generate_credential_provider_idl_file") {
+  script = "//build/util/version.py"
+
+  inputs = [
+    "gaia_credential_provider_idl.templ",
+  ]
+  outputs = [
+    "$target_gen_dir/gaia_credential_provider.idl",
+  ]
+
+  args = [
+    "-e",
+    "GAIA_CREDENTIAL_PROVIDER_CLSID='$gaia_credential_provider_clsid'",
+    rebase_path(inputs[0], root_build_dir),
+    rebase_path(outputs[0], root_build_dir),
+  ]
+}
+
 midl("gaia_credential_provider_idl") {
-  sources = [
-    "gaia_credential_provider.idl",
+  dynamic_guid = gaia_credential_provider_clsid
+  deps = [
+    ":generate_credential_provider_idl_file",
   ]
   header_file = "gaia_credential_provider_i.h"
+  sources = get_target_outputs(":generate_credential_provider_idl_file")
 }
 
 grit("resources") {
diff --git a/chrome/credential_provider/gaiacp/dllmain.cc b/chrome/credential_provider/gaiacp/dllmain.cc
index a7f4e8e..68f37b0 100644
--- a/chrome/credential_provider/gaiacp/dllmain.cc
+++ b/chrome/credential_provider/gaiacp/dllmain.cc
@@ -74,6 +74,7 @@
     LOGFN(INFO) << "_AtlModule.DllRegisterServer hr=" << putHR(hr);
   }
 
+#if defined(GOOGLE_CHROME_BUILD)
   // Register with Google Update.
   if (SUCCEEDED(hr)) {
     base::win::RegKey key(HKEY_LOCAL_MACHINE,
@@ -92,17 +93,22 @@
       }
     }
   }
+#endif  // defined(GOOGLE_CHROME_BUILD)
 
   return hr;
 }
 
 // DllUnregisterServer - Removes entries from the system registry.
 STDAPI DllUnregisterServer(void) {
-  // Unegister with Google Update.
+#if defined(GOOGLE_CHROME_BUILD)
+  // Unregister with Google Update.
   base::win::RegKey key(HKEY_LOCAL_MACHINE, L"", DELETE | KEY_WOW64_32KEY);
   LONG sts = key.DeleteKey(credential_provider::kRegUpdaterClientsAppPath);
 
   bool all_succeeded = sts == ERROR_SUCCESS;
+#else
+  bool all_succeeded = true;
+#endif
 
   HRESULT hr =
       credential_provider::CGaiaCredentialBase::OnDllUnregisterServer();
diff --git a/chrome/credential_provider/gaiacp/gaia_credential.h b/chrome/credential_provider/gaiacp/gaia_credential.h
index c26e469..b29f828 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential.h
+++ b/chrome/credential_provider/gaiacp/gaia_credential.h
@@ -14,7 +14,6 @@
 // Implementation of an ICredentialProviderCredential backed by a Gaia account.
 class ATL_NO_VTABLE CGaiaCredential
     : public CComObjectRootEx<CComMultiThreadModel>,
-      public CComCoClass<CGaiaCredential, &CLSID_GaiaCredential>,
       public CGaiaCredentialBase {
  public:
   DECLARE_NO_REGISTRY()
@@ -45,8 +44,6 @@
                                       BSTR* error_text) override;
 };
 
-OBJECT_ENTRY_AUTO(__uuidof(GaiaCredential), CGaiaCredential)
-
 }  // namespace credential_provider
 
 #endif  // CHROME_CREDENTIAL_PROVIDER_GAIACP_GAIA_CREDENTIAL_H_
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
index f2e7ab0..49c1e8b8 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -512,6 +512,7 @@
 
 // static
 void CGaiaCredentialBase::TellOmahaDidRun() {
+#if defined(GOOGLE_CHROME_BUILD)
   // Tell omaha that product was used.  Best effort only.
   //
   // This code always runs as LocalSystem, which means that HKCU maps to
@@ -527,6 +528,7 @@
     if (sts != ERROR_SUCCESS)
       LOGFN(INFO) << "Unable to write omaha dr value sts=" << sts;
   }
+#endif  // defined(GOOGLE_CHROME_BUILD)
 }
 
 // static
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.rgs b/chrome/credential_provider/gaiacp/gaia_credential_provider.rgs
index 38330ed..fdaa1d3 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider.rgs
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.rgs
@@ -2,13 +2,12 @@
 {

 	NoRemove CLSID

 	{

-		ForceRemove {0B5BFDF0-4594-47AC-940A-CFC69ABC561C} = s 'GaiaCredentialProvider Class'

+		ForceRemove %CREDENTIAL_PROVIDER_CLASS_GUID% = s 'Google Credential Provider Class'

 		{

 			InprocServer32 = s '%MODULE%'

 			{

 				val ThreadingModel = s 'Apartment'

 			}

-			TypeLib = s '{4ADC3A52-8673-4CE3-81F6-833D18BEEBA2}'

 			Version = s '%VERSION%'

 		}

 	}

@@ -27,7 +26,7 @@
 					{

 						NoRemove 'Credential Providers'

 						{

-							ForceRemove {0B5BFDF0-4594-47AC-940A-CFC69ABC561C} = s 'Google Credential Provider'

+							ForceRemove %CREDENTIAL_PROVIDER_CLASS_GUID% = s 'Google Credential Provider'

 							{

 							}

 						}

diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.idl b/chrome/credential_provider/gaiacp/gaia_credential_provider_idl.templ
similarity index 65%
rename from chrome/credential_provider/gaiacp/gaia_credential_provider.idl
rename to chrome/credential_provider/gaiacp/gaia_credential_provider_idl.templ
index c37971c..c49c134 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider.idl
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_idl.templ
@@ -1,90 +1,76 @@
 // Copyright 2018 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.
-

-// This file will be processed by the MIDL tool to

-// produce the type library (GaiaCredentialProvider.tlb) and marshalling code.

-

-import "oaidl.idl";

-import "ocidl.idl";

-

-[

-	object,

-	uuid(CEC9EF6C-B2E6-4BB6-8F1E-1747BA4F7138),

-	pointer_default(unique)

-]

-interface IGaiaCredentialProvider : IUnknown {

-  HRESULT OnUserAuthenticated([in] IUnknown* credential,

-                              [in] BSTR username,

-                              [in] BSTR password,

-                              [in] BSTR sid);

-};

-

-[

-	object,

-	uuid(224CE2FB-2977-4585-BD46-1BAE8D7964DE),

-	pointer_default(unique)

-]

-interface IGaiaCredentialProviderForTesting : IUnknown {

-  HRESULT SetReauthCheckDoneEvent([in] INT_PTR event);

-};

-

-[

-	object,

-	uuid(E5BF88DF-9966-465B-B233-C1CAC7510A59),

-	pointer_default(unique)

-]

-interface IGaiaCredential : IUnknown {

-  HRESULT Initialize([in] IGaiaCredentialProvider* provider);

-  HRESULT Terminate();

-  HRESULT FinishAuthentication([in] BSTR username,

-                               [in] BSTR password,

-                               [in] BSTR fullname,

-                               [out] BSTR* sid,

-                               [out] BSTR* error_text);

-  HRESULT OnUserAuthenticated([in] BSTR username,

-                              [in] BSTR password,

-                              [in] BSTR sid);

-  HRESULT ReportError([in] LONG status,

-                      [in] LONG substatus,

-                      [in] BSTR status_text);

-};

-

-[

-	object,

-	uuid(CC75BCEA-A636-4798-BF8E-0FF64D743451),

-	pointer_default(unique)

-]

-interface IReauthCredential : IUnknown {

-  HRESULT SetUserInfo([in] BSTR sid, [in] BSTR email);

-};

-

-[

-	uuid(4ADC3A52-8673-4CE3-81F6-833D18BEEBA2),

-	version(1.0),

-]

-library GaiaCredentialProviderLib

-{

-	importlib("stdole2.tlb");

-	[

-		uuid(0B5BFDF0-4594-47AC-940A-CFC69ABC561C)

-	]

-	coclass GaiaCredentialProvider

-	{

-		[default] interface IGaiaCredentialProvider;

-	};

-	[

-		uuid(44AF95AC-6B23-4C54-94BE-EDB1CB52DAFD)

-	]

-	coclass GaiaCredential

-	{

-		[default] interface IGaiaCredential;

-	};

-	[

-		uuid(E6CC5D8B-54C2-4586-ADC3-748ED16284B7)

-	]

-	coclass ReauthCredential

-	{

-		[default] interface IGaiaCredential;

-	};

-};

+
+// This file will be processed by the MIDL tool to
+// produce the type library (GaiaCredentialProvider.tlb) and marshalling code.
+
+import "oaidl.idl";
+import "ocidl.idl";
+
+[
+  object,
+  uuid(CEC9EF6C-B2E6-4BB6-8F1E-1747BA4F7138),
+  pointer_default(unique)
+]
+interface IGaiaCredentialProvider : IUnknown {
+  HRESULT OnUserAuthenticated([in] IUnknown* credential,
+                              [in] BSTR username,
+                              [in] BSTR password,
+                              [in] BSTR sid);
+};
+
+[
+  object,
+  uuid(224CE2FB-2977-4585-BD46-1BAE8D7964DE),
+  pointer_default(unique)
+]
+interface IGaiaCredentialProviderForTesting : IUnknown {
+  HRESULT SetReauthCheckDoneEvent([in] INT_PTR event);
+};
+
+[
+  object,
+  uuid(E5BF88DF-9966-465B-B233-C1CAC7510A59),
+  pointer_default(unique)
+]
+interface IGaiaCredential : IUnknown {
+  HRESULT Initialize([in] IGaiaCredentialProvider* provider);
+  HRESULT Terminate();
+  HRESULT FinishAuthentication([in] BSTR username,
+                               [in] BSTR password,
+                               [in] BSTR fullname,
+                               [out] BSTR* sid,
+                               [out] BSTR* error_text);
+  HRESULT OnUserAuthenticated([in] BSTR username,
+                              [in] BSTR password,
+                              [in] BSTR sid);
+  HRESULT ReportError([in] LONG status,
+                      [in] LONG substatus,
+                      [in] BSTR status_text);
+};
+
+[
+  object,
+  uuid(CC75BCEA-A636-4798-BF8E-0FF64D743451),
+  pointer_default(unique)
+]
+interface IReauthCredential : IUnknown {
+  HRESULT SetUserInfo([in] BSTR sid, [in] BSTR email);
+};
+
+[
+  uuid(4ADC3A52-8673-4CE3-81F6-833D18BEEBA2),
+  version(1.0),
+]
+library GaiaCredentialProviderLib
+{
+  importlib("stdole2.tlb");
+  [
+    uuid(@GAIA_CREDENTIAL_PROVIDER_CLSID@),
+  ]
+  coclass GaiaCredentialProvider
+  {
+    [default] interface IGaiaCredentialProvider;
+  };
+};
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
index 2a694ed..e8720c1 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_module.cc
@@ -48,8 +48,11 @@
   eventlog_path =
       eventlog_path.Append(FILE_PATH_LITERAL("gcp_eventlog_provider.dll"));
 
+  wchar_t guid_in_wchar[64];
+  StringFromGUID2(CLSID_GaiaCredentialProvider, guid_in_wchar, base::size(guid_in_wchar));
+
   ATL::_ATL_REGMAP_ENTRY regmap[] = {
-      {L"APPID", L"{C2DDF2F2-F760-4B27-92F4-3461EE8A7A0B}"},
+      {L"CREDENTIAL_PROVIDER_CLASS_GUID", guid_in_wchar},
       {L"VERSION", TEXT(CHROME_VERSION_STRING)},
       {L"EVENTLOG_PATH", eventlog_path.value().c_str()},
       {nullptr, nullptr},
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
index 5decba9..2924512 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
@@ -87,14 +87,12 @@
 
   // Expect "good" sid to still in the registry, "bad" one to be cleaned up.
   base::win::RegKey key;
-  ASSERT_EQ(ERROR_SUCCESS,
-            key.Open(HKEY_LOCAL_MACHINE, L"Software\\Google\\GCP\\Users\\",
-                     KEY_READ));
+  ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE,
+                                    GetUsersRootKeyForTesting(), KEY_READ));
   EXPECT_EQ(ERROR_SUCCESS, key.OpenKey(OLE2CW(sid_good), KEY_READ));
 
-  ASSERT_EQ(ERROR_SUCCESS,
-            key.Open(HKEY_LOCAL_MACHINE, L"Software\\Google\\GCP\\Users\\",
-                     KEY_READ));
+  ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE,
+                                    GetUsersRootKeyForTesting(), KEY_READ));
   EXPECT_NE(ERROR_SUCCESS, key.OpenKey(OLE2CW(sid_bad), KEY_READ));
 }
 
diff --git a/chrome/credential_provider/gaiacp/reauth_credential.h b/chrome/credential_provider/gaiacp/reauth_credential.h
index 443d6b97..00358df 100644
--- a/chrome/credential_provider/gaiacp/reauth_credential.h
+++ b/chrome/credential_provider/gaiacp/reauth_credential.h
@@ -13,7 +13,6 @@
 // Implementation of a ICredentialProviderCredential backed by a Gaia account.
 class ATL_NO_VTABLE CReauthCredential
     : public CComObjectRootEx<CComMultiThreadModel>,
-      public CComCoClass<CReauthCredential, &CLSID_ReauthCredential>,
       public CGaiaCredentialBase,
       public IReauthCredential {
  public:
@@ -62,8 +61,6 @@
   CComBSTR user_email_;
 };
 
-OBJECT_ENTRY_AUTO(__uuidof(ReauthCredential), CReauthCredential)
-
 }  // namespace credential_provider
 
 #endif  // CHROME_CREDENTIAL_PROVIDER_GAIACP_REAUTH_CREDENTIAL_H_
diff --git a/chrome/credential_provider/gaiacp/reg_utils.cc b/chrome/credential_provider/gaiacp/reg_utils.cc
index 471209b..446dc85 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.cc
+++ b/chrome/credential_provider/gaiacp/reg_utils.cc
@@ -15,9 +15,13 @@
 namespace {
 
 // Root registry key for GCP configuration and state.
-// TODO(crbug.com/883943): This should be different between Chromium and
-// Google Chrome builds.
-const wchar_t kGcpRootKeyName[] = L"Software\\Google\\GCP";
+#if defined(GOOGLE_CHROME_BUILD)
+#define CREDENTIAL_PROVIDER_REGISTRY_KEY L"Software\\Google\\GCP"
+#else
+#define CREDENTIAL_PROVIDER_REGISTRY_KEY L"Software\\Chromium\\GCP"
+#endif  // defined(GOOGLE_CHROME_BUILD)
+
+const wchar_t kGcpRootKeyName[] = CREDENTIAL_PROVIDER_REGISTRY_KEY;
 
 HRESULT GetRegDWORD(const base::string16& key_name,
                     const base::string16& name,
@@ -177,4 +181,8 @@
   return S_OK;
 }
 
+const wchar_t* GetUsersRootKeyForTesting() {
+  return CREDENTIAL_PROVIDER_REGISTRY_KEY L"\\Users";
+}
+
 }  // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/reg_utils.h b/chrome/credential_provider/gaiacp/reg_utils.h
index 5a066b9d..9dd6492 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.h
+++ b/chrome/credential_provider/gaiacp/reg_utils.h
@@ -53,6 +53,9 @@
 // Gets token handles for all users created by this credential provider.
 HRESULT GetUserTokenHandles(std::map<base::string16, base::string16>* handles);
 
+// Returns the root registry key that needs to be verified in unit tests.
+const wchar_t* GetUsersRootKeyForTesting();
+
 }  // namespace credential_provider
 
 #endif  // CHROME_CREDENTIAL_PROVIDER_GAIACP_REG_UTILS_H_
diff --git a/chrome/credential_provider/gaiacp/scoped_user_profile.cc b/chrome/credential_provider/gaiacp/scoped_user_profile.cc
index 29b09e3..ceed754 100644
--- a/chrome/credential_provider/gaiacp/scoped_user_profile.cc
+++ b/chrome/credential_provider/gaiacp/scoped_user_profile.cc
@@ -23,7 +23,11 @@
 namespace {
 
 // Registry key under HKCU to write account info into.
+#if defined(GOOGLE_CHROME_BUILD)
 const wchar_t kRegAccountsPath[] = L"Software\\Google\\Accounts";
+#else
+const wchar_t kRegAccountsPath[] = L"Software\\Chromium\\Accounts";
+#endif  // defined(GOOGLE_CHROME_BUILD)
 
 std::string GetEncryptedRefreshToken(
     base::win::ScopedHandle::Handle logon_handle,
diff --git a/chrome/credential_provider/setup/setup_lib.cc b/chrome/credential_provider/setup/setup_lib.cc
index a0e753e..af7abf3 100644
--- a/chrome/credential_provider/setup/setup_lib.cc
+++ b/chrome/credential_provider/setup/setup_lib.cc
@@ -57,7 +57,7 @@
     return base::FilePath();
   }
 
-  dest_path = dest_path.Append(FILE_PATH_LITERAL("Google"))
+  dest_path = dest_path.Append(GetInstallParentDirectoryName())
                   .Append(FILE_PATH_LITERAL("Credential Provider"));
 
   if (!base::CreateDirectory(dest_path)) {
@@ -379,4 +379,12 @@
   *count = base::size(kFilenames);
 }
 
+base::FilePath::StringType GetInstallParentDirectoryName() {
+#if defined(GOOGLE_CHROME_BUILD)
+  return FILE_PATH_LITERAL("Google");
+#else
+  return FILE_PATH_LITERAL("Chromium");
+#endif
+}
+
 }  // namespace credential_provider
diff --git a/chrome/credential_provider/setup/setup_lib.h b/chrome/credential_provider/setup/setup_lib.h
index b578537..a202050c 100644
--- a/chrome/credential_provider/setup/setup_lib.h
+++ b/chrome/credential_provider/setup/setup_lib.h
@@ -51,6 +51,9 @@
 void GetInstalledFileBasenames(const base::FilePath::CharType* const** names,
                                size_t* count);
 
+// Gets the brand specific path in which to install GCPW.
+base::FilePath::StringType GetInstallParentDirectoryName();
+
 }  // namespace credential_provider
 
 #endif  // CHROME_CREDENTIAL_PROVIDER_SETUP_SETUP_LIB_H_
diff --git a/chrome/credential_provider/test/gcp_setup_unittests.cc b/chrome/credential_provider/test/gcp_setup_unittests.cc
index 64de979..77db864 100644
--- a/chrome/credential_provider/test/gcp_setup_unittests.cc
+++ b/chrome/credential_provider/test/gcp_setup_unittests.cc
@@ -17,11 +17,13 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/process/launch.h"
 #include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
 #include "base/syslog_logging.h"
 #include "base/test/scoped_path_override.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/win/registry.h"
 #include "build/build_config.h"
+#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
 #include "chrome/credential_provider/gaiacp/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/gcp_utils.h"
 #include "chrome/credential_provider/setup/setup_lib.h"
@@ -39,13 +41,14 @@
 
   void ExpectAllFilesToExist(bool exist, const base::string16& product_version);
   void ExpectCredentialProviderToBeRegistered(
-    bool registered,
-    const base::string16& product_version);
+      bool registered,
+      const base::string16& product_version);
 
   base::FilePath installed_path_for_version(
       const base::string16& product_version) {
     return scoped_temp_prog_dir_.GetPath()
-        .Append(FILE_PATH_LITERAL("Google\\Credential Provider"))
+        .Append(GetInstallParentDirectoryName())
+        .Append(FILE_PATH_LITERAL("Credential Provider"))
         .Append(product_version);
   }
 
@@ -118,10 +121,15 @@
 void GcpSetupTest::ExpectCredentialProviderToBeRegistered(
     bool registered,
     const base::string16& product_version) {
+  wchar_t guid_in_wchar[64];
+  StringFromGUID2(CLSID_GaiaCredentialProvider, guid_in_wchar,
+                  base::size(guid_in_wchar));
+
   // Make sure COM object is registered.
-  base::string16 subkey(
-      L"CLSID\\{0B5BFDF0-4594-47AC-940A-CFC69ABC561C}\\InprocServer32");
-  base::win::RegKey clsid_key(HKEY_CLASSES_ROOT, subkey.c_str(), KEY_READ);
+  base::string16 register_key_path =
+      base::StringPrintf(L"CLSID\\%ls\\InprocServer32", guid_in_wchar);
+  base::win::RegKey clsid_key(HKEY_CLASSES_ROOT, register_key_path.c_str(),
+                              KEY_READ);
   EXPECT_EQ(registered, clsid_key.Valid());
 
   if (registered) {
@@ -132,12 +140,12 @@
     EXPECT_EQ(path.value(), value);
   }
 
+  base::string16 cp_key_path = base::StringPrintf(
+      L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
+      L"Authentication\\Credential Providers\\%ls", guid_in_wchar);
+
   // Make sure credential provider is registered.
-  base::win::RegKey cp_key(HKEY_LOCAL_MACHINE,
-                           L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
-                           L"Authentication\\Credential Providers\\"
-                           L"{0B5BFDF0-4594-47AC-940A-CFC69ABC561C}",
-                           KEY_READ);
+  base::win::RegKey cp_key(HKEY_LOCAL_MACHINE, cp_key_path.c_str(), KEY_READ);
   EXPECT_EQ(registered, cp_key.Valid());
 
   // Make sure eventlog source is registered.
@@ -228,8 +236,8 @@
 
   FakeOSUserManager::UserInfo old_user_info =
       fake_os_user_manager()->GetUserInfo(kGaiaAccountName);
-  base::string16 old_password = fake_scoped_lsa_policy_factory()
-      ->private_data()[kLsaKeyGaiaPassword];
+  base::string16 old_password =
+      fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaPassword];
   EXPECT_FALSE(old_password.empty());
 
   logging::ResetEventSourceForTesting();
@@ -245,9 +253,9 @@
   // Make sure kGaiaAccountName info and private data are unchanged.
   EXPECT_EQ(old_user_info,
             fake_os_user_manager()->GetUserInfo(kGaiaAccountName));
-  EXPECT_EQ(old_password,
-            fake_scoped_lsa_policy_factory()
-                ->private_data()[kLsaKeyGaiaPassword]);
+  EXPECT_EQ(
+      old_password,
+      fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaPassword]);
 }
 
 TEST_F(GcpSetupTest, DoInstallOverOldLockedInstall) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7acb691..0e870a2c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2500,7 +2500,6 @@
     "../browser/metrics/thread_watcher_android_unittest.cc",
     "../browser/metrics/thread_watcher_unittest.cc",
     "../browser/navigation_predictor/navigation_predictor_unittest.cc",
-    "../browser/net/chrome_accept_language_settings_unittest.cc",
     "../browser/net/chrome_network_delegate_unittest.cc",
     "../browser/net/dns_probe_runner_unittest.cc",
     "../browser/net/dns_probe_service_unittest.cc",
@@ -3381,7 +3380,7 @@
       "../browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc",
       "../browser/feature_engagement/session_duration_updater_unittest.cc",
       "../browser/ui/in_product_help/active_tab_tracker_unittest.cc",
-      "../browser/ui/in_product_help/reopen_tab_iph_trigger_unittest.cc",
+      "../browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc",
     ]
     deps += [ "//components/feature_engagement/test:test_support" ]
   }
diff --git a/chrome/test/base/interactive_test_utils.h b/chrome/test/base/interactive_test_utils.h
index eb0fdfd..96de5b8f 100644
--- a/chrome/test/base/interactive_test_utils.h
+++ b/chrome/test/base/interactive_test_utils.h
@@ -92,6 +92,9 @@
 // Makes focus shift to the given View without clicking it.
 void FocusView(const Browser* browser, ViewID vid);
 
+// Wait until the view identified by |view_id| in |browser| gets focused.
+void WaitUntilViewFocused(const Browser* browser, ViewID view_id);
+
 // A collection of utilities that are used from interactive_ui_tests. These are
 // separated from ui_test_utils.h to ensure that browser_tests don't use them,
 // since they depend on focus which isn't possible for sharded test.
diff --git a/chrome/test/base/interactive_test_utils_aura.cc b/chrome/test/base/interactive_test_utils_aura.cc
index 61f0d3ef6..50fec613 100644
--- a/chrome/test/base/interactive_test_utils_aura.cc
+++ b/chrome/test/base/interactive_test_utils_aura.cc
@@ -4,10 +4,47 @@
 
 #include "chrome/test/base/interactive_test_utils_aura.h"
 
+#include "base/run_loop.h"
 #include "build/build_config.h"
 #include "chrome/test/base/interactive_test_utils.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/client/focus_client.h"
 #include "ui/aura/window.h"
 
+namespace {
+
+class FocusWaiter : public aura::client::FocusChangeObserver {
+ public:
+  explicit FocusWaiter(aura::Window* window) : window_(window) {
+    aura::client::GetFocusClient(window_)->AddObserver(this);
+  }
+
+  ~FocusWaiter() override {
+    aura::client::GetFocusClient(window_)->RemoveObserver(this);
+  }
+
+  void WaitUntilFocus() {
+    if (window_->HasFocus())
+      return;
+    run_loop_.Run();
+  }
+
+ private:
+  // aura::client::FocusChangeObserver:
+  void OnWindowFocused(aura::Window* gained_focus,
+                       aura::Window* lost_focus) override {
+    if (gained_focus == window_)
+      run_loop_.QuitWhenIdle();
+  }
+
+  aura::Window* window_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusWaiter);
+};
+
+}  // namespace
+
 namespace ui_test_utils {
 
 #if !defined(OS_WIN)
@@ -30,4 +67,9 @@
   return true;
 }
 
+void WaitUntilWindowFocused(aura::Window* window) {
+  FocusWaiter waiter(window);
+  waiter.WaitUntilFocus();
+}
+
 }  // namespace ui_test_utils
diff --git a/chrome/test/base/interactive_test_utils_aura.h b/chrome/test/base/interactive_test_utils_aura.h
index e157a56..75eae2f 100644
--- a/chrome/test/base/interactive_test_utils_aura.h
+++ b/chrome/test/base/interactive_test_utils_aura.h
@@ -16,6 +16,9 @@
 void HideNativeWindowAura(gfx::NativeWindow window);
 bool ShowAndFocusNativeWindowAura(gfx::NativeWindow window);
 
+// Wait until |window| gets focused.
+void WaitUntilWindowFocused(aura::Window* window);
+
 }  // namespace ui_test_utils
 
 #endif  // CHROME_TEST_BASE_INTERACTIVE_TEST_UTILS_AURA_H_
diff --git a/chrome/test/base/interactive_test_utils_views.cc b/chrome/test/base/interactive_test_utils_views.cc
index 46cbac5..19f773f3 100644
--- a/chrome/test/base/interactive_test_utils_views.cc
+++ b/chrome/test/base/interactive_test_utils_views.cc
@@ -12,6 +12,45 @@
 #include "ui/base/ui_features.h"
 #include "ui/views/focus/focus_manager.h"
 
+#if defined(USE_AURA)
+#include "chrome/test/base/interactive_test_utils_aura.h"
+#endif
+
+namespace {
+
+class FocusWaiter : public views::FocusChangeListener {
+ public:
+  explicit FocusWaiter(views::View* view) : view_(view) {
+    view_->GetFocusManager()->AddFocusChangeListener(this);
+  }
+  ~FocusWaiter() override {
+    view_->GetFocusManager()->RemoveFocusChangeListener(this);
+  }
+
+  void WaitUntilFocused() {
+    if (view_->HasFocus())
+      return;
+    run_loop_.Run();
+  }
+
+ private:
+  // views::FocusChangeListener:
+  void OnWillChangeFocus(views::View* focused_before,
+                         views::View* focused_now) override {}
+  void OnDidChangeFocus(views::View* focused_before,
+                        views::View* focused_now) override {
+    if (focused_now == view_)
+      run_loop_.QuitWhenIdle();
+  }
+
+  views::View* view_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(FocusWaiter);
+};
+
+}  // namespace
+
 namespace ui_test_utils {
 
 bool IsViewFocused(const Browser* browser, ViewID vid) {
@@ -50,4 +89,18 @@
   return center;
 }
 
+void WaitUntilViewFocused(const Browser* browser, ViewID vid) {
+#if defined(USE_AURA)
+  BrowserWindow* browser_window = browser->window();
+  DCHECK(browser_window);
+  gfx::NativeWindow window = browser_window->GetNativeWindow();
+  DCHECK(window);
+  WaitUntilWindowFocused(window);
+#endif
+
+  FocusWaiter waiter(
+      BrowserView::GetBrowserViewForBrowser(browser)->GetViewByID(vid));
+  waiter.WaitUntilFocused();
+}
+
 }  // namespace ui_test_utils
diff --git a/chrome/test/base/test_browser_window.cc b/chrome/test/base/test_browser_window.cc
index 4af45fe..7b9116b 100644
--- a/chrome/test/base/test_browser_window.cc
+++ b/chrome/test/base/test_browser_window.cc
@@ -150,6 +150,11 @@
   return content::KeyboardEventProcessingResult::NOT_HANDLED;
 }
 
+bool TestBrowserWindow::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  return false;
+}
+
 bool TestBrowserWindow::IsBookmarkBarVisible() const {
   return false;
 }
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 4d5e2b67..1584a2b 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -99,8 +99,8 @@
   void ShowAppMenu() override {}
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) override;
-  void HandleKeyboardEvent(
-      const content::NativeWebKeyboardEvent& event) override {}
+  bool HandleKeyboardEvent(
+      const content::NativeWebKeyboardEvent& event) override;
   bool IsBookmarkBarVisible() const override;
   bool IsBookmarkBarAnimating() const override;
   bool IsTabStripEditable() const override;
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index a84935c..cbf3749 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -237,6 +237,7 @@
         'LaunchDesktopTest.*',
         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2579
         'ChromeDriverTest.testTakeElementScreenshot',
+        'ChromeDriverTest.testTakeElementScreenshotInIframe',
     ]
 )
 _ANDROID_NEGATIVE_FILTER['chrome_stable'] = (
@@ -248,6 +249,8 @@
         'ChromeDriverTest.testGetWindowHandles',
         'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
         'ChromeDriverTest.testSwitchToWindow',
+        # Feature not yet supported in this version
+        'ChromeDriverTest.testGenerateTestReport',
     ]
 )
 _ANDROID_NEGATIVE_FILTER['chrome_beta'] = (
@@ -257,6 +260,8 @@
         'ChromeDriverTest.testGetWindowHandles',
         'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
         'ChromeDriverTest.testSwitchToWindow',
+        # Feature not yet supported in this version
+        'ChromeDriverTest.testGenerateTestReport',
     ]
 )
 _ANDROID_NEGATIVE_FILTER['chromium'] = (
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index a8382d28..fe8be1a9 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -176,6 +176,9 @@
 
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2601
     'BasicKeyboardInterfaceTest.testSelectionSelectBySymbol',
+
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2611
+    'CorrectEventFiringTest.testClickAnElementThatDisappear',
 ]
 
 _OS_NEGATIVE_FILTER['android:chrome_stable'] = (
diff --git a/chrome/test/data/android/feed/background_styles.gcl.bin b/chrome/test/data/android/feed/background_styles.gcl.bin
index ea02969..3ed6aae 100644
--- a/chrome/test/data/android/feed/background_styles.gcl.bin
+++ b/chrome/test/data/android/feed/background_styles.gcl.bin
Binary files differ
diff --git a/chrome/test/data/android/feed/feed_large.gcl.bin b/chrome/test/data/android/feed/feed_large.gcl.bin
index 8e8462e..22c8360 100644
--- a/chrome/test/data/android/feed/feed_large.gcl.bin
+++ b/chrome/test/data/android/feed/feed_large.gcl.bin
Binary files differ
diff --git a/chrome/test/data/android/feed/feed_paging.gcl.bin b/chrome/test/data/android/feed/feed_paging.gcl.bin
index 79212ba..cf78b1b0 100644
--- a/chrome/test/data/android/feed/feed_paging.gcl.bin
+++ b/chrome/test/data/android/feed/feed_paging.gcl.bin
Binary files differ
diff --git a/chrome/test/data/android/feed/feed_with_hero.gcl.bin b/chrome/test/data/android/feed/feed_with_hero.gcl.bin
index 60ce43b..1a0e50ee 100644
--- a/chrome/test/data/android/feed/feed_with_hero.gcl.bin
+++ b/chrome/test/data/android/feed/feed_with_hero.gcl.bin
Binary files differ
diff --git a/chrome/test/data/android/feed/feed_with_overlay.gcl.bin b/chrome/test/data/android/feed/feed_with_overlay.gcl.bin
index 62c7504..ded5371 100644
--- a/chrome/test/data/android/feed/feed_with_overlay.gcl.bin
+++ b/chrome/test/data/android/feed/feed_with_overlay.gcl.bin
Binary files differ
diff --git a/chrome/test/data/webui/print_preview/advanced_dialog_test.js b/chrome/test/data/webui/print_preview/advanced_dialog_test.js
index ceb9c91..7db075e 100644
--- a/chrome/test/data/webui/print_preview/advanced_dialog_test.js
+++ b/chrome/test/data/webui/print_preview/advanced_dialog_test.js
@@ -61,7 +61,7 @@
       dialog.destination = destination;
 
       document.body.appendChild(dialog);
-      return test_util.eventToPromise('cr-dialog-open', dialog);
+      Polymer.dom.flush();
     }
 
     /**
@@ -99,93 +99,88 @@
     // Tests that the search box does not appear when there is only one option,
     // and that the vendor item is correctly displayed.
     test(assert(TestNames.AdvancedSettings1Option), function() {
-      return setupDialog(1).then(() => verifyListWithItemCount(1));
+      setupDialog(1);
+      verifyListWithItemCount(1);
     });
 
     // Tests that the search box appears when there are two options, and that
     // the items are correctly displayed.
     test(assert(TestNames.AdvancedSettings2Options), function() {
-      return setupDialog(2).then(() => verifyListWithItemCount(2));
+      setupDialog(2);
+      verifyListWithItemCount(2);
     });
 
     // Tests that the advanced settings dialog correctly updates the settings
     // value for vendor items when the apply button is clicked.
     test(assert(TestNames.AdvancedSettingsApply), function() {
-      return setupDialog(3)
-          .then(() => {
-            setItemValues();
+      setupDialog(3);
+      setItemValues();
 
-            const buttons = dialog.shadowRoot.querySelectorAll('paper-button');
-            assertEquals(2, buttons.length);
-            const whenDialogClose = test_util.eventToPromise('close', dialog);
+      const buttons = dialog.shadowRoot.querySelectorAll('paper-button');
+      assertEquals(2, buttons.length);
+      const whenDialogClose = test_util.eventToPromise('close', dialog);
 
-            // Click apply button.
-            buttons[1].click();
-            return whenDialogClose;
-          })
-          .then(() => {
-            // Check that the setting has been set.
-            const setting = dialog.getSettingValue('vendorItems');
-            assertEquals(6, setting.printArea);
-            assertEquals(1, setting.paperType);
-            assertEquals('XYZ', setting.watermark);
-          });
+      // Click apply button.
+      buttons[1].click();
+      return whenDialogClose.then(() => {
+        // Check that the setting has been set.
+        const setting = dialog.getSettingValue('vendorItems');
+        assertEquals(6, setting.printArea);
+        assertEquals(1, setting.paperType);
+        assertEquals('XYZ', setting.watermark);
+      });
     });
 
     // Tests that the advanced settings dialog does not update the settings
     // value for vendor items when the close button is clicked.
     test(assert(TestNames.AdvancedSettingsClose), function() {
-      return setupDialog(3)
-          .then(() => {
-            setItemValues();
+      setupDialog(3);
+      setItemValues();
 
-            const buttons = dialog.shadowRoot.querySelectorAll('paper-button');
-            assertEquals(2, buttons.length);
-            const whenDialogClose = test_util.eventToPromise('close', dialog);
+      const buttons = dialog.shadowRoot.querySelectorAll('paper-button');
+      assertEquals(2, buttons.length);
+      const whenDialogClose = test_util.eventToPromise('close', dialog);
 
-            // Click close button.
-            buttons[0].click();
-            return whenDialogClose;
-          })
-          .then(() => {
-            // Check that the setting has not been set.
-            const setting = dialog.getSettingValue('vendorItems');
-            assertEquals(undefined, setting.printArea);
-            assertEquals(undefined, setting.paperType);
-            assertEquals(undefined, setting.watermark);
-          });
+      // Click close button.
+      buttons[0].click();
+      return whenDialogClose.then(() => {
+        // Check that the setting has not been set.
+        const setting = dialog.getSettingValue('vendorItems');
+        assertEquals(undefined, setting.printArea);
+        assertEquals(undefined, setting.paperType);
+        assertEquals(undefined, setting.watermark);
+      });
     });
 
     // Tests that the dialog correctly shows and hides settings based on the
     // value of the search query.
     test(assert(TestNames.AdvancedSettingsFilter), function() {
-      return setupDialog(3).then(() => {
-        const searchBox = dialog.$.searchBox;
-        const items = dialog.shadowRoot.querySelectorAll(
-            'print-preview-advanced-settings-item');
-        const noMatchHint = dialog.$$('.no-settings-match-hint');
+      setupDialog(3);
+      const searchBox = dialog.$.searchBox;
+      const items = dialog.shadowRoot.querySelectorAll(
+          'print-preview-advanced-settings-item');
+      const noMatchHint = dialog.$$('.no-settings-match-hint');
 
-        // Query is initialized to null. All items are shown and the hint is
-        // hidden.
-        items.forEach(item => assertFalse(item.hidden));
-        assertTrue(noMatchHint.hidden);
+      // Query is initialized to null. All items are shown and the hint is
+      // hidden.
+      items.forEach(item => assertFalse(item.hidden));
+      assertTrue(noMatchHint.hidden);
 
-        // Searching for Watermark should show only the watermark setting.
-        searchBox.searchQuery = /(Watermark)/i;
-        items.forEach((item, index) => assertEquals(index != 2, item.hidden));
-        assertTrue(noMatchHint.hidden);
+      // Searching for Watermark should show only the watermark setting.
+      searchBox.searchQuery = /(Watermark)/i;
+      items.forEach((item, index) => assertEquals(index != 2, item.hidden));
+      assertTrue(noMatchHint.hidden);
 
-        // Searching for A4 should show only the print area setting.
-        searchBox.searchQuery = /(A4)/i;
-        items.forEach((item, index) => assertEquals(index != 0, item.hidden));
-        assertTrue(noMatchHint.hidden);
+      // Searching for A4 should show only the print area setting.
+      searchBox.searchQuery = /(A4)/i;
+      items.forEach((item, index) => assertEquals(index != 0, item.hidden));
+      assertTrue(noMatchHint.hidden);
 
-        // Searching for WXYZ should show no settings and display the "no match"
-        // hint.
-        searchBox.searchQuery = /(WXYZ)/i;
-        items.forEach(item => assertTrue(item.hidden));
-        assertFalse(noMatchHint.hidden);
-      });
+      // Searching for WXYZ should show no settings and display the "no match"
+      // hint.
+      searchBox.searchQuery = /(WXYZ)/i;
+      items.forEach(item => assertTrue(item.hidden));
+      assertFalse(noMatchHint.hidden);
     });
   });
 
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index d1cb014d..5557577 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -22,12 +22,6 @@
   cast_test_extra_flags = ""
 }
 
-config("playready_config") {
-  if (use_playready) {
-    defines = [ "PLAYREADY_CDM_AVAILABLE" ]
-  }
-}
-
 # Depends on all non-test targets that should be built by the Chromecast
 # internal build infrastructure.
 group("all") {
@@ -577,22 +571,23 @@
 buildflag_header("chromecast_buildflags") {
   header = "chromecast_buildflags.h"
   flags = [
+    "DEFAULT_COMMAND_LINE_FLAGS=\"$default_command_line_flags\"",
+    "DISABLE_SECURE_FLAC_OPUS_DECODING=$disable_secure_flac_and_opus_decoding",
     "ENABLE_ASSISTANT=$enable_assistant",
+    "ENABLE_CAST_FRAGMENT=$enable_cast_fragment",
+    "ENABLE_CHROMECAST_EXTENSIONS=$enable_chromecast_extensions",
+    "ENABLE_HEADLESS_MUSIC_MODE=$enable_headless_music_mode",
+    "ENABLE_PLAYREADY=$enable_playready",
     "ENABLE_VOLUME_TABLES_ACCESS=$enable_volume_tables_access",
     "IS_ANDROID_THINGS=$is_android_things",
+    "IS_ANDROID_THINGS_NON_PUBLIC=$is_android_things_non_public",
     "IS_CAST_AUDIO_ONLY=$is_cast_audio_only",
     "IS_CAST_DESKTOP_BUILD=$is_cast_desktop_build",
     "IS_CAST_USING_CMA_BACKEND=$is_cast_using_cma_backend",
-    "SUPPORTS_MULTIZONE=$supports_multizone",
-    "ENABLE_HEADLESS_MUSIC_MODE=$enable_headless_music_mode",
-    "ENABLE_CHROMECAST_EXTENSIONS=$enable_chromecast_extensions",
-    "ENABLE_CAST_FRAGMENT=$enable_cast_fragment",
-    "IS_ANDROID_THINGS_NON_PUBLIC=$is_android_things_non_public",
     "IS_SINGLE_VOLUME=$is_single_volume",
+    "SUPPORTS_MULTIZONE=$supports_multizone",
     "USE_ANDROID_USER_AGENT=$use_android_user_agent",
     "USE_CHROMECAST_CDMS=$use_chromecast_cdms",
-    "DEFAULT_COMMAND_LINE_FLAGS=\"$default_command_line_flags\"",
-    "DISABLE_SECURE_FLAC_OPUS_DECODING=$disable_secure_flac_and_opus_decoding",
   ]
 }
 
diff --git a/chromecast/chromecast.gni b/chromecast/chromecast.gni
index fef8c70..76fa24b0 100644
--- a/chromecast/chromecast.gni
+++ b/chromecast/chromecast.gni
@@ -109,7 +109,7 @@
 
 declare_args() {
   # Use Playready CDMs for internal non-desktop builds.
-  use_playready = !is_cast_desktop_build && chromecast_branding != "public"
+  enable_playready = !is_cast_desktop_build && chromecast_branding != "public"
 }
 
 # This is the release version, which takes the form <major>.<minor>. Internal
diff --git a/chromecast/common/media/BUILD.gn b/chromecast/common/media/BUILD.gn
index 2b42534..30a78086 100644
--- a/chromecast/common/media/BUILD.gn
+++ b/chromecast/common/media/BUILD.gn
@@ -13,14 +13,11 @@
     ]
     deps = [
       "//base",
+      "//chromecast:chromecast_buildflags",
       "//chromecast/media",
       "//chromecast/media/cdm",
       "//components/cdm/common",
       "//media",
     ]
   }
-
-  if (use_playready) {
-    public_configs = [ "//chromecast:playready_config" ]
-  }
 }
diff --git a/chromecast/common/media/cast_media_drm_bridge_client.cc b/chromecast/common/media/cast_media_drm_bridge_client.cc
index 334a677..b9b8969a 100644
--- a/chromecast/common/media/cast_media_drm_bridge_client.cc
+++ b/chromecast/common/media/cast_media_drm_bridge_client.cc
@@ -15,18 +15,18 @@
 
 void CastMediaDrmBridgeClient::AddKeySystemUUIDMappings(KeySystemUuidMap* map) {
 // Note: MediaDrmBridge adds the Widevine UUID mapping automatically.
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
   (*map)[kChromecastPlayreadyKeySystem] = playready_delegate_.GetUUID();
-#endif
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 }
 
 ::media::MediaDrmBridgeDelegate*
 CastMediaDrmBridgeClient::GetMediaDrmBridgeDelegate(
     const ::media::UUID& scheme_uuid) {
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
   if (scheme_uuid == playready_delegate_.GetUUID())
     return &playready_delegate_;
-#endif
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
   if (scheme_uuid == widevine_delegate_.GetUUID())
     return &widevine_delegate_;
diff --git a/chromecast/common/media/cast_media_drm_bridge_client.h b/chromecast/common/media/cast_media_drm_bridge_client.h
index 69b381d..86c7d80 100644
--- a/chromecast/common/media/cast_media_drm_bridge_client.h
+++ b/chromecast/common/media/cast_media_drm_bridge_client.h
@@ -8,6 +8,7 @@
 #include <map>
 
 #include "base/macros.h"
+#include "chromecast/chromecast_buildflags.h"
 #include "chromecast/media/cdm/playready_drm_delegate_android.h"
 #include "components/cdm/common/widevine_drm_delegate_android.h"
 #include "media/base/android/media_drm_bridge_client.h"
@@ -26,9 +27,9 @@
   ::media::MediaDrmBridgeDelegate* GetMediaDrmBridgeDelegate(
       const ::media::UUID& scheme_uuid) override;
 
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
   PlayreadyDrmDelegateAndroid playready_delegate_;
-#endif
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
   cdm::WidevineDrmDelegateAndroid widevine_delegate_;
 
diff --git a/chromecast/media/base/BUILD.gn b/chromecast/media/base/BUILD.gn
index 450a06d2..1acff20 100644
--- a/chromecast/media/base/BUILD.gn
+++ b/chromecast/media/base/BUILD.gn
@@ -11,12 +11,9 @@
     "key_systems_common.h",
   ]
 
-  if (use_playready) {
-    public_configs = [ "//chromecast:playready_config" ]
-  }
-
   deps = [
     "//base",
+    "//chromecast:chromecast_buildflags",
     "//chromecast/public/media",
     "//media",
     "//third_party/widevine/cdm:buildflags",
diff --git a/chromecast/media/base/key_systems_common.cc b/chromecast/media/base/key_systems_common.cc
index 26e3414..3854184c 100644
--- a/chromecast/media/base/key_systems_common.cc
+++ b/chromecast/media/base/key_systems_common.cc
@@ -16,9 +16,9 @@
 namespace chromecast {
 namespace media {
 
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
 const char kChromecastPlayreadyKeySystem[] = "com.chromecast.playready";
-#endif  // defined(PLAYREADY_CDM_AVAILABLE)
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
 CastKeySystem GetKeySystemByName(const std::string& key_system_name) {
 #if BUILDFLAG(ENABLE_WIDEVINE)
@@ -27,11 +27,11 @@
   }
 #endif  // BUILDFLAG(ENABLE_WIDEVINE)
 
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
   if (key_system_name.compare(kChromecastPlayreadyKeySystem) == 0) {
     return KEY_SYSTEM_PLAYREADY;
   }
-#endif  // defined(PLAYREADY_CDM_AVAILABLE)
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
   if (::media::IsClearKey(key_system_name)) {
     return KEY_SYSTEM_CLEAR_KEY;
diff --git a/chromecast/media/base/key_systems_common.h b/chromecast/media/base/key_systems_common.h
index 7e9c992..75baa1ad4 100644
--- a/chromecast/media/base/key_systems_common.h
+++ b/chromecast/media/base/key_systems_common.h
@@ -7,14 +7,15 @@
 
 #include <string>
 
+#include "chromecast/chromecast_buildflags.h"
 #include "chromecast/public/media/cast_key_system.h"
 
 namespace chromecast {
 namespace media {
 
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
 extern const char kChromecastPlayreadyKeySystem[];
-#endif  // defined(PLAYREADY_CDM_AVAILABLE)
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
 // Translates a key system string into a CastKeySystem, calling into the
 // platform for known key systems if needed.
diff --git a/chromecast/media/cdm/BUILD.gn b/chromecast/media/cdm/BUILD.gn
index 5e524e9..543fd6d 100644
--- a/chromecast/media/cdm/BUILD.gn
+++ b/chromecast/media/cdm/BUILD.gn
@@ -34,7 +34,7 @@
     ]
   }
 
-  if (is_android && use_playready) {
+  if (is_android && enable_playready) {
     sources += [
       "playready_drm_delegate_android.cc",
       "playready_drm_delegate_android.h",
diff --git a/chromecast/media/cma/pipeline/av_pipeline_impl.cc b/chromecast/media/cma/pipeline/av_pipeline_impl.cc
index e104ca8..f5cf915 100644
--- a/chromecast/media/cma/pipeline/av_pipeline_impl.cc
+++ b/chromecast/media/cma/pipeline/av_pipeline_impl.cc
@@ -19,6 +19,7 @@
 #include "chromecast/media/cma/base/cma_logging.h"
 #include "chromecast/media/cma/base/coded_frame_provider.h"
 #include "chromecast/media/cma/base/decoder_buffer_base.h"
+#include "chromecast/media/cma/pipeline/cdm_decryptor.h"
 #include "chromecast/media/cma/pipeline/decrypt_util.h"
 #include "chromecast/public/media/cast_decrypt_config.h"
 #include "media/base/audio_decoder_config.h"
@@ -202,13 +203,6 @@
   if (audio_config.IsValidConfig() || video_config.IsValidConfig())
     OnUpdateConfig(buffer->stream_id(), audio_config, video_config);
 
-  if (!decryptor_) {
-    decryptor_ = CreateDecryptor();
-    DCHECK(decryptor_);
-    decryptor_->Init(base::BindRepeating(&AvPipelineImpl::OnBufferDecrypted,
-                                         decrypt_weak_factory_.GetWeakPtr()));
-  }
-
   pending_buffer_ = buffer;
   ProcessPendingBuffer();
 }
@@ -249,10 +243,24 @@
     }
 
     DCHECK_NE(decrypt_context->GetKeySystem(), KEY_SYSTEM_NONE);
+
+    if (!decryptor_) {
+      decryptor_ = CreateStreamDecryptor(decrypt_context->GetKeySystem());
+      DCHECK(decryptor_);
+      decryptor_->Init(base::BindRepeating(&AvPipelineImpl::OnBufferDecrypted,
+                                           decrypt_weak_factory_.GetWeakPtr()));
+    }
+
     pending_buffer_->set_decrypt_context(std::move(decrypt_context));
   }
 
-  decryptor_->Decrypt(std::move(pending_buffer_));
+  if (decryptor_) {
+    decryptor_->Decrypt(std::move(pending_buffer_));
+    return;
+  }
+
+  DCHECK(ready_buffers_.empty());
+  PushReadyBuffer(std::move(pending_buffer_));
 }
 
 void AvPipelineImpl::PushAllReadyBuffers() {
@@ -431,5 +439,15 @@
   }
 }
 
+std::unique_ptr<StreamDecryptor> AvPipelineImpl::CreateStreamDecryptor(
+    CastKeySystem key_system) {
+  if (key_system == KEY_SYSTEM_CLEAR_KEY) {
+    // Clear Key only supports clear output.
+    return std::make_unique<CdmDecryptor>(true /* clear_buffer_needed */);
+  }
+
+  return CreateDecryptor();
+}
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/av_pipeline_impl.h b/chromecast/media/cma/pipeline/av_pipeline_impl.h
index e5635b1..3d8e3c1d 100644
--- a/chromecast/media/cma/pipeline/av_pipeline_impl.h
+++ b/chromecast/media/cma/pipeline/av_pipeline_impl.h
@@ -132,6 +132,9 @@
                       bool is_at_max_capacity);
   void UpdatePlayableFrames();
 
+  std::unique_ptr<StreamDecryptor> CreateStreamDecryptor(
+      CastKeySystem key_system);
+
   base::ThreadChecker thread_checker_;
 
   CmaBackend::Decoder* const decoder_;
diff --git a/chromecast/renderer/BUILD.gn b/chromecast/renderer/BUILD.gn
index cd88cec..e0a433ea 100644
--- a/chromecast/renderer/BUILD.gn
+++ b/chromecast/renderer/BUILD.gn
@@ -40,10 +40,6 @@
     sources += [ "cast_content_renderer_client_simple.cc" ]
   }
 
-  if (use_playready) {
-    configs += [ "//chromecast:playready_config" ]
-  }
-
   deps = [
     "//base",
     "//chromecast:chromecast_buildflags",
diff --git a/chromecast/renderer/media/key_systems_cast.cc b/chromecast/renderer/media/key_systems_cast.cc
index 0f1d2e1f..b38e089 100644
--- a/chromecast/renderer/media/key_systems_cast.cc
+++ b/chromecast/renderer/media/key_systems_cast.cc
@@ -32,7 +32,7 @@
 namespace media {
 namespace {
 
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
 class PlayReadyKeySystemProperties : public ::media::KeySystemProperties {
  public:
   PlayReadyKeySystemProperties(SupportedCodecs supported_non_secure_codecs,
@@ -108,7 +108,7 @@
 #endif  // defined(OS_ANDROID)
   const bool persistent_license_support_;
 };
-#endif  // PLAYREADY_CDM_AVAILABLE
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
 #if BUILDFLAG(IS_CAST_USING_CMA_BACKEND)
 SupportedCodecs GetCastEmeSupportedCodecs() {
@@ -152,10 +152,10 @@
   // |codecs| may not be used if Widevine and Playready aren't supported.
   ANALYZER_ALLOW_UNUSED(codecs);
 
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
   key_systems_properties->emplace_back(new PlayReadyKeySystemProperties(
       codecs, codecs, enable_persistent_license_support));
-#endif  // defined(PLAYREADY_CDM_AVAILABLE)
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
 #if BUILDFLAG(ENABLE_WIDEVINE)
   using Robustness = cdm::WidevineKeySystemProperties::Robustness;
@@ -178,7 +178,7 @@
 #endif                                      // BUILDFLAG(ENABLE_WIDEVINE)
 }
 #elif defined(OS_ANDROID)
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
 void AddCastPlayreadyKeySystemAndroid(
     std::vector<std::unique_ptr<::media::KeySystemProperties>>*
         key_systems_properties) {
@@ -193,14 +193,14 @@
       response.non_secure_codecs, response.secure_codecs,
       false /* persistent_license_support */));
 }
-#endif  // defined(PLAYREADY_CDM_AVAILABLE)
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
 void AddCastAndroidKeySystems(
     std::vector<std::unique_ptr<::media::KeySystemProperties>>*
         key_systems_properties) {
-#if defined(PLAYREADY_CDM_AVAILABLE)
+#if BUILDFLAG(ENABLE_PLAYREADY)
   AddCastPlayreadyKeySystemAndroid(key_systems_properties);
-#endif  // defined(PLAYREADY_CDM_AVAILABLE)
+#endif  // BUILDFLAG(ENABLE_PLAYREADY)
 
 #if BUILDFLAG(ENABLE_WIDEVINE)
   cdm::AddAndroidWidevine(key_systems_properties);
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index cf300519..2602fac1 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -549,6 +549,9 @@
 // This makes it easier to test layout logic.
 const char kShowLoginDevOverlay[] = "show-login-dev-overlay";
 
+// Show Play Store in Demo Mode.
+const char kShowPlayInDemoMode[] = "show-play-in-demo-mode";
+
 // Indicates that a stub implementation of CrosSettings that stores settings in
 // memory without signing should be used, treating current user as the owner.
 // This also modifies OwnerSettingsServiceChromeOS::HandlesSetting such that no
@@ -713,5 +716,9 @@
       kInstantTetheringBackgroundAdvertisementSupport);
 }
 
+bool ShouldShowPlayStoreInDemoMode() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(kShowPlayInDemoMode);
+}
+
 }  // namespace switches
 }  // namespace chromeos
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 61755eb9..72d39c20 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -153,6 +153,7 @@
 CHROMEOS_EXPORT extern const char kShowAndroidFilesInFilesApp[];
 CHROMEOS_EXPORT extern const char kFilesAppDisableMyFilesNavigation[];
 CHROMEOS_EXPORT extern const char kShowLoginDevOverlay[];
+CHROMEOS_EXPORT extern const char kShowPlayInDemoMode[];
 CHROMEOS_EXPORT extern const char kStubCrosSettings[];
 CHROMEOS_EXPORT extern const char kTestEncryptionMigrationUI[];
 CHROMEOS_EXPORT extern const char kTetherStub[];
@@ -223,6 +224,10 @@
 // background advertisement model
 CHROMEOS_EXPORT bool IsInstantTetheringBackgroundAdvertisingSupported();
 
+// Returns true if Play Store should be available in Demo Mode.
+// TODO(michaelpg): Remove after M71 branch to re-enable Play Store by default.
+CHROMEOS_EXPORT bool ShouldShowPlayStoreInDemoMode();
+
 }  // namespace switches
 }  // namespace chromeos
 
diff --git a/chromeos/components/tether/tether_host_fetcher_impl.cc b/chromeos/components/tether/tether_host_fetcher_impl.cc
index de504841..6b24aa41 100644
--- a/chromeos/components/tether/tether_host_fetcher_impl.cc
+++ b/chromeos/components/tether/tether_host_fetcher_impl.cc
@@ -15,39 +15,6 @@
 
 namespace tether {
 
-namespace {
-
-enum class TetherHostSource {
-  UNKNOWN,
-  MULTIDEVICE_SETUP_CLIENT,
-  DEVICE_SYNC_CLIENT,
-  REMOTE_DEVICE_PROVIDER
-};
-
-TetherHostSource GetTetherHostSourceBasedOnFlags() {
-  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
-      base::FeatureList::IsEnabled(
-          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
-    return TetherHostSource::MULTIDEVICE_SETUP_CLIENT;
-  }
-  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
-      !base::FeatureList::IsEnabled(
-          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
-    return TetherHostSource::DEVICE_SYNC_CLIENT;
-  }
-  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
-      !base::FeatureList::IsEnabled(
-          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
-    return TetherHostSource::REMOTE_DEVICE_PROVIDER;
-  }
-  NOTREACHED() << "TetherHostFetcherImpl: Unexpected feature flag state of "
-               << "kMultiDeviceApi disabled and kEnableUnifiedMultiDeviceSetup "
-               << "enabled.";
-  return TetherHostSource::UNKNOWN;
-}
-
-}  // namespace
-
 // static
 TetherHostFetcherImpl::Factory*
     TetherHostFetcherImpl::Factory::factory_instance_ = nullptr;
@@ -89,35 +56,36 @@
       device_sync_client_(device_sync_client),
       multidevice_setup_client_(multidevice_setup_client),
       weak_ptr_factory_(this) {
-  switch (GetTetherHostSourceBasedOnFlags()) {
-    case TetherHostSource::MULTIDEVICE_SETUP_CLIENT:
-      multidevice_setup_client_->AddObserver(this);
-      break;
-    case TetherHostSource::DEVICE_SYNC_CLIENT:
-      device_sync_client_->AddObserver(this);
-      break;
-    case TetherHostSource::REMOTE_DEVICE_PROVIDER:
-      remote_device_provider_->AddObserver(this);
-      break;
-    case TetherHostSource::UNKNOWN:
-      break;
+  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi))
+    device_sync_client_->AddObserver(this);
+
+  if (base::FeatureList::IsEnabled(
+          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
+    multidevice_setup_client_->AddObserver(this);
   }
+
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      !base::FeatureList::IsEnabled(
+          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
+    remote_device_provider_->AddObserver(this);
+  }
+
   CacheCurrentTetherHosts();
 }
 
 TetherHostFetcherImpl::~TetherHostFetcherImpl() {
-  switch (GetTetherHostSourceBasedOnFlags()) {
-    case TetherHostSource::MULTIDEVICE_SETUP_CLIENT:
-      multidevice_setup_client_->RemoveObserver(this);
-      break;
-    case TetherHostSource::DEVICE_SYNC_CLIENT:
-      device_sync_client_->RemoveObserver(this);
-      break;
-    case TetherHostSource::REMOTE_DEVICE_PROVIDER:
-      remote_device_provider_->RemoveObserver(this);
-      break;
-    case TetherHostSource::UNKNOWN:
-      break;
+  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi))
+    device_sync_client_->RemoveObserver(this);
+
+  if (base::FeatureList::IsEnabled(
+          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
+    multidevice_setup_client_->RemoveObserver(this);
+  }
+
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      !base::FeatureList::IsEnabled(
+          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
+    remote_device_provider_->RemoveObserver(this);
   }
 }
 
@@ -157,6 +125,10 @@
   CacheCurrentTetherHosts();
 }
 
+void TetherHostFetcherImpl::OnReady() {
+  CacheCurrentTetherHosts();
+}
+
 void TetherHostFetcherImpl::CacheCurrentTetherHosts() {
   cryptauth::RemoteDeviceRefList updated_list = GenerateHostDeviceList();
   if (updated_list == current_remote_device_list_)
@@ -218,6 +190,63 @@
   return host_list;
 }
 
+bool TetherHostFetcherImpl::IsInLegacyHostMode() {
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) ||
+      !device_sync_client_->is_ready()) {
+    return false;
+  }
+
+  bool has_supported_tether_host = false;
+  for (const cryptauth::RemoteDeviceRef& remote_device_ref :
+       device_sync_client_->GetSyncedDevices()) {
+    cryptauth::SoftwareFeatureState better_together_host_state =
+        remote_device_ref.GetSoftwareFeatureState(
+            cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST);
+    // If there's any valid Better Together host, don't support legacy mode.
+    if (better_together_host_state ==
+            cryptauth::SoftwareFeatureState::kSupported ||
+        better_together_host_state ==
+            cryptauth::SoftwareFeatureState::kEnabled) {
+      return false;
+    }
+
+    cryptauth::SoftwareFeatureState magic_tether_host_state =
+        remote_device_ref.GetSoftwareFeatureState(
+            cryptauth::SoftwareFeature::MAGIC_TETHER_HOST);
+    if (magic_tether_host_state ==
+            cryptauth::SoftwareFeatureState::kSupported ||
+        magic_tether_host_state == cryptauth::SoftwareFeatureState::kEnabled) {
+      has_supported_tether_host = true;
+    }
+  }
+
+  return has_supported_tether_host;
+}
+
+TetherHostFetcherImpl::TetherHostSource
+TetherHostFetcherImpl::GetTetherHostSourceBasedOnFlags() {
+  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      !base::FeatureList::IsEnabled(
+          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
+    return TetherHostSource::DEVICE_SYNC_CLIENT;
+  }
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      !base::FeatureList::IsEnabled(
+          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
+    return TetherHostSource::REMOTE_DEVICE_PROVIDER;
+  }
+  if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      base::FeatureList::IsEnabled(
+          chromeos::features::kEnableUnifiedMultiDeviceSetup)) {
+    return IsInLegacyHostMode() ? TetherHostSource::DEVICE_SYNC_CLIENT
+                                : TetherHostSource::MULTIDEVICE_SETUP_CLIENT;
+  }
+  NOTREACHED() << "TetherHostFetcherImpl: Unexpected feature flag state of "
+               << "kMultiDeviceApi disabled and kEnableUnifiedMultiDeviceSetup "
+               << "enabled.";
+  return TetherHostSource::UNKNOWN;
+}
+
 }  // namespace tether
 
 }  // namespace chromeos
diff --git a/chromeos/components/tether/tether_host_fetcher_impl.h b/chromeos/components/tether/tether_host_fetcher_impl.h
index e8d9049..b1b07e3 100644
--- a/chromeos/components/tether/tether_host_fetcher_impl.h
+++ b/chromeos/components/tether/tether_host_fetcher_impl.h
@@ -30,12 +30,11 @@
 // Note: TetherHostFetcherImpl, and the Tether feature as a whole, is currently
 // in the middle of a migration from using RemoteDeviceProvider to
 // DeviceSyncClient and eventually to MultiDeviceSetupClient. Its constructor
-// accepts all three objects, but expects only of one of them to be valid, and
-// the others null. (This is controlled at a higher level by
-// features::kMultiDeviceApi and features::kEnableUnifiedMultiDeviceSetup.).
-// Once Tether has been fully migrated, RemoteDeviceProvider and eventually
-// DeviceSyncClient will be ripped out of this class. See
-// https://crbug.com/848956.
+// accepts all three objects, but some may be null. (This is controlled at a
+// higher level by features::kMultiDeviceApi and
+// features::kEnableUnifiedMultiDeviceSetup.). Once Tether has been fully
+// migrated, RemoteDeviceProvider and eventually DeviceSyncClient will be ripped
+// out of this class. See https://crbug.com/848956.
 class TetherHostFetcherImpl
     : public TetherHostFetcher,
       public cryptauth::RemoteDeviceProvider::Observer,
@@ -76,6 +75,7 @@
 
   // device_sync::DeviceSyncClient::Observer:
   void OnNewDevicesSynced() override;
+  void OnReady() override;
 
   // multidevice_setup::MultiDeviceSetupClient::Observer:
   void OnHostStatusChanged(
@@ -94,8 +94,22 @@
                             multidevice_setup_client_);
 
  private:
+  enum class TetherHostSource {
+    UNKNOWN,
+    MULTIDEVICE_SETUP_CLIENT,
+    DEVICE_SYNC_CLIENT,
+    REMOTE_DEVICE_PROVIDER
+  };
+
   void CacheCurrentTetherHosts();
   cryptauth::RemoteDeviceRefList GenerateHostDeviceList();
+  TetherHostSource GetTetherHostSourceBasedOnFlags();
+  // This returns true if there is no BETTER_TOGETHER_HOST supported or enabled,
+  // but there *are* MAGIC_TETHER_HOSTs supported or enabled. This can only
+  // happen if the user's phone has not yet fully updated to the new multidevice
+  // world.
+  // TODO(crbug.com/894585): Remove this legacy special case after M71.
+  bool IsInLegacyHostMode();
 
   cryptauth::RemoteDeviceProvider* remote_device_provider_;
   device_sync::DeviceSyncClient* device_sync_client_;
diff --git a/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc b/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc
index ccbd10c..f0d1c64a 100644
--- a/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc
+++ b/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc
@@ -122,13 +122,15 @@
         tether_host_source == TetherHostSource::REMOTE_DEVICE_PROVIDER
             ? fake_remote_device_provider_.get()
             : nullptr,
-        tether_host_source == TetherHostSource::DEVICE_SYNC_CLIENT
+        tether_host_source == TetherHostSource::DEVICE_SYNC_CLIENT ||
+                tether_host_source == TetherHostSource::MULTIDEVICE_SETUP_CLIENT
             ? fake_device_sync_client_.get()
             : nullptr,
         tether_host_source == TetherHostSource::MULTIDEVICE_SETUP_CLIENT
             ? fake_multidevice_setup_client_.get()
             : nullptr);
 
+    fake_device_sync_client_->NotifyReady();
     test_observer_ = std::make_unique<TestObserver>();
     tether_host_fetcher_->AddObserver(test_observer_.get());
   }
@@ -175,6 +177,9 @@
     // Mark the first device enabled instead of supported.
     list[0].software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
         cryptauth::SoftwareFeatureState::kEnabled;
+    list[0]
+        .software_features[cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST] =
+        cryptauth::SoftwareFeatureState::kEnabled;
 
     return list;
   }
@@ -259,8 +264,17 @@
     EXPECT_EQ(2u, test_observer_->num_updates());
   }
 
-  void TestSingleTetherHost() {
+  void TestSingleTetherHost(bool use_legacy_mode = false) {
     InitializeTest();
+    if (use_legacy_mode) {
+      test_remote_device_list_[0]
+          .software_features[cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST] =
+          cryptauth::SoftwareFeatureState::kNotSupported;
+      test_remote_device_ref_list_ =
+          CreateTestRemoteDeviceRefList(test_remote_device_list_);
+      SetSyncedDevices(test_remote_device_list_);
+      NotifyNewDevicesSynced();
+    }
 
     VerifySingleTetherHost(test_remote_device_ref_list_[0].GetDeviceId(),
                            test_remote_device_ref_list_[0]);
@@ -291,7 +305,7 @@
                            base::nullopt);
   }
 
-  void TestFetchAllTetherHosts() {
+  void TestFetchAllTetherHosts(bool use_legacy_mode = false) {
     InitializeTest();
 
     // Create a list of test devices, only some of which are valid tether hosts.
@@ -302,6 +316,11 @@
     test_remote_device_list_[4]
         .software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
         cryptauth::SoftwareFeatureState::kNotSupported;
+    if (use_legacy_mode) {
+      test_remote_device_list_[0]
+          .software_features[cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST] =
+          cryptauth::SoftwareFeatureState::kNotSupported;
+    }
 
     SetSyncedDevices(test_remote_device_list_);
     NotifyNewDevicesSynced();
@@ -309,9 +328,12 @@
     cryptauth::RemoteDeviceRefList expected_host_device_list;
     switch (GetTetherHostSourceBasedOnFlags()) {
       case TetherHostSource::MULTIDEVICE_SETUP_CLIENT:
-        expected_host_device_list =
-            CreateTestRemoteDeviceRefList({test_remote_device_list_[0]});
-        break;
+        if (!use_legacy_mode) {
+          expected_host_device_list =
+              CreateTestRemoteDeviceRefList({test_remote_device_list_[0]});
+          break;
+        }
+        FALLTHROUGH;
       case TetherHostSource::DEVICE_SYNC_CLIENT:
       case TetherHostSource::REMOTE_DEVICE_PROVIDER:
         expected_host_device_list = CreateTestRemoteDeviceRefList(
@@ -374,6 +396,11 @@
   SetOnlyMultiDeviceApiFeatureEnabled();
   TestFetchAllTetherHosts();
 }
+TEST_F(TetherHostFetcherImplTest,
+       TestFetchAllTetherHosts_MultideviceApiAndSetupEnabledInLegacyMode) {
+  SetMultiDeviceApiAndSetupFeaturesEnabled();
+  TestFetchAllTetherHosts(true /* use_legacy_mode */);
+}
 
 // TestSingleTetherHost
 TEST_F(TetherHostFetcherImplTest, TestSingleTetherHost) {
@@ -389,6 +416,11 @@
   SetOnlyMultiDeviceApiFeatureEnabled();
   TestSingleTetherHost();
 }
+TEST_F(TetherHostFetcherImplTest,
+       TestSingleTetherHost_MultideviceApiAndSetupEnabledInLegacyMode) {
+  SetMultiDeviceApiAndSetupFeaturesEnabled();
+  TestSingleTetherHost(true /* use_legacy_mode */);
+}
 
 // TestSingleTetherHost_IdDoesNotCorrespondToDevice
 TEST_F(TetherHostFetcherImplTest,
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 468b9c97..edf9a53e 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -385,14 +385,15 @@
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&AssistantManagerServiceImpl::OnShowHtmlOnMainThread,
-                     weak_factory_.GetWeakPtr(), html.str()));
+                     weak_factory_.GetWeakPtr(), html.str(), /*fallback=*/""));
 }
 
-void AssistantManagerServiceImpl::OnShowHtml(const std::string& html) {
+void AssistantManagerServiceImpl::OnShowHtml(const std::string& html,
+                                             const std::string& fallback) {
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&AssistantManagerServiceImpl::OnShowHtmlOnMainThread,
-                     weak_factory_.GetWeakPtr(), html));
+                     weak_factory_.GetWeakPtr(), html, fallback));
 }
 
 void AssistantManagerServiceImpl::OnShowSuggestions(
@@ -440,6 +441,10 @@
   notification_ptr->opaque_token = notification.opaque_token;
   notification_ptr->grouping_key = notification.grouping_key;
   notification_ptr->obfuscated_gaia_id = notification.obfuscated_gaia_id;
+  for (const auto& button : notification.buttons) {
+    notification_ptr->buttons.push_back(mojom::AssistantNotificationButton::New(
+        button.label, GURL(button.action_url)));
+  }
 
   main_thread_task_runner_->PostTask(
       FROM_HERE,
@@ -898,9 +903,10 @@
 }
 
 void AssistantManagerServiceImpl::OnShowHtmlOnMainThread(
-    const std::string& html) {
+    const std::string& html,
+    const std::string& fallback) {
   interaction_subscribers_.ForAllPtrs(
-      [&html](auto* ptr) { ptr->OnHtmlResponse(html); });
+      [&html, &fallback](auto* ptr) { ptr->OnHtmlResponse(html, fallback); });
 }
 
 void AssistantManagerServiceImpl::OnShowSuggestionsOnMainThread(
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index 0f1b391..0c0572ad 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -102,7 +102,8 @@
 
   // AssistantActionObserver overrides:
   void OnShowContextualQueryFallback() override;
-  void OnShowHtml(const std::string& html) override;
+  void OnShowHtml(const std::string& html,
+                  const std::string& fallback) override;
   void OnShowSuggestions(
       const std::vector<action::Suggestion>& suggestions) override;
   void OnShowText(const std::string& text) override;
@@ -164,7 +165,8 @@
   void OnConversationTurnStartedOnMainThread(bool is_mic_open);
   void OnConversationTurnFinishedOnMainThread(
       assistant_client::ConversationStateListener::Resolution resolution);
-  void OnShowHtmlOnMainThread(const std::string& html);
+  void OnShowHtmlOnMainThread(const std::string& html,
+                              const std::string& fallback);
   void OnShowSuggestionsOnMainThread(
       const std::vector<mojom::AssistantSuggestionPtr>& suggestions);
   void OnShowTextOnMainThread(const std::string& text);
diff --git a/chromeos/services/assistant/public/mojom/assistant.mojom b/chromeos/services/assistant/public/mojom/assistant.mojom
index 6428e55..321e6e8 100644
--- a/chromeos/services/assistant/public/mojom/assistant.mojom
+++ b/chromeos/services/assistant/public/mojom/assistant.mojom
@@ -45,9 +45,12 @@
       AssistantNotificationSubscriber subscriber);
 
   // Retrieves a notification. A voiceless interaction will be sent to server to
-  // retrieve the notification payload, which can trigger other assistant's
-  // events such as OnTextResponse to show the result in the UI. The reteived
-  // notification will be removed from the UI.
+  // retrieve the notification of |action_index|, which can trigger other
+  // Assistant events such as OnTextResponse to show the result in the UI. The
+  // retrieved notification will be removed from the UI.
+  // |action_index| is the index of the tapped action. The main UI in the
+  // notification contains the top level action, which index is 0. The buttons
+  // have the additional actions, which are indexed starting from 1.
   RetrieveNotification(AssistantNotification notification, int32 action_index);
 
   // Dismisses a notification.
@@ -76,8 +79,8 @@
   // Assistant interaction has ended with the specified |resolution|.
   OnInteractionFinished(AssistantInteractionResolution resolution);
 
-  // Assistant got Html response from server.
-  OnHtmlResponse(string response);
+  // Assistant got Html response with fallback text from server.
+  OnHtmlResponse(string response, string fallback);
 
   // Assistant got suggestions response from server.
   OnSuggestionsResponse(array<AssistantSuggestion> response);
@@ -184,6 +187,15 @@
   url.mojom.Url action_url;
 };
 
+// Models a notification button.
+struct AssistantNotificationButton {
+  // Display text of the button.
+  string label;
+
+  // Optional URL to open when the tap action is invoked on the button.
+  url.mojom.Url action_url;
+};
+
 // Models an Assistant notification.
 struct AssistantNotification {
   // Title of the notification.
@@ -192,9 +204,13 @@
   // Body text of the notification.
   string message;
 
-  // URL to open when the tap action is invoked on the notification.
+  // Optional URL to open when the tap action is invoked on the notification
+  // main UI.
   url.mojom.Url action_url;
 
+  // List of buttons in the notification.
+  array<AssistantNotificationButton> buttons;
+
   // An id that uniquely identifies a notification.
   string notification_id;
 
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 49f65b3..9f292e6 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -34,8 +34,6 @@
     "actions/select_option_action.h",
     "actions/show_details_action.cc",
     "actions/show_details_action.h",
-    "actions/show_progress_bar_action.cc",
-    "actions/show_progress_bar_action.h",
     "actions/stop_action.cc",
     "actions/stop_action.h",
     "actions/tell_action.cc",
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 549a5ae..eaacb76 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -143,12 +143,6 @@
   // Show contextual information.
   virtual void ShowDetails(const DetailsProto& details) = 0;
 
-  // Show the progress bar with |message| and set it at |progress|%.
-  virtual void ShowProgressBar(int progress, const std::string& message) = 0;
-
-  // Hide the progress bar.
-  virtual void HideProgressBar();
-
  protected:
   ActionDelegate() = default;
 };
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 2d5dc6d..6a6f8559 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -116,8 +116,6 @@
   MOCK_METHOD1(StopCurrentScript, void(const std::string& message));
   MOCK_METHOD0(HideDetails, void());
   MOCK_METHOD1(ShowDetails, void(const DetailsProto& details));
-  MOCK_METHOD2(ShowProgressBar, void(int progress, const std::string& message));
-  MOCK_METHOD0(HideProgressBar, void());
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/show_progress_bar_action.cc b/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
deleted file mode 100644
index 06ffc46..0000000
--- a/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill_assistant/browser/actions/show_progress_bar_action.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "components/autofill_assistant/browser/actions/action_delegate.h"
-
-namespace autofill_assistant {
-
-ShowProgressBarAction::ShowProgressBarAction(const ActionProto& proto)
-    : Action(proto) {
-  DCHECK(proto_.has_show_progress_bar());
-}
-
-ShowProgressBarAction::~ShowProgressBarAction() {}
-
-void ShowProgressBarAction::ProcessAction(ActionDelegate* delegate,
-                                          ProcessActionCallback callback) {
-  int progress =
-      std::min(100, std::max(0, proto_.show_progress_bar().progress()));
-  if (proto_.show_progress_bar().done() || progress == 100) {
-    delegate->HideProgressBar();
-  } else {
-    delegate->ShowProgressBar(progress, proto_.show_progress_bar().message());
-  }
-
-  processed_action_proto_ = std::make_unique<ProcessedActionProto>();
-  UpdateProcessedAction(ACTION_APPLIED);
-  std::move(callback).Run(std::move(processed_action_proto_));
-}
-
-}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/show_progress_bar_action.h b/components/autofill_assistant/browser/actions/show_progress_bar_action.h
deleted file mode 100644
index 4655a87..0000000
--- a/components/autofill_assistant/browser/actions/show_progress_bar_action.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2018 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_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_SHOW_PROGRESS_BAR_ACTION_H_
-#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_SHOW_PROGRESS_BAR_ACTION_H_
-
-#include "components/autofill_assistant/browser/actions/action.h"
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-
-namespace autofill_assistant {
-// An action to show the current progress.
-class ShowProgressBarAction : public Action {
- public:
-  explicit ShowProgressBarAction(const ActionProto& proto);
-  ~ShowProgressBarAction() override;
-
-  // Overrides Action:
-  void ProcessAction(ActionDelegate* delegate,
-                     ProcessActionCallback callback) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ShowProgressBarAction);
-};
-
-}  // namespace autofill_assistant
-#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_SHOW_PROGRESS_BAR_ACTION_H_
diff --git a/components/autofill_assistant/browser/mock_ui_controller.h b/components/autofill_assistant/browser/mock_ui_controller.h
index e7000b68..99abdfea 100644
--- a/components/autofill_assistant/browser/mock_ui_controller.h
+++ b/components/autofill_assistant/browser/mock_ui_controller.h
@@ -47,8 +47,6 @@
                callback));
   MOCK_METHOD0(HideDetails, void());
   MOCK_METHOD1(ShowDetails, void(const DetailsProto& details));
-  MOCK_METHOD2(ShowProgressBar, void(int progress, const std::string& message));
-  MOCK_METHOD0(HideProgressBar, void());
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index a88f53b..fc576fa 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -16,7 +16,6 @@
 #include "components/autofill_assistant/browser/actions/reset_action.h"
 #include "components/autofill_assistant/browser/actions/select_option_action.h"
 #include "components/autofill_assistant/browser/actions/show_details_action.h"
-#include "components/autofill_assistant/browser/actions/show_progress_bar_action.h"
 #include "components/autofill_assistant/browser/actions/stop_action.h"
 #include "components/autofill_assistant/browser/actions/tell_action.h"
 #include "components/autofill_assistant/browser/actions/unsupported_action.h"
@@ -212,10 +211,6 @@
             std::make_unique<GetPaymentInformationAction>(action));
         break;
       }
-      case ActionProto::ActionInfoCase::kShowProgressBar: {
-        actions->emplace_back(std::make_unique<ShowProgressBarAction>(action));
-        break;
-      }
       default:
       case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
         DLOG(ERROR) << "Unknown or unsupported action with action_case="
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index dd75825..56417d8 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -137,14 +137,6 @@
   delegate_->GetWebController()->FocusElement(selectors, std::move(callback));
 }
 
-void ScriptExecutor::ShowProgressBar(int progress, const std::string& message) {
-  delegate_->GetUiController()->ShowProgressBar(progress, message);
-}
-
-void ScriptExecutor::HideProgressBar() {
-  delegate_->GetUiController()->HideProgressBar();
-}
-
 void ScriptExecutor::SetFieldValue(const std::vector<std::string>& selectors,
                                    const std::string& value,
                                    base::OnceCallback<void(bool)> callback) {
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index aff39ca..7b8c0ad 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -97,8 +97,6 @@
   void StopCurrentScript(const std::string& message) override;
   void HideDetails() override;
   void ShowDetails(const DetailsProto& details) override;
-  void ShowProgressBar(int progress, const std::string& message) override;
-  void HideProgressBar() override;
 
  private:
   void OnGetActions(bool result, const std::string& response);
diff --git a/components/autofill_assistant/browser/script_tracker.cc b/components/autofill_assistant/browser/script_tracker.cc
index 3837fd7..f732d45 100644
--- a/components/autofill_assistant/browser/script_tracker.cc
+++ b/components/autofill_assistant/browser/script_tracker.cc
@@ -90,11 +90,17 @@
   ScriptExecutor::RunScriptCallback run_script_callback = base::BindOnce(
       &ScriptTracker::OnScriptRun, weak_ptr_factory_.GetWeakPtr(), script_path,
       std::move(callback));
-  // Postpone running script until finishing preconditions check.
+  // Postpone running script until finishing the current round of preconditions
+  // check.
   if (!batch_element_checker_ && !must_recheck_) {
     executor_->Run(std::move(run_script_callback));
   } else {
     pending_run_script_callback_ = std::move(run_script_callback);
+    // Do not recheck and retry when there is a script pending to run. Note
+    // that |batch_element_checker_| may take a long time to wait on retrying
+    // unsatisfied preconditions check without stop trying.
+    must_recheck_.Reset();
+    batch_element_checker_->StopTrying();
   }
 }
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 70ced1b..8dad0e3 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -199,7 +199,6 @@
     UseCreditCardProto use_card = 28;
     UseAddressProto use_address = 29;
     UploadDomProto upload_dom = 18;
-    ShowProgressBarProto show_progress_bar = 24;
     HighlightElementProto highlight_element = 31;
     ShowDetailsProto show_details = 32;
     ResetProto reset = 34;
@@ -348,20 +347,6 @@
   optional ElementReferenceProto tree_root = 1;
 }
 
-// Shows the progress bar.
-message ShowProgressBarProto {
-  // Specifies whether the progress is done and should be removed.
-  optional bool done = 2;
-
-  // Message to show on the progress bar while loading.
-  optional string message = 3;
-
-  // Value between 0 and 100 indicating the current progress. Values above 100
-  // will be capped to 100, values below 0 will be capped to 0 by the client.
-  // NOTE: Setting |progress| to 100 is an equivalent of setting |done| to true.
-  optional int32 progress = 6;
-}
-
 // Contain all arguments to perform a highlight element action.
 message HighlightElementProto {
   // The element to highlight.
diff --git a/components/autofill_assistant/browser/ui_controller.h b/components/autofill_assistant/browser/ui_controller.h
index 9105c33..2c9f343 100644
--- a/components/autofill_assistant/browser/ui_controller.h
+++ b/components/autofill_assistant/browser/ui_controller.h
@@ -72,12 +72,6 @@
   // Show contextual information.
   virtual void ShowDetails(const DetailsProto& details) = 0;
 
-  // Show the progress bar with |message| and set it at |progress|%.
-  virtual void ShowProgressBar(int progress, const std::string& message) = 0;
-
-  // Hide the progress bar.
-  virtual void HideProgressBar();
-
  protected:
   UiController() = default;
 };
diff --git a/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom b/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
index 741e0c8..f475fcdd 100644
--- a/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
+++ b/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
@@ -59,6 +59,7 @@
              [MinVersion=2] array<ExtensionId>? extension_ids)
       => (PromptAcceptance prompt_acceptance);
 
+  // Actually uninstalls the extensions.
   // Params:
   //  - extension_ids: list of IDs of extensions that will be removed from
   //        Chrome. If there are any invalid IDs, none are removed and false is
diff --git a/components/crash/content/browser/crash_metrics_reporter_android.cc b/components/crash/content/browser/crash_metrics_reporter_android.cc
index 14394505..bf6d85a2 100644
--- a/components/crash/content/browser/crash_metrics_reporter_android.cc
+++ b/components/crash/content/browser/crash_metrics_reporter_android.cc
@@ -147,9 +147,14 @@
   const uint64_t vm_size_kb = info.blink_oom_metrics.current_vm_size_kb;
   const uint64_t blink_usage_kb = info.blink_oom_metrics.current_blink_usage_kb;
 
-  if (info.process_type == content::PROCESS_TYPE_GPU && app_foreground &&
-      android_oom_kill) {
-    ReportCrashCount(ProcessedCrashCounts::kGpuForegroundOom, &reported_counts);
+  if (app_foreground && android_oom_kill) {
+    if (info.process_type == content::PROCESS_TYPE_GPU) {
+      ReportCrashCount(ProcessedCrashCounts::kGpuForegroundOom,
+                       &reported_counts);
+    } else if (info.process_type == content::PROCESS_TYPE_UTILITY) {
+      ReportCrashCount(ProcessedCrashCounts::kUtilityForegroundOom,
+                       &reported_counts);
+    }
   }
 
   if (info.process_type == content::PROCESS_TYPE_RENDERER &&
@@ -263,10 +268,15 @@
   }
 
   if (has_valid_dump) {
-    ReportCrashCount(info.process_type == content::PROCESS_TYPE_GPU
-                         ? ProcessedCrashCounts::kGpuCrashAll
-                         : ProcessedCrashCounts::kRendererCrashAll,
-                     &reported_counts);
+    if (info.process_type == content::PROCESS_TYPE_RENDERER) {
+      ReportCrashCount(ProcessedCrashCounts::kRendererCrashAll,
+                       &reported_counts);
+    } else if (info.process_type == content::PROCESS_TYPE_GPU) {
+      ReportCrashCount(ProcessedCrashCounts::kGpuCrashAll, &reported_counts);
+    } else if (info.process_type == content::PROCESS_TYPE_UTILITY) {
+      ReportCrashCount(ProcessedCrashCounts::kUtilityCrashAll,
+                       &reported_counts);
+    }
   }
 
   if (app_foreground && android_oom_kill &&
diff --git a/components/crash/content/browser/crash_metrics_reporter_android.h b/components/crash/content/browser/crash_metrics_reporter_android.h
index e8311b6..f098ac2 100644
--- a/components/crash/content/browser/crash_metrics_reporter_android.h
+++ b/components/crash/content/browser/crash_metrics_reporter_android.h
@@ -49,7 +49,9 @@
     kRendererForegroundInvisibleWithModerateBindingOom = 14,
     kRendererForegroundVisibleAllocationFailure = 15,
     kRendererAllocationFailureAll = 16,
-    kMaxValue = kRendererAllocationFailureAll
+    kUtilityForegroundOom = 17,
+    kUtilityCrashAll = 18,
+    kMaxValue = kUtilityCrashAll
   };
   using ReportedCrashTypeSet = base::flat_set<ProcessedCrashCounts>;
 
diff --git a/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc b/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
index f10bda7..6ab90298 100644
--- a/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
+++ b/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
@@ -138,6 +138,51 @@
       "GPU.GPUProcessDetailedExitStatus");
 }
 
+TEST_F(CrashMetricsReporterTest, UtilityProcessOOM) {
+  ChildExitObserver::TerminationInfo termination_info;
+  termination_info.process_host_id = 1;
+  termination_info.pid = base::kNullProcessHandle;
+  termination_info.process_type = content::PROCESS_TYPE_UTILITY;
+  termination_info.app_state =
+      base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+  termination_info.normal_termination = false;
+  termination_info.binding_state = base::android::ChildBindingState::STRONG;
+  termination_info.was_killed_intentionally_by_browser = false;
+  termination_info.was_oom_protected_status = true;
+  termination_info.renderer_has_visible_clients = true;
+
+  TestOomCrashProcessing(
+      termination_info,
+      {CrashMetricsReporter::ProcessedCrashCounts::kUtilityForegroundOom},
+      nullptr);
+}
+
+TEST_F(CrashMetricsReporterTest, UtilityProcessAll) {
+  ChildExitObserver::TerminationInfo termination_info;
+  termination_info.process_host_id = 1;
+  termination_info.pid = base::kNullProcessHandle;
+  termination_info.process_type = content::PROCESS_TYPE_UTILITY;
+  termination_info.app_state =
+      base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+  termination_info.normal_termination = false;
+  termination_info.binding_state = base::android::ChildBindingState::STRONG;
+  termination_info.was_killed_intentionally_by_browser = false;
+  termination_info.was_oom_protected_status = true;
+  termination_info.renderer_has_visible_clients = true;
+
+  CrashMetricsReporterObserver crash_dump_observer;
+  CrashMetricsReporter::GetInstance()->AddObserver(&crash_dump_observer);
+
+  CrashMetricsReporter::GetInstance()->CrashDumpProcessed(
+      termination_info,
+      breakpad::CrashDumpManager::CrashDumpStatus::kValidDump);
+  crash_dump_observer.WaitForProcessed();
+
+  EXPECT_EQ(CrashMetricsReporter::ReportedCrashTypeSet(
+                {CrashMetricsReporter::ProcessedCrashCounts::kUtilityCrashAll}),
+            crash_dump_observer.recorded_crash_types());
+}
+
 TEST_F(CrashMetricsReporterTest, RendererSubframeOOM) {
   ChildExitObserver::TerminationInfo termination_info;
   termination_info.process_host_id = 1;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index 68d8f3c..21844b4b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -48,7 +48,6 @@
 #include "net/url_request/url_fetcher_delegate.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if defined(OS_ANDROID)
@@ -208,8 +207,6 @@
 }
 
 void DataReductionProxyConfig::InitializeOnIOThread(
-    const scoped_refptr<net::URLRequestContextGetter>&
-        basic_url_request_context_getter,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     NetworkPropertiesManager* manager) {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index 306cd78..22ab7af 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -37,7 +37,6 @@
 namespace net {
 class ProxyServer;
 class URLRequest;
-class URLRequestContextGetter;
 }  // namespace net
 
 namespace data_reduction_proxy {
@@ -95,13 +94,9 @@
   ~DataReductionProxyConfig() override;
 
   // Performs initialization on the IO thread.
-  // |basic_url_request_context_getter| is the net::URLRequestContextGetter that
-  // disables the use of alternative protocols and proxies.
-  // |url_request_context_getter| is the default net::URLRequestContextGetter
-  // used for making URL requests.
+  // |url_loader_factory| is the network::URLLoaderFactory instance used for
+  // making URL requests. The requests disable the use of proxies.
   void InitializeOnIOThread(
-      const scoped_refptr<net::URLRequestContextGetter>&
-          basic_url_request_context_getter,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       NetworkPropertiesManager* manager);
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
index 3999227..839987a 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -130,21 +130,20 @@
   class TestResponder {
    public:
     void ExecuteCallback(SecureProxyCheckerCallback callback) {
-      callback.Run(response, status.error(), http_response_code);
+      callback.Run(response, net_error, http_response_code);
     }
 
     std::string response;
-    net::URLRequestStatus status;
+    net::Error net_error;
     int http_response_code;
   };
 
-  // TODO(tonikitoo): Get rid of the use of net::URLRequestStatus altogether.
   void CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType connection_type,
       const std::string& response,
       bool is_captive_portal,
       int response_code,
-      net::URLRequestStatus status,
+      net::Error net_error,
       SecureProxyCheckFetchResult expected_fetch_result,
       const std::vector<net::ProxyServer>& expected_proxies_for_http) {
     base::HistogramTester histogram_tester;
@@ -152,7 +151,7 @@
 
     TestResponder responder;
     responder.response = response;
-    responder.status = status;
+    responder.net_error = net_error;
     responder.http_response_code = response_code;
     EXPECT_CALL(*mock_config(), SecureProxyCheck(_))
         .Times(1)
@@ -162,10 +161,9 @@
     test_context_->RunUntilIdle();
     EXPECT_EQ(expected_proxies_for_http, GetConfiguredProxiesForHttp());
 
-    if (!status.is_success() &&
-        status.error() != net::ERR_INTERNET_DISCONNECTED) {
+    if (net_error != net::OK && net_error != net::ERR_INTERNET_DISCONNECTED) {
       histogram_tester.ExpectUniqueSample("DataReductionProxy.ProbeURLNetError",
-                                          std::abs(status.error()), 1);
+                                          std::abs(net_error), 1);
     } else {
       histogram_tester.ExpectTotalCount("DataReductionProxy.ProbeURLNetError",
                                         0);
@@ -285,12 +283,13 @@
 
 TEST_F(DataReductionProxyConfigTest, TestOnNetworkChanged) {
   RecreateContextWithMockConfig();
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://secure_origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
       "insecure_origin.net:80", net::ProxyServer::SCHEME_HTTP);
 
+  int kInvalidResponseCode = -1;
+
   SetProxiesForHttpOnCommandLine({kHttpsProxy, kHttpProxy});
   ResetSettings();
 
@@ -302,35 +301,35 @@
   // remains unrestricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, "OK", false,
-      net::HTTP_OK, kSuccess, SUCCEEDED_PROXY_ALREADY_ENABLED,
+      net::HTTP_OK, net::OK, SUCCEEDED_PROXY_ALREADY_ENABLED,
       {kHttpsProxy, kHttpProxy});
 
   // Connection change triggers a secure proxy check that succeeds but captive
   // portal fails. Proxy is restricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, "OK", true, net::HTTP_OK,
-      kSuccess, SUCCEEDED_PROXY_ALREADY_ENABLED,
+      net::OK, SUCCEEDED_PROXY_ALREADY_ENABLED,
       std::vector<net::ProxyServer>(1, kHttpProxy));
 
   // Connection change triggers a secure proxy check that fails. Proxy is
   // restricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, "Bad", false,
-      net::HTTP_OK, kSuccess, FAILED_PROXY_DISABLED,
+      net::HTTP_OK, net::OK, FAILED_PROXY_DISABLED,
       std::vector<net::ProxyServer>(1, kHttpProxy));
 
   // Connection change triggers a secure proxy check that succeeds. Proxies
   // are unrestricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, "OK", false,
-      net::HTTP_OK, kSuccess, SUCCEEDED_PROXY_ENABLED,
+      net::HTTP_OK, net::OK, SUCCEEDED_PROXY_ENABLED,
       {kHttpsProxy, kHttpProxy});
 
   // Connection change triggers a secure proxy check that fails. Proxy is
   // restricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, "Bad", true,
-      net::HTTP_OK, kSuccess, FAILED_PROXY_DISABLED,
+      net::HTTP_OK, net::OK, FAILED_PROXY_DISABLED,
       std::vector<net::ProxyServer>(1, kHttpProxy));
 
   // Connection change triggers a secure proxy check that fails due to the
@@ -338,23 +337,21 @@
   // unrestricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, std::string(), false,
-      net::URLFetcher::RESPONSE_CODE_INVALID,
-      net::URLRequestStatus(net::URLRequestStatus::FAILED,
-                            net::ERR_INTERNET_DISCONNECTED),
+      kInvalidResponseCode, net::ERR_INTERNET_DISCONNECTED,
       INTERNET_DISCONNECTED, std::vector<net::ProxyServer>(1, kHttpProxy));
 
   // Connection change triggers a secure proxy check that fails. Proxy remains
   // restricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, "Bad", false,
-      net::HTTP_OK, kSuccess, FAILED_PROXY_ALREADY_DISABLED,
+      net::HTTP_OK, net::OK, FAILED_PROXY_ALREADY_DISABLED,
       std::vector<net::ProxyServer>(1, kHttpProxy));
 
   // Connection change triggers a secure proxy check that succeeds. Proxy is
   // unrestricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, "OK", false,
-      net::HTTP_OK, kSuccess, SUCCEEDED_PROXY_ENABLED,
+      net::HTTP_OK, net::OK, SUCCEEDED_PROXY_ENABLED,
       {kHttpsProxy, kHttpProxy});
 
   // Connection change triggers a secure proxy check that fails due to the
@@ -362,23 +359,19 @@
   // unrestricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, std::string(), false,
-      net::URLFetcher::RESPONSE_CODE_INVALID,
-      net::URLRequestStatus(net::URLRequestStatus::FAILED,
-                            net::ERR_INTERNET_DISCONNECTED),
+      kInvalidResponseCode, net::ERR_INTERNET_DISCONNECTED,
       INTERNET_DISCONNECTED, {kHttpsProxy, kHttpProxy});
 
   // Connection change triggers a secure proxy check that fails because of a
   // redirect response, e.g. by a captive portal. Proxy is restricted.
   CheckSecureProxyCheckOnNetworkChange(
       network::mojom::ConnectionType::CONNECTION_WIFI, "Bad", false,
-      net::HTTP_FOUND,
-      net::URLRequestStatus(net::URLRequestStatus::CANCELED, net::ERR_ABORTED),
-      FAILED_PROXY_DISABLED, std::vector<net::ProxyServer>(1, kHttpProxy));
+      net::HTTP_FOUND, net::ERR_ABORTED, FAILED_PROXY_DISABLED,
+      std::vector<net::ProxyServer>(1, kHttpProxy));
 }
 
 // Verifies that the warm up URL is fetched correctly.
 TEST_F(DataReductionProxyConfigTest, WarmupURL) {
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://secure_origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
@@ -426,8 +419,7 @@
     NetworkPropertiesManager network_properties_manager(
         base::DefaultClock::GetInstance(), test_context_->pref_service(),
         test_context_->task_runner());
-    config.InitializeOnIOThread(test_context_->request_context_getter(),
-                                test_context_->url_loader_factory(),
+    config.InitializeOnIOThread(test_context_->url_loader_factory(),
                                 &network_properties_manager);
     RunUntilIdle();
 
@@ -865,7 +857,6 @@
 
 TEST_F(DataReductionProxyConfigTest, HandleWarmupFetcherResponse) {
   base::HistogramTester histogram_tester;
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
@@ -1015,7 +1006,6 @@
 // as failed when the warmup fetched callback returns an invalid proxy.
 TEST_F(DataReductionProxyConfigTest,
        HandleWarmupFetcherResponse_InvalidProxyServer) {
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
@@ -1043,7 +1033,6 @@
 // as failed when the warmup fetched callback returns a direct proxy.
 TEST_F(DataReductionProxyConfigTest,
        HandleWarmupFetcherResponse_DirectProxyServer) {
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
@@ -1071,7 +1060,6 @@
   constexpr size_t kMaxWarmupURLFetchAttempts = 3;
 
   base::HistogramTester histogram_tester;
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
@@ -1210,7 +1198,6 @@
 // Tests the behavior when warmup URL fetcher times out.
 TEST_F(DataReductionProxyConfigTest, HandleWarmupFetcherTimeout) {
   base::HistogramTester histogram_tester;
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
@@ -1268,7 +1255,6 @@
   constexpr size_t kMaxWarmupURLFetchAttempts = 3;
 
   base::HistogramTester histogram_tester;
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index e1c378e..47b1dd45 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -39,58 +39,6 @@
 
 namespace data_reduction_proxy {
 
-// A |net::URLRequestContextGetter| which uses only vanilla HTTP/HTTPS for
-// performing requests. This is used by the secure proxy check to prevent the
-// use of SPDY and QUIC which may be used by the primary request contexts.
-class BasicHTTPURLRequestContextGetter : public net::URLRequestContextGetter {
- public:
-  BasicHTTPURLRequestContextGetter(
-      const std::string& user_agent,
-      const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner);
-
-  // Overridden from net::URLRequestContextGetter:
-  net::URLRequestContext* GetURLRequestContext() override;
-  scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
-      const override;
-
- private:
-  ~BasicHTTPURLRequestContextGetter() override;
-
-  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
-  std::unique_ptr<net::HttpUserAgentSettings> user_agent_settings_;
-  std::unique_ptr<net::URLRequestContext> url_request_context_;
-
-  DISALLOW_COPY_AND_ASSIGN(BasicHTTPURLRequestContextGetter);
-};
-
-BasicHTTPURLRequestContextGetter::BasicHTTPURLRequestContextGetter(
-    const std::string& user_agent,
-    const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner)
-    : network_task_runner_(network_task_runner),
-      user_agent_settings_(
-          new net::StaticHttpUserAgentSettings(std::string(), user_agent)) {
-}
-
-net::URLRequestContext*
-BasicHTTPURLRequestContextGetter::GetURLRequestContext() {
-  if (!url_request_context_) {
-    net::URLRequestContextBuilder builder;
-    builder.set_proxy_resolution_service(net::ProxyResolutionService::CreateDirect());
-    builder.SetSpdyAndQuicEnabled(false, false);
-    url_request_context_ = builder.Build();
-  }
-
-  return url_request_context_.get();
-}
-
-scoped_refptr<base::SingleThreadTaskRunner>
-BasicHTTPURLRequestContextGetter::GetNetworkTaskRunner() const {
-  return network_task_runner_;
-}
-
-BasicHTTPURLRequestContextGetter::~BasicHTTPURLRequestContextGetter() {
-}
-
 DataReductionProxyIOData::DataReductionProxyIOData(
     Client client,
     PrefService* prefs,
@@ -107,8 +55,6 @@
       data_use_observer_(nullptr),
       enabled_(enabled),
       url_request_context_getter_(nullptr),
-      basic_url_request_context_getter_(
-          new BasicHTTPURLRequestContextGetter(user_agent, io_task_runner)),
       channel_(channel),
       effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
       weak_factory_(this) {
@@ -212,8 +158,7 @@
   auto url_loader_factory = network::SharedURLLoaderFactory::Create(
       std::move(url_loader_factory_info_));
 
-  config_->InitializeOnIOThread(basic_url_request_context_getter_.get(),
-                                url_loader_factory,
+  config_->InitializeOnIOThread(url_loader_factory,
                                 network_properties_manager_.get());
   bypass_stats_->InitializeOnIOThread();
   proxy_delegate_->InitializeOnIOThread(this);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
index 7e7cde8..4564a4cb 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -291,10 +291,6 @@
   // The net::URLRequestContextGetter used for making URL requests.
   net::URLRequestContextGetter* url_request_context_getter_;
 
-  // A net::URLRequestContextGetter used for making secure proxy checks. It
-  // does not use alternate protocols.
-  scoped_refptr<net::URLRequestContextGetter> basic_url_request_context_getter_;
-
   // The network::SharedURLLoaderFactoryInfo used for making URL requests.
   std::unique_ptr<network::SharedURLLoaderFactoryInfo> url_loader_factory_info_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
index 8b21faa..2e6d3b84 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
@@ -110,14 +110,6 @@
           false /* enabled */, std::string() /* user_agent */,
           std::string() /* channel */));
 
-  // Check that the SimpleURLRequestContextGetter uses vanilla HTTP.
-  net::URLRequestContext* request_context =
-      io_data->basic_url_request_context_getter_->GetURLRequestContext();
-  const net::HttpNetworkSession::Params* http_params =
-      request_context->GetNetworkSessionParams();
-  EXPECT_FALSE(http_params->enable_http2);
-  EXPECT_FALSE(http_params->enable_quic);
-
   // Check that io_data creates an interceptor. Such an interceptor is
   // thoroughly tested by DataReductionProxyInterceptoTest.
   std::unique_ptr<net::URLRequestInterceptor> interceptor =
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
index f3adcf4..d49586e1 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -518,7 +518,6 @@
           pref_service.get(), task_runner, std::move(config),
           std::move(request_options), std::move(configurator),
           test_network_connection_tracker, true /* enabled */));
-  io_data->SetSimpleURLRequestContextGetter(request_context_getter);
 
   if (use_test_config_client_) {
     test_context_flags |= USE_TEST_CONFIG_CLIENT;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
index 1f26741..1495d884 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -247,11 +247,6 @@
     proxy_delegate_ = std::move(proxy_delegate);
   }
 
-  void SetSimpleURLRequestContextGetter(
-      const scoped_refptr<net::URLRequestContextGetter> context_getter) {
-    basic_url_request_context_getter_ = context_getter;
-  }
-
   base::WeakPtr<DataReductionProxyIOData> GetWeakPtr() {
     return weak_factory_.GetWeakPtr();
   }
diff --git a/components/download/internal/common/download_db_cache.cc b/components/download/internal/common/download_db_cache.cc
index 784a381..3778fc44 100644
--- a/components/download/internal/common/download_db_cache.cc
+++ b/components/download/internal/common/download_db_cache.cc
@@ -7,7 +7,6 @@
 #include "components/download/database/download_db.h"
 #include "components/download/database/download_db_conversions.h"
 #include "components/download/database/download_db_entry.h"
-#include "components/download/database/in_progress/download_entry.h"
 #include "components/download/public/common/download_features.h"
 #include "components/download/public/common/download_stats.h"
 #include "components/download/public/common/download_utils.h"
@@ -96,12 +95,12 @@
   return UkmInfo(DownloadSource::UNKNOWN, GetUniqueDownloadId());
 }
 
-void CleanUpInProgressEntry(DownloadDBEntry& entry) {
-  if (!entry.download_info)
+void CleanUpInProgressEntry(DownloadDBEntry* entry) {
+  if (!entry->download_info)
     return;
 
   base::Optional<InProgressInfo>& in_progress_info =
-      entry.download_info->in_progress_info;
+      entry->download_info->in_progress_info;
   if (!in_progress_info)
     return;
 
@@ -129,15 +128,14 @@
 
 DownloadDBCache::~DownloadDBCache() = default;
 
-void DownloadDBCache::Initialize(const std::vector<DownloadEntry>& entries,
-                                 InitializeCallback callback) {
+void DownloadDBCache::Initialize(InitializeCallback callback) {
   // TODO(qinmin): migrate all the data from InProgressCache into
   // |download_db_|.
   if (!initialized_) {
     RecordInProgressDBCount(kInitializationCount);
-    download_db_->Initialize(base::BindOnce(
-        &DownloadDBCache::OnDownloadDBInitialized, weak_factory_.GetWeakPtr(),
-        entries, std::move(callback)));
+    download_db_->Initialize(
+        base::BindOnce(&DownloadDBCache::OnDownloadDBInitialized,
+                       weak_factory_.GetWeakPtr(), std::move(callback)));
     return;
   }
 
@@ -230,12 +228,10 @@
 }
 
 void DownloadDBCache::OnDownloadDBInitialized(
-    const std::vector<DownloadEntry>& entries,
     InitializeCallback callback,
     bool success) {
   if (success) {
     RecordInProgressDBCount(kInitializationSucceededCount);
-    MigrateFromInProgressCache(entries);
     download_db_->LoadEntries(
         base::BindOnce(&DownloadDBCache::OnDownloadDBEntriesLoaded,
                        weak_factory_.GetWeakPtr(), std::move(callback)));
@@ -253,8 +249,14 @@
   initialized_ = success;
   RecordInProgressDBCount(success ? kLoadSucceededCount : kLoadFailedCount);
   for (auto& entry : *entries) {
-    CleanUpInProgressEntry(entry);
-    entries_[entry.download_info->guid] = entry;
+    // If the entry is from the metadata cache migration, just remove it from
+    // DB as the data is not being cleaned up properly.
+    if (entry.download_info->id < 0) {
+      RemoveEntry(entry.download_info->guid);
+    } else {
+      CleanUpInProgressEntry(&entry);
+      entries_[entry.download_info->guid] = entry;
+    }
   }
   std::move(callback).Run(success, std::move(entries));
 }
@@ -264,25 +266,4 @@
   update_timer_.SetTaskRunner(task_runner);
 }
 
-void DownloadDBCache::MigrateFromInProgressCache(
-    const std::vector<DownloadEntry>& entries) {
-  if (entries.empty())
-    return;
-  RecordInProgressDBCount(kCacheMigrationCount);
-  std::vector<DownloadDBEntry> db_entries;
-  for (const auto& entry : entries) {
-    DCHECK(entries_.find(entry.guid) == entries_.end());
-    db_entries.emplace_back(
-        DownloadDBConversions::DownloadDBEntryFromDownloadEntry(entry));
-  }
-  download_db_->AddOrReplaceEntries(
-      db_entries, base::BindOnce(&DownloadDBCache::OnInProgressCacheMigrated,
-                                 weak_factory_.GetWeakPtr()));
-}
-
-void DownloadDBCache::OnInProgressCacheMigrated(bool success) {
-  RecordInProgressDBCount(success ? kCacheMigrationSucceededCount
-                                  : kCacheMigrationFailedCount);
-}
-
 }  //  namespace download
diff --git a/components/download/internal/common/download_db_cache.h b/components/download/internal/common/download_db_cache.h
index 36b3b38..f0aafcf 100644
--- a/components/download/internal/common/download_db_cache.h
+++ b/components/download/internal/common/download_db_cache.h
@@ -21,7 +21,6 @@
 
 class DownloadDB;
 struct DownloadDBEntry;
-struct DownloadEntry;
 
 // Responsible for caching the metadata of all in progress downloads.
 class COMPONENTS_DOWNLOAD_EXPORT DownloadDBCache
@@ -33,8 +32,7 @@
   using InitializeCallback =
       base::OnceCallback<void(bool /* success */,
                               std::unique_ptr<std::vector<DownloadDBEntry>>)>;
-  void Initialize(const std::vector<DownloadEntry>& entries,
-                  InitializeCallback callback);
+  void Initialize(InitializeCallback callback);
 
   base::Optional<DownloadDBEntry> RetrieveEntry(const std::string& guid);
   void AddOrReplaceEntry(const DownloadDBEntry& entry);
@@ -54,9 +52,7 @@
   void OnDownloadRemoved(DownloadItem* download) override;
 
   // Called when the |download_db_| is initialized.
-  void OnDownloadDBInitialized(const std::vector<DownloadEntry>& entries,
-                               InitializeCallback callback,
-                               bool success);
+  void OnDownloadDBInitialized(InitializeCallback callback, bool success);
 
   // Called when all the download db entries are loaded.
   void OnDownloadDBEntriesLoaded(
@@ -64,12 +60,6 @@
       bool success,
       std::unique_ptr<std::vector<DownloadDBEntry>> entries);
 
-  // Migrate DownloadEntry from in-progress cache.
-  void MigrateFromInProgressCache(const std::vector<DownloadEntry>& entries);
-
-  // Called when InProgressCache is migrated.
-  void OnInProgressCacheMigrated(bool success);
-
   void SetTimerTaskRunnerForTesting(
       scoped_refptr<base::SequencedTaskRunner> task_runner);
 
diff --git a/components/download/internal/common/download_db_cache_unittest.cc b/components/download/internal/common/download_db_cache_unittest.cc
index f656586d..34aa5c2 100644
--- a/components/download/internal/common/download_db_cache_unittest.cc
+++ b/components/download/internal/common/download_db_cache_unittest.cc
@@ -34,6 +34,8 @@
   DownloadDBEntry entry;
   DownloadInfo download_info;
   download_info.guid = base::GenerateGUID();
+  static int id = 0;
+  download_info.id = ++id;
   entry.download_info = download_info;
   return entry;
 }
@@ -44,44 +46,6 @@
          "," + guid;
 }
 
-std::unique_ptr<DownloadItem> CreateDownloadItem(const std::string& guid) {
-  std::unique_ptr<MockDownloadItem> item(
-      new ::testing::NiceMock<MockDownloadItem>());
-  ON_CALL(*item, GetGuid()).WillByDefault(ReturnRefOfCopy(guid));
-  ON_CALL(*item, GetUrlChain())
-      .WillByDefault(ReturnRefOfCopy(std::vector<GURL>()));
-  ON_CALL(*item, GetReferrerUrl()).WillByDefault(ReturnRefOfCopy(GURL()));
-  ON_CALL(*item, GetSiteUrl()).WillByDefault(ReturnRefOfCopy(GURL()));
-  ON_CALL(*item, GetTabUrl()).WillByDefault(ReturnRefOfCopy(GURL()));
-  ON_CALL(*item, GetTabReferrerUrl()).WillByDefault(ReturnRefOfCopy(GURL()));
-  ON_CALL(*item, GetETag()).WillByDefault(ReturnRefOfCopy(std::string("etag")));
-  ON_CALL(*item, GetLastModifiedTime())
-      .WillByDefault(ReturnRefOfCopy(std::string("last-modified")));
-  ON_CALL(*item, GetMimeType()).WillByDefault(Return("text/html"));
-  ON_CALL(*item, GetOriginalMimeType()).WillByDefault(Return("text/html"));
-  ON_CALL(*item, GetTotalBytes()).WillByDefault(Return(1000));
-  ON_CALL(*item, GetFullPath())
-      .WillByDefault(ReturnRefOfCopy(base::FilePath()));
-  ON_CALL(*item, GetTargetFilePath())
-      .WillByDefault(ReturnRefOfCopy(base::FilePath()));
-  ON_CALL(*item, GetReceivedBytes()).WillByDefault(Return(1000));
-  ON_CALL(*item, GetStartTime()).WillByDefault(Return(base::Time()));
-  ON_CALL(*item, GetEndTime()).WillByDefault(Return(base::Time()));
-  ON_CALL(*item, GetReceivedSlices())
-      .WillByDefault(
-          ReturnRefOfCopy(std::vector<DownloadItem::ReceivedSlice>()));
-  ON_CALL(*item, GetHash()).WillByDefault(ReturnRefOfCopy(std::string("hash")));
-  ON_CALL(*item, IsTransient()).WillByDefault(Return(false));
-  ON_CALL(*item, GetDangerType())
-      .WillByDefault(Return(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
-  ON_CALL(*item, GetLastReason())
-      .WillByDefault(Return(DOWNLOAD_INTERRUPT_REASON_NONE));
-  ON_CALL(*item, GetState()).WillByDefault(Return(DownloadItem::IN_PROGRESS));
-  ON_CALL(*item, IsPaused()).WillByDefault(Return(false));
-  ON_CALL(*item, GetBytesWasted()).WillByDefault(Return(10));
-  return std::move(item);
-}
-
 }  // namespace
 
 class DownloadDBCacheTest : public testing::Test {
@@ -144,7 +108,6 @@
   CreateDBCache();
   std::vector<DownloadDBEntry> loaded_entries;
   db_cache_->Initialize(
-      std::vector<DownloadEntry>(),
       base::BindOnce(&DownloadDBCacheTest::InitCallback, base::Unretained(this),
                      &loaded_entries));
   db_->InitCallback(true);
@@ -165,7 +128,6 @@
   CreateDBCache();
   std::vector<DownloadDBEntry> loaded_entries;
   db_cache_->Initialize(
-      std::vector<DownloadEntry>(),
       base::BindOnce(&DownloadDBCacheTest::InitCallback, base::Unretained(this),
                      &loaded_entries));
   db_->InitCallback(true);
@@ -191,7 +153,6 @@
   CreateDBCache();
   std::vector<DownloadDBEntry> loaded_entries;
   db_cache_->Initialize(
-      std::vector<DownloadEntry>(),
       base::BindOnce(&DownloadDBCacheTest::InitCallback, base::Unretained(this),
                      &loaded_entries));
   db_->InitCallback(true);
@@ -232,7 +193,6 @@
   CreateDBCache();
   std::vector<DownloadDBEntry> loaded_entries;
   db_cache_->Initialize(
-      std::vector<DownloadEntry>(),
       base::BindOnce(&DownloadDBCacheTest::InitCallback, base::Unretained(this),
                      &loaded_entries));
   db_->InitCallback(true);
@@ -262,7 +222,6 @@
   CreateDBCache();
   std::vector<DownloadDBEntry> loaded_entries;
   db_cache_->Initialize(
-      std::vector<DownloadEntry>(),
       base::BindOnce(&DownloadDBCacheTest::InitCallback, base::Unretained(this),
                      &loaded_entries));
   db_->InitCallback(true);
@@ -292,7 +251,6 @@
   CreateDBCache();
   std::vector<DownloadDBEntry> loaded_entries;
   db_cache_->Initialize(
-      std::vector<DownloadEntry>(),
       base::BindOnce(&DownloadDBCacheTest::InitCallback, base::Unretained(this),
                      &loaded_entries));
   db_->InitCallback(true);
@@ -318,51 +276,4 @@
   ASSERT_EQ(remaining, loaded_entries[0]);
 }
 
-// Tests that Migrating a DownloadEntry from InProgressCache should store
-// a DownloadDBEntry in the DownloadDB.
-TEST_F(DownloadDBCacheTest, MigrateFromInProgressCache) {
-  CreateDBCache();
-  std::vector<DownloadEntry> download_entries;
-  download_entries.emplace_back(
-      "guid1", "foo.com", DownloadSource::DRAG_AND_DROP, true,
-      DownloadUrlParameters::RequestHeadersType(), 100);
-  download_entries.emplace_back(
-      "guid2", "foobar.com", DownloadSource::UNKNOWN, false,
-      DownloadUrlParameters::RequestHeadersType(), 200);
-
-  std::vector<DownloadDBEntry> loaded_entries;
-  db_cache_->Initialize(
-      download_entries,
-      base::BindOnce(&DownloadDBCacheTest::InitCallback, base::Unretained(this),
-                     &loaded_entries));
-  db_->InitCallback(true);
-  db_->UpdateCallback(true);
-  db_->LoadCallback(true);
-  ASSERT_FALSE(loaded_entries.empty());
-
-  std::unique_ptr<DownloadItem> item = CreateDownloadItem("guid1");
-  OnDownloadUpdated(item.get());
-
-  ASSERT_EQ(task_runner_->GetPendingTaskCount(), 1u);
-  ASSERT_GT(task_runner_->NextPendingTaskDelay(), base::TimeDelta());
-  task_runner_->FastForwardUntilNoTasksRemain();
-  db_->UpdateCallback(true);
-
-  DownloadDBEntry entry1 = CreateDownloadDBEntryFromItem(
-      *item, UkmInfo(DownloadSource::DRAG_AND_DROP, 100), true,
-      DownloadUrlParameters::RequestHeadersType());
-  DownloadDBEntry entry2 =
-      DownloadDBConversions::DownloadDBEntryFromDownloadEntry(
-          download_entries[1]);
-
-  DownloadDB* download_db = GetDownloadDB();
-  download_db->LoadEntries(base::BindOnce(&DownloadDBCacheTest::InitCallback,
-                                          base::Unretained(this),
-                                          &loaded_entries));
-  db_->LoadCallback(true);
-  ASSERT_EQ(loaded_entries.size(), 2u);
-  ASSERT_TRUE(entry1 == loaded_entries[0]);
-  ASSERT_TRUE(entry2 == loaded_entries[1]);
-}
-
 }  // namespace download
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc
index 1666c2d..51e5c904 100644
--- a/components/download/internal/common/in_progress_download_manager.cc
+++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -36,15 +36,14 @@
   if (!entry.download_info)
     return nullptr;
 
-  // DownloadDBEntry migrated from in-progress cache doesn't have Ids.
-  if (!entry.download_info->id)
+  // DownloadDBEntry migrated from in-progress cache has negative Ids.
+  if (entry.download_info->id < 0)
     return nullptr;
 
   base::Optional<InProgressInfo> in_progress_info =
       entry.download_info->in_progress_info;
   if (!in_progress_info)
     return nullptr;
-
   return std::make_unique<DownloadItemImpl>(
       delegate, entry.download_info->guid, entry.download_info->id,
       in_progress_info->current_path, in_progress_info->target_path,
@@ -494,7 +493,6 @@
                 DownloadNamespace::NAMESPACE_BROWSER_DOWNLOAD,
                 metadata_cache_dir));
   download_db_cache_->Initialize(
-      download_metadata_cache_->GetAllEntries(),
       base::BindOnce(&InProgressDownloadManager::OnInitialized,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/components/embedder_support/android/delegate/web_contents_delegate_android.cc b/components/embedder_support/android/delegate/web_contents_delegate_android.cc
index cfc076b..40067ec 100644
--- a/components/embedder_support/android/delegate/web_contents_delegate_android.cc
+++ b/components/embedder_support/android/delegate/web_contents_delegate_android.cc
@@ -304,7 +304,7 @@
   Java_WebContentsDelegateAndroid_onUpdateUrl(env, obj, java_url);
 }
 
-void WebContentsDelegateAndroid::HandleKeyboardEvent(
+bool WebContentsDelegateAndroid::HandleKeyboardEvent(
     WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
   const JavaRef<jobject>& key_event = event.os_event;
@@ -312,9 +312,10 @@
     JNIEnv* env = AttachCurrentThread();
     ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
     if (obj.is_null())
-      return;
+      return true;
     Java_WebContentsDelegateAndroid_handleKeyboardEvent(env, obj, key_event);
   }
+  return true;
 }
 
 bool WebContentsDelegateAndroid::TakeFocus(WebContents* source, bool reverse) {
diff --git a/components/embedder_support/android/delegate/web_contents_delegate_android.h b/components/embedder_support/android/delegate/web_contents_delegate_android.h
index 8a48fab..fbb08794 100644
--- a/components/embedder_support/android/delegate/web_contents_delegate_android.h
+++ b/components/embedder_support/android/delegate/web_contents_delegate_android.h
@@ -97,7 +97,7 @@
                               int32_t line_no,
                               const base::string16& source_id) override;
   void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   bool TakeFocus(content::WebContents* source, bool reverse) override;
diff --git a/components/feed/content/feed_offline_host.cc b/components/feed/content/feed_offline_host.cc
index e3bebb6..ff8616f1 100644
--- a/components/feed/content/feed_offline_host.cc
+++ b/components/feed/content/feed_offline_host.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/feed/core/feed_scheduler_host.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
 #include "components/offline_pages/core/prefetch/prefetch_service.h"
 #include "url/gurl.h"
 
@@ -53,12 +54,26 @@
   void OnGetPages(std::string feed_url,
                   const std::vector<OfflinePageItem>& pages) {
     if (!pages.empty()) {
-      OfflinePageItem newest =
+      OfflinePageItem best =
           *std::max_element(pages.begin(), pages.end(), [](auto lhs, auto rhs) {
-            return lhs.creation_time < rhs.creation_time;
+            // Prefer prefetched articles over any other. They are typically of
+            // higher quality.
+            bool leftIsPrefetch = lhs.client_id.name_space ==
+                                  offline_pages::kSuggestedArticlesNamespace;
+            bool rightIsPrefetch = rhs.client_id.name_space ==
+                                   offline_pages::kSuggestedArticlesNamespace;
+            if (leftIsPrefetch != rightIsPrefetch) {
+              // Only one is prefetch, if that is |rhs|, then they're in the
+              // correct order.
+              return rightIsPrefetch;
+            } else {
+              // Newer articles are also better, but not as important as being
+              // prefetched.
+              return lhs.creation_time < rhs.creation_time;
+            }
           });
       urls_.push_back(feed_url);
-      on_each_result_.Run(std::move(feed_url), newest.offline_id);
+      on_each_result_.Run(feed_url, best.offline_id);
     }
   }
 
diff --git a/components/feed/content/feed_offline_host_unittest.cc b/components/feed/content/feed_offline_host_unittest.cc
index 5a7da1e5..6d03c13 100644
--- a/components/feed/content/feed_offline_host_unittest.cc
+++ b/components/feed/content/feed_offline_host_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/feed/core/content_metadata.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
 #include "components/offline_pages/core/offline_page_item.h"
 #include "components/offline_pages/core/offline_page_types.h"
 #include "components/offline_pages/core/prefetch/stub_prefetch_service.h"
@@ -52,12 +53,14 @@
   void AddOfflinedPage(const std::string& url,
                        const std::string& original_url,
                        int64_t offline_id,
-                       base::Time creation_time) {
+                       base::Time creation_time,
+                       std::string name_space) {
     OfflinePageItem item;
     item.url = GURL(url);
     item.original_url = GURL(original_url);
     item.offline_id = offline_id;
     item.creation_time = creation_time;
+    item.client_id = offline_pages::ClientId(name_space, "");
     url_to_offline_page_item_.emplace(url, item);
     if (!original_url.empty()) {
       url_to_offline_page_item_.emplace(original_url, item);
@@ -65,7 +68,7 @@
   }
 
   void AddOfflinedPage(const std::string& url, int64_t offline_id) {
-    AddOfflinedPage(url, "", offline_id, base::Time());
+    AddOfflinedPage(url, "", offline_id, base::Time(), "");
   }
 
   MOCK_METHOD1(AddObserver, void(Observer*));
@@ -233,7 +236,7 @@
 }
 
 TEST_F(FeedOfflineHostTest, GetOfflineIdOriginalUrl) {
-  offline_page_model()->AddOfflinedPage(kUrl1, kUrl2, 4, base::Time());
+  offline_page_model()->AddOfflinedPage(kUrl1, kUrl2, 4, base::Time(), "");
 
   std::vector<std::string> actual;
   host()->GetOfflineStatus({kUrl2}, base::BindOnce(&CopyStatus, &actual));
@@ -246,7 +249,7 @@
 }
 
 TEST_F(FeedOfflineHostTest, GetOfflineIdRequestUrl) {
-  offline_page_model()->AddOfflinedPage(kUrl2, kUrl1, 4, base::Time());
+  offline_page_model()->AddOfflinedPage(kUrl2, kUrl1, 4, base::Time(), "");
 
   std::vector<std::string> actual;
   host()->GetOfflineStatus({kUrl2}, base::BindOnce(&CopyStatus, &actual));
@@ -259,9 +262,9 @@
 }
 
 TEST_F(FeedOfflineHostTest, GetOfflineIdNewer) {
-  offline_page_model()->AddOfflinedPage(kUrl1, "", 4, base::Time());
+  offline_page_model()->AddOfflinedPage(kUrl1, "", 4, base::Time(), "");
   offline_page_model()->AddOfflinedPage(
-      kUrl1, "", 5, base::Time() + base::TimeDelta::FromHours(1));
+      kUrl1, "", 5, base::Time() + base::TimeDelta::FromHours(1), "");
 
   std::vector<std::string> actual;
   host()->GetOfflineStatus({kUrl1}, base::BindOnce(&CopyStatus, &actual));
@@ -272,6 +275,23 @@
   EXPECT_EQ(host()->GetOfflineId(kUrl1).value(), 5);
 }
 
+TEST_F(FeedOfflineHostTest, GetOfflineIdNamespace) {
+  // Even though id of 5 is newer, id of 4 will be chosen because it has the
+  // preferred namespace.
+  offline_page_model()->AddOfflinedPage(
+      kUrl1, "", 4, base::Time(), offline_pages::kSuggestedArticlesNamespace);
+  offline_page_model()->AddOfflinedPage(
+      kUrl1, "", 5, base::Time() + base::TimeDelta::FromHours(1), "");
+
+  std::vector<std::string> actual;
+  host()->GetOfflineStatus({kUrl1}, base::BindOnce(&CopyStatus, &actual));
+  RunUntilIdle();
+
+  EXPECT_EQ(1U, actual.size());
+  EXPECT_EQ(kUrl1, actual[0]);
+  EXPECT_EQ(host()->GetOfflineId(kUrl1).value(), 4);
+}
+
 TEST_F(FeedOfflineHostTest, GetCurrentArticleSuggestions) {
   std::vector<PrefetchSuggestion> actual;
   host()->GetCurrentArticleSuggestions(
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc
index 2c02354e..6d796a7 100644
--- a/components/guest_view/browser/guest_view_base.cc
+++ b/components/guest_view/browser/guest_view_base.cc
@@ -632,15 +632,15 @@
   embedder_web_contents()->GetDelegate()->ContentsZoomChange(zoom_in);
 }
 
-void GuestViewBase::HandleKeyboardEvent(
+bool GuestViewBase::HandleKeyboardEvent(
     WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
   if (!attached())
-    return;
+    return false;
 
   // Send the keyboard events back to the embedder to reprocess them.
-  embedder_web_contents()->GetDelegate()->
-      HandleKeyboardEvent(embedder_web_contents(), event);
+  return embedder_web_contents()->GetDelegate()->HandleKeyboardEvent(
+      embedder_web_contents(), event);
 }
 
 void GuestViewBase::LoadingStateChanged(WebContents* source,
diff --git a/components/guest_view/browser/guest_view_base.h b/components/guest_view/browser/guest_view_base.h
index 0fb2510..64410d68 100644
--- a/components/guest_view/browser/guest_view_base.h
+++ b/components/guest_view/browser/guest_view_base.h
@@ -220,7 +220,7 @@
   virtual void OnRenderFrameHostDeleted(int process_id, int routing_id);
 
   // WebContentsDelegate implementation.
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   bool PreHandleGestureEvent(content::WebContents* source,
diff --git a/components/image_fetcher/core/BUILD.gn b/components/image_fetcher/core/BUILD.gn
index aaf75d16..374aa67 100644
--- a/components/image_fetcher/core/BUILD.gn
+++ b/components/image_fetcher/core/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "cached_image_fetcher.cc",
     "cached_image_fetcher.h",
+    "cached_image_fetcher_service.cc",
+    "cached_image_fetcher_service.h",
     "image_data_fetcher.cc",
     "image_data_fetcher.h",
     "image_decoder.h",
@@ -22,6 +24,7 @@
   public_deps = [
     "//base",
     "//components/data_use_measurement/core",
+    "//components/keyed_service/core",
     "//net",
     "//services/network/public/cpp",
     "//ui/gfx",
diff --git a/components/image_fetcher/core/DEPS b/components/image_fetcher/core/DEPS
index ab5ae2fd..030417a3 100644
--- a/components/image_fetcher/core/DEPS
+++ b/components/image_fetcher/core/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/keyed_service",
   "+components/leveldb_proto",
   "+components/prefs",
   "+ui/gfx/codec",
diff --git a/components/image_fetcher/core/cached_image_fetcher.cc b/components/image_fetcher/core/cached_image_fetcher.cc
index 437b066..9811a518 100644
--- a/components/image_fetcher/core/cached_image_fetcher.cc
+++ b/components/image_fetcher/core/cached_image_fetcher.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/timer/elapsed_timer.h"
 #include "components/image_fetcher/core/image_decoder.h"
 #include "components/image_fetcher/core/request_metadata.h"
 #include "components/image_fetcher/core/storage/image_cache.h"
@@ -19,6 +21,13 @@
 
 namespace {
 
+// Tracks the various forms of timing events.
+enum class LoadTimeType {
+  kLoadFromCache = 0,
+  kLoadFromNetwork = 1,
+  kLoadFromNetworkAfterCacheHit = 2
+};
+
 void DataCallbackIfPresent(ImageDataFetcherCallback data_callback,
                            const std::string& image_data,
                            const image_fetcher::RequestMetadata& metadata) {
@@ -51,13 +60,36 @@
       std::vector<gfx::PNGCodec::Comment>(), dest);
 }
 
+void ReportLoadTime(LoadTimeType type, base::Time start_time) {
+  base::TimeDelta time_delta = base::Time::Now() - start_time;
+  switch (type) {
+    case LoadTimeType::kLoadFromCache:
+      UMA_HISTOGRAM_TIMES("CachedImageFetcher.ImageLoadFromCacheTime",
+                          time_delta);
+      break;
+    case LoadTimeType::kLoadFromNetwork:
+      UMA_HISTOGRAM_TIMES("CachedImageFetcher.ImageLoadFromNetworkTime",
+                          time_delta);
+      break;
+    case LoadTimeType::kLoadFromNetworkAfterCacheHit:
+      UMA_HISTOGRAM_TIMES(
+          "CachedImageFetcher.ImageLoadFromNetworkAfterCacheHit", time_delta);
+      break;
+  }
+}
+
 }  // namespace
 
+// static
+void CachedImageFetcher::ReportEvent(CachedImageFetcherEvent event) {
+  UMA_HISTOGRAM_ENUMERATION("CachedImageFetcher.Events", event);
+}
+
 CachedImageFetcher::CachedImageFetcher(
     std::unique_ptr<ImageFetcher> image_fetcher,
-    std::unique_ptr<ImageCache> image_cache)
+    scoped_refptr<ImageCache> image_cache)
     : image_fetcher_(std::move(image_fetcher)),
-      image_cache_(std::move(image_cache)),
+      image_cache_(image_cache),
       weak_ptr_factory_(this) {
   DCHECK(image_fetcher_);
   DCHECK(image_cache_);
@@ -94,12 +126,15 @@
   image_cache_->LoadImage(
       image_url.spec(),
       base::BindOnce(&CachedImageFetcher::OnImageFetchedFromCache,
-                     weak_ptr_factory_.GetWeakPtr(), id, image_url,
-                     std::move(data_callback), std::move(image_callback),
-                     traffic_annotation));
+                     weak_ptr_factory_.GetWeakPtr(), base::Time::Now(), id,
+                     image_url, std::move(data_callback),
+                     std::move(image_callback), traffic_annotation));
+
+  ReportEvent(CachedImageFetcherEvent::kImageRequest);
 }
 
 void CachedImageFetcher::OnImageFetchedFromCache(
+    base::Time start_time,
     const std::string& id,
     const GURL& image_url,
     ImageDataFetcherCallback data_callback,
@@ -108,21 +143,28 @@
     std::string image_data) {
   if (image_data.empty()) {
     // Fetching from the DB failed, start a network fetch.
-    FetchImageFromNetwork(id, image_url, std::move(data_callback),
-                          std::move(image_callback), traffic_annotation);
+    FetchImageFromNetwork(/* cache_hit */ false, start_time, id, image_url,
+                          std::move(data_callback), std::move(image_callback),
+                          traffic_annotation);
+
+    ReportEvent(CachedImageFetcherEvent::kCacheMiss);
   } else {
+    DataCallbackIfPresent(std::move(data_callback), image_data,
+                          RequestMetadata());
     // TODO(wylieb): On Android, do this in-process.
     GetImageDecoder()->DecodeImage(
         image_data, desired_image_frame_size_,
         base::BindRepeating(&CachedImageFetcher::OnImageDecodedFromCache,
-                            weak_ptr_factory_.GetWeakPtr(), id, image_url,
-                            base::Passed(std::move(data_callback)),
+                            weak_ptr_factory_.GetWeakPtr(), start_time, id,
+                            image_url, base::Passed(std::move(data_callback)),
                             base::Passed(std::move(image_callback)),
                             traffic_annotation, image_data));
+    ReportEvent(CachedImageFetcherEvent::kCacheHit);
   }
 }
 
 void CachedImageFetcher::OnImageDecodedFromCache(
+    base::Time start_time,
     const std::string& id,
     const GURL& image_url,
     ImageDataFetcherCallback data_callback,
@@ -131,19 +173,22 @@
     const std::string& image_data,
     const gfx::Image& image) {
   if (image.IsEmpty()) {
-    FetchImageFromNetwork(id, image_url, std::move(data_callback),
-                          std::move(image_callback), traffic_annotation);
-    // Decoding error, delete image from cache.
-    image_cache_->DeleteImage(image_url.spec());
+    // Upon failure, fetch from the network.
+    FetchImageFromNetwork(/* cache_hit */ true, start_time, id, image_url,
+                          std::move(data_callback), std::move(image_callback),
+                          traffic_annotation);
+
+    ReportEvent(CachedImageFetcherEvent::kCacheDecodingError);
   } else {
-    DataCallbackIfPresent(std::move(data_callback), image_data,
-                          RequestMetadata());
     ImageCallbackIfPresent(std::move(image_callback), id, image,
                            RequestMetadata());
+    ReportLoadTime(LoadTimeType::kLoadFromCache, start_time);
   }
 }
 
 void CachedImageFetcher::FetchImageFromNetwork(
+    bool cache_hit,
+    base::Time start_time,
     const std::string& id,
     const GURL& image_url,
     ImageDataFetcherCallback data_callback,
@@ -153,16 +198,20 @@
   // the image cache, and the image will be returned to the caller.
   image_fetcher_->FetchImageAndData(
       id, image_url,
-      base::BindOnce(&CachedImageFetcher::OnImageDataFetchedFromNetwork,
+      base::BindOnce(&CachedImageFetcher::DecodeDataForCaching,
                      weak_ptr_factory_.GetWeakPtr(), std::move(data_callback),
                      image_url),
       base::BindOnce(&CachedImageFetcher::OnImageFetchedFromNetwork,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(image_callback)),
+                     weak_ptr_factory_.GetWeakPtr(), cache_hit, start_time,
+                     std::move(image_callback), image_url),
       traffic_annotation);
 }
 
 void CachedImageFetcher::OnImageFetchedFromNetwork(
+    bool cache_hit,
+    base::Time start_time,
     ImageFetcherCallback image_callback,
+    const GURL& image_url,
     const std::string& id,
     const gfx::Image& image,
     const RequestMetadata& request_metadata) {
@@ -170,9 +219,19 @@
   // caller.
   ImageCallbackIfPresent(std::move(image_callback), id, image,
                          request_metadata);
+
+  // Report failure if the image is empty.
+  if (image.IsEmpty()) {
+    ReportEvent(CachedImageFetcherEvent::kFailure);
+  }
+
+  // Report to different histograms depending upon if there was a cache hit.
+  ReportLoadTime(cache_hit ? LoadTimeType::kLoadFromNetworkAfterCacheHit
+                           : LoadTimeType::kLoadFromNetwork,
+                 start_time);
 }
 
-void CachedImageFetcher::OnImageDataFetchedFromNetwork(
+void CachedImageFetcher::DecodeDataForCaching(
     ImageDataFetcherCallback data_callback,
     const GURL& image_url,
     const std::string& image_data,
@@ -180,14 +239,19 @@
   DataCallbackIfPresent(std::move(data_callback), image_data, request_metadata);
   GetImageDecoder()->DecodeImage(
       image_data, /* Decoding for cache shouldn't specify size */ gfx::Size(),
-      base::BindRepeating(&CachedImageFetcher::OnImageDecodedFromNetwork,
+      base::BindRepeating(&CachedImageFetcher::EncodeDataAndCache,
                           weak_ptr_factory_.GetWeakPtr(), image_url));
 }
 
-void CachedImageFetcher::OnImageDecodedFromNetwork(const GURL& image_url,
-                                                   const gfx::Image& image) {
+void CachedImageFetcher::EncodeDataAndCache(const GURL& image_url,
+                                            const gfx::Image& image) {
   std::vector<unsigned char> encoded_data;
-  if (!EncodeSkBitmapToPNG(*image.ToSkBitmap(), &encoded_data)) {
+  // If the image is empty, or there's a problem encoding the image, don't save
+  // it.
+  if (image.IsEmpty() ||
+      !EncodeSkBitmapToPNG(*image.ToSkBitmap(), &encoded_data)) {
+    ReportEvent(CachedImageFetcherEvent::kTranscodingError);
+    image_cache_->DeleteImage(image_url.spec());
     return;
   }
 
diff --git a/components/image_fetcher/core/cached_image_fetcher.h b/components/image_fetcher/core/cached_image_fetcher.h
index 1b3ebe0..1ce56c40 100644
--- a/components/image_fetcher/core/cached_image_fetcher.h
+++ b/components/image_fetcher/core/cached_image_fetcher.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "components/image_fetcher/core/image_decoder.h"
@@ -29,14 +30,31 @@
 class ImageFetcher;
 struct RequestMetadata;
 
+// Enum for the result of the fetch, reported through UMA Present in enums.xml
+// as CachedImageFetcherEvent. New values should be added at the end and things
+// should not be renumbered.
+enum class CachedImageFetcherEvent {
+  kImageRequest = 0,
+  kCacheHit = 1,
+  kCacheMiss = 2,
+  kCacheDecodingError = 3,
+  kTranscodingError = 4,
+  kFailure = 5,
+  kMaxValue = kFailure,
+};
+
 // TODO(wylieb): Transcode the image once it's downloaded.
 // TODO(wylieb): Consider creating a struct to encapsulate the request.
 // CachedImageFetcher takes care of fetching images from the network and caching
 // them.
 class CachedImageFetcher : public ImageFetcher {
  public:
+  // Report CachedImageFetcher events, used by sub-systems to report events (as
+  // well as CachedImageFetcher).
+  static void ReportEvent(CachedImageFetcherEvent event);
+
   CachedImageFetcher(std::unique_ptr<ImageFetcher> image_fetcher,
-                     std::unique_ptr<ImageCache> image_cache);
+                     scoped_refptr<ImageCache> image_cache);
   ~CachedImageFetcher() override;
 
   // ImageFetcher:
@@ -55,6 +73,7 @@
  private:
   // Cache
   void OnImageFetchedFromCache(
+      base::Time start_time,
       const std::string& id,
       const GURL& image_url,
       ImageDataFetcherCallback image_data_callback,
@@ -62,6 +81,7 @@
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       std::string image_data);
   void OnImageDecodedFromCache(
+      base::Time start_time,
       const std::string& id,
       const GURL& image_url,
       ImageDataFetcherCallback image_data_callback,
@@ -72,25 +92,31 @@
 
   // Network
   void FetchImageFromNetwork(
+      bool cache_hit,
+      base::Time start_time,
       const std::string& id,
       const GURL& image_url,
       ImageDataFetcherCallback image_data_callback,
       ImageFetcherCallback image_callback,
       const net::NetworkTrafficAnnotationTag& traffic_annotation);
-  void OnImageFetchedFromNetwork(ImageFetcherCallback image_callback,
+  void OnImageFetchedFromNetwork(bool cache_hit,
+                                 base::Time start_time,
+                                 ImageFetcherCallback image_callback,
+                                 const GURL& image_url,
                                  const std::string& id,
                                  const gfx::Image& image,
                                  const RequestMetadata& request_metadata);
-  void OnImageDataFetchedFromNetwork(
-      ImageDataFetcherCallback image_data_callback,
-      const GURL& image_url,
-      const std::string& image_data,
-      const RequestMetadata& request_metadata);
-  void OnImageDecodedFromNetwork(const GURL& image_url,
-                                 const gfx::Image& image);
+  void DecodeDataForCaching(ImageDataFetcherCallback image_data_callback,
+                            const GURL& image_url,
+                            const std::string& image_data,
+                            const RequestMetadata& request_metadata);
+  void EncodeDataAndCache(const GURL& image_url, const gfx::Image& image);
 
+  // ImageFetcher has some state that's stored, so it's owned by
+  // CachedImageFetcher.
   std::unique_ptr<ImageFetcher> image_fetcher_;
-  std::unique_ptr<ImageCache> image_cache_;
+
+  scoped_refptr<ImageCache> image_cache_;
 
   gfx::Size desired_image_frame_size_;
 
diff --git a/components/image_fetcher/core/cached_image_fetcher_service.cc b/components/image_fetcher/core/cached_image_fetcher_service.cc
new file mode 100644
index 0000000..af3d9d7
--- /dev/null
+++ b/components/image_fetcher/core/cached_image_fetcher_service.cc
@@ -0,0 +1,37 @@
+// Copyright 2018 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/image_fetcher/core/cached_image_fetcher_service.h"
+
+#include <utility>
+
+#include "base/time/clock.h"
+#include "components/image_fetcher/core/cached_image_fetcher.h"
+#include "components/image_fetcher/core/image_decoder.h"
+#include "components/image_fetcher/core/image_fetcher_impl.h"
+#include "components/image_fetcher/core/storage/image_cache.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace image_fetcher {
+
+CachedImageFetcherService::CachedImageFetcherService(
+    CreateImageDecoderCallback create_image_decoder_fn,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    scoped_refptr<ImageCache> image_cache)
+    : create_image_decoder_callback_(create_image_decoder_fn),
+      url_loader_factory_(url_loader_factory),
+      image_cache_(image_cache) {}
+
+CachedImageFetcherService::~CachedImageFetcherService() = default;
+
+// TODO(wylieb): Store CachedImageFetcher once it's stateless.
+std::unique_ptr<CachedImageFetcher>
+CachedImageFetcherService::CreateCachedImageFetcher() {
+  return std::make_unique<CachedImageFetcher>(
+      std::make_unique<ImageFetcherImpl>(create_image_decoder_callback_.Run(),
+                                         url_loader_factory_),
+      image_cache_);
+}
+
+}  // namespace image_fetcher
diff --git a/components/image_fetcher/core/cached_image_fetcher_service.h b/components/image_fetcher/core/cached_image_fetcher_service.h
new file mode 100644
index 0000000..e337ebdf
--- /dev/null
+++ b/components/image_fetcher/core/cached_image_fetcher_service.h
@@ -0,0 +1,52 @@
+// Copyright 2018 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_IMAGE_FETCHER_CORE_CACHED_IMAGE_FETCHER_SERVICE_H_
+#define COMPONENTS_IMAGE_FETCHER_CORE_CACHED_IMAGE_FETCHER_SERVICE_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace image_fetcher {
+
+class CachedImageFetcher;
+class ImageCache;
+class ImageDecoder;
+
+using CreateImageDecoderCallback =
+    base::RepeatingCallback<std::unique_ptr<ImageDecoder>()>;
+
+// Keyed service responsible for managing the lifetime of CachedImageFetcher.
+// Persists the ImageCache, and uses it to create instances of the
+// CachedImageFethcer.
+class CachedImageFetcherService : public KeyedService {
+ public:
+  explicit CachedImageFetcherService(
+      CreateImageDecoderCallback create_image_decoder_callback,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      scoped_refptr<ImageCache> image_cache);
+  ~CachedImageFetcherService() override;
+
+  // Create an instance of CachedImageFetcher based on the ImageCache.
+  std::unique_ptr<CachedImageFetcher> CreateCachedImageFetcher();
+
+ private:
+  CreateImageDecoderCallback create_image_decoder_callback_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  scoped_refptr<ImageCache> image_cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(CachedImageFetcherService);
+};
+
+}  // namespace image_fetcher
+
+#endif  // COMPONENTS_IMAGE_FETCHER_CORE_CACHED_IMAGE_FETCHER_SERVICE_H_
diff --git a/components/image_fetcher/core/cached_image_fetcher_unittest.cc b/components/image_fetcher/core/cached_image_fetcher_unittest.cc
index 76bc141..683b4be0 100644
--- a/components/image_fetcher/core/cached_image_fetcher_unittest.cc
+++ b/components/image_fetcher/core/cached_image_fetcher_unittest.cc
@@ -12,6 +12,8 @@
 
 #include "base/bind.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_clock.h"
@@ -46,6 +48,15 @@
 const GURL kImageUrl = GURL("http://gstatic.img.com/foo.jpg");
 constexpr char kImageData[] = "data";
 
+const char kCachedImageFetcherEventHistogramName[] =
+    "CachedImageFetcher.Events";
+const char kCacheLoadHistogramName[] =
+    "CachedImageFetcher.ImageLoadFromCacheTime";
+const char kNetworkLoadHistogramName[] =
+    "CachedImageFetcher.ImageLoadFromNetworkTime";
+const char kNetworkLoadAfterCacheHitHistogram[] =
+    "CachedImageFetcher.ImageLoadFromNetworkAfterCacheHit";
+
 }  // namespace
 
 class ComponentizedCachedImageFetcherTest : public testing::Test {
@@ -72,10 +83,9 @@
         data_dir_.GetPath(), base::SequencedTaskRunnerHandle::Get());
 
     ImageCache::RegisterProfilePrefs(test_prefs_.registry());
-    auto image_cache = std::make_unique<ImageCache>(
+    image_cache_ = base::MakeRefCounted<ImageCache>(
         std::move(data_store), std::move(metadata_store), &test_prefs_, &clock_,
         base::SequencedTaskRunnerHandle::Get());
-    image_cache_ = image_cache.get();
 
     image_cache_->SaveImage(kImageUrl.spec(), kImageData);
     RunUntilIdle();
@@ -93,7 +103,7 @@
     cached_image_fetcher_ = std::make_unique<CachedImageFetcher>(
         std::make_unique<image_fetcher::ImageFetcherImpl>(std::move(decoder),
                                                           shared_factory_),
-        std::move(image_cache));
+        image_cache_);
 
     RunUntilIdle();
   }
@@ -103,11 +113,12 @@
   CachedImageFetcher* cached_image_fetcher() {
     return cached_image_fetcher_.get();
   }
-  ImageCache* image_cache() { return image_cache_; }
+  scoped_refptr<ImageCache> image_cache() { return image_cache_; }
   FakeImageDecoder* image_decoder() { return fake_image_decoder_; }
   network::TestURLLoaderFactory* test_url_loader_factory() {
     return &test_url_loader_factory_;
   }
+  base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
   MOCK_METHOD1(OnImageLoaded, void(std::string));
 
@@ -117,7 +128,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
   FakeImageDecoder* fake_image_decoder_;
 
-  ImageCache* image_cache_;
+  scoped_refptr<ImageCache> image_cache_;
   base::SimpleTestClock clock_;
   TestingPrefServiceSimple test_prefs_;
   base::ScopedTempDir data_dir_;
@@ -125,6 +136,7 @@
   std::map<std::string, CachedImageMetadataProto> metadata_store_;
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::HistogramTester histogram_tester_;
 
   DISALLOW_COPY_AND_ASSIGN(ComponentizedCachedImageFetcherTest);
 };
@@ -158,6 +170,13 @@
       TRAFFIC_ANNOTATION_FOR_TESTS);
 
   RunUntilIdle();
+
+  histogram_tester().ExpectTotalCount(kCacheLoadHistogramName, 1);
+  histogram_tester().ExpectBucketCount(kCachedImageFetcherEventHistogramName,
+                                       CachedImageFetcherEvent::kImageRequest,
+                                       1);
+  histogram_tester().ExpectBucketCount(kCachedImageFetcherEventHistogramName,
+                                       CachedImageFetcherEvent::kCacheHit, 1);
 }
 
 TEST_F(ComponentizedCachedImageFetcherTest, FetchImagePopulatesCache) {
@@ -175,6 +194,14 @@
         TRAFFIC_ANNOTATION_FOR_TESTS);
 
     RunUntilIdle();
+
+    histogram_tester().ExpectTotalCount(kNetworkLoadHistogramName, 1);
+    histogram_tester().ExpectBucketCount(kCachedImageFetcherEventHistogramName,
+                                         CachedImageFetcherEvent::kImageRequest,
+                                         1);
+    histogram_tester().ExpectBucketCount(kCachedImageFetcherEventHistogramName,
+                                         CachedImageFetcherEvent::kCacheMiss,
+                                         1);
   }
   // Make sure the image data is in the database.
   {
@@ -202,30 +229,26 @@
   }
 }
 
-TEST_F(ComponentizedCachedImageFetcherTest, DecodingErrorWillDeleteCache) {
+TEST_F(ComponentizedCachedImageFetcherTest, FetchDecodingErrorDeletesCache) {
   // Save the image in the database.
   image_cache()->SaveImage(kImageUrl.spec(), kImageData);
   RunUntilIdle();
-  {
-    // Set decoding always error.
-    image_decoder()->SetDecodingValid(false);
 
-    base::MockCallback<ImageFetcherCallback> image_callback;
-    cached_image_fetcher()->FetchImage(kImageUrl.spec(), kImageUrl,
-                                       image_callback.Get(),
-                                       TRAFFIC_ANNOTATION_FOR_TESTS);
+  image_decoder()->SetDecodingValid(false);
+  base::MockCallback<ImageDataFetcherCallback> data_callback;
+  base::MockCallback<ImageFetcherCallback> image_callback;
+  EXPECT_CALL(data_callback, Run(NonEmptyString(), _));
+  EXPECT_CALL(image_callback, Run(kImageUrl.spec(), EmptyImage(), _));
+  test_url_loader_factory()->AddResponse(kImageUrl.spec(), kImageData);
+  cached_image_fetcher()->FetchImageAndData(
+      kImageUrl.spec(), kImageUrl, data_callback.Get(), image_callback.Get(),
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+  RunUntilIdle();
 
-    RunUntilIdle();
-  }
-  // Make sure the image data was deleted from database.
-  {
-    EXPECT_CALL(*this, OnImageLoaded(std::string()));
-    image_cache()->LoadImage(
-        kImageUrl.spec(),
-        base::BindOnce(&ComponentizedCachedImageFetcherTest::OnImageLoaded,
-                       base::Unretained(this)));
-    RunUntilIdle();
-  }
+  histogram_tester().ExpectTotalCount(kNetworkLoadAfterCacheHitHistogram, 1);
+  histogram_tester().ExpectBucketCount(
+      kCachedImageFetcherEventHistogramName,
+      CachedImageFetcherEvent::kTranscodingError, 1);
 }
 
 }  // namespace image_fetcher
diff --git a/components/image_fetcher/core/storage/image_cache.h b/components/image_fetcher/core/storage/image_cache.h
index de06d32..431fb1c 100644
--- a/components/image_fetcher/core/storage/image_cache.h
+++ b/components/image_fetcher/core/storage/image_cache.h
@@ -7,7 +7,9 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "components/image_fetcher/core/storage/image_store_types.h"
 
@@ -21,12 +23,13 @@
 
 namespace image_fetcher {
 
+class ImageCache;
 class ImageDataStore;
 class ImageMetadataStore;
 
 // Persist image meta/data via the given implementations of ImageDataStore and
 // ImageMetadataStore.
-class ImageCache {
+class ImageCache : public base::RefCounted<ImageCache> {
  public:
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
@@ -35,7 +38,6 @@
              PrefService* pref_service,
              base::Clock* clock,
              scoped_refptr<base::SequencedTaskRunner> task_runner);
-  ~ImageCache();
 
   // Adds or updates the image data for the |url|. If the class hasn't been
   // initialized yet, the call is queued.
@@ -50,6 +52,8 @@
 
  private:
   friend class ImageCacheTest;
+  friend class base::RefCounted<ImageCache>;
+  ~ImageCache();
 
   // Queue or start |request| depending if the cache is initialized.
   void QueueOrStartRequest(base::OnceClosure request);
diff --git a/components/image_fetcher/core/storage/image_cache_unittest.cc b/components/image_fetcher/core/storage/image_cache_unittest.cc
index 7e5fbd7f..289c3ce5 100644
--- a/components/image_fetcher/core/storage/image_cache_unittest.cc
+++ b/components/image_fetcher/core/storage/image_cache_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "components/image_fetcher/core/storage/image_cache.h"
 
+#include <map>
+#include <utility>
+
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_task_environment.h"
@@ -49,7 +52,7 @@
     data_store_ = data_store.get();
 
     ImageCache::RegisterProfilePrefs(test_prefs_.registry());
-    image_cache_ = std::make_unique<ImageCache>(
+    image_cache_ = base::MakeRefCounted<ImageCache>(
         std::move(data_store), std::move(metadata_store), &test_prefs_, &clock_,
         base::SequencedTaskRunnerHandle::Get());
   }
@@ -116,7 +119,7 @@
   MOCK_METHOD1(DataCallback, void(std::string));
 
  private:
-  std::unique_ptr<ImageCache> image_cache_;
+  scoped_refptr<ImageCache> image_cache_;
   ImageMetadataStoreLevelDB* metadata_store_;
   ImageDataStoreDisk* data_store_;
   base::SimpleTestClock clock_;
diff --git a/components/metrics/call_stack_profile_metrics_provider.cc b/components/metrics/call_stack_profile_metrics_provider.cc
index 36a7090..3334a8c 100644
--- a/components/metrics/call_stack_profile_metrics_provider.cc
+++ b/components/metrics/call_stack_profile_metrics_provider.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
 #include "base/time/time.h"
 #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
 
@@ -115,31 +116,32 @@
   // Returns true if collection is enabled for a given profile based on its
   // |profile_start_time|. The |lock_| must be held prior to calling this
   // method.
-  bool IsCollectionEnabledForProfile(base::TimeTicks profile_start_time) const;
+  bool IsCollectionEnabledForProfile(base::TimeTicks profile_start_time) const
+      EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   // Whether there is spare capacity to store an additional profile.
   // The |lock_| must be held prior to calling this method.
-  bool HasSpareCapacity() const;
+  bool HasSpareCapacity() const EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   mutable base::Lock lock_;
 
   // If true, profiles provided to MaybeCollect*Profile should be collected.
   // Otherwise they will be ignored.
-  bool collection_enabled_;
+  bool collection_enabled_ GUARDED_BY(lock_);
 
   // The last time collection was disabled. Used to determine if collection was
   // disabled at any point since a profile was started.
-  base::TimeTicks last_collection_disable_time_;
+  base::TimeTicks last_collection_disable_time_ GUARDED_BY(lock_);
 
   // The last time collection was enabled. Used to determine if collection was
   // enabled at any point since a profile was started.
-  base::TimeTicks last_collection_enable_time_;
+  base::TimeTicks last_collection_enable_time_ GUARDED_BY(lock_);
 
   // The set of completed unserialized profiles that should be reported.
-  std::vector<SampledProfile> unserialized_profiles_;
+  std::vector<SampledProfile> unserialized_profiles_ GUARDED_BY(lock_);
 
   // The set of completed serialized profiles that should be reported.
-  std::vector<std::string> serialized_profiles_;
+  std::vector<std::string> serialized_profiles_ GUARDED_BY(lock_);
 
   DISALLOW_COPY_AND_ASSIGN(PendingProfiles);
 };
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
index 7690cd7..ea14865 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
@@ -254,7 +254,7 @@
   for (auto& suggestion : suggestions) {
     urls.push_back(SuggestionToPrefetchURL(std::move(suggestion)));
   }
-  AddCandidatePrefetchURLs(kNTPSuggestionsNamespace, urls);
+  AddCandidatePrefetchURLs(kSuggestedArticlesNamespace, urls);
 }
 
 void PrefetchDispatcherImpl::StopBackgroundTask() {
diff --git a/components/omnibox/browser/omnibox_client.h b/components/omnibox/browser/omnibox_client.h
index 018d609..50e7f6a 100644
--- a/components/omnibox/browser/omnibox_client.h
+++ b/components/omnibox/browser/omnibox_client.h
@@ -170,6 +170,9 @@
   // Discards the state for all pending and transient navigations.
   virtual void DiscardNonCommittedNavigations() {}
 
+  // Opens and shows a new incognito browser window.
+  virtual void NewIncognitoWindow() {}
+
   // Presents translation prompt for current tab web contents.
   virtual void PromptPageTranslation() {}
 };
diff --git a/components/omnibox/browser/omnibox_pedal_implementations.cc b/components/omnibox/browser/omnibox_pedal_implementations.cc
index f78276b..a650fc6 100644
--- a/components/omnibox/browser/omnibox_pedal_implementations.cc
+++ b/components/omnibox/browser/omnibox_pedal_implementations.cc
@@ -149,6 +149,26 @@
 
 // =============================================================================
 
+class OmniboxPedalLaunchIncognito : public OmniboxPedalCommon {
+ public:
+  OmniboxPedalLaunchIncognito()
+      : OmniboxPedalCommon(
+            LabelStrings(
+                IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_HINT,
+                IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_HINT_SHORT,
+                IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_SUGGESTION_CONTENTS),
+            GURL(),
+            {
+                "what is incognito", "what's incognito mode",
+            }) {}
+
+  void Execute(ExecutionContext& context) const override {
+    context.client_.NewIncognitoWindow();
+  }
+};
+
+// =============================================================================
+
 class OmniboxPedalTranslate : public OmniboxPedalCommon {
  public:
   OmniboxPedalTranslate()
@@ -182,6 +202,7 @@
   add(new OmniboxPedalManagePasswords());
   add(new OmniboxPedalChangeHomePage());
   add(new OmniboxPedalUpdateCreditCard());
+  add(new OmniboxPedalLaunchIncognito());
   add(new OmniboxPedalTranslate());
   return pedals;
 }
diff --git a/components/omnibox_strings.grdp b/components/omnibox_strings.grdp
index 63156fec..42bd0a13 100644
--- a/components/omnibox_strings.grdp
+++ b/components/omnibox_strings.grdp
@@ -128,6 +128,16 @@
     Update credit card autofill info in Chrome settings
   </message>
 
+  <message name="IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_HINT" desc="The button text contents to suggest pedal action, launch incognito.">
+    Open Incognito Window
+  </message>
+  <message name="IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_HINT_SHORT" desc="The short one-word button text contents to suggest pedal action, launch incognito.">
+    Open
+  </message>
+  <message name="IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_SUGGESTION_CONTENTS" desc="The suggestion content text to suggest pedal action, launch incognito.">
+    Open new Chrome incognito window
+  </message>
+
   <message name="IDS_OMNIBOX_PEDAL_TRANSLATE_HINT" desc="The button text contents to suggest pedal action, translate.">
     Translate Page
   </message>
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index b3619af8..d9263e9c 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -311,11 +311,6 @@
   return true;
 }
 
-blink::WebLayerTreeView*
-WebViewPlugin::WebViewHelper::InitializeLayerTreeView() {
-  return nullptr;
-}
-
 void WebViewPlugin::WebViewHelper::DidInvalidateRect(const WebRect& rect) {
   if (plugin_->container_)
     plugin_->container_->InvalidateRect(rect);
diff --git a/components/plugins/renderer/webview_plugin.h b/components/plugins/renderer/webview_plugin.h
index 652eea6..470e115 100644
--- a/components/plugins/renderer/webview_plugin.h
+++ b/components/plugins/renderer/webview_plugin.h
@@ -172,10 +172,9 @@
                        blink::WebDragOperationsMask,
                        const SkBitmap&,
                        const blink::WebPoint&) override;
-    // TODO(ojan): Remove this override and have this class use a non-null
-    // layerTreeView.
+    // TODO(ojan): Remove this override and have this class give a
+    // LayerTreeView to the WebWidget. Or stop making this a WebView?
     bool AllowsBrokenNullLayerTreeView() const override;
-    blink::WebLayerTreeView* InitializeLayerTreeView() override;
     void DidInvalidateRect(const blink::WebRect&) override;
     void DidChangeCursor(const blink::WebCursorInfo& cursor) override;
     void ScheduleAnimation() override;
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 71731e9..c888622 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -638,9 +638,6 @@
    public:
     // blink::WebWidgetClient implementation.
     bool AllowsBrokenNullLayerTreeView() const override { return true; }
-    blink::WebLayerTreeView* InitializeLayerTreeView() override {
-      return nullptr;
-    }
   };
 
   HeaderAndFooterClient frame_client;
@@ -730,10 +727,9 @@
  private:
   // blink::WebViewClient:
   void DidStopLoading() override;
-  // TODO(ojan): Remove this override and have this class use a non-null
-  // layerTreeView.
+  // TODO(ojan): Remove this override and have this class give a LayerTreeView
+  // to the WebWidget.
   bool AllowsBrokenNullLayerTreeView() const override;
-  blink::WebLayerTreeView* InitializeLayerTreeView() override;
   WebWidgetClient* WidgetClient() override { return this; }
 
   // blink::WebLocalFrameClient:
@@ -891,11 +887,6 @@
   return true;
 }
 
-blink::WebLayerTreeView*
-PrepareFrameAndViewForPrint::InitializeLayerTreeView() {
-  return nullptr;
-}
-
 void PrepareFrameAndViewForPrint::DidStopLoading() {
   DCHECK(!on_ready_.is_null());
   // Don't call callback here, because it can delete |this| and WebView that is
diff --git a/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc b/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
index 7ce24ab..36b2c05 100644
--- a/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
+++ b/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
@@ -331,6 +331,8 @@
     return safe_browsing::SB_THREAT_TYPE_URL_PHISHING;
   if (url == kChromeUISafeBrowsingMatchUnwantedUrl)
     return safe_browsing::SB_THREAT_TYPE_URL_UNWANTED;
+  if (url == kChromeUISafeBrowsingMatchBillingUrl)
+    return safe_browsing::SB_THREAT_TYPE_BILLING;
 
   return safe_browsing::SB_THREAT_TYPE_SAFE;
 }
diff --git a/components/safe_browsing/web_ui/constants.cc b/components/safe_browsing/web_ui/constants.cc
index 2518ac80..08f3363 100644
--- a/components/safe_browsing/web_ui/constants.cc
+++ b/components/safe_browsing/web_ui/constants.cc
@@ -10,6 +10,8 @@
 const char kChromeUISafeBrowsingHost[] = "safe-browsing";
 const char kSbUnderConstruction[] =
     "The safe browsing page is under construction.";
+const char kChromeUISafeBrowsingMatchBillingUrl[] =
+    "chrome://safe-browsing/match?type=billing";
 const char kChromeUISafeBrowsingMatchMalwareUrl[] =
     "chrome://safe-browsing/match?type=malware";
 const char kChromeUISafeBrowsingMatchPhishingUrl[] =
diff --git a/components/safe_browsing/web_ui/constants.h b/components/safe_browsing/web_ui/constants.h
index fc1cf9be..6b54de01 100644
--- a/components/safe_browsing/web_ui/constants.h
+++ b/components/safe_browsing/web_ui/constants.h
@@ -10,6 +10,7 @@
 extern const char kChromeUISafeBrowsingURL[];
 extern const char kChromeUISafeBrowsingHost[];
 extern const char kSbUnderConstruction[];
+extern const char kChromeUISafeBrowsingMatchBillingUrl[];
 extern const char kChromeUISafeBrowsingMatchMalwareUrl[];
 extern const char kChromeUISafeBrowsingMatchPhishingUrl[];
 extern const char kChromeUISafeBrowsingMatchUnwantedUrl[];
diff --git a/components/security_interstitials/core/safe_browsing_loud_error_ui.cc b/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
index 8b7d2d9..a795a923 100644
--- a/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
+++ b/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
@@ -311,7 +311,6 @@
     base::DictionaryValue* load_time_data) {
   load_time_data->SetBoolean("phishing", false);
   load_time_data->SetBoolean("overridable", true);
-  load_time_data->SetBoolean("hide_primary_button", false);
 
   load_time_data->SetString("heading",
                             l10n_util::GetStringUTF16(IDS_BILLING_HEADING));
diff --git a/components/sync/engine_impl/model_type_registry.cc b/components/sync/engine_impl/model_type_registry.cc
index 3f192ce..0389c538 100644
--- a/components/sync/engine_impl/model_type_registry.cc
+++ b/components/sync/engine_impl/model_type_registry.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -126,7 +127,9 @@
   if (do_migration) {
     // TODO(crbug.com/658002): Store a pref before attempting migration
     // indicating that it was attempted so we can avoid failure loops.
-    if (uss_migrator_.Run(type, user_share_, worker_ptr)) {
+    int migrated_entity_count = 0;
+    if (uss_migrator_.Run(type, user_share_, worker_ptr,
+                          &migrated_entity_count)) {
       // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
       UMA_HISTOGRAM_ENUMERATION("Sync.USSMigrationSuccess",
                                 ModelTypeToHistogramInt(type),
@@ -141,6 +144,12 @@
                                 ModelTypeToHistogramInt(type),
                                 static_cast<int>(MODEL_TYPE_COUNT));
     }
+
+    // Note that a partial failure may still contribute to the counts histogram.
+    base::UmaHistogramCounts100000(
+        std::string("Sync.USSMigrationEntityCount.") +
+            ModelTypeToHistogramSuffix(type),
+        migrated_entity_count);
   }
 
   // We want to check that we haven't accidentally enabled both the non-blocking
diff --git a/components/sync/engine_impl/model_type_registry_unittest.cc b/components/sync/engine_impl/model_type_registry_unittest.cc
index f2aa337..ba2d712 100644
--- a/components/sync/engine_impl/model_type_registry_unittest.cc
+++ b/components/sync/engine_impl/model_type_registry_unittest.cc
@@ -92,7 +92,8 @@
  private:
   bool MigrateDirectory(ModelType type,
                         UserShare* user_share,
-                        ModelTypeWorker* worker) {
+                        ModelTypeWorker* worker,
+                        int* migrated_entity_count) {
     migration_attempted_ = true;
     return true;
   }
diff --git a/components/sync/engine_impl/uss_migrator.cc b/components/sync/engine_impl/uss_migrator.cc
index 9e1e69b..057268f 100644
--- a/components/sync/engine_impl/uss_migrator.cc
+++ b/components/sync/engine_impl/uss_migrator.cc
@@ -84,18 +84,11 @@
   }
 }
 
-}  // namespace
-
-bool MigrateDirectoryData(ModelType type,
-                          UserShare* user_share,
-                          ModelTypeWorker* worker) {
-  return MigrateDirectoryDataWithBatchSize(type, user_share, worker, 64);
-}
-
 bool MigrateDirectoryDataWithBatchSize(ModelType type,
+                                       int batch_size,
                                        UserShare* user_share,
                                        ModelTypeWorker* worker,
-                                       int batch_size) {
+                                       int* cumulative_migrated_entity_count) {
   DCHECK_NE(PASSWORDS, type);
   ReadTransaction trans(FROM_HERE, user_share);
 
@@ -143,6 +136,7 @@
       }
     }
 
+    *cumulative_migrated_entity_count += entity_ptrs.size();
     worker->ProcessGetUpdatesResponse(progress, context, entity_ptrs, nullptr);
   }
 
@@ -150,4 +144,25 @@
   return true;
 }
 
+}  // namespace
+
+bool MigrateDirectoryData(ModelType type,
+                          UserShare* user_share,
+                          ModelTypeWorker* worker,
+                          int* migrated_entity_count) {
+  *migrated_entity_count = 0;
+  return MigrateDirectoryDataWithBatchSize(type, 64, user_share, worker,
+                                           migrated_entity_count);
+}
+
+bool MigrateDirectoryDataWithBatchSizeForTesting(
+    ModelType type,
+    int batch_size,
+    UserShare* user_share,
+    ModelTypeWorker* worker,
+    int* cumulative_migrated_entity_count) {
+  return MigrateDirectoryDataWithBatchSize(type, batch_size, user_share, worker,
+                                           cumulative_migrated_entity_count);
+}
+
 }  // namespace syncer
diff --git a/components/sync/engine_impl/uss_migrator.h b/components/sync/engine_impl/uss_migrator.h
index 84a6fd2d..ee9e81e 100644
--- a/components/sync/engine_impl/uss_migrator.h
+++ b/components/sync/engine_impl/uss_migrator.h
@@ -13,21 +13,25 @@
 class ModelTypeWorker;
 struct UserShare;
 
-using UssMigrator =
-    base::Callback<bool(ModelType, UserShare*, ModelTypeWorker*)>;
+using UssMigrator = base::RepeatingCallback<
+    bool(ModelType, UserShare*, ModelTypeWorker*, int*)>;
 
 // Pulls all the data for |type| out of the directory and sends it to |worker|
 // as the result of an initial GetUpdates. Returns whether migration succeeded.
+// |user_share|, |worker| and |migrated_entity_count| must not be null.
 bool MigrateDirectoryData(ModelType type,
                           UserShare* user_share,
-                          ModelTypeWorker* worker);
+                          ModelTypeWorker* worker,
+                          int* migrated_entity_count);
 
 // A version of the above with |batch_size| as a parameter so it can be lowered
 // for unit testing.
-bool MigrateDirectoryDataWithBatchSize(ModelType type,
-                                       UserShare* user_share,
-                                       ModelTypeWorker* worker,
-                                       int batch_size);
+bool MigrateDirectoryDataWithBatchSizeForTesting(
+    ModelType type,
+    int batch_size,
+    UserShare* user_share,
+    ModelTypeWorker* worker,
+    int* cumulative_migrated_entity_count);
 
 }  // namespace syncer
 
diff --git a/components/sync/engine_impl/uss_migrator_unittest.cc b/components/sync/engine_impl/uss_migrator_unittest.cc
index ba54e7f9..936f855 100644
--- a/components/sync/engine_impl/uss_migrator_unittest.cc
+++ b/components/sync/engine_impl/uss_migrator_unittest.cc
@@ -117,14 +117,17 @@
   SetProgressMarkerToken(kToken1);
   int64_t metahandle = InsertEntity(kTag1, kValue1);
   base::Time ctime = GetCtimeForEntity(metahandle);
+  int migrated_entity_count;
 
-  ASSERT_TRUE(MigrateDirectoryData(kModelType, user_share(), worker()));
+  ASSERT_TRUE(MigrateDirectoryData(kModelType, user_share(), worker(),
+                                   &migrated_entity_count));
 
   // No nudge should happen in the happy case.
   EXPECT_EQ(0, nudge_handler()->GetNumInitialDownloadNudges());
   // One update with one entity in it.
   EXPECT_EQ(1U, processor()->GetNumUpdateResponses());
   EXPECT_EQ(1U, processor()->GetNthUpdateResponse(0).size());
+  EXPECT_EQ(1, migrated_entity_count);
 
   const sync_pb::ModelTypeState& state = processor()->GetNthUpdateState(0);
   EXPECT_EQ(kToken1, state.progress_marker().token());
@@ -144,16 +147,20 @@
 }
 
 TEST_F(UssMigratorTest, MigrateMultiple) {
+  int migrated_entity_count;
+
   CreateTypeRoot();
   SetProgressMarkerToken(kToken1);
   InsertEntity(kTag1, kValue1);
   InsertEntity(kTag2, kValue2);
   InsertEntity(kTag3, kValue3);
 
-  ASSERT_TRUE(MigrateDirectoryData(kModelType, user_share(), worker()));
+  ASSERT_TRUE(MigrateDirectoryData(kModelType, user_share(), worker(),
+                                   &migrated_entity_count));
 
   EXPECT_EQ(1U, processor()->GetNumUpdateResponses());
   EXPECT_EQ(3U, processor()->GetNthUpdateResponse(0).size());
+  EXPECT_EQ(3, migrated_entity_count);
 
   UpdateResponseDataList updates = processor()->GetNthUpdateResponse(0);
   EXPECT_EQ(kTag1, updates.at(0).entity.value().specifics.preference().name());
@@ -162,17 +169,25 @@
 }
 
 TEST_F(UssMigratorTest, MigrateMultipleBatches) {
+  // Some arbitrary number of entities that represents entities migrated in
+  // previous calls to MigrateDirectoryDataWithBatchSizeForTesting().
+  const int kPreviouslyMigratedEntityCount = 13;
+
   CreateTypeRoot();
   SetProgressMarkerToken(kToken1);
   InsertEntity(kTag1, kValue1);
   InsertEntity(kTag2, kValue2);
   InsertEntity(kTag3, kValue3);
 
-  ASSERT_TRUE(
-      MigrateDirectoryDataWithBatchSize(kModelType, user_share(), worker(), 2));
+  int cumulative_migrated_entity_count = kPreviouslyMigratedEntityCount;
+  ASSERT_TRUE(MigrateDirectoryDataWithBatchSizeForTesting(
+      kModelType, 2, user_share(), worker(),
+      &cumulative_migrated_entity_count));
 
   EXPECT_EQ(1U, processor()->GetNumUpdateResponses());
   EXPECT_EQ(3U, processor()->GetNthUpdateResponse(0).size());
+  EXPECT_EQ(kPreviouslyMigratedEntityCount + 3,
+            cumulative_migrated_entity_count);
 
   UpdateResponseDataList updates = processor()->GetNthUpdateResponse(0);
   EXPECT_EQ(kTag1, updates.at(0).entity.value().specifics.preference().name());
@@ -181,33 +196,45 @@
 }
 
 TEST_F(UssMigratorTest, MigrateIgnoresTombstone) {
+  int migrated_entity_count;
+
   CreateTypeRoot();
   SetProgressMarkerToken(kToken1);
   DeleteEntity(kTag1);
 
-  ASSERT_TRUE(MigrateDirectoryData(kModelType, user_share(), worker()));
+  ASSERT_TRUE(MigrateDirectoryData(kModelType, user_share(), worker(),
+                                   &migrated_entity_count));
 
   EXPECT_EQ(0, nudge_handler()->GetNumInitialDownloadNudges());
   EXPECT_EQ(1U, processor()->GetNumUpdateResponses());
   EXPECT_EQ(0U, processor()->GetNthUpdateResponse(0).size());
+  EXPECT_EQ(0, migrated_entity_count);
 }
 
 TEST_F(UssMigratorTest, MigrateZero) {
+  int migrated_entity_count;
+
   CreateTypeRoot();
   SetProgressMarkerToken(kToken1);
 
-  ASSERT_TRUE(MigrateDirectoryData(kModelType, user_share(), worker()));
+  ASSERT_TRUE(MigrateDirectoryData(kModelType, user_share(), worker(),
+                                   &migrated_entity_count));
 
   EXPECT_EQ(0, nudge_handler()->GetNumInitialDownloadNudges());
   EXPECT_EQ(1U, processor()->GetNumUpdateResponses());
   EXPECT_EQ(0U, processor()->GetNthUpdateResponse(0).size());
+  EXPECT_EQ(0, migrated_entity_count);
 }
 
 TEST_F(UssMigratorTest, MissingTypeRoot) {
+  int migrated_entity_count;
+
   EXPECT_EQ(0, nudge_handler()->GetNumInitialDownloadNudges());
-  ASSERT_FALSE(MigrateDirectoryData(kModelType, user_share(), worker()));
+  ASSERT_FALSE(MigrateDirectoryData(kModelType, user_share(), worker(),
+                                    &migrated_entity_count));
   EXPECT_EQ(1, nudge_handler()->GetNumInitialDownloadNudges());
   EXPECT_EQ(0U, processor()->GetNumUpdateResponses());
+  EXPECT_EQ(0, migrated_entity_count);
 }
 
 }  // namespace syncer
diff --git a/components/ui_devtools/dom_agent.cc b/components/ui_devtools/dom_agent.cc
index 203c1be..c12800a 100644
--- a/components/ui_devtools/dom_agent.cc
+++ b/components/ui_devtools/dom_agent.cc
@@ -5,6 +5,8 @@
 #include "components/ui_devtools/dom_agent.h"
 
 #include <memory>
+#include <string>
+#include <utility>
 
 #include "components/ui_devtools/devtools_server.h"
 #include "components/ui_devtools/root_element.h"
@@ -12,7 +14,9 @@
 
 namespace ui_devtools {
 
-using namespace ui_devtools::protocol;
+using ui_devtools::protocol::Array;
+using ui_devtools::protocol::DOM::Node;
+using ui_devtools::protocol::Response;
 
 DOMAgent::DOMAgent() {}
 
@@ -25,7 +29,7 @@
   return Response::OK();
 }
 
-Response DOMAgent::getDocument(std::unique_ptr<DOM::Node>* out_root) {
+Response DOMAgent::getDocument(std::unique_ptr<Node>* out_root) {
   *out_root = BuildInitialTree();
   return Response::OK();
 }
@@ -45,17 +49,28 @@
     node_id_to_ui_element_[child->node_id()] = child;
     return;
   }
-  // If tree is being built, don't add child to dom tree again.
-  if (is_building_tree_)
-    return;
+
   DCHECK(node_id_to_ui_element_.count(parent->node_id()));
 
+  auto* current_parent = parent;
+  while (current_parent) {
+    if (current_parent->is_updating()) {
+      // One of the parents is updating, so no need to update here.
+      return;
+    }
+    current_parent = current_parent->parent();
+  }
+
+  child->set_is_updating(true);
+
   const auto& children = parent->children();
   auto iter = std::find(children.begin(), children.end(), child);
   int prev_node_id =
       (iter == children.end() - 1) ? 0 : (*std::next(iter))->node_id();
   frontend()->childNodeInserted(parent->node_id(), prev_node_id,
                                 BuildTreeForUIElement(child));
+
+  child->set_is_updating(false);
 }
 
 void DOMAgent::OnUIElementReordered(UIElement* parent, UIElement* child) {
@@ -107,27 +122,26 @@
 
 // TODO(mhashmi): Make ids reusable
 
-std::unique_ptr<DOM::Node> DOMAgent::BuildNode(
+std::unique_ptr<Node> DOMAgent::BuildNode(
     const std::string& name,
     std::unique_ptr<Array<std::string>> attributes,
-    std::unique_ptr<Array<DOM::Node>> children,
+    std::unique_ptr<Array<Node>> children,
     int node_ids) {
   constexpr int kDomElementNodeType = 1;
-  std::unique_ptr<DOM::Node> node = DOM::Node::create()
-                                        .setNodeId(node_ids)
-                                        .setBackendNodeId(node_ids)
-                                        .setNodeName(name)
-                                        .setNodeType(kDomElementNodeType)
-                                        .setAttributes(std::move(attributes))
-                                        .build();
+  std::unique_ptr<Node> node = Node::create()
+                                   .setNodeId(node_ids)
+                                   .setBackendNodeId(node_ids)
+                                   .setNodeName(name)
+                                   .setNodeType(kDomElementNodeType)
+                                   .setAttributes(std::move(attributes))
+                                   .build();
   node->setChildNodeCount(static_cast<int>(children->length()));
   node->setChildren(std::move(children));
   return node;
 }
 
-std::unique_ptr<DOM::Node> DOMAgent::BuildDomNodeFromUIElement(
-    UIElement* root) {
-  std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+std::unique_ptr<Node> DOMAgent::BuildDomNodeFromUIElement(UIElement* root) {
+  std::unique_ptr<Array<Node>> children = Array<Node>::create();
   for (auto* it : root->children())
     children->addItem(BuildDomNodeFromUIElement(it));
 
@@ -135,19 +149,19 @@
                    std::move(children), root->node_id());
 }
 
-std::unique_ptr<DOM::Node> DOMAgent::BuildInitialTree() {
-  is_building_tree_ = true;
-  std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+std::unique_ptr<Node> DOMAgent::BuildInitialTree() {
+  std::unique_ptr<Array<Node>> children = Array<Node>::create();
 
   element_root_ = std::make_unique<RootElement>(this);
+  element_root_->set_is_updating(true);
 
   for (auto* child : CreateChildrenForRoot()) {
     children->addItem(BuildTreeForUIElement(child));
     element_root_->AddChild(child);
   }
-  std::unique_ptr<DOM::Node> root_node =
+  std::unique_ptr<Node> root_node =
       BuildNode("root", nullptr, std::move(children), element_root_->node_id());
-  is_building_tree_ = false;
+  element_root_->set_is_updating(false);
   return root_node;
 }
 
@@ -164,7 +178,6 @@
 }
 
 void DOMAgent::Reset() {
-  is_building_tree_ = false;
   element_root_.reset();
   node_id_to_ui_element_.clear();
   observers_.Clear();
diff --git a/components/ui_devtools/dom_agent.h b/components/ui_devtools/dom_agent.h
index 3d36fae..f8135e35 100644
--- a/components/ui_devtools/dom_agent.h
+++ b/components/ui_devtools/dom_agent.h
@@ -5,6 +5,11 @@
 #ifndef COMPONENTS_UI_DEVTOOLS_DOM_AGENT_H_
 #define COMPONENTS_UI_DEVTOOLS_DOM_AGENT_H_
 
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
 #include "base/observer_list.h"
 #include "components/ui_devtools/DOM.h"
 #include "components/ui_devtools/devtools_base_agent.h"
@@ -44,7 +49,7 @@
   void AddObserver(DOMAgentObserver* observer);
   void RemoveObserver(DOMAgentObserver* observer);
   UIElement* GetElementFromNodeId(int node_id) const;
-  UIElement* element_root() const { return element_root_.get(); };
+  UIElement* element_root() const { return element_root_.get(); }
 
   // Returns parent id of the element with id |node_id|. Returns 0 if parent
   // does not exist.
@@ -72,7 +77,6 @@
   void RemoveDomNode(UIElement* ui_element);
   void Reset();
 
-  bool is_building_tree_ = false;
   std::unique_ptr<UIElement> element_root_;
   std::unordered_map<int, UIElement*> node_id_to_ui_element_;
 
diff --git a/components/ui_devtools/string_util.h b/components/ui_devtools/string_util.h
index 2d5004f..50995dc 100644
--- a/components/ui_devtools/string_util.h
+++ b/components/ui_devtools/string_util.h
@@ -74,6 +74,19 @@
   static std::unique_ptr<Value> parseJSON(const String& string);
 };
 
+// A read-only sequence of uninterpreted bytes with reference-counted storage.
+// Though the templates for generating the protocol bindings reference
+// this type, thus far it's not used in the Chrome layer, so we provide no
+// implementation here and rely on the linker optimizing it away. If this
+// changes, look to content/browser/devtools/protocol_string{.h,.cc} for
+// inspiration.
+class Binary {
+ public:
+  const uint8_t* data() const;
+  size_t size() const;
+  String toBase64() const;
+  static Binary fromBase64(const String& base64, bool* success);
+};
 }  // namespace protocol
 }  // namespace ui_devtools
 
diff --git a/components/ui_devtools/ui_element.h b/components/ui_devtools/ui_element.h
index c4fd3131..5d20b5c 100644
--- a/components/ui_devtools/ui_element.h
+++ b/components/ui_devtools/ui_element.h
@@ -6,6 +6,8 @@
 #define COMPONENTS_UI_DEVTOOLS_UI_ELEMENT_H_
 
 #include <memory>
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "base/macros.h"
@@ -28,13 +30,15 @@
 class UI_DEVTOOLS_EXPORT UIElement {
  public:
   virtual ~UIElement();
-  int node_id() const { return node_id_; };
+  int node_id() const { return node_id_; }
   std::string GetTypeName() const;
-  UIElement* parent() const { return parent_; };
-  void set_parent(UIElement* parent) { parent_ = parent; };
-  UIElementDelegate* delegate() const { return delegate_; };
-  UIElementType type() const { return type_; };
-  const std::vector<UIElement*>& children() const { return children_; };
+  UIElement* parent() const { return parent_; }
+  void set_parent(UIElement* parent) { parent_ = parent; }
+  UIElementDelegate* delegate() const { return delegate_; }
+  UIElementType type() const { return type_; }
+  const std::vector<UIElement*>& children() const { return children_; }
+  bool is_updating() const { return is_updating_; }
+  void set_is_updating(bool is_updating) { is_updating_ = is_updating; }
 
   // |child| is inserted in front of |before|. If |before| is null, it
   // is inserted at the end. Parent takes ownership of the added child.
@@ -70,7 +74,7 @@
   template <typename BackingT, typename T>
   static BackingT* GetBackingElement(const UIElement* element) {
     return T::From(element);
-  };
+  }
 
  protected:
   UIElement(const UIElementType type,
@@ -83,6 +87,7 @@
   std::vector<UIElement*> children_;
   UIElement* parent_;
   UIElementDelegate* delegate_;
+  bool is_updating_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(UIElement);
 };
diff --git a/components/ui_devtools/views/dom_agent_aura.cc b/components/ui_devtools/views/dom_agent_aura.cc
index d147fb6..626c8a5e 100644
--- a/components/ui_devtools/views/dom_agent_aura.cc
+++ b/components/ui_devtools/views/dom_agent_aura.cc
@@ -5,6 +5,8 @@
 #include "components/ui_devtools/views/dom_agent_aura.h"
 
 #include <memory>
+#include <utility>
+#include <vector>
 
 #include "components/ui_devtools/devtools_server.h"
 #include "components/ui_devtools/root_element.h"
@@ -22,7 +24,8 @@
 namespace ui_devtools {
 namespace {
 
-using namespace ui_devtools::protocol;
+using ui_devtools::protocol::DOM::Node;
+using ui_devtools::protocol::Array;
 // TODO(mhashmi): Make ids reusable
 
 views::Widget* GetWidgetFromWindow(gfx::NativeWindow window) {
@@ -52,7 +55,7 @@
   return children;
 }
 
-std::unique_ptr<DOM::Node> DOMAgentAura::BuildTreeForUIElement(
+std::unique_ptr<Node> DOMAgentAura::BuildTreeForUIElement(
     UIElement* ui_element) {
   if (ui_element->type() == UIElementType::WINDOW) {
     return BuildTreeForWindow(
@@ -70,10 +73,10 @@
   return nullptr;
 }
 
-std::unique_ptr<DOM::Node> DOMAgentAura::BuildTreeForWindow(
+std::unique_ptr<Node> DOMAgentAura::BuildTreeForWindow(
     UIElement* window_element_root,
     aura::Window* window) {
-  std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+  std::unique_ptr<Array<Node>> children = Array<Node>::create();
   views::Widget* widget = GetWidgetFromWindow(window);
   if (widget) {
     UIElement* widget_element =
@@ -89,16 +92,16 @@
     children->addItem(BuildTreeForWindow(window_element, child));
     window_element_root->AddChild(window_element);
   }
-  std::unique_ptr<DOM::Node> node =
+  std::unique_ptr<Node> node =
       BuildNode("Window", window_element_root->GetAttributes(),
                 std::move(children), window_element_root->node_id());
   return node;
 }
 
-std::unique_ptr<DOM::Node> DOMAgentAura::BuildTreeForRootWidget(
+std::unique_ptr<Node> DOMAgentAura::BuildTreeForRootWidget(
     UIElement* widget_element,
     views::Widget* widget) {
-  std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+  std::unique_ptr<Array<Node>> children = Array<Node>::create();
 
   UIElement* view_element =
       new ViewElement(widget->GetRootView(), this, widget_element);
@@ -106,24 +109,35 @@
   children->addItem(BuildTreeForView(view_element, widget->GetRootView()));
   widget_element->AddChild(view_element);
 
-  std::unique_ptr<DOM::Node> node =
+  std::unique_ptr<Node> node =
       BuildNode("Widget", widget_element->GetAttributes(), std::move(children),
                 widget_element->node_id());
   return node;
 }
 
-std::unique_ptr<DOM::Node> DOMAgentAura::BuildTreeForView(
-    UIElement* view_element,
-    views::View* view) {
-  std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+std::unique_ptr<Node> DOMAgentAura::BuildTreeForView(UIElement* view_element,
+                                                     views::View* view) {
+  std::unique_ptr<Array<Node>> children = Array<Node>::create();
 
   for (auto* child : view->GetChildrenInZOrder()) {
-    UIElement* view_element_child = new ViewElement(child, this, view_element);
+    // When building the subtree, a particular view could be visited multiple
+    // times because for each view of the subtree, we would call
+    // BuildTreeForView(..) on that view which causes the subtree with that view
+    // as root being visited again.  Here we check if we already constructed the
+    // ViewElement and skip true.
+    UIElement* view_element_child = nullptr;
+    auto id =
+        view_element->FindUIElementIdForBackendElement<views::View>(child);
+    if (id > 0) {
+      view_element_child = GetElementFromNodeId(id);
+    } else {
+      view_element_child = new ViewElement(child, this, view_element);
+      view_element->AddChild(view_element_child);
+    }
 
     children->addItem(BuildTreeForView(view_element_child, child));
-    view_element->AddChild(view_element_child);
   }
-  std::unique_ptr<DOM::Node> node =
+  std::unique_ptr<Node> node =
       BuildNode("View", view_element->GetAttributes(), std::move(children),
                 view_element->node_id());
   return node;
diff --git a/components/url_formatter/idn_spoof_checker.cc b/components/url_formatter/idn_spoof_checker.cc
index ad4ccfd..5bdc6f5 100644
--- a/components/url_formatter/idn_spoof_checker.cc
+++ b/components/url_formatter/idn_spoof_checker.cc
@@ -236,7 +236,8 @@
   //   - {U+050D (ԍ), U+100c (ဌ)} => g
   //   - {U+0D1F (ട), U+0E23 (ร), U+0EA3 (ຣ), U+0EAE (ຮ)} => s
   //   - U+1042 (၂) => j
-  //   - {U+0437 (з), U+0499 (ҙ), U+04E1 (ӡ), U+10D5 (ვ), U+1012 (ဒ)} => 3
+  //   - {U+0437 (з), U+0499 (ҙ), U+04E1 (ӡ), U+1012 (ဒ), U+10D5 (ვ),
+  //      U+10DE (პ)} => 3
   //   - {U+0E1A (บ), U+0E9A (ບ)} => u
   extra_confusable_mapper_.reset(icu::Transliterator::createFromRules(
       UNICODE_STRING_SIMPLE("ExtraConf"),
@@ -247,7 +248,7 @@
           "[мӎ] > m; [єҽҿၔ] > e; ґ > r; [ғӻ] > f;"
           "[ҫင] > c; ұ > y; [χҳӽӿ] > x;"
           "ԃ  > d; [ԍဌ] > g; [ടรຣຮ] > s; ၂ > j;"
-          "[зҙӡვဒ] > 3; [บບ] > u"),
+          "[зҙӡဒვპ] > 3; [บບ] > u"),
       UTRANS_FORWARD, parse_error, status));
   DCHECK(U_SUCCESS(status))
       << "Spoofchecker initalization failed due to an error: "
diff --git a/components/url_formatter/url_formatter_unittest.cc b/components/url_formatter/url_formatter_unittest.cc
index 32bb24f..361eb3ec 100644
--- a/components/url_formatter/url_formatter_unittest.cc
+++ b/components/url_formatter/url_formatter_unittest.cc
@@ -552,14 +552,19 @@
      L"12\x04e1"
      L"4567890.com",
      false},
+    // 12ဒ4567890.com
+    {"xn--124567890-6s6a.com",
+     L"12\x1012"
+     L"4567890.com",
+     false},
     // 12ვ4567890.com
     {"xn--124567890-we8a.com",
      L"12\x10D5"
      L"4567890.com",
      false},
-    // 12ဒ4567890.com
-    {"xn--124567890-6s6a.com",
-     L"12\x1012"
+    // 12პ4567890.com
+    {"xn--124567890-hh8a.com",
+     L"12\x10DE"
      L"4567890.com",
      false},
     // 123Ꮞ567890.com
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index d695eb2..06122b22a 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -90,6 +90,7 @@
     "quads/debug_border_draw_quad.h",
     "quads/draw_quad.cc",
     "quads/draw_quad.h",
+    "quads/frame_deadline.cc",
     "quads/frame_deadline.h",
     "quads/largest_draw_quad.cc",
     "quads/largest_draw_quad.h",
diff --git a/components/viz/common/quads/frame_deadline.cc b/components/viz/common/quads/frame_deadline.cc
new file mode 100644
index 0000000..f5f5619b
--- /dev/null
+++ b/components/viz/common/quads/frame_deadline.cc
@@ -0,0 +1,43 @@
+// Copyright 2018 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/viz/common/quads/frame_deadline.h"
+
+#include <cinttypes>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+
+namespace viz {
+
+base::TimeTicks FrameDeadline::ToWallTime(
+    base::Optional<uint32_t> default_deadline_in_frames) const {
+  uint32_t deadline_in_frames = deadline_in_frames_;
+  if (use_default_lower_bound_deadline_) {
+    deadline_in_frames =
+        std::max(deadline_in_frames, default_deadline_in_frames.value_or(
+                                         std::numeric_limits<uint32_t>::max()));
+  }
+  return frame_start_time_ + deadline_in_frames * frame_interval_;
+}
+
+std::string FrameDeadline::ToString() const {
+  const base::TimeDelta start_time_delta =
+      frame_start_time_ - base::TimeTicks();
+  return base::StringPrintf(
+      "FrameDeadline(start time: %" PRId64
+      " ms, deadline in frames: %s, frame interval: %" PRId64 " ms)",
+      start_time_delta.InMilliseconds(),
+      use_default_lower_bound_deadline_
+          ? "unresolved"
+          : base::UintToString(deadline_in_frames_).c_str(),
+      frame_interval_.InMilliseconds());
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const FrameDeadline& frame_deadline) {
+  return out << frame_deadline.ToString();
+}
+
+}  // namespace viz
diff --git a/components/viz/common/quads/frame_deadline.h b/components/viz/common/quads/frame_deadline.h
index faa992f..eef30ed 100644
--- a/components/viz/common/quads/frame_deadline.h
+++ b/components/viz/common/quads/frame_deadline.h
@@ -8,9 +8,20 @@
 #include "components/viz/common/viz_common_export.h"
 
 #include "base/time/time.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
 
 namespace viz {
 
+// FrameDeadline is a class that represents a CompositorFrame's deadline for
+// activation. The deadline consists of three components: start time, deadline
+// in frames, and frame interval. All three components are stored individually
+// in this class in order to allow resolution of this deadline that incorporates
+// a system default deadline in the Viz service. In particular, the computation
+// to translate FrameDeadline into wall time is:
+// if use system default lower bound deadline:
+//    start time + max(deadline in frames, default deadline) * frame interval
+// else:
+//    start time + deadline in frames * frame interval
 class VIZ_COMMON_EXPORT FrameDeadline {
  public:
   FrameDeadline() = default;
@@ -27,6 +38,12 @@
 
   FrameDeadline& operator=(const FrameDeadline& other) = default;
 
+  // Converts this FrameDeadline object into a wall time given a system default
+  // deadline in frames.
+  base::TimeTicks ToWallTime(
+      base::Optional<uint32_t> default_deadline_in_frames =
+          base::nullopt) const;
+
   bool operator==(const FrameDeadline& other) const {
     return other.frame_start_time_ == frame_start_time_ &&
            other.deadline_in_frames_ == deadline_in_frames_ &&
@@ -49,13 +66,18 @@
     return use_default_lower_bound_deadline_;
   }
 
+  std::string ToString() const;
+
  private:
   base::TimeTicks frame_start_time_;
   uint32_t deadline_in_frames_ = 0u;
-  base::TimeDelta frame_interval_;
+  base::TimeDelta frame_interval_ = BeginFrameArgs::DefaultInterval();
   bool use_default_lower_bound_deadline_ = true;
 };
 
+VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
+                                           const FrameDeadline& frame_deadline);
+
 }  // namespace viz
 
 #endif  // COMPONENTS_VIZ_COMMON_QUADS_FRAME_DEADLINE_H_
diff --git a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
index ad341cfd..28f2d7e 100644
--- a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
+++ b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -2756,12 +2756,20 @@
 
   // |child_id2| Surface should not activate because |child_id1| was never
   // added as a dependency by a parent.
-  child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
+  child_support1().SubmitCompositorFrame(
+      child_id2.local_surface_id(),
+      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+                          std::vector<TransferableResource>(),
+                          MakeDeadline(1u)));
   Surface* child_surface2 = GetSurfaceForId(child_id2);
   ASSERT_NE(nullptr, child_surface2);
   EXPECT_TRUE(child_surface2->HasPendingFrame());
   EXPECT_FALSE(child_surface2->HasActiveFrame());
+  EXPECT_TRUE(child_surface2->has_deadline());
+
+  FrameDeadline deadline = MakeDefaultDeadline();
+  base::TimeTicks deadline_wall_time = deadline.ToWallTime();
+  EXPECT_EQ(deadline_wall_time, child_surface2->deadline_for_testing());
 
   // The parent finally embeds a child surface that hasn't arrived which
   // activates |child_id2|'s Surface in order for the child to make forward
@@ -2970,7 +2978,6 @@
   EXPECT_TRUE(child_surface2->HasActiveFrame());
   EXPECT_FALSE(child_surface2->has_deadline());
 
-  // This failed before the latest change.
   EXPECT_TRUE(child_surface2->HasDependentFrame());
 }
 
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index bc9c06b0..ded72a2 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -508,9 +508,13 @@
   const FrameDeadline& deadline = current_frame.metadata.deadline;
   uint32_t deadline_in_frames = deadline.deadline_in_frames();
 
+  bool block_activation =
+      block_activation_on_parent_ && !seen_first_surface_dependency_;
+
   // If no default deadline is available then all deadlines are treated as
   // effectively infinite deadlines.
-  if (!default_deadline || deadline.use_default_lower_bound_deadline()) {
+  if (!default_deadline || deadline.use_default_lower_bound_deadline() ||
+      block_activation) {
     deadline_in_frames = std::max(
         deadline_in_frames,
         default_deadline.value_or(std::numeric_limits<uint32_t>::max()));
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h
index ba53c4dc..e8c1afe2 100644
--- a/components/viz/service/surfaces/surface.h
+++ b/components/viz/service/surfaces/surface.h
@@ -105,6 +105,10 @@
 
   bool has_deadline() const { return deadline_ && deadline_->has_deadline(); }
 
+  base::Optional<base::TimeTicks> deadline_for_testing() const {
+    return deadline_->deadline_for_testing();
+  }
+
   // Inherits the same deadline as the one specified by |surface|. A deadline
   // may be set further out in order to avoid doing unnecessary work while a
   // parent surface is blocked on dependencies. A deadline may be shortened
diff --git a/components/viz/service/surfaces/surface_dependency_deadline.cc b/components/viz/service/surfaces/surface_dependency_deadline.cc
index bb5faca..65d8abe 100644
--- a/components/viz/service/surfaces/surface_dependency_deadline.cc
+++ b/components/viz/service/surfaces/surface_dependency_deadline.cc
@@ -31,8 +31,7 @@
 bool SurfaceDependencyDeadline::Set(const FrameDeadline& frame_deadline) {
   CancelInternal(false);
   start_time_ = frame_deadline.frame_start_time();
-  deadline_ = start_time_ + frame_deadline.deadline_in_frames() *
-                                frame_deadline.frame_interval();
+  deadline_ = frame_deadline.ToWallTime();
   begin_frame_source_->AddObserver(this);
   return has_deadline();
 }
diff --git a/content/browser/frame_host/interstitial_page_impl.cc b/content/browser/frame_host/interstitial_page_impl.cc
index f206f1e..95d70ed7 100644
--- a/content/browser/frame_host/interstitial_page_impl.cc
+++ b/content/browser/frame_host/interstitial_page_impl.cc
@@ -594,10 +594,9 @@
   return false;
 }
 
-void InterstitialPageImpl::HandleKeyboardEvent(
-      const NativeWebKeyboardEvent& event) {
-  if (enabled())
-    render_widget_host_delegate_->HandleKeyboardEvent(event);
+bool InterstitialPageImpl::HandleKeyboardEvent(
+    const NativeWebKeyboardEvent& event) {
+  return enabled() && render_widget_host_delegate_->HandleKeyboardEvent(event);
 }
 
 WebContents* InterstitialPageImpl::web_contents() const {
diff --git a/content/browser/frame_host/interstitial_page_impl.h b/content/browser/frame_host/interstitial_page_impl.h
index 1bd73270..486a40e 100644
--- a/content/browser/frame_host/interstitial_page_impl.h
+++ b/content/browser/frame_host/interstitial_page_impl.h
@@ -174,7 +174,7 @@
   KeyboardEventProcessingResult PreHandleKeyboardEvent(
       const NativeWebKeyboardEvent& event) override;
   bool PreHandleMouseEvent(const blink::WebMouseEvent& event) override;
-  void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override;
+  bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override;
   TextInputManager* GetTextInputManager() override;
   RenderWidgetHostInputEventRouter* GetInputEventRouter() override;
   BrowserAccessibilityManager* GetRootBrowserAccessibilityManager() override;
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
index e94cc9b..3b1b3dc 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
@@ -70,7 +70,7 @@
 
   std::string event_name = WebInputEvent::GetName(type);
 
-  if (latency.source_event_type() == ui::KEY_PRESS)
+  if (latency.source_event_type() == ui::SourceEventType::KEY_PRESS)
     event_name = "KeyPress";
 
   std::string default_action_status =
@@ -112,7 +112,7 @@
     active_multi_finger_gesture_ = touch_event.touches_length != 1;
   }
 
-  if (latency->source_event_type() == ui::KEY_PRESS) {
+  if (latency->source_event_type() == ui::SourceEventType::KEY_PRESS) {
     DCHECK(event.GetType() == WebInputEvent::kChar ||
            event.GetType() == WebInputEvent::kRawKeyDown);
   }
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index af242bb..419a683 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -35,6 +35,11 @@
   return false;
 }
 
+bool RenderWidgetHostDelegate::HandleKeyboardEvent(
+    const NativeWebKeyboardEvent& event) {
+  return false;
+}
+
 bool RenderWidgetHostDelegate::ShouldIgnoreInputEvents() {
   return false;
 }
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index 70130160..ae9853a66 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -110,7 +110,7 @@
   // Callback to inform the browser that the renderer did not process the
   // specified events. This gives an opportunity to the browser to process the
   // event (used for keyboard shortcuts).
-  virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {}
+  virtual bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event);
 
   // Callback to inform the browser that the renderer did not process the
   // specified mouse wheel event.  Returns true if the browser has handled
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 16e04ce..cc830dff 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -596,9 +596,10 @@
                : KeyboardEventProcessingResult::NOT_HANDLED;
   }
 
-  void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override {
+  bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override {
     unhandled_keyboard_event_type_ = event.GetType();
     unhandled_keyboard_event_called_ = true;
+    return true;
   }
 
   bool HandleWheelEvent(const blink::WebMouseWheelEvent& event) override {
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index c553ee7..b881bf4 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2232,13 +2232,12 @@
                    : KeyboardEventProcessingResult::NOT_HANDLED;
 }
 
-void WebContentsImpl::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
+bool WebContentsImpl::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
   if (browser_plugin_embedder_ &&
       browser_plugin_embedder_->HandleKeyboardEvent(event)) {
-    return;
+    return true;
   }
-  if (delegate_)
-    delegate_->HandleKeyboardEvent(this, event);
+  return delegate_ && delegate_->HandleKeyboardEvent(this, event);
 }
 
 bool WebContentsImpl::HandleWheelEvent(
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 82c7539e..ab486b8d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -717,7 +717,7 @@
 
   KeyboardEventProcessingResult PreHandleKeyboardEvent(
       const NativeWebKeyboardEvent& event) override;
-  void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override;
+  bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override;
   bool HandleWheelEvent(const blink::WebMouseWheelEvent& event) override;
   bool PreHandleGestureEvent(const blink::WebGestureEvent& event) override;
   BrowserAccessibilityManager* GetRootBrowserAccessibilityManager() override;
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index a216418..8f9eb56 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -94,6 +94,12 @@
   return KeyboardEventProcessingResult::NOT_HANDLED;
 }
 
+bool WebContentsDelegate::HandleKeyboardEvent(
+    WebContents* source,
+    const NativeWebKeyboardEvent& event) {
+  return false;
+}
+
 bool WebContentsDelegate::PreHandleGestureEvent(
     WebContents* source,
     const blink::WebGestureEvent& event) {
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index df6045d..b26e4ea 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -253,9 +253,11 @@
       const NativeWebKeyboardEvent& event);
 
   // Allows delegates to handle unhandled keyboard messages coming back from
-  // the renderer.
-  virtual void HandleKeyboardEvent(WebContents* source,
-                                   const NativeWebKeyboardEvent& event) {}
+  // the renderer. Returns true if the event was handled, false otherwise. A
+  // true value means no more processing should happen on the event. The default
+  // return value is false
+  virtual bool HandleKeyboardEvent(WebContents* source,
+                                   const NativeWebKeyboardEvent& event);
 
   // Allows delegates to handle gesture events before sending to the renderer.
   // Returns true if the |event| was handled and thus shouldn't be processed
diff --git a/content/renderer/input/input_event_prediction.cc b/content/renderer/input/input_event_prediction.cc
index 5b0579d3..3efc8b4 100644
--- a/content/renderer/input/input_event_prediction.cc
+++ b/content/renderer/input/input_event_prediction.cc
@@ -26,28 +26,43 @@
 constexpr char kInputEventPredictorTypeLsq[] = "lsq";
 constexpr char kInputEventPredictorTypeKalman[] = "kalman";
 
+constexpr uint32_t kPredictEventCount = 3;
+constexpr base::TimeDelta kPredictionInterval =
+    base::TimeDelta::FromMilliseconds(8);
+
 }  // namespace
 
-InputEventPrediction::InputEventPrediction() {
-  std::string predictor_type = GetFieldTrialParamValueByFeature(
-      features::kResamplingInputEvents, kPredictor);
-  if (predictor_type == kInputEventPredictorTypeLsq)
-    selected_predictor_type_ = PredictorType::kLsq;
-  else if (predictor_type == kInputEventPredictorTypeKalman)
-    selected_predictor_type_ = PredictorType::kKalman;
-  else
-    selected_predictor_type_ = PredictorType::kEmpty;
-
-  mouse_predictor_ = CreatePredictor();
+InputEventPrediction::InputEventPrediction(bool enable_resampling)
+    : enable_resampling_(enable_resampling) {
+  SetUpPredictorType();
 }
 
 InputEventPrediction::~InputEventPrediction() {}
 
+void InputEventPrediction::SetUpPredictorType() {
+  // Resampling predictor type is set from field trial parameters.
+  // When resampling is disable, use kalman filter predictor for
+  // creating predicted events.
+  if (enable_resampling_) {
+    std::string predictor_type = GetFieldTrialParamValueByFeature(
+        features::kResamplingInputEvents, kPredictor);
+    if (predictor_type == kInputEventPredictorTypeLsq)
+      selected_predictor_type_ = PredictorType::kLsq;
+    else if (predictor_type == kInputEventPredictorTypeKalman)
+      selected_predictor_type_ = PredictorType::kKalman;
+    else
+      selected_predictor_type_ = PredictorType::kEmpty;
+  } else {
+    selected_predictor_type_ = PredictorType::kKalman;
+  }
+
+  mouse_predictor_ = CreatePredictor();
+}
+
 void InputEventPrediction::HandleEvents(
-    const blink::WebCoalescedInputEvent& coalesced_event,
-    base::TimeTicks frame_time,
-    blink::WebInputEvent* event) {
-  switch (event->GetType()) {
+    blink::WebCoalescedInputEvent& coalesced_event,
+    base::TimeTicks frame_time) {
+  switch (coalesced_event.Event().GetType()) {
     case WebInputEvent::kMouseMove:
     case WebInputEvent::kTouchMove:
     case WebInputEvent::kPointerMove: {
@@ -55,7 +70,20 @@
       for (size_t i = 0; i < coalesced_size; i++)
         UpdatePrediction(coalesced_event.CoalescedEvent(i));
 
-      ApplyResampling(frame_time, event);
+      if (enable_resampling_)
+        ApplyResampling(frame_time, coalesced_event.EventPointer());
+
+      base::TimeTicks predict_time =
+          enable_resampling_
+              ? coalesced_event.EventPointer()->TimeStamp() +
+                    kPredictionInterval
+              : std::max(frame_time,
+                         coalesced_event.EventPointer()->TimeStamp());
+      for (uint32_t i = 0; i < kPredictEventCount; i++) {
+        if (!AddPredictedEvent(predict_time, coalesced_event))
+          break;
+        predict_time += kPredictionInterval;
+      }
       break;
     }
     case WebInputEvent::kTouchScrollStarted:
@@ -63,7 +91,7 @@
       pointer_id_predictor_map_.clear();
       break;
     default:
-      ResetPredictor(*event);
+      ResetPredictor(coalesced_event.Event());
   }
 }
 
@@ -104,14 +132,14 @@
   if (event->GetType() == WebInputEvent::kTouchMove) {
     WebTouchEvent* touch_event = static_cast<WebTouchEvent*>(event);
     for (unsigned i = 0; i < touch_event->touches_length; ++i) {
-      if (ResampleSinglePointer(frame_time, &touch_event->touches[i]))
+      if (GetPointerPrediction(frame_time, &touch_event->touches[i]))
         event->SetTimeStamp(frame_time);
     }
   } else if (event->GetType() == WebInputEvent::kMouseMove) {
-    if (ResampleSinglePointer(frame_time, static_cast<WebMouseEvent*>(event)))
+    if (GetPointerPrediction(frame_time, static_cast<WebMouseEvent*>(event)))
       event->SetTimeStamp(frame_time);
   } else if (event->GetType() == WebInputEvent::kPointerMove) {
-    if (ResampleSinglePointer(frame_time, static_cast<WebPointerEvent*>(event)))
+    if (GetPointerPrediction(frame_time, static_cast<WebPointerEvent*>(event)))
       event->SetTimeStamp(frame_time);
   }
 }
@@ -132,6 +160,35 @@
   }
 }
 
+bool InputEventPrediction::AddPredictedEvent(
+    base::TimeTicks predict_time,
+    blink::WebCoalescedInputEvent& coalesced_event) {
+  ui::WebScopedInputEvent predicted_event =
+      ui::WebInputEventTraits::Clone(coalesced_event.Event());
+  bool success = false;
+  if (predicted_event->GetType() == WebInputEvent::kTouchMove) {
+    WebTouchEvent& touch_event = static_cast<WebTouchEvent&>(*predicted_event);
+    success = true;
+    for (unsigned i = 0; i < touch_event.touches_length; ++i) {
+      if (!GetPointerPrediction(predict_time, &touch_event.touches[i]))
+        success = false;
+    }
+  } else if (predicted_event->GetType() == WebInputEvent::kMouseMove) {
+    if (GetPointerPrediction(predict_time,
+                             &static_cast<WebMouseEvent&>(*predicted_event)))
+      success = true;
+  } else if (predicted_event->GetType() == WebInputEvent::kPointerMove) {
+    if (GetPointerPrediction(predict_time,
+                             &static_cast<WebPointerEvent&>(*predicted_event)))
+      success = true;
+  }
+  if (success) {
+    predicted_event->SetTimeStamp(predict_time);
+    coalesced_event.AddPredictedEvent(*predicted_event);
+  }
+  return success;
+}
+
 void InputEventPrediction::UpdateSinglePointer(
     const WebPointerProperties& event,
     base::TimeTicks event_time) {
@@ -152,12 +209,12 @@
   }
 }
 
-bool InputEventPrediction::ResampleSinglePointer(base::TimeTicks frame_time,
-                                                 WebPointerProperties* event) {
+bool InputEventPrediction::GetPointerPrediction(base::TimeTicks predict_time,
+                                                WebPointerProperties* event) {
   ui::InputPredictor::InputData predict_result;
   if (event->pointer_type == WebPointerProperties::PointerType::kMouse) {
     if (mouse_predictor_->HasPrediction() &&
-        mouse_predictor_->GeneratePrediction(frame_time, &predict_result)) {
+        mouse_predictor_->GeneratePrediction(predict_time, &predict_result)) {
       event->SetPositionInWidget(predict_result.pos);
       return true;
     }
@@ -168,7 +225,7 @@
     auto predictor = pointer_id_predictor_map_.find(event->id);
     if (predictor != pointer_id_predictor_map_.end() &&
         predictor->second->HasPrediction() &&
-        predictor->second->GeneratePrediction(frame_time, &predict_result)) {
+        predictor->second->GeneratePrediction(predict_time, &predict_result)) {
       event->SetPositionInWidget(predict_result.pos);
       return true;
     }
diff --git a/content/renderer/input/input_event_prediction.h b/content/renderer/input/input_event_prediction.h
index 3bcc9ae3..3059a28 100644
--- a/content/renderer/input/input_event_prediction.h
+++ b/content/renderer/input/input_event_prediction.h
@@ -21,22 +21,35 @@
 // This class stores prediction of all active pointers.
 class CONTENT_EXPORT InputEventPrediction {
  public:
-  InputEventPrediction();
+  // enable_resampling is true when kResamplingInputEvents is enabled.
+  explicit InputEventPrediction(bool enable_resampling);
   ~InputEventPrediction();
 
-  void HandleEvents(const blink::WebCoalescedInputEvent& coalesced_event,
-                    base::TimeTicks frame_time,
-                    blink::WebInputEvent* event);
+  // Handle Resampling/Prediction of WebInputEvents. This function is mainly
+  // doing three things:
+  // 1. Maintain/Updates predictor using current CoalescedEvents vector.
+  // 2. When enable_resampling is true, change coalesced_event->EventPointer()'s
+  //    coordinates to the position at frame time.
+  // 3. Generates 3 predicted events when prediction is available, add the
+  //    PredictedEvent to coalesced_event.
+  void HandleEvents(blink::WebCoalescedInputEvent& coalesced_event,
+                    base::TimeTicks frame_time);
 
   // Initialize predictor for different pointer.
   std::unique_ptr<ui::InputPredictor> CreatePredictor() const;
 
  private:
   friend class InputEventPredictionTest;
+  FRIEND_TEST_ALL_PREFIXES(PredictedEventTest, ResamplingDisabled);
+  FRIEND_TEST_ALL_PREFIXES(InputEventPredictionTest, PredictorType);
 
   enum class PredictorType { kEmpty, kLsq, kKalman };
 
-  // The following three function is for handling multiple TouchPoints in a
+  // Set predictor type from field parameters of kResamplingInputEvent flag if
+  // it's enable. Otherwise use Kalman filter predictor.
+  void SetUpPredictorType();
+
+  // The following functions are for handling multiple TouchPoints in a
   // WebTouchEvent. They should be more neat when WebTouchEvent is elimated.
   // Cast events from WebInputEvent to WebPointerProperties. Call
   // UpdateSinglePointer for each pointer.
@@ -44,17 +57,22 @@
   // Cast events from WebInputEvent to WebPointerProperties. Call
   // ResamplingSinglePointer for each poitner.
   void ApplyResampling(base::TimeTicks frame_time, WebInputEvent* event);
-  // Cast events from WebInputEvent to WebPointerProperties. Call
-  // ResetSinglePredictor for each pointer.
+  // Reset predictor for each pointer in WebInputEvent by  ResetSinglePredictor.
   void ResetPredictor(const WebInputEvent& event);
 
+  // Add predicted event to WebCoalescedInputEvent if prediction is available.
+  bool AddPredictedEvent(base::TimeTicks predict_time,
+                         blink::WebCoalescedInputEvent& coalesced_event);
+
   // Get single predictor based on event id and type, and update the predictor
   // with new events coords.
   void UpdateSinglePointer(const WebPointerProperties& event,
                            base::TimeTicks time);
-  // Get single predictor based on event id and type, apply resampling event
-  // coordinates.
-  bool ResampleSinglePointer(base::TimeTicks time, WebPointerProperties* event);
+  // Get prediction result of a single predictor based on the predict_time,
+  // and apply predicted result to the event. Return false if no prediction
+  // available.
+  bool GetPointerPrediction(base::TimeTicks predict_time,
+                            WebPointerProperties* event);
   // Get single predictor based on event id and type. For mouse, reset the
   // predictor, for other pointer type, remove it from mapping.
   void ResetSinglePredictor(const WebPointerProperties& event);
@@ -67,6 +85,8 @@
   // predictor.
   PredictorType selected_predictor_type_;
 
+  bool enable_resampling_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(InputEventPrediction);
 };
 
diff --git a/content/renderer/input/input_event_prediction_unittest.cc b/content/renderer/input/input_event_prediction_unittest.cc
index 04ef067..36a0162 100644
--- a/content/renderer/input/input_event_prediction_unittest.cc
+++ b/content/renderer/input/input_event_prediction_unittest.cc
@@ -4,7 +4,12 @@
 
 #include "content/renderer/input/input_event_prediction.h"
 
+#include "base/metrics/field_trial_param_associator.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/test/scoped_feature_list.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
+
+#include "content/public/common/content_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/blink/prediction/empty_predictor.h"
@@ -14,12 +19,15 @@
 using blink::WebPointerProperties;
 using blink::WebTouchEvent;
 
+namespace {}  // namespace
+
 namespace content {
 
 class InputEventPredictionTest : public testing::Test {
  public:
   InputEventPredictionTest() {
-    event_predictor_ = std::make_unique<InputEventPrediction>();
+    event_predictor_ =
+        std::make_unique<InputEventPrediction>(true /* enable_resampling */);
   }
 
   int GetPredictorMapSize() const {
@@ -49,16 +57,66 @@
   void HandleEvents(const WebInputEvent& event) {
     blink::WebCoalescedInputEvent coalesced_event(event);
     event_predictor_->HandleEvents(coalesced_event,
-                                   WebInputEvent::GetStaticTimeStampForTests(),
-                                   coalesced_event.EventPointer());
+                                   WebInputEvent::GetStaticTimeStampForTests());
+  }
+
+  void ConfigureFieldTrial(const std::string& predictor_type) {
+    const std::string kTrialName = "TestTrial";
+    const std::string kGroupName = "TestGroup";
+
+    field_trial_list_.reset();
+    field_trial_list_.reset(new base::FieldTrialList(nullptr));
+    scoped_refptr<base::FieldTrial> trial =
+        base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
+    base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
+
+    std::map<std::string, std::string> params;
+    params["predictor"] = predictor_type;
+    base::FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
+        kTrialName, kGroupName, params);
+
+    std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+    feature_list->RegisterFieldTrialOverride(
+        features::kResamplingInputEvents.name,
+        base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get());
+    base::FeatureList::ClearInstanceForTesting();
+    scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+
+    EXPECT_EQ(params["predictor"],
+              GetFieldTrialParamValueByFeature(features::kResamplingInputEvents,
+                                               "predictor"));
   }
 
  protected:
   std::unique_ptr<InputEventPrediction> event_predictor_;
 
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<base::FieldTrialList> field_trial_list_;
+
   DISALLOW_COPY_AND_ASSIGN(InputEventPredictionTest);
 };
 
+TEST_F(InputEventPredictionTest, PredictorType) {
+  EXPECT_TRUE(event_predictor_->enable_resampling_);
+  EXPECT_EQ(event_predictor_->selected_predictor_type_,
+            InputEventPrediction::PredictorType::kEmpty);
+
+  ConfigureFieldTrial("empty");
+  event_predictor_->SetUpPredictorType();
+  EXPECT_EQ(event_predictor_->selected_predictor_type_,
+            InputEventPrediction::PredictorType::kEmpty);
+
+  ConfigureFieldTrial("kalman");
+  event_predictor_->SetUpPredictorType();
+  EXPECT_EQ(event_predictor_->selected_predictor_type_,
+            InputEventPrediction::PredictorType::kKalman);
+
+  ConfigureFieldTrial("lsq");
+  event_predictor_->SetUpPredictorType();
+  EXPECT_EQ(event_predictor_->selected_predictor_type_,
+            InputEventPrediction::PredictorType::kLsq);
+}
+
 TEST_F(InputEventPredictionTest, MouseEvent) {
   WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build(
       WebInputEvent::kMouseMove, 10, 10, 0);
@@ -210,4 +268,46 @@
   EXPECT_EQ(GetPredictorMapSize(), 0);
 }
 
+class PredictedEventTest : public InputEventPredictionTest {
+ public:
+  PredictedEventTest() {
+    event_predictor_ =
+        std::make_unique<InputEventPrediction>(false /* enable_resampling */);
+  }
+};
+
+TEST_F(PredictedEventTest, ResamplingDisabled) {
+  // When resampling is disable, use kalman filter predictor to generate
+  // predicted event.
+  EXPECT_FALSE(event_predictor_->enable_resampling_);
+  EXPECT_EQ(event_predictor_->selected_predictor_type_,
+            InputEventPrediction::PredictorType::kKalman);
+
+  // Send 3 mouse move to get kalman predictor ready.
+  WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build(
+      WebInputEvent::kMouseMove, 10, 10, 0);
+  HandleEvents(mouse_move);
+  mouse_move =
+      SyntheticWebMouseEventBuilder::Build(WebInputEvent::kMouseMove, 11, 9, 0);
+  HandleEvents(mouse_move);
+
+  mouse_move =
+      SyntheticWebMouseEventBuilder::Build(WebInputEvent::kMouseMove, 12, 8, 0);
+  HandleEvents(mouse_move);
+
+  // The 4th move event should generate predicted events.
+  mouse_move =
+      SyntheticWebMouseEventBuilder::Build(WebInputEvent::kMouseMove, 13, 7, 0);
+  blink::WebCoalescedInputEvent coalesced_event(mouse_move);
+  event_predictor_->HandleEvents(coalesced_event, ui::EventTimeForNow());
+
+  EXPECT_GT(coalesced_event.PredictedEventSize(), 0u);
+
+  // Verify when resampling event is disabled, event coordinate doesn't change.
+  const WebMouseEvent& event =
+      static_cast<const blink::WebMouseEvent&>(coalesced_event.Event());
+  EXPECT_EQ(event.PositionInWidget().x, 13);
+  EXPECT_EQ(event.PositionInWidget().y, 7);
+}
+
 }  // namespace content
diff --git a/content/renderer/input/main_thread_event_queue.cc b/content/renderer/input/main_thread_event_queue.cc
index 301dc04a..3727d6d 100644
--- a/content/renderer/input/main_thread_event_queue.cc
+++ b/content/renderer/input/main_thread_event_queue.cc
@@ -236,10 +236,8 @@
       use_raf_fallback_timer_(true) {
   raf_fallback_timer_.SetTaskRunner(main_task_runner);
 
-  event_predictor_ =
-      base::FeatureList::IsEnabled(features::kResamplingInputEvents)
-          ? std::make_unique<InputEventPrediction>()
-          : nullptr;
+  event_predictor_ = std::make_unique<InputEventPrediction>(
+      base::FeatureList::IsEnabled(features::kResamplingInputEvents));
 }
 
 MainThreadEventQueue::~MainThreadEventQueue() {}
@@ -586,8 +584,7 @@
     base::TimeTicks frame_time) {
   if (item->IsWebInputEvent() && allow_raf_aligned_input_ && event_predictor_) {
     QueuedWebInputEvent* event = static_cast<QueuedWebInputEvent*>(item.get());
-    event_predictor_->HandleEvents(event->coalesced_event(), frame_time,
-                                   &event->event());
+    event_predictor_->HandleEvents(event->coalesced_event(), frame_time);
   }
 }
 
diff --git a/content/renderer/input/scoped_web_input_event_with_latency_info.cc b/content/renderer/input/scoped_web_input_event_with_latency_info.cc
index b829c1a..47e6fd00 100644
--- a/content/renderer/input/scoped_web_input_event_with_latency_info.cc
+++ b/content/renderer/input/scoped_web_input_event_with_latency_info.cc
@@ -53,4 +53,9 @@
   return *event_;
 }
 
+blink::WebCoalescedInputEvent&
+ScopedWebInputEventWithLatencyInfo::coalesced_event() {
+  return *event_;
+}
+
 }  // namespace content
diff --git a/content/renderer/input/scoped_web_input_event_with_latency_info.h b/content/renderer/input/scoped_web_input_event_with_latency_info.h
index 590e902..f8d37654 100644
--- a/content/renderer/input/scoped_web_input_event_with_latency_info.h
+++ b/content/renderer/input/scoped_web_input_event_with_latency_info.h
@@ -31,6 +31,7 @@
   const blink::WebInputEvent& event() const;
   const blink::WebCoalescedInputEvent& coalesced_event() const;
   blink::WebInputEvent& event();
+  blink::WebCoalescedInputEvent& coalesced_event();
   const ui::LatencyInfo latencyInfo() const { return latency_; }
 
   void CoalesceWith(const ScopedWebInputEventWithLatencyInfo& other);
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 844fa67..70c10096 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -502,9 +502,12 @@
   DCHECK(!webwidget_internal_);
   DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
 
+  RenderThreadImpl* render_thread_impl = RenderThreadImpl::current();
+
   input_handler_ = std::make_unique<RenderWidgetInputHandler>(this, this);
 
-  RenderThreadImpl* render_thread_impl = RenderThreadImpl::current();
+  LayerTreeView* layer_tree_view = InitializeLayerTreeView();
+  web_widget->SetLayerTreeView(layer_tree_view);
 
   blink::scheduler::WebThreadScheduler* main_thread_scheduler = nullptr;
   if (render_thread_impl)
@@ -1400,47 +1403,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // WebWidgetClient
 
-blink::WebLayerTreeView* RenderWidget::InitializeLayerTreeView() {
-  DCHECK(!host_closing_);
-
-  layer_tree_view_ = std::make_unique<LayerTreeView>(
-      this, compositor_deps_->GetCompositorMainThreadTaskRunner(),
-      compositor_deps_->GetCompositorImplThreadTaskRunner(),
-      compositor_deps_->GetTaskGraphRunner(),
-      compositor_deps_->GetWebMainThreadScheduler());
-  layer_tree_view_->Initialize(
-      GenerateLayerTreeSettings(compositor_deps_, for_oopif_,
-                                screen_info_.rect.size(),
-                                screen_info_.device_scale_factor),
-      compositor_deps_->CreateUkmRecorderFactory());
-
-  UpdateSurfaceAndScreenInfo(local_surface_id_from_parent_,
-                             compositor_viewport_pixel_size_, screen_info_);
-  layer_tree_view_->SetRasterColorSpace(
-      screen_info_.color_space.GetRasterColorSpace());
-  layer_tree_view_->SetContentSourceId(current_content_source_id_);
-  // For background pages and certain tests, we don't want to trigger
-  // LayerTreeFrameSink creation.
-  bool should_generate_frame_sink =
-      !compositor_never_visible_ && RenderThreadImpl::current();
-  if (!should_generate_frame_sink)
-    layer_tree_view_->SetNeverVisible();
-
-  StartCompositor();
-  DCHECK_NE(MSG_ROUTING_NONE, routing_id_);
-  layer_tree_view_->SetFrameSinkId(
-      viz::FrameSinkId(RenderThread::Get()->GetClientId(), routing_id_));
-
-  RenderThreadImpl* render_thread = RenderThreadImpl::current();
-  if (render_thread) {
-    input_event_queue_ = new MainThreadEventQueue(
-        this, render_thread->GetWebMainThreadScheduler()->InputTaskRunner(),
-        render_thread->GetWebMainThreadScheduler(), should_generate_frame_sink);
-  }
-
-  return layer_tree_view_.get();
-}
-
 void RenderWidget::IntrinsicSizingInfoChanged(
     const blink::WebIntrinsicSizingInfo& sizing_info) {
   Send(new WidgetHostMsg_IntrinsicSizingInfoChanged(routing_id_, sizing_info));
@@ -1560,6 +1522,48 @@
   SetPendingWindowRect(initial_rect_);
 }
 
+LayerTreeView* RenderWidget::InitializeLayerTreeView() {
+  TRACE_EVENT0("blink", "RenderWidget::InitializeLayerTreeView");
+  DCHECK(!host_closing_);
+
+  layer_tree_view_ = std::make_unique<LayerTreeView>(
+      this, compositor_deps_->GetCompositorMainThreadTaskRunner(),
+      compositor_deps_->GetCompositorImplThreadTaskRunner(),
+      compositor_deps_->GetTaskGraphRunner(),
+      compositor_deps_->GetWebMainThreadScheduler());
+  layer_tree_view_->Initialize(
+      GenerateLayerTreeSettings(compositor_deps_, for_oopif_,
+                                screen_info_.rect.size(),
+                                screen_info_.device_scale_factor),
+      compositor_deps_->CreateUkmRecorderFactory());
+
+  UpdateSurfaceAndScreenInfo(local_surface_id_from_parent_,
+                             compositor_viewport_pixel_size_, screen_info_);
+  layer_tree_view_->SetRasterColorSpace(
+      screen_info_.color_space.GetRasterColorSpace());
+  layer_tree_view_->SetContentSourceId(current_content_source_id_);
+  // For background pages and certain tests, we don't want to trigger
+  // LayerTreeFrameSink creation.
+  bool should_generate_frame_sink =
+      !compositor_never_visible_ && RenderThreadImpl::current();
+  if (!should_generate_frame_sink)
+    layer_tree_view_->SetNeverVisible();
+
+  StartCompositor();
+  DCHECK_NE(MSG_ROUTING_NONE, routing_id_);
+  layer_tree_view_->SetFrameSinkId(
+      viz::FrameSinkId(RenderThread::Get()->GetClientId(), routing_id_));
+
+  RenderThreadImpl* render_thread = RenderThreadImpl::current();
+  if (render_thread) {
+    input_event_queue_ = base::MakeRefCounted<MainThreadEventQueue>(
+        this, render_thread->GetWebMainThreadScheduler()->InputTaskRunner(),
+        render_thread->GetWebMainThreadScheduler(), should_generate_frame_sink);
+  }
+
+  return layer_tree_view_.get();
+}
+
 void RenderWidget::DoDeferredClose() {
   WillCloseLayerTreeView();
   Send(new WidgetHostMsg_Close(routing_id_));
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index cd85950..af9d4c3 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -301,7 +301,6 @@
                       const gfx::Rect& window_screen_rect) override;
 
   // blink::WebWidgetClient
-  blink::WebLayerTreeView* InitializeLayerTreeView() override;
   void IntrinsicSizingInfoChanged(
       const blink::WebIntrinsicSizingInfo&) override;
   void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) override;
@@ -561,6 +560,8 @@
 
   static scoped_refptr<base::SingleThreadTaskRunner> GetCleanupTaskRunner();
 
+  LayerTreeView* InitializeLayerTreeView();
+
   void DoDeferredClose();
   void NotifyOnClose();
 
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index ebb5925..738b93e 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -137,6 +137,11 @@
   virtual ~PepperWidget() {}
 
   // WebWidget API
+  void SetLayerTreeView(blink::WebLayerTreeView*) override {
+    // Does nothing, as the LayerTreeView can be accessed from the RenderWidget
+    // directly.
+  }
+
   void Close() override { delete this; }
 
   WebSize Size() override { return size_; }
@@ -341,8 +346,6 @@
       layer_tree_view()->ClearRootLayer();
     return;
   }
-  if (!layer_tree_view())
-    InitializeLayerTreeView();
   UpdateLayerBounds();
   layer_->SetIsDrawable(true);
   layer_tree_view()->SetRootLayer(layer_);
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index e610614..269f94d 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -131,6 +131,7 @@
 
 class StubWebWidget : public blink::WebWidget {
  public:
+  void SetLayerTreeView(blink::WebLayerTreeView*) override {}
   blink::WebURL GetURLForDebugTrace() override { return {}; }
 };
 
@@ -361,8 +362,6 @@
 // Tests that if a RenderWidget is auto-resized, it requests a new
 // viz::LocalSurfaceId to be allocated on the impl thread.
 TEST_F(RenderWidgetUnittest, AutoResizeAllocatedLocalSurfaceId) {
-  widget()->InitializeLayerTreeView();
-
   viz::ParentLocalSurfaceIdAllocator allocator;
 
   // Enable auto-resize.
diff --git a/content/shell/browser/layout_test/blink_test_controller.cc b/content/shell/browser/layout_test/blink_test_controller.cc
index c1ff79a..93f64ce 100644
--- a/content/shell/browser/layout_test/blink_test_controller.cc
+++ b/content/shell/browser/layout_test/blink_test_controller.cc
@@ -534,11 +534,29 @@
   composite_all_frames_node_queue_ = std::queue<Node*>();
   weak_factory_.InvalidateWeakPtrs();
 
+  bool discard_main_window = false;
+  bool discard_secondary_window = false;
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kResetShellBetweenTests)) {
+    discard_main_window = true;
+    discard_secondary_window = true;
+  }
+
 #if defined(OS_ANDROID)
   // Re-using the shell's main window on Android causes issues with networking
   // requests never succeeding. See http://crbug.com/277652.
-  DiscardMainWindow();
+  discard_main_window = true;
 #endif
+
+  if (discard_main_window) {
+    DiscardMainWindow();
+    devtools_window_ = nullptr;
+  }
+  if (discard_secondary_window && secondary_window_) {
+    secondary_window_->Close();
+    secondary_window_ = nullptr;
+  }
+
   return true;
 }
 
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h
index fbe8e4a..4faf60e 100644
--- a/content/shell/browser/shell.h
+++ b/content/shell/browser/shell.h
@@ -175,7 +175,7 @@
       RenderFrameHost* frame,
       const BluetoothChooser::EventHandler& event_handler) override;
 #if defined(OS_MACOSX)
-  void HandleKeyboardEvent(WebContents* source,
+  bool HandleKeyboardEvent(WebContents* source,
                            const NativeWebKeyboardEvent& event) override;
 #endif
   bool DidAddMessageToConsole(WebContents* source,
diff --git a/content/shell/browser/shell_mac.mm b/content/shell/browser/shell_mac.mm
index c283fe8..80fd97e 100644
--- a/content/shell/browser/shell_mac.mm
+++ b/content/shell/browser/shell_mac.mm
@@ -317,10 +317,10 @@
   }
 }
 
-void Shell::HandleKeyboardEvent(WebContents* source,
+bool Shell::HandleKeyboardEvent(WebContents* source,
                                 const NativeWebKeyboardEvent& event) {
   if (event.skip_in_browser || headless_ || hide_toolbar_)
-    return;
+    return false;
 
   // The event handling to get this strictly right is a tangle; cheat here a bit
   // by just letting the menus have a chance at it.
@@ -328,11 +328,13 @@
     if (([event.os_event modifierFlags] & NSCommandKeyMask) &&
         [[event.os_event characters] isEqual:@"l"]) {
       [window_ makeFirstResponder:url_edit_view_];
-      return;
+      return true;
     }
 
     [[NSApp mainMenu] performKeyEquivalent:event.os_event];
+    return true;
   }
+  return false;
 }
 
 }  // namespace content
diff --git a/content/shell/common/layout_test/layout_test_switches.cc b/content/shell/common/layout_test/layout_test_switches.cc
index 262a246..5e00a75 100644
--- a/content/shell/common/layout_test/layout_test_switches.cc
+++ b/content/shell/common/layout_test/layout_test_switches.cc
@@ -46,6 +46,10 @@
 // Encode binary layout test results (images, audio) using base64.
 const char kEncodeBinary[] = "encode-binary";
 
+// Request Content Shell between tests. This causes tests to run twice as
+// slowly, but provides more consistent results.
+const char kResetShellBetweenTests[] = "reset-shell-between-tests";
+
 // Request the render trees of pages to be dumped as text once they have
 // finished loading.
 const char kRunWebTests[] = "run-web-tests";
diff --git a/content/shell/common/layout_test/layout_test_switches.h b/content/shell/common/layout_test/layout_test_switches.h
index ebbf47c..5be1c267 100644
--- a/content/shell/common/layout_test/layout_test_switches.h
+++ b/content/shell/common/layout_test/layout_test_switches.h
@@ -28,6 +28,7 @@
 extern const char kAlwaysUseComplexText[];
 extern const char kEnableLeakDetection[];
 extern const char kEncodeBinary[];
+extern const char kResetShellBetweenTests[];
 extern const char kRunWebTests[];
 extern const char kStableReleaseMode[];
 extern const char kTestsInBlink[];
diff --git a/content/shell/test_runner/web_view_test_proxy.cc b/content/shell/test_runner/web_view_test_proxy.cc
index 28529d5..a7a7b28b 100644
--- a/content/shell/test_runner/web_view_test_proxy.cc
+++ b/content/shell/test_runner/web_view_test_proxy.cc
@@ -29,9 +29,6 @@
 void ProxyWebWidgetClient::DidInvalidateRect(const blink::WebRect& r) {
   base_class_widget_client_->DidInvalidateRect(r);
 }
-blink::WebLayerTreeView* ProxyWebWidgetClient::InitializeLayerTreeView() {
-  return base_class_widget_client_->InitializeLayerTreeView();
-}
 bool ProxyWebWidgetClient::AllowsBrokenNullLayerTreeView() const {
   return base_class_widget_client_->AllowsBrokenNullLayerTreeView();
 }
diff --git a/content/shell/test_runner/web_view_test_proxy.h b/content/shell/test_runner/web_view_test_proxy.h
index 5d91c52e..30a95923 100644
--- a/content/shell/test_runner/web_view_test_proxy.h
+++ b/content/shell/test_runner/web_view_test_proxy.h
@@ -61,7 +61,6 @@
 
   // blink::WebWidgetClient implementation.
   void DidInvalidateRect(const blink::WebRect&) override;
-  blink::WebLayerTreeView* InitializeLayerTreeView() override;
   bool AllowsBrokenNullLayerTreeView() const override;
   void ScheduleAnimation() override;
   void IntrinsicSizingInfoChanged(
diff --git a/content/shell/test_runner/web_widget_test_client.cc b/content/shell/test_runner/web_widget_test_client.cc
index 343997f..042e8aa 100644
--- a/content/shell/test_runner/web_widget_test_client.cc
+++ b/content/shell/test_runner/web_widget_test_client.cc
@@ -102,12 +102,6 @@
   web_widget_test_proxy_base_->event_sender()->DoDragDrop(data, mask);
 }
 
-blink::WebLayerTreeView* WebWidgetTestClient::InitializeLayerTreeView() {
-  // This call should go to the production client, not here.
-  NOTREACHED();
-  return nullptr;
-}
-
 bool WebWidgetTestClient::AllowsBrokenNullLayerTreeView() const {
   // This call should go to the production client, not here.
   NOTREACHED();
diff --git a/content/shell/test_runner/web_widget_test_client.h b/content/shell/test_runner/web_widget_test_client.h
index d589721..f4c6666 100644
--- a/content/shell/test_runner/web_widget_test_client.h
+++ b/content/shell/test_runner/web_widget_test_client.h
@@ -44,7 +44,6 @@
                      const blink::WebPoint& image_offset) override;
 
   // WebWidgetClient overrides that are not used.
-  blink::WebLayerTreeView* InitializeLayerTreeView() override;
   bool AllowsBrokenNullLayerTreeView() const override;
 
  private:
diff --git a/content/test/gpu/gpu_tests/context_lost_expectations.py b/content/test/gpu/gpu_tests/context_lost_expectations.py
index 2953bab..cbcf163 100644
--- a/content/test/gpu/gpu_tests/context_lost_expectations.py
+++ b/content/test/gpu/gpu_tests/context_lost_expectations.py
@@ -27,10 +27,6 @@
     self.Skip('ContextLost_WebGLContextLostFromSelectElement',
               ['win8', 'nvidia'], bug=524808)
 
-    # Flaky on Win10
-    self.Flaky('ContextLost_WebGLUnblockedAfterUserInitiatedReload',
-              ['win10', 'debug'], bug=895765)
-
     # Flakily timing out on Win x64 Debug bot.
     # Unfortunately we can't identify this separately from the 32-bit bots.
     # Also unfortunately, the flaky retry mechanism doesn't work well in
diff --git a/content/test/gpu/gpu_tests/gpu_process_expectations.py b/content/test/gpu/gpu_tests/gpu_process_expectations.py
index ef1f7d8..743db5d2 100644
--- a/content/test/gpu/gpu_tests/gpu_process_expectations.py
+++ b/content/test/gpu/gpu_tests/gpu_process_expectations.py
@@ -16,3 +16,5 @@
     # Test needs fixing for Nexus 9
     self.Fail('GpuProcess_disabling_workarounds_works', ['android', 'nvidia'],
               bug=895020)
+    self.Fail('GpuProcess_webgl_disabled_extension', ['android', 'nvidia'],
+              bug=895945)
diff --git a/docs/README.md b/docs/README.md
index 86db0ce7..579c5f4 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -330,6 +330,7 @@
     install Chromium OS on VMWare.
 *   [User Data Directory](user_data_dir.md) - How the user data and cache
     directories are determined on all platforms.
+*   [Mojo](../mojo/README.md) - IPC mechanism used by services.
 
 ### Probably Obsolete
 *   [TPM Quick Reference](tpm_quick_ref.md) - Trusted Platform Module notes.
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index cfccd40..99fc005 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -405,7 +405,7 @@
   return content::KeyboardEventProcessingResult::NOT_HANDLED;
 }
 
-void AppWindow::HandleKeyboardEvent(
+bool AppWindow::HandleKeyboardEvent(
     WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
   // If the window is currently fullscreen and not forced, ESC should leave
@@ -414,10 +414,10 @@
   if (event.windows_key_code == ui::VKEY_ESCAPE && IsFullscreen() &&
       !IsForcedFullscreen()) {
     Restore();
-    return;
+    return true;
   }
 
-  native_app_window_->HandleKeyboardEvent(event);
+  return native_app_window_->HandleKeyboardEvent(event);
 }
 
 void AppWindow::RequestToLockMouse(WebContents* web_contents,
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h
index 8e80ca8..7950d9a 100644
--- a/extensions/browser/app_window/app_window.h
+++ b/extensions/browser/app_window/app_window.h
@@ -433,7 +433,7 @@
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   void RequestToLockMouse(content::WebContents* web_contents,
diff --git a/extensions/browser/app_window/native_app_window.h b/extensions/browser/app_window/native_app_window.h
index 0ae528f..f6f4ed5 100644
--- a/extensions/browser/app_window/native_app_window.h
+++ b/extensions/browser/app_window/native_app_window.h
@@ -58,7 +58,7 @@
 
   // Allows the window to handle unhandled keyboard messages coming back from
   // the renderer.
-  virtual void HandleKeyboardEvent(
+  virtual bool HandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) = 0;
 
   // Returns true if the window has no frame, as for a window opened by
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index 613f905..3f156db5 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -588,13 +588,13 @@
          web_view_guest_delegate_->HandleContextMenu(params);
 }
 
-void WebViewGuest::HandleKeyboardEvent(
+bool WebViewGuest::HandleKeyboardEvent(
     WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
   if (HandleKeyboardShortcuts(event))
-    return;
+    return true;
 
-  GuestViewBase::HandleKeyboardEvent(source, event);
+  return GuestViewBase::HandleKeyboardEvent(source, event);
 }
 
 bool WebViewGuest::PreHandleGestureEvent(WebContents* source,
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h
index d61df18..343f162 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.h
+++ b/extensions/browser/guest_view/web_view/web_view_guest.h
@@ -210,7 +210,7 @@
                               const base::string16& source_id) final;
   void CloseContents(content::WebContents* source) final;
   bool HandleContextMenu(const content::ContextMenuParams& params) final;
-  void HandleKeyboardEvent(content::WebContents* source,
+  bool HandleKeyboardEvent(content::WebContents* source,
                            const content::NativeWebKeyboardEvent& event) final;
   void LoadProgressChanged(content::WebContents* source, double progress) final;
   bool PreHandleGestureEvent(content::WebContents* source,
diff --git a/extensions/components/native_app_window/native_app_window_views.cc b/extensions/components/native_app_window/native_app_window_views.cc
index 59e4f97..9eaea15 100644
--- a/extensions/components/native_app_window/native_app_window_views.cc
+++ b/extensions/components/native_app_window/native_app_window_views.cc
@@ -379,10 +379,10 @@
   // Stub implementation. See also ChromeNativeAppWindowViews.
 }
 
-void NativeAppWindowViews::HandleKeyboardEvent(
+bool NativeAppWindowViews::HandleKeyboardEvent(
     const content::NativeWebKeyboardEvent& event) {
-  unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
-                                                        GetFocusManager());
+  return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+      event, GetFocusManager());
 }
 
 bool NativeAppWindowViews::IsFrameless() const {
diff --git a/extensions/components/native_app_window/native_app_window_views.h b/extensions/components/native_app_window/native_app_window_views.h
index 5c9b875..6bcc3e5 100644
--- a/extensions/components/native_app_window/native_app_window_views.h
+++ b/extensions/components/native_app_window/native_app_window_views.h
@@ -132,7 +132,7 @@
       const std::vector<extensions::DraggableRegion>& regions) override;
   SkRegion* GetDraggableRegion() override;
   void UpdateShape(std::unique_ptr<ShapeRects> rects) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) override;
   bool IsFrameless() const override;
   bool HasFrameColor() const override;
diff --git a/extensions/shell/browser/shell_native_app_window.cc b/extensions/shell/browser/shell_native_app_window.cc
index 25741f4..33b3bf5 100644
--- a/extensions/shell/browser/shell_native_app_window.cc
+++ b/extensions/shell/browser/shell_native_app_window.cc
@@ -136,9 +136,10 @@
   NOTIMPLEMENTED();
 }
 
-void ShellNativeAppWindow::HandleKeyboardEvent(
-      const content::NativeWebKeyboardEvent& event) {
+bool ShellNativeAppWindow::HandleKeyboardEvent(
+    const content::NativeWebKeyboardEvent& event) {
   // No special handling. The WebContents will handle it.
+  return false;
 }
 
 bool ShellNativeAppWindow::IsFrameless() const {
diff --git a/extensions/shell/browser/shell_native_app_window.h b/extensions/shell/browser/shell_native_app_window.h
index a282a41..a00a052 100644
--- a/extensions/shell/browser/shell_native_app_window.h
+++ b/extensions/shell/browser/shell_native_app_window.h
@@ -53,7 +53,7 @@
       const std::vector<DraggableRegion>& regions) override;
   SkRegion* GetDraggableRegion() override;
   void UpdateShape(std::unique_ptr<ShapeRects> rects) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) override;
   bool IsFrameless() const override;
   bool HasFrameColor() const override;
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index b2d68024..d2ff1a5c 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -213,6 +213,7 @@
     "command_buffer/tests/gl_ext_multisample_compatibility_unittest.cc",
     "command_buffer/tests/gl_ext_srgb_unittest.cc",
     "command_buffer/tests/gl_ext_window_rectangles_unittest.cc",
+    "command_buffer/tests/gl_fence_sync_unittest.cc",
     "command_buffer/tests/gl_gpu_memory_buffer_unittest.cc",
     "command_buffer/tests/gl_iosurface_readback_workaround_unittest.cc",
     "command_buffer/tests/gl_lose_context_chromium_unittest.cc",
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h
index b8deb0b..6b0b363 100644
--- a/gpu/GLES2/gl2extchromium.h
+++ b/gpu/GLES2/gl2extchromium.h
@@ -1272,6 +1272,10 @@
     GLsizei height);
 #endif /* GL_CHROMIUM_unpremultiply_and_dither_copy */
 
+#ifndef GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT
+#define GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT 0x8868
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index 149deef..70cae33 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -725,6 +725,7 @@
     'valid': [
       'GL_QUERY_RESULT_EXT',
       'GL_QUERY_RESULT_AVAILABLE_EXT',
+      'GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT',
     ],
   },
   'QueryParameter': {
@@ -3707,7 +3708,12 @@
     'extension': "CHROMIUM_sync_point",
   },
   'WaitSyncTokenCHROMIUM': {
-    'type': 'NoCommand',
+    'type': 'Custom',
+    'impl_func': False,
+    'cmd_args': 'GLint namespace_id, '
+                'GLuint64 command_buffer_id, '
+                'GLuint64 release_count',
+    'client_test': False,
     'extension': "CHROMIUM_sync_point",
   },
   'DiscardBackbufferCHROMIUM': {
diff --git a/gpu/command_buffer/build_raster_cmd_buffer.py b/gpu/command_buffer/build_raster_cmd_buffer.py
index 871ad640..e7bdf1f2 100755
--- a/gpu/command_buffer/build_raster_cmd_buffer.py
+++ b/gpu/command_buffer/build_raster_cmd_buffer.py
@@ -38,6 +38,7 @@
     'valid': [
       'GL_QUERY_RESULT_EXT',
       'GL_QUERY_RESULT_AVAILABLE_EXT',
+      'GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT',
     ],
   },
   'QueryTarget': {
@@ -365,7 +366,12 @@
     'type': 'NoCommand',
   },
   'WaitSyncTokenCHROMIUM': {
-    'type': 'NoCommand',
+    'type': 'Custom',
+    'impl_func': False,
+    'cmd_args': 'GLint namespace_id, '
+                'GLuint64 command_buffer_id, '
+                'GLuint64 release_count',
+    'client_test': False,
   },
   'InitializeDiscardableTextureCHROMIUM': {
     'type': 'Custom',
diff --git a/gpu/command_buffer/client/client_test_helper.h b/gpu/command_buffer/client/client_test_helper.h
index eebbd286..9008bde 100644
--- a/gpu/command_buffer/client/client_test_helper.h
+++ b/gpu/command_buffer/client/client_test_helper.h
@@ -137,7 +137,7 @@
     DoSignalSyncToken(sync_token, &callback);
   }
 
-  MOCK_METHOD1(WaitSyncToken, void(const SyncToken&));
+  MOCK_METHOD1(WaitSyncTokenHint, void(const SyncToken&));
   MOCK_METHOD1(CanWaitUnverifiedSyncToken, bool(const SyncToken&));
   MOCK_METHOD2(CreateGpuFence,
                void(uint32_t gpu_fence_id, ClientGpuFence source));
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index 1c531c2..b422c307 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -2734,6 +2734,16 @@
   }
 }
 
+void WaitSyncTokenCHROMIUM(GLint namespace_id,
+                           GLuint64 command_buffer_id,
+                           GLuint64 release_count) {
+  gles2::cmds::WaitSyncTokenCHROMIUM* c =
+      GetCmdSpace<gles2::cmds::WaitSyncTokenCHROMIUM>();
+  if (c) {
+    c->Init(namespace_id, command_buffer_id, release_count);
+  }
+}
+
 void UnpremultiplyAndDitherCopyCHROMIUM(GLuint source_id,
                                         GLuint dest_id,
                                         GLint x,
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index 253e934..9ee4ed69 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -1193,20 +1193,26 @@
   }
 
   bool valid_value = false;
+  const bool flush_if_pending =
+      pname != GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT;
   switch (pname) {
     case GL_QUERY_RESULT_EXT:
-      if (!query->CheckResultsAvailable(helper_)) {
+      if (!query->CheckResultsAvailable(helper_, flush_if_pending)) {
         helper_->WaitForToken(query->token());
-        if (!query->CheckResultsAvailable(helper_)) {
+        if (!query->CheckResultsAvailable(helper_, flush_if_pending)) {
           FinishHelper();
-          CHECK(query->CheckResultsAvailable(helper_));
+          CHECK(query->CheckResultsAvailable(helper_, flush_if_pending));
         }
       }
       *params = query->GetResult();
       valid_value = true;
       break;
     case GL_QUERY_RESULT_AVAILABLE_EXT:
-      *params = query->CheckResultsAvailable(helper_);
+      *params = query->CheckResultsAvailable(helper_, flush_if_pending);
+      valid_value = true;
+      break;
+    case GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT:
+      *params = query->CheckResultsAvailable(helper_, flush_if_pending);
       valid_value = true;
       break;
     default:
@@ -6300,9 +6306,14 @@
     return;
   }
 
+  helper_->WaitSyncTokenCHROMIUM(
+      static_cast<GLint>(sync_token.namespace_id()),
+      sync_token.command_buffer_id().GetUnsafeValue(),
+      sync_token.release_count());
+
   // Enqueue sync token in flush after inserting command so that it's not
   // included in an automatic flush.
-  gpu_control_->WaitSyncToken(verified_sync_token);
+  gpu_control_->WaitSyncTokenHint(verified_sync_token);
 }
 
 namespace {
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index 660aced8..5d9c7af 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -3931,9 +3931,12 @@
 
   struct Cmds {
     cmds::InsertFenceSyncCHROMIUM insert_fence_sync;
+    cmds::WaitSyncTokenCHROMIUM wait_sync_token;
   };
   Cmds expected;
   expected.insert_fence_sync.Init(kFenceSync);
+  expected.wait_sync_token.Init(kNamespaceId, kCommandBufferId.GetUnsafeValue(),
+                                kFenceSync);
 
   EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(kNamespaceId));
   EXPECT_CALL(*gpu_control_, GetCommandBufferID())
@@ -3943,7 +3946,7 @@
   EXPECT_CALL(*gpu_control_, EnsureWorkVisible());
   gl_->GenSyncTokenCHROMIUM(sync_token_data);
 
-  EXPECT_CALL(*gpu_control_, WaitSyncToken(sync_token));
+  EXPECT_CALL(*gpu_control_, WaitSyncTokenHint(sync_token));
   gl_->WaitSyncTokenCHROMIUM(sync_token_data);
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
diff --git a/gpu/command_buffer/client/gpu_control.h b/gpu/command_buffer/client/gpu_control.h
index a1191f8..8e3954c 100644
--- a/gpu/command_buffer/client/gpu_control.h
+++ b/gpu/command_buffer/client/gpu_control.h
@@ -105,9 +105,11 @@
                                base::OnceClosure callback) = 0;
 
   // This allows the command buffer proxy to mark the next flush with sync token
-  // dependencies for the gpu scheduler, or to block prior to the flush in case
-  // of android webview.
-  virtual void WaitSyncToken(const SyncToken& sync_token) = 0;
+  // dependencies for the gpu scheduler. This is used in addition to the
+  // WaitSyncToken command in the command buffer which is still needed. For
+  // example, the WaitSyncToken command is used to pull texture updates when
+  // used in conjunction with MailboxManagerSync.
+  virtual void WaitSyncTokenHint(const SyncToken& sync_token) = 0;
 
   // Under some circumstances a sync token may be used which has not been
   // verified to have been flushed. For example, fence syncs queued on the same
diff --git a/gpu/command_buffer/client/query_tracker.cc b/gpu/command_buffer/client/query_tracker.cc
index 400807d..ef8a972 100644
--- a/gpu/command_buffer/client/query_tracker.cc
+++ b/gpu/command_buffer/client/query_tracker.cc
@@ -203,8 +203,8 @@
   MarkAsPending(client->cmd_buffer_helper()->InsertToken(), submit_count);
 }
 
-bool QueryTracker::Query::CheckResultsAvailable(
-    CommandBufferHelper* helper) {
+bool QueryTracker::Query::CheckResultsAvailable(CommandBufferHelper* helper,
+                                                bool flush_if_pending) {
   if (Pending()) {
     bool processed_all = base::subtle::Acquire_Load(
                              &info_.sync->process_count) == submit_count();
@@ -236,7 +236,8 @@
       }
       state_ = kComplete;
     } else {
-      if ((helper->flush_generation() - flush_count_ - 1) >= 0x80000000) {
+      if (flush_if_pending &&
+          (helper->flush_generation() - flush_count_ - 1) >= 0x80000000) {
         helper->Flush();
       } else {
         // Insert no-ops so that eventually the GPU process will see more work.
diff --git a/gpu/command_buffer/client/query_tracker.h b/gpu/command_buffer/client/query_tracker.h
index 5c12fb6d..1596e985 100644
--- a/gpu/command_buffer/client/query_tracker.h
+++ b/gpu/command_buffer/client/query_tracker.h
@@ -170,7 +170,12 @@
       return state_ == kPending;
     }
 
-    bool CheckResultsAvailable(CommandBufferHelper* helper);
+    // Checks whether the result of this query is available.
+    // If the result is pending and |flush_if_pending| is true, this will ensure
+    // that at least the commands up till the EndQuery for this query are
+    // flushed.
+    bool CheckResultsAvailable(CommandBufferHelper* helper,
+                               bool flush_if_pending);
 
     uint64_t GetResult() const;
 
diff --git a/gpu/command_buffer/client/query_tracker_unittest.cc b/gpu/command_buffer/client/query_tracker_unittest.cc
index 48d6ba5..01a1460 100644
--- a/gpu/command_buffer/client/query_tracker_unittest.cc
+++ b/gpu/command_buffer/client/query_tracker_unittest.cc
@@ -301,19 +301,30 @@
   // Store FlushGeneration count after EndQuery is called
   uint32_t gen1 = GetFlushGeneration();
 
-  // Check CheckResultsAvailable.
-  EXPECT_FALSE(query->CheckResultsAvailable(helper_.get()));
+  bool flush_if_pending = false;
+  EXPECT_FALSE(query->CheckResultsAvailable(helper_.get(), flush_if_pending));
   EXPECT_FALSE(query->NeverUsed());
   EXPECT_TRUE(query->Pending());
 
+  // No flush should happen if |flush_if_pending| is false.
   uint32_t gen2 = GetFlushGeneration();
+  EXPECT_EQ(gen1, gen2);
+
+  flush_if_pending = true;
+
+  // Check CheckResultsAvailable.
+  EXPECT_FALSE(query->CheckResultsAvailable(helper_.get(), flush_if_pending));
+  EXPECT_FALSE(query->NeverUsed());
+  EXPECT_TRUE(query->Pending());
+
+  gen2 = GetFlushGeneration();
   EXPECT_NE(gen1, gen2);
 
   // Repeated calls to CheckResultsAvailable should not flush unnecessarily
-  EXPECT_FALSE(query->CheckResultsAvailable(helper_.get()));
+  EXPECT_FALSE(query->CheckResultsAvailable(helper_.get(), flush_if_pending));
   gen1 = GetFlushGeneration();
   EXPECT_EQ(gen1, gen2);
-  EXPECT_FALSE(query->CheckResultsAvailable(helper_.get()));
+  EXPECT_FALSE(query->CheckResultsAvailable(helper_.get(), flush_if_pending));
   gen1 = GetFlushGeneration();
   EXPECT_EQ(gen1, gen2);
 
@@ -323,7 +334,7 @@
   sync->result = kResult;
 
   // Check CheckResultsAvailable.
-  EXPECT_TRUE(query->CheckResultsAvailable(helper_.get()));
+  EXPECT_TRUE(query->CheckResultsAvailable(helper_.get(), flush_if_pending));
   EXPECT_EQ(kResult, query->GetResult());
   EXPECT_FALSE(query->NeverUsed());
   EXPECT_FALSE(query->Pending());
diff --git a/gpu/command_buffer/client/raster_cmd_helper_autogen.h b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
index d513c791..ee345a055 100644
--- a/gpu/command_buffer/client/raster_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
@@ -103,6 +103,16 @@
   }
 }
 
+void WaitSyncTokenCHROMIUM(GLint namespace_id,
+                           GLuint64 command_buffer_id,
+                           GLuint64 release_count) {
+  raster::cmds::WaitSyncTokenCHROMIUM* c =
+      GetCmdSpace<raster::cmds::WaitSyncTokenCHROMIUM>();
+  if (c) {
+    c->Init(namespace_id, command_buffer_id, release_count);
+  }
+}
+
 void UnpremultiplyAndDitherCopyCHROMIUM(GLuint source_id,
                                         GLuint dest_id,
                                         GLint x,
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index e016b1e..692cfd7 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -690,20 +690,22 @@
   }
 
   bool valid_value = false;
+  const bool flush_if_pending =
+      pname != GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT;
   switch (pname) {
     case GL_QUERY_RESULT_EXT:
-      if (!query->CheckResultsAvailable(helper_)) {
+      if (!query->CheckResultsAvailable(helper_, flush_if_pending)) {
         helper_->WaitForToken(query->token());
-        if (!query->CheckResultsAvailable(helper_)) {
+        if (!query->CheckResultsAvailable(helper_, flush_if_pending)) {
           FinishHelper();
-          CHECK(query->CheckResultsAvailable(helper_));
+          CHECK(query->CheckResultsAvailable(helper_, flush_if_pending));
         }
       }
       *params = query->GetResult();
       valid_value = true;
       break;
     case GL_QUERY_RESULT_AVAILABLE_EXT:
-      *params = query->CheckResultsAvailable(helper_);
+      *params = query->CheckResultsAvailable(helper_, flush_if_pending);
       valid_value = true;
       break;
     default:
@@ -955,7 +957,14 @@
     return;
   }
 
-  gpu_control_->WaitSyncToken(verified_sync_token);
+  helper_->WaitSyncTokenCHROMIUM(
+      static_cast<GLint>(sync_token.namespace_id()),
+      sync_token.command_buffer_id().GetUnsafeValue(),
+      sync_token.release_count());
+
+  // Enqueue sync token in flush after inserting command so that it's not
+  // included in an automatic flush.
+  gpu_control_->WaitSyncTokenHint(verified_sync_token);
 }
 
 namespace {
diff --git a/gpu/command_buffer/client/raster_implementation_unittest.cc b/gpu/command_buffer/client/raster_implementation_unittest.cc
index f3edff0b..662a39a 100644
--- a/gpu/command_buffer/client/raster_implementation_unittest.cc
+++ b/gpu/command_buffer/client/raster_implementation_unittest.cc
@@ -736,9 +736,12 @@
 
   struct Cmds {
     cmds::InsertFenceSyncCHROMIUM insert_fence_sync;
+    cmds::WaitSyncTokenCHROMIUM wait_sync_token;
   };
   Cmds expected;
   expected.insert_fence_sync.Init(kFenceSync);
+  expected.wait_sync_token.Init(kNamespaceId, kCommandBufferId.GetUnsafeValue(),
+                                kFenceSync);
 
   EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(kNamespaceId));
   EXPECT_CALL(*gpu_control_, GetCommandBufferID())
@@ -748,7 +751,7 @@
   EXPECT_CALL(*gpu_control_, EnsureWorkVisible());
   gl_->GenSyncTokenCHROMIUM(sync_token_data);
 
-  EXPECT_CALL(*gpu_control_, WaitSyncToken(sync_token));
+  EXPECT_CALL(*gpu_control_, WaitSyncTokenHint(sync_token));
   gl_->WaitSyncTokenCHROMIUM(sync_token_data);
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index 148c87c..29c250c 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -13519,6 +13519,73 @@
 static_assert(offsetof(InsertFenceSyncCHROMIUM, release_count_1) == 8,
               "offset of InsertFenceSyncCHROMIUM release_count_1 should be 8");
 
+struct WaitSyncTokenCHROMIUM {
+  typedef WaitSyncTokenCHROMIUM ValueType;
+  static const CommandId kCmdId = kWaitSyncTokenCHROMIUM;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLint _namespace_id,
+            GLuint64 _command_buffer_id,
+            GLuint64 _release_count) {
+    SetHeader();
+    namespace_id = _namespace_id;
+    GLES2Util::MapUint64ToTwoUint32(static_cast<uint64_t>(_command_buffer_id),
+                                    &command_buffer_id_0, &command_buffer_id_1);
+    GLES2Util::MapUint64ToTwoUint32(static_cast<uint64_t>(_release_count),
+                                    &release_count_0, &release_count_1);
+  }
+
+  void* Set(void* cmd,
+            GLint _namespace_id,
+            GLuint64 _command_buffer_id,
+            GLuint64 _release_count) {
+    static_cast<ValueType*>(cmd)->Init(_namespace_id, _command_buffer_id,
+                                       _release_count);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  GLuint64 command_buffer_id() const volatile {
+    return static_cast<GLuint64>(GLES2Util::MapTwoUint32ToUint64(
+        command_buffer_id_0, command_buffer_id_1));
+  }
+
+  GLuint64 release_count() const volatile {
+    return static_cast<GLuint64>(
+        GLES2Util::MapTwoUint32ToUint64(release_count_0, release_count_1));
+  }
+
+  gpu::CommandHeader header;
+  int32_t namespace_id;
+  uint32_t command_buffer_id_0;
+  uint32_t command_buffer_id_1;
+  uint32_t release_count_0;
+  uint32_t release_count_1;
+};
+
+static_assert(sizeof(WaitSyncTokenCHROMIUM) == 24,
+              "size of WaitSyncTokenCHROMIUM should be 24");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, header) == 0,
+              "offset of WaitSyncTokenCHROMIUM header should be 0");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, namespace_id) == 4,
+              "offset of WaitSyncTokenCHROMIUM namespace_id should be 4");
+static_assert(
+    offsetof(WaitSyncTokenCHROMIUM, command_buffer_id_0) == 8,
+    "offset of WaitSyncTokenCHROMIUM command_buffer_id_0 should be 8");
+static_assert(
+    offsetof(WaitSyncTokenCHROMIUM, command_buffer_id_1) == 12,
+    "offset of WaitSyncTokenCHROMIUM command_buffer_id_1 should be 12");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, release_count_0) == 16,
+              "offset of WaitSyncTokenCHROMIUM release_count_0 should be 16");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, release_count_1) == 20,
+              "offset of WaitSyncTokenCHROMIUM release_count_1 should be 20");
+
 struct UnpremultiplyAndDitherCopyCHROMIUM {
   typedef UnpremultiplyAndDitherCopyCHROMIUM ValueType;
   static const CommandId kCmdId = kUnpremultiplyAndDitherCopyCHROMIUM;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index ccb500d..ad5eec7 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -4520,6 +4520,21 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(GLES2FormatTest, WaitSyncTokenCHROMIUM) {
+  cmds::WaitSyncTokenCHROMIUM& cmd =
+      *GetBufferAs<cmds::WaitSyncTokenCHROMIUM>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLint>(11), static_cast<GLuint64>(12),
+              static_cast<GLuint64>(13));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::WaitSyncTokenCHROMIUM::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLint>(11), cmd.namespace_id);
+  EXPECT_EQ(static_cast<GLuint64>(12), cmd.command_buffer_id());
+  EXPECT_EQ(static_cast<GLuint64>(13), cmd.release_count());
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(GLES2FormatTest, UnpremultiplyAndDitherCopyCHROMIUM) {
   cmds::UnpremultiplyAndDitherCopyCHROMIUM& cmd =
       *GetBufferAs<cmds::UnpremultiplyAndDitherCopyCHROMIUM>();
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index b0ca0c4..5d8a9131 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -290,63 +290,64 @@
   OP(DiscardFramebufferEXTImmediate)                       /* 531 */ \
   OP(LoseContextCHROMIUM)                                  /* 532 */ \
   OP(InsertFenceSyncCHROMIUM)                              /* 533 */ \
-  OP(UnpremultiplyAndDitherCopyCHROMIUM)                   /* 534 */ \
-  OP(DrawBuffersEXTImmediate)                              /* 535 */ \
-  OP(DiscardBackbufferCHROMIUM)                            /* 536 */ \
-  OP(ScheduleOverlayPlaneCHROMIUM)                         /* 537 */ \
-  OP(ScheduleCALayerSharedStateCHROMIUM)                   /* 538 */ \
-  OP(ScheduleCALayerCHROMIUM)                              /* 539 */ \
-  OP(ScheduleCALayerInUseQueryCHROMIUMImmediate)           /* 540 */ \
-  OP(CommitOverlayPlanesCHROMIUM)                          /* 541 */ \
-  OP(FlushDriverCachesCHROMIUM)                            /* 542 */ \
-  OP(ScheduleDCLayerSharedStateCHROMIUM)                   /* 543 */ \
-  OP(ScheduleDCLayerCHROMIUM)                              /* 544 */ \
-  OP(MatrixLoadfCHROMIUMImmediate)                         /* 545 */ \
-  OP(MatrixLoadIdentityCHROMIUM)                           /* 546 */ \
-  OP(GenPathsCHROMIUM)                                     /* 547 */ \
-  OP(DeletePathsCHROMIUM)                                  /* 548 */ \
-  OP(IsPathCHROMIUM)                                       /* 549 */ \
-  OP(PathCommandsCHROMIUM)                                 /* 550 */ \
-  OP(PathParameterfCHROMIUM)                               /* 551 */ \
-  OP(PathParameteriCHROMIUM)                               /* 552 */ \
-  OP(PathStencilFuncCHROMIUM)                              /* 553 */ \
-  OP(StencilFillPathCHROMIUM)                              /* 554 */ \
-  OP(StencilStrokePathCHROMIUM)                            /* 555 */ \
-  OP(CoverFillPathCHROMIUM)                                /* 556 */ \
-  OP(CoverStrokePathCHROMIUM)                              /* 557 */ \
-  OP(StencilThenCoverFillPathCHROMIUM)                     /* 558 */ \
-  OP(StencilThenCoverStrokePathCHROMIUM)                   /* 559 */ \
-  OP(StencilFillPathInstancedCHROMIUM)                     /* 560 */ \
-  OP(StencilStrokePathInstancedCHROMIUM)                   /* 561 */ \
-  OP(CoverFillPathInstancedCHROMIUM)                       /* 562 */ \
-  OP(CoverStrokePathInstancedCHROMIUM)                     /* 563 */ \
-  OP(StencilThenCoverFillPathInstancedCHROMIUM)            /* 564 */ \
-  OP(StencilThenCoverStrokePathInstancedCHROMIUM)          /* 565 */ \
-  OP(BindFragmentInputLocationCHROMIUMBucket)              /* 566 */ \
-  OP(ProgramPathFragmentInputGenCHROMIUM)                  /* 567 */ \
-  OP(CoverageModulationCHROMIUM)                           /* 568 */ \
-  OP(BlendBarrierKHR)                                      /* 569 */ \
-  OP(ApplyScreenSpaceAntialiasingCHROMIUM)                 /* 570 */ \
-  OP(BindFragDataLocationIndexedEXTBucket)                 /* 571 */ \
-  OP(BindFragDataLocationEXTBucket)                        /* 572 */ \
-  OP(GetFragDataIndexEXT)                                  /* 573 */ \
-  OP(UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate) /* 574 */ \
-  OP(OverlayPromotionHintCHROMIUM)                         /* 575 */ \
-  OP(SwapBuffersWithBoundsCHROMIUMImmediate)               /* 576 */ \
-  OP(SetDrawRectangleCHROMIUM)                             /* 577 */ \
-  OP(SetEnableDCLayersCHROMIUM)                            /* 578 */ \
-  OP(InitializeDiscardableTextureCHROMIUM)                 /* 579 */ \
-  OP(UnlockDiscardableTextureCHROMIUM)                     /* 580 */ \
-  OP(LockDiscardableTextureCHROMIUM)                       /* 581 */ \
-  OP(TexStorage2DImageCHROMIUM)                            /* 582 */ \
-  OP(SetColorSpaceMetadataCHROMIUM)                        /* 583 */ \
-  OP(WindowRectanglesEXTImmediate)                         /* 584 */ \
-  OP(CreateGpuFenceINTERNAL)                               /* 585 */ \
-  OP(WaitGpuFenceCHROMIUM)                                 /* 586 */ \
-  OP(DestroyGpuFenceCHROMIUM)                              /* 587 */ \
-  OP(SetReadbackBufferShadowAllocationINTERNAL)            /* 588 */ \
-  OP(FramebufferTextureMultiviewLayeredANGLE)              /* 589 */ \
-  OP(MaxShaderCompilerThreadsKHR)                          /* 590 */
+  OP(WaitSyncTokenCHROMIUM)                                /* 534 */ \
+  OP(UnpremultiplyAndDitherCopyCHROMIUM)                   /* 535 */ \
+  OP(DrawBuffersEXTImmediate)                              /* 536 */ \
+  OP(DiscardBackbufferCHROMIUM)                            /* 537 */ \
+  OP(ScheduleOverlayPlaneCHROMIUM)                         /* 538 */ \
+  OP(ScheduleCALayerSharedStateCHROMIUM)                   /* 539 */ \
+  OP(ScheduleCALayerCHROMIUM)                              /* 540 */ \
+  OP(ScheduleCALayerInUseQueryCHROMIUMImmediate)           /* 541 */ \
+  OP(CommitOverlayPlanesCHROMIUM)                          /* 542 */ \
+  OP(FlushDriverCachesCHROMIUM)                            /* 543 */ \
+  OP(ScheduleDCLayerSharedStateCHROMIUM)                   /* 544 */ \
+  OP(ScheduleDCLayerCHROMIUM)                              /* 545 */ \
+  OP(MatrixLoadfCHROMIUMImmediate)                         /* 546 */ \
+  OP(MatrixLoadIdentityCHROMIUM)                           /* 547 */ \
+  OP(GenPathsCHROMIUM)                                     /* 548 */ \
+  OP(DeletePathsCHROMIUM)                                  /* 549 */ \
+  OP(IsPathCHROMIUM)                                       /* 550 */ \
+  OP(PathCommandsCHROMIUM)                                 /* 551 */ \
+  OP(PathParameterfCHROMIUM)                               /* 552 */ \
+  OP(PathParameteriCHROMIUM)                               /* 553 */ \
+  OP(PathStencilFuncCHROMIUM)                              /* 554 */ \
+  OP(StencilFillPathCHROMIUM)                              /* 555 */ \
+  OP(StencilStrokePathCHROMIUM)                            /* 556 */ \
+  OP(CoverFillPathCHROMIUM)                                /* 557 */ \
+  OP(CoverStrokePathCHROMIUM)                              /* 558 */ \
+  OP(StencilThenCoverFillPathCHROMIUM)                     /* 559 */ \
+  OP(StencilThenCoverStrokePathCHROMIUM)                   /* 560 */ \
+  OP(StencilFillPathInstancedCHROMIUM)                     /* 561 */ \
+  OP(StencilStrokePathInstancedCHROMIUM)                   /* 562 */ \
+  OP(CoverFillPathInstancedCHROMIUM)                       /* 563 */ \
+  OP(CoverStrokePathInstancedCHROMIUM)                     /* 564 */ \
+  OP(StencilThenCoverFillPathInstancedCHROMIUM)            /* 565 */ \
+  OP(StencilThenCoverStrokePathInstancedCHROMIUM)          /* 566 */ \
+  OP(BindFragmentInputLocationCHROMIUMBucket)              /* 567 */ \
+  OP(ProgramPathFragmentInputGenCHROMIUM)                  /* 568 */ \
+  OP(CoverageModulationCHROMIUM)                           /* 569 */ \
+  OP(BlendBarrierKHR)                                      /* 570 */ \
+  OP(ApplyScreenSpaceAntialiasingCHROMIUM)                 /* 571 */ \
+  OP(BindFragDataLocationIndexedEXTBucket)                 /* 572 */ \
+  OP(BindFragDataLocationEXTBucket)                        /* 573 */ \
+  OP(GetFragDataIndexEXT)                                  /* 574 */ \
+  OP(UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate) /* 575 */ \
+  OP(OverlayPromotionHintCHROMIUM)                         /* 576 */ \
+  OP(SwapBuffersWithBoundsCHROMIUMImmediate)               /* 577 */ \
+  OP(SetDrawRectangleCHROMIUM)                             /* 578 */ \
+  OP(SetEnableDCLayersCHROMIUM)                            /* 579 */ \
+  OP(InitializeDiscardableTextureCHROMIUM)                 /* 580 */ \
+  OP(UnlockDiscardableTextureCHROMIUM)                     /* 581 */ \
+  OP(LockDiscardableTextureCHROMIUM)                       /* 582 */ \
+  OP(TexStorage2DImageCHROMIUM)                            /* 583 */ \
+  OP(SetColorSpaceMetadataCHROMIUM)                        /* 584 */ \
+  OP(WindowRectanglesEXTImmediate)                         /* 585 */ \
+  OP(CreateGpuFenceINTERNAL)                               /* 586 */ \
+  OP(WaitGpuFenceCHROMIUM)                                 /* 587 */ \
+  OP(DestroyGpuFenceCHROMIUM)                              /* 588 */ \
+  OP(SetReadbackBufferShadowAllocationINTERNAL)            /* 589 */ \
+  OP(FramebufferTextureMultiviewLayeredANGLE)              /* 590 */ \
+  OP(MaxShaderCompilerThreadsKHR)                          /* 591 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index 5129de10..b3ce56f 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -1732,6 +1732,9 @@
         0x8867, "GL_QUERY_RESULT_AVAILABLE_EXT",
     },
     {
+        0x8868, "GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT",
+    },
+    {
         0x8869, "GL_MAX_VERTEX_ATTRIBS",
     },
     {
@@ -5696,6 +5699,8 @@
   static const EnumToString string_table[] = {
       {GL_QUERY_RESULT_EXT, "GL_QUERY_RESULT_EXT"},
       {GL_QUERY_RESULT_AVAILABLE_EXT, "GL_QUERY_RESULT_AVAILABLE_EXT"},
+      {GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT,
+       "GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT"},
   };
   return GLES2Util::GetQualifiedEnumString(string_table,
                                            arraysize(string_table), value);
diff --git a/gpu/command_buffer/common/raster_cmd_format_autogen.h b/gpu/command_buffer/common/raster_cmd_format_autogen.h
index 3ce82fa..075d6a9 100644
--- a/gpu/command_buffer/common/raster_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_autogen.h
@@ -446,6 +446,75 @@
 static_assert(offsetof(InsertFenceSyncCHROMIUM, release_count_1) == 8,
               "offset of InsertFenceSyncCHROMIUM release_count_1 should be 8");
 
+struct WaitSyncTokenCHROMIUM {
+  typedef WaitSyncTokenCHROMIUM ValueType;
+  static const CommandId kCmdId = kWaitSyncTokenCHROMIUM;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLint _namespace_id,
+            GLuint64 _command_buffer_id,
+            GLuint64 _release_count) {
+    SetHeader();
+    namespace_id = _namespace_id;
+    gles2::GLES2Util::MapUint64ToTwoUint32(
+        static_cast<uint64_t>(_command_buffer_id), &command_buffer_id_0,
+        &command_buffer_id_1);
+    gles2::GLES2Util::MapUint64ToTwoUint32(
+        static_cast<uint64_t>(_release_count), &release_count_0,
+        &release_count_1);
+  }
+
+  void* Set(void* cmd,
+            GLint _namespace_id,
+            GLuint64 _command_buffer_id,
+            GLuint64 _release_count) {
+    static_cast<ValueType*>(cmd)->Init(_namespace_id, _command_buffer_id,
+                                       _release_count);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  GLuint64 command_buffer_id() const volatile {
+    return static_cast<GLuint64>(gles2::GLES2Util::MapTwoUint32ToUint64(
+        command_buffer_id_0, command_buffer_id_1));
+  }
+
+  GLuint64 release_count() const volatile {
+    return static_cast<GLuint64>(gles2::GLES2Util::MapTwoUint32ToUint64(
+        release_count_0, release_count_1));
+  }
+
+  gpu::CommandHeader header;
+  int32_t namespace_id;
+  uint32_t command_buffer_id_0;
+  uint32_t command_buffer_id_1;
+  uint32_t release_count_0;
+  uint32_t release_count_1;
+};
+
+static_assert(sizeof(WaitSyncTokenCHROMIUM) == 24,
+              "size of WaitSyncTokenCHROMIUM should be 24");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, header) == 0,
+              "offset of WaitSyncTokenCHROMIUM header should be 0");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, namespace_id) == 4,
+              "offset of WaitSyncTokenCHROMIUM namespace_id should be 4");
+static_assert(
+    offsetof(WaitSyncTokenCHROMIUM, command_buffer_id_0) == 8,
+    "offset of WaitSyncTokenCHROMIUM command_buffer_id_0 should be 8");
+static_assert(
+    offsetof(WaitSyncTokenCHROMIUM, command_buffer_id_1) == 12,
+    "offset of WaitSyncTokenCHROMIUM command_buffer_id_1 should be 12");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, release_count_0) == 16,
+              "offset of WaitSyncTokenCHROMIUM release_count_0 should be 16");
+static_assert(offsetof(WaitSyncTokenCHROMIUM, release_count_1) == 20,
+              "offset of WaitSyncTokenCHROMIUM release_count_1 should be 20");
+
 struct UnpremultiplyAndDitherCopyCHROMIUM {
   typedef UnpremultiplyAndDitherCopyCHROMIUM ValueType;
   static const CommandId kCmdId = kUnpremultiplyAndDitherCopyCHROMIUM;
diff --git a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
index a9051c0..58d0f73 100644
--- a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
@@ -159,6 +159,21 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(RasterFormatTest, WaitSyncTokenCHROMIUM) {
+  cmds::WaitSyncTokenCHROMIUM& cmd =
+      *GetBufferAs<cmds::WaitSyncTokenCHROMIUM>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLint>(11), static_cast<GLuint64>(12),
+              static_cast<GLuint64>(13));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::WaitSyncTokenCHROMIUM::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLint>(11), cmd.namespace_id);
+  EXPECT_EQ(static_cast<GLuint64>(12), cmd.command_buffer_id());
+  EXPECT_EQ(static_cast<GLuint64>(13), cmd.release_count());
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(RasterFormatTest, UnpremultiplyAndDitherCopyCHROMIUM) {
   cmds::UnpremultiplyAndDitherCopyCHROMIUM& cmd =
       *GetBufferAs<cmds::UnpremultiplyAndDitherCopyCHROMIUM>();
diff --git a/gpu/command_buffer/common/raster_cmd_ids_autogen.h b/gpu/command_buffer/common/raster_cmd_ids_autogen.h
index 52c2507..39f0670 100644
--- a/gpu/command_buffer/common/raster_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_ids_autogen.h
@@ -23,26 +23,27 @@
   OP(EndQueryEXT)                              /* 264 */ \
   OP(LoseContextCHROMIUM)                      /* 265 */ \
   OP(InsertFenceSyncCHROMIUM)                  /* 266 */ \
-  OP(UnpremultiplyAndDitherCopyCHROMIUM)       /* 267 */ \
-  OP(BeginRasterCHROMIUMImmediate)             /* 268 */ \
-  OP(RasterCHROMIUM)                           /* 269 */ \
-  OP(EndRasterCHROMIUM)                        /* 270 */ \
-  OP(CreateTransferCacheEntryINTERNAL)         /* 271 */ \
-  OP(DeleteTransferCacheEntryINTERNAL)         /* 272 */ \
-  OP(UnlockTransferCacheEntryINTERNAL)         /* 273 */ \
-  OP(CreateTexture)                            /* 274 */ \
-  OP(SetColorSpaceMetadata)                    /* 275 */ \
-  OP(ProduceTextureDirectImmediate)            /* 276 */ \
-  OP(CreateAndConsumeTextureINTERNALImmediate) /* 277 */ \
-  OP(TexParameteri)                            /* 278 */ \
-  OP(BindTexImage2DCHROMIUM)                   /* 279 */ \
-  OP(ReleaseTexImage2DCHROMIUM)                /* 280 */ \
-  OP(TexStorage2D)                             /* 281 */ \
-  OP(CopySubTexture)                           /* 282 */ \
-  OP(TraceBeginCHROMIUM)                       /* 283 */ \
-  OP(TraceEndCHROMIUM)                         /* 284 */ \
-  OP(SetActiveURLCHROMIUM)                     /* 285 */ \
-  OP(ResetActiveURLCHROMIUM)                   /* 286 */
+  OP(WaitSyncTokenCHROMIUM)                    /* 267 */ \
+  OP(UnpremultiplyAndDitherCopyCHROMIUM)       /* 268 */ \
+  OP(BeginRasterCHROMIUMImmediate)             /* 269 */ \
+  OP(RasterCHROMIUM)                           /* 270 */ \
+  OP(EndRasterCHROMIUM)                        /* 271 */ \
+  OP(CreateTransferCacheEntryINTERNAL)         /* 272 */ \
+  OP(DeleteTransferCacheEntryINTERNAL)         /* 273 */ \
+  OP(UnlockTransferCacheEntryINTERNAL)         /* 274 */ \
+  OP(CreateTexture)                            /* 275 */ \
+  OP(SetColorSpaceMetadata)                    /* 276 */ \
+  OP(ProduceTextureDirectImmediate)            /* 277 */ \
+  OP(CreateAndConsumeTextureINTERNALImmediate) /* 278 */ \
+  OP(TexParameteri)                            /* 279 */ \
+  OP(BindTexImage2DCHROMIUM)                   /* 280 */ \
+  OP(ReleaseTexImage2DCHROMIUM)                /* 281 */ \
+  OP(TexStorage2D)                             /* 282 */ \
+  OP(CopySubTexture)                           /* 283 */ \
+  OP(TraceBeginCHROMIUM)                       /* 284 */ \
+  OP(TraceEndCHROMIUM)                         /* 285 */ \
+  OP(SetActiveURLCHROMIUM)                     /* 286 */ \
+  OP(ResetActiveURLCHROMIUM)                   /* 287 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index 14d4dc2b..7578d60 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -219,6 +219,7 @@
     "shader_translator.h",
     "shader_translator_cache.cc",
     "shader_translator_cache.h",
+    "shared_image_backing.cc",
     "shared_image_backing.h",
     "shared_image_backing_factory.h",
     "shared_image_backing_factory_gl_texture.cc",
diff --git a/gpu/command_buffer/service/command_buffer_direct.cc b/gpu/command_buffer/service/command_buffer_direct.cc
index 03fd9a5..8c7f861 100644
--- a/gpu/command_buffer/service/command_buffer_direct.cc
+++ b/gpu/command_buffer/service/command_buffer_direct.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 
 namespace gpu {
@@ -18,11 +19,37 @@
 
 CommandBufferDirect::CommandBufferDirect(
     TransferBufferManager* transfer_buffer_manager)
-    : service_(this, transfer_buffer_manager),
-      command_buffer_id_(
-          CommandBufferId::FromUnsafeValue(g_next_command_buffer_id++)) {}
+    : CommandBufferDirect(transfer_buffer_manager, nullptr) {}
 
-CommandBufferDirect::~CommandBufferDirect() = default;
+CommandBufferDirect::CommandBufferDirect(
+    TransferBufferManager* transfer_buffer_manager,
+    SyncPointManager* sync_point_manager)
+    : service_(this, transfer_buffer_manager),
+      sync_point_manager_(sync_point_manager),
+      command_buffer_id_(
+          CommandBufferId::FromUnsafeValue(g_next_command_buffer_id++)) {
+  if (sync_point_manager_) {
+    sync_point_order_data_ = sync_point_manager_->CreateSyncPointOrderData();
+    sync_point_client_state_ = sync_point_manager_->CreateSyncPointClientState(
+        GetNamespaceID(), GetCommandBufferID(),
+        sync_point_order_data_->sequence_id());
+  } else {
+    sync_point_order_data_ = nullptr;
+    sync_point_client_state_ = nullptr;
+  }
+}
+
+CommandBufferDirect::~CommandBufferDirect() {
+  sync_point_manager_ = nullptr;
+  if (sync_point_order_data_) {
+    sync_point_order_data_->Destroy();
+    sync_point_order_data_ = nullptr;
+  }
+  if (sync_point_client_state_) {
+    sync_point_client_state_->Destroy();
+    sync_point_client_state_ = nullptr;
+  }
+}
 
 CommandBuffer::State CommandBufferDirect::GetLastState() {
   service_.UpdateState();
@@ -49,7 +76,32 @@
 
 void CommandBufferDirect::Flush(int32_t put_offset) {
   DCHECK(handler_);
+  uint32_t order_num = 0;
+  if (sync_point_manager_) {
+    // If sync point manager is supported, assign order numbers to commands.
+    if (paused_order_num_) {
+      // Was previous paused, continue to process the order number.
+      order_num = paused_order_num_;
+      paused_order_num_ = 0;
+    } else {
+      order_num = sync_point_order_data_->GenerateUnprocessedOrderNumber();
+    }
+    sync_point_order_data_->BeginProcessingOrderNumber(order_num);
+  }
+
+  if (pause_commands_) {
+    // Do not process commands, simply store the current order number.
+    paused_order_num_ = order_num;
+
+    sync_point_order_data_->PauseProcessingOrderNumber(order_num);
+    return;
+  }
+
   service_.Flush(put_offset, handler_);
+  if (sync_point_manager_) {
+    // Finish processing order number here.
+    sync_point_order_data_->FinishProcessingOrderNumber(order_num);
+  }
 }
 
 void CommandBufferDirect::OrderingBarrier(int32_t put_offset) {
@@ -83,7 +135,17 @@
                                       const std::string& shader) {}
 
 void CommandBufferDirect::OnFenceSyncRelease(uint64_t release) {
-  NOTIMPLEMENTED();
+  DCHECK(sync_point_client_state_);
+  service_.SetReleaseCount(release);
+  sync_point_client_state_->ReleaseFenceSync(release);
+}
+
+bool CommandBufferDirect::OnWaitSyncToken(const gpu::SyncToken& sync_token) {
+  DCHECK(sync_point_manager_);
+  if (sync_point_manager_->IsSyncTokenReleased(sync_token))
+    return false;
+  service_.SetScheduled(false);
+  return true;
 }
 
 void CommandBufferDirect::OnDescheduleUntilFinished() {
@@ -96,6 +158,35 @@
 
 void CommandBufferDirect::OnSwapBuffers(uint64_t swap_id, uint32_t flags) {}
 
+gpu::CommandBufferNamespace CommandBufferDirect::GetNamespaceID() const {
+  return gpu::CommandBufferNamespace::IN_PROCESS;
+}
+
+CommandBufferId CommandBufferDirect::GetCommandBufferID() const {
+  return command_buffer_id_;
+}
+
+void CommandBufferDirect::SetCommandsPaused(bool paused) {
+  pause_commands_ = paused;
+}
+
+void CommandBufferDirect::SignalSyncToken(const gpu::SyncToken& sync_token,
+                                          base::OnceClosure callback) {
+  if (sync_point_manager_) {
+    DCHECK(!paused_order_num_);
+    uint32_t order_num =
+        sync_point_order_data_->GenerateUnprocessedOrderNumber();
+    sync_point_order_data_->BeginProcessingOrderNumber(order_num);
+    base::RepeatingClosure maybe_pass_callback =
+        base::AdaptCallbackForRepeating(std::move(callback));
+    if (!sync_point_client_state_->Wait(sync_token, maybe_pass_callback))
+      maybe_pass_callback.Run();
+    sync_point_order_data_->FinishProcessingOrderNumber(order_num);
+  } else {
+    std::move(callback).Run();
+  }
+}
+
 scoped_refptr<Buffer> CommandBufferDirect::CreateTransferBufferWithId(
     size_t size,
     int32_t id) {
diff --git a/gpu/command_buffer/service/command_buffer_direct.h b/gpu/command_buffer/service/command_buffer_direct.h
index 0eec8e6..7897b08 100644
--- a/gpu/command_buffer/service/command_buffer_direct.h
+++ b/gpu/command_buffer/service/command_buffer_direct.h
@@ -16,6 +16,10 @@
 
 class AsyncAPIInterface;
 class TransferBufferManager;
+class SyncPointClientState;
+class SyncPointManager;
+class SyncPointOrderData;
+struct SyncToken;
 
 class GPU_EXPORT CommandBufferDirect : public CommandBuffer,
                                        public CommandBufferServiceClient,
@@ -23,7 +27,10 @@
  public:
   using MakeCurrentCallback = base::Callback<bool()>;
 
+  CommandBufferDirect(TransferBufferManager* transfer_buffer_manager,
+                      SyncPointManager* sync_point_manager);
   explicit CommandBufferDirect(TransferBufferManager* transfer_buffer_manager);
+
   ~CommandBufferDirect() override;
 
   void set_handler(AsyncAPIInterface* handler) { handler_ = handler; }
@@ -49,11 +56,19 @@
   void OnConsoleMessage(int32_t id, const std::string& message) override;
   void CacheShader(const std::string& key, const std::string& shader) override;
   void OnFenceSyncRelease(uint64_t release) override;
+  bool OnWaitSyncToken(const gpu::SyncToken&) override;
   void OnDescheduleUntilFinished() override;
   void OnRescheduleAfterFinished() override;
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
   void ScheduleGrContextCleanup() override {}
 
+  CommandBufferNamespace GetNamespaceID() const;
+  CommandBufferId GetCommandBufferID() const;
+
+  void SetCommandsPaused(bool paused);
+  void SignalSyncToken(const gpu::SyncToken& sync_token,
+                       base::OnceClosure callback);
+
   scoped_refptr<Buffer> CreateTransferBufferWithId(size_t size, int32_t id);
 
   void SetGetOffsetForTest(int32_t get_offset) {
@@ -62,7 +77,13 @@
 
  private:
   CommandBufferService service_;
+  SyncPointManager* sync_point_manager_;
+
   AsyncAPIInterface* handler_ = nullptr;
+  scoped_refptr<SyncPointOrderData> sync_point_order_data_;
+  scoped_refptr<SyncPointClientState> sync_point_client_state_;
+  bool pause_commands_ = false;
+  uint32_t paused_order_num_ = 0;
   const CommandBufferId command_buffer_id_;
 };
 
diff --git a/gpu/command_buffer/service/decoder_client.h b/gpu/command_buffer/service/decoder_client.h
index c412e33..6715536 100644
--- a/gpu/command_buffer/service/decoder_client.h
+++ b/gpu/command_buffer/service/decoder_client.h
@@ -14,6 +14,8 @@
 
 namespace gpu {
 
+struct SyncToken;
+
 class GPU_EXPORT DecoderClient {
  public:
   virtual ~DecoderClient() = default;
@@ -29,6 +31,14 @@
   // reschedule waiting decoders.
   virtual void OnFenceSyncRelease(uint64_t release) = 0;
 
+  // Called when the decoder needs to wait on a sync token. If the wait is valid
+  // (fence sync is not released yet), the client must unschedule the command
+  // buffer and return true. The client is responsible for rescheduling the
+  // command buffer when the fence is released.  If the wait is a noop (fence is
+  // already released) or invalid, the client must leave the command buffer
+  // scheduled, and return false.
+  virtual bool OnWaitSyncToken(const gpu::SyncToken&) = 0;
+
   // Called when the decoder needs to be descheduled while waiting for a fence
   // completion. The client is responsible for descheduling the command buffer
   // before returning, and then calling PerformPollingWork periodically to test
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 12d9dafa..75a8b109 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -4970,10 +4970,12 @@
   gpu_trace_commands_ = gpu_tracer_->IsTracing() && *gpu_decoder_category_;
   gpu_debug_commands_ = log_commands() || debug() || gpu_trace_commands_;
   query_manager_->ProcessFrameBeginUpdates();
+  query_manager_->BeginProcessingCommands();
 }
 
 void GLES2DecoderImpl::EndDecoding() {
   gpu_tracer_->EndDecoding();
+  query_manager_->EndProcessingCommands();
 }
 
 ErrorState* GLES2DecoderImpl::GetErrorState() {
@@ -16186,6 +16188,34 @@
   return error::kNoError;
 }
 
+error::Error GLES2DecoderImpl::HandleWaitSyncTokenCHROMIUM(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile gles2::cmds::WaitSyncTokenCHROMIUM& c =
+      *static_cast<const volatile gles2::cmds::WaitSyncTokenCHROMIUM*>(
+          cmd_data);
+
+  const gpu::CommandBufferNamespace kMinNamespaceId =
+      gpu::CommandBufferNamespace::INVALID;
+  const gpu::CommandBufferNamespace kMaxNamespaceId =
+      gpu::CommandBufferNamespace::NUM_COMMAND_BUFFER_NAMESPACES;
+
+  gpu::CommandBufferNamespace namespace_id =
+      static_cast<gpu::CommandBufferNamespace>(c.namespace_id);
+  if ((namespace_id < static_cast<int32_t>(kMinNamespaceId)) ||
+       (namespace_id >= static_cast<int32_t>(kMaxNamespaceId))) {
+    namespace_id = gpu::CommandBufferNamespace::INVALID;
+  }
+  const CommandBufferId command_buffer_id =
+      CommandBufferId::FromUnsafeValue(c.command_buffer_id());
+  const uint64_t release = c.release_count();
+
+  gpu::SyncToken sync_token;
+  sync_token.Set(namespace_id, command_buffer_id, release);
+  return client_->OnWaitSyncToken(sync_token) ? error::kDeferCommandUntilLater
+                                              : error::kNoError;
+}
+
 error::Error GLES2DecoderImpl::HandleDiscardBackbufferCHROMIUM(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 42e5cf1..13b30ea1 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -4189,6 +4189,15 @@
   return error::kNoError;
 }
 
+error::Error GLES2DecoderPassthroughImpl::DoWaitSyncTokenCHROMIUM(
+    CommandBufferNamespace namespace_id,
+    CommandBufferId command_buffer_id,
+    GLuint64 release_count) {
+  SyncToken sync_token(namespace_id, command_buffer_id, release_count);
+  return client_->OnWaitSyncToken(sync_token) ? error::kDeferCommandUntilLater
+                                              : error::kNoError;
+}
+
 error::Error GLES2DecoderPassthroughImpl::DoDrawBuffersEXT(
     GLsizei count,
     const volatile GLenum* bufs) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
index dff256f..b694ab9 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
@@ -1685,6 +1685,31 @@
   return DoInsertFenceSyncCHROMIUM(release_count);
 }
 
+error::Error GLES2DecoderPassthroughImpl::HandleWaitSyncTokenCHROMIUM(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile gles2::cmds::WaitSyncTokenCHROMIUM& c =
+      *static_cast<const volatile gles2::cmds::WaitSyncTokenCHROMIUM*>(
+          cmd_data);
+  CommandBufferNamespace namespace_id =
+      static_cast<gpu::CommandBufferNamespace>(c.namespace_id);
+  const uint64_t release_count = c.release_count();
+  CommandBufferId command_buffer_id =
+      CommandBufferId::FromUnsafeValue(c.command_buffer_id());
+
+  const CommandBufferNamespace kMinNamespaceId =
+      CommandBufferNamespace::INVALID;
+  const CommandBufferNamespace kMaxNamespaceId =
+      CommandBufferNamespace::NUM_COMMAND_BUFFER_NAMESPACES;
+  if ((namespace_id < static_cast<int32_t>(kMinNamespaceId)) ||
+      (namespace_id >= static_cast<int32_t>(kMaxNamespaceId))) {
+    namespace_id = gpu::CommandBufferNamespace::INVALID;
+  }
+
+  return DoWaitSyncTokenCHROMIUM(namespace_id, command_buffer_id,
+                                 release_count);
+}
+
 error::Error GLES2DecoderPassthroughImpl::HandleDiscardBackbufferCHROMIUM(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index f6b1a73..03fdfe5 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -141,6 +141,9 @@
 void GLES2DecoderTestBase::CacheShader(const std::string& key,
                                        const std::string& shader) {}
 void GLES2DecoderTestBase::OnFenceSyncRelease(uint64_t release) {}
+bool GLES2DecoderTestBase::OnWaitSyncToken(const gpu::SyncToken&) {
+  return false;
+}
 void GLES2DecoderTestBase::OnDescheduleUntilFinished() {}
 void GLES2DecoderTestBase::OnRescheduleAfterFinished() {}
 void GLES2DecoderTestBase::OnSwapBuffers(uint64_t swap_id, uint32_t flags) {}
@@ -2392,6 +2395,9 @@
 void GLES2DecoderPassthroughTestBase::CacheShader(const std::string& key,
                                                   const std::string& shader) {}
 void GLES2DecoderPassthroughTestBase::OnFenceSyncRelease(uint64_t release) {}
+bool GLES2DecoderPassthroughTestBase::OnWaitSyncToken(const gpu::SyncToken&) {
+  return false;
+}
 void GLES2DecoderPassthroughTestBase::OnDescheduleUntilFinished() {}
 void GLES2DecoderPassthroughTestBase::OnRescheduleAfterFinished() {}
 void GLES2DecoderPassthroughTestBase::OnSwapBuffers(uint64_t swap_id,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index 72113d1..28de0a1f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -59,6 +59,7 @@
   void OnConsoleMessage(int32_t id, const std::string& message) override;
   void CacheShader(const std::string& key, const std::string& shader) override;
   void OnFenceSyncRelease(uint64_t release) override;
+  bool OnWaitSyncToken(const gpu::SyncToken&) override;
   void OnDescheduleUntilFinished() override;
   void OnRescheduleAfterFinished() override;
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
@@ -847,6 +848,7 @@
   void OnConsoleMessage(int32_t id, const std::string& message) override;
   void CacheShader(const std::string& key, const std::string& shader) override;
   void OnFenceSyncRelease(uint64_t release) override;
+  bool OnWaitSyncToken(const gpu::SyncToken&) override;
   void OnDescheduleUntilFinished() override;
   void OnRescheduleAfterFinished() override;
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
index fe157ec7..74e2dde 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -715,6 +715,7 @@
   switch (value) {
     case GL_QUERY_RESULT_EXT:
     case GL_QUERY_RESULT_AVAILABLE_EXT:
+    case GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT:
       return true;
   }
   return false;
diff --git a/gpu/command_buffer/service/memory_program_cache_unittest.cc b/gpu/command_buffer/service/memory_program_cache_unittest.cc
index 0aa22b2..d221cb7 100644
--- a/gpu/command_buffer/service/memory_program_cache_unittest.cc
+++ b/gpu/command_buffer/service/memory_program_cache_unittest.cc
@@ -94,6 +94,7 @@
     shader_cache_shader_ = shader;
   }
   void OnFenceSyncRelease(uint64_t release) override {}
+  bool OnWaitSyncToken(const gpu::SyncToken&) override { return false; }
   void OnDescheduleUntilFinished() override {}
   void OnRescheduleAfterFinished() override {}
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override {}
diff --git a/gpu/command_buffer/service/passthrough_program_cache_unittest.cc b/gpu/command_buffer/service/passthrough_program_cache_unittest.cc
index a362e236..9deae3c 100644
--- a/gpu/command_buffer/service/passthrough_program_cache_unittest.cc
+++ b/gpu/command_buffer/service/passthrough_program_cache_unittest.cc
@@ -44,6 +44,7 @@
   void CacheShader(const std::string& key, const std::string& shader) override {
   }
   void OnFenceSyncRelease(uint64_t release) override {}
+  bool OnWaitSyncToken(const gpu::SyncToken&) override { return false; }
   void OnDescheduleUntilFinished() override {}
   void OnRescheduleAfterFinished() override {}
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override {}
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index e2e55c7..a6a1e4c 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -90,6 +90,7 @@
   void CacheShader(const std::string& key, const std::string& shader) override {
   }
   void OnFenceSyncRelease(uint64_t release) override {}
+  bool OnWaitSyncToken(const gpu::SyncToken&) override { return false; }
   void OnDescheduleUntilFinished() override {}
   void OnRescheduleAfterFinished() override {}
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override {}
diff --git a/gpu/command_buffer/service/query_manager.cc b/gpu/command_buffer/service/query_manager.cc
index 994898de..588b119 100644
--- a/gpu/command_buffer/service/query_manager.cc
+++ b/gpu/command_buffer/service/query_manager.cc
@@ -32,23 +32,48 @@
   void Resume() override;
   void Process(bool did_finish) override;
   void Destroy(bool have_context) override;
+  void BeginProcessingCommands() override;
+  void EndProcessingCommands() override;
 
  protected:
   ~CommandsIssuedQuery() override;
 
  private:
-  base::TimeTicks begin_time_;
+  enum class CommandProcessingState {
+    // Used prior to receiving the Begin notification and after End which
+    // completes the query.
+    kNotStarted,
+
+    // Used when the query is active and the commands with the associated
+    // context are being processed.
+    kProcessingCommands,
+
+    // Used when the query is active but the associated context has been
+    // de-scheduled.
+    kNotProcessingCommands
+  };
+
+  void Reset();
+
+  CommandProcessingState command_processing_state_ =
+      CommandProcessingState::kNotStarted;
+  base::TimeDelta elapsed_time_;
+  base::TimeTicks begin_command_processing_time_;
 };
 
 CommandsIssuedQuery::CommandsIssuedQuery(QueryManager* manager,
                                          GLenum target,
                                          scoped_refptr<gpu::Buffer> buffer,
                                          QuerySync* sync)
-    : Query(manager, target, std::move(buffer), sync) {}
+    : Query(manager, target, std::move(buffer), sync) {
+  Reset();
+}
 
 void CommandsIssuedQuery::Begin() {
+  DCHECK_EQ(command_processing_state_, CommandProcessingState::kNotStarted);
+
   MarkAsActive();
-  begin_time_ = base::TimeTicks::Now();
+  BeginProcessingCommands();
 }
 
 void CommandsIssuedQuery::Pause() {
@@ -60,9 +85,20 @@
 }
 
 void CommandsIssuedQuery::End(base::subtle::Atomic32 submit_count) {
-  const base::TimeDelta elapsed = base::TimeTicks::Now() - begin_time_;
+  base::TimeDelta elapsed = elapsed_time_;
+  if (begin_command_processing_time_ != base::TimeTicks())
+    elapsed += base::TimeTicks::Now() - begin_command_processing_time_;
+
   MarkAsPending(submit_count);
   MarkAsCompleted(elapsed.InMicroseconds());
+
+  Reset();
+}
+
+void CommandsIssuedQuery::Reset() {
+  command_processing_state_ = CommandProcessingState::kNotStarted;
+  begin_command_processing_time_ = base::TimeTicks();
+  elapsed_time_ = base::TimeDelta();
 }
 
 void CommandsIssuedQuery::QueryCounter(base::subtle::Atomic32 submit_count) {
@@ -79,6 +115,29 @@
   }
 }
 
+void CommandsIssuedQuery::BeginProcessingCommands() {
+  DCHECK_NE(command_processing_state_,
+            CommandProcessingState::kProcessingCommands);
+  DCHECK_EQ(begin_command_processing_time_, base::TimeTicks());
+
+  command_processing_state_ = CommandProcessingState::kProcessingCommands;
+  begin_command_processing_time_ = base::TimeTicks::Now();
+}
+
+void CommandsIssuedQuery::EndProcessingCommands() {
+  DCHECK_NE(command_processing_state_,
+            CommandProcessingState::kNotProcessingCommands);
+
+  // The query may been ended before all commands associated with the context
+  // were processed.
+  if (command_processing_state_ == CommandProcessingState::kNotStarted)
+    return;
+
+  command_processing_state_ = CommandProcessingState::kNotProcessingCommands;
+  elapsed_time_ += base::TimeTicks::Now() - begin_command_processing_time_;
+  begin_command_processing_time_ = base::TimeTicks();
+}
+
 CommandsIssuedQuery::~CommandsIssuedQuery() = default;
 
 class CommandsCompletedQuery : public QueryManager::Query {
@@ -409,4 +468,14 @@
   }
 }
 
+void QueryManager::BeginProcessingCommands() {
+  for (std::pair<const GLenum, scoped_refptr<Query>>& it : active_queries_)
+    it.second->BeginProcessingCommands();
+}
+
+void QueryManager::EndProcessingCommands() {
+  for (std::pair<const GLenum, scoped_refptr<Query>>& it : active_queries_)
+    it.second->EndProcessingCommands();
+}
+
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/query_manager.h b/gpu/command_buffer/service/query_manager.h
index 355bd0c..1b6b8c57 100644
--- a/gpu/command_buffer/service/query_manager.h
+++ b/gpu/command_buffer/service/query_manager.h
@@ -79,6 +79,9 @@
 
     virtual void Destroy(bool have_context) = 0;
 
+    virtual void BeginProcessingCommands() {}
+    virtual void EndProcessingCommands() {}
+
     void AddCallback(base::OnceClosure callback);
 
    protected:
@@ -202,6 +205,9 @@
   void PauseQueries();
   void ResumeQueries();
 
+  void BeginProcessingCommands();
+  void EndProcessingCommands();
+
   // Processes pending queries. Returns false if any queries are pointing
   // to invalid shared memory. |did_finish| is true if this is called as
   // a result of calling glFinish().
diff --git a/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h
index e41d3af3..2e25dac 100644
--- a/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h
@@ -20,6 +20,7 @@
   switch (value) {
     case GL_QUERY_RESULT_EXT:
     case GL_QUERY_RESULT_AVAILABLE_EXT:
+    case GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT:
       return true;
   }
   return false;
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 5636e9e..fcec23c 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -294,6 +294,7 @@
     case kInsertFenceSyncCHROMIUM:
     case kRasterCHROMIUM:
     case kUnlockTransferCacheEntryINTERNAL:
+    case kWaitSyncTokenCHROMIUM:
       return true;
     default:
       return false;
@@ -1471,10 +1472,12 @@
   gpu_tracer_->BeginDecoding();
   gpu_trace_commands_ = gpu_tracer_->IsTracing() && *gpu_decoder_category_;
   gpu_debug_commands_ = log_commands() || debug() || gpu_trace_commands_;
+  query_manager_->BeginProcessingCommands();
 }
 
 void RasterDecoderImpl::EndDecoding() {
   gpu_tracer_->EndDecoding();
+  query_manager_->EndProcessingCommands();
 }
 
 const char* RasterDecoderImpl::GetCommandName(unsigned int command_id) const {
@@ -1855,6 +1858,34 @@
   }
 }
 
+error::Error RasterDecoderImpl::HandleWaitSyncTokenCHROMIUM(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  const volatile gles2::cmds::WaitSyncTokenCHROMIUM& c =
+      *static_cast<const volatile gles2::cmds::WaitSyncTokenCHROMIUM*>(
+          cmd_data);
+
+  static constexpr CommandBufferNamespace kMinNamespaceId =
+      CommandBufferNamespace::INVALID;
+  static constexpr CommandBufferNamespace kMaxNamespaceId =
+      CommandBufferNamespace::NUM_COMMAND_BUFFER_NAMESPACES;
+
+  CommandBufferNamespace namespace_id =
+      static_cast<CommandBufferNamespace>(c.namespace_id);
+  if ((namespace_id < static_cast<int32_t>(kMinNamespaceId)) ||
+      (namespace_id >= static_cast<int32_t>(kMaxNamespaceId))) {
+    namespace_id = CommandBufferNamespace::INVALID;
+  }
+  const CommandBufferId command_buffer_id =
+      CommandBufferId::FromUnsafeValue(c.command_buffer_id());
+  const uint64_t release = c.release_count();
+
+  SyncToken sync_token;
+  sync_token.Set(namespace_id, command_buffer_id, release);
+  return client_->OnWaitSyncToken(sync_token) ? error::kDeferCommandUntilLater
+                                              : error::kNoError;
+}
+
 error::Error RasterDecoderImpl::HandleSetColorSpaceMetadata(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index 1d25d7f..403202e1 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -581,6 +581,7 @@
   void CacheShader(const std::string& key, const std::string& shader) override {
   }
   void OnFenceSyncRelease(uint64_t release) override {}
+  bool OnWaitSyncToken(const gpu::SyncToken&) override { return false; }
   void OnDescheduleUntilFinished() override {}
   void OnRescheduleAfterFinished() override {}
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override {}
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
index 1e2361b..0b05964 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -71,6 +71,9 @@
 void RasterDecoderTestBase::CacheShader(const std::string& key,
                                         const std::string& shader) {}
 void RasterDecoderTestBase::OnFenceSyncRelease(uint64_t release) {}
+bool RasterDecoderTestBase::OnWaitSyncToken(const gpu::SyncToken&) {
+  return false;
+}
 void RasterDecoderTestBase::OnDescheduleUntilFinished() {}
 void RasterDecoderTestBase::OnRescheduleAfterFinished() {}
 void RasterDecoderTestBase::OnSwapBuffers(uint64_t swap_id, uint32_t flags) {}
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.h b/gpu/command_buffer/service/raster_decoder_unittest_base.h
index 60adc576..88baebb 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.h
@@ -58,6 +58,7 @@
   void OnConsoleMessage(int32_t id, const std::string& message) override;
   void CacheShader(const std::string& key, const std::string& shader) override;
   void OnFenceSyncRelease(uint64_t release) override;
+  bool OnWaitSyncToken(const gpu::SyncToken&) override;
   void OnDescheduleUntilFinished() override;
   void OnRescheduleAfterFinished() override;
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
diff --git a/gpu/command_buffer/service/shared_image_backing.cc b/gpu/command_buffer/service/shared_image_backing.cc
new file mode 100644
index 0000000..ddfff04
--- /dev/null
+++ b/gpu/command_buffer/service/shared_image_backing.cc
@@ -0,0 +1,26 @@
+// Copyright 2018 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 "gpu/command_buffer/service/shared_image_backing.h"
+
+namespace gpu {
+
+SharedImageBacking::SharedImageBacking(const Mailbox& mailbox,
+                                       viz::ResourceFormat format,
+                                       const gfx::Size& size,
+                                       const gfx::ColorSpace& color_space,
+                                       uint32_t usage)
+    : mailbox_(mailbox),
+      format_(format),
+      size_(size),
+      color_space_(color_space),
+      usage_(usage) {}
+
+SharedImageBacking::~SharedImageBacking() = default;
+
+size_t SharedImageBacking::EstimatedSize() const {
+  return 0;
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_backing.h b/gpu/command_buffer/service/shared_image_backing.h
index 07cdf4c1..c5519cd 100644
--- a/gpu/command_buffer/service/shared_image_backing.h
+++ b/gpu/command_buffer/service/shared_image_backing.h
@@ -11,6 +11,13 @@
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
 
+namespace base {
+namespace trace_event {
+class ProcessMemoryDump;
+class MemoryAllocatorDump;
+}  // namespace trace_event
+}  // namespace base
+
 namespace gpu {
 class MailboxManager;
 
@@ -20,33 +27,42 @@
 // TODO(ericrk): Add SharedImageRepresentation and Begin/End logic.
 class SharedImageBacking {
  public:
-  SharedImageBacking(viz::ResourceFormat format,
+  SharedImageBacking(const Mailbox& mailbox,
+                     viz::ResourceFormat format,
                      const gfx::Size& size,
                      const gfx::ColorSpace& color_space,
-                     uint32_t usage)
-      : format_(format),
-        size_(size),
-        color_space_(color_space),
-        usage_(usage) {}
+                     uint32_t usage);
 
-  virtual ~SharedImageBacking() {}
+  virtual ~SharedImageBacking();
 
   viz::ResourceFormat format() const { return format_; }
   const gfx::Size& size() const { return size_; }
   const gfx::ColorSpace& color_space() const { return color_space_; }
   uint32_t usage() const { return usage_; }
+  const Mailbox& mailbox() const { return mailbox_; }
+
+  // Memory dump helpers:
+  // Returns the estimated size of the backing. If 0 is returned, the dump will
+  // be omitted.
+  virtual size_t EstimatedSize() const;
+  // Allows the backing to attach additional data to the dump or dump
+  // additional sub paths.
+  virtual void OnMemoryDump(const std::string& dump_name,
+                            base::trace_event::MemoryAllocatorDump* dump,
+                            base::trace_event::ProcessMemoryDump* pmd,
+                            uint64_t client_tracing_id) {}
 
   // Prepares the backing for use with the legacy mailbox system.
   // TODO(ericrk): Remove this once the new codepath is complete.
-  virtual bool ProduceLegacyMailbox(const Mailbox& mailbox,
-                                    MailboxManager* mailbox_manager) = 0;
+  virtual bool ProduceLegacyMailbox(MailboxManager* mailbox_manager) = 0;
   virtual void Destroy(bool have_context) = 0;
 
  private:
-  viz::ResourceFormat format_;
-  gfx::Size size_;
-  gfx::ColorSpace color_space_;
-  uint32_t usage_;
+  const Mailbox mailbox_;
+  const viz::ResourceFormat format_;
+  const gfx::Size size_;
+  const gfx::ColorSpace color_space_;
+  const uint32_t usage_;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_backing_factory.h b/gpu/command_buffer/service/shared_image_backing_factory.h
index e6f3d11..a2ddef5 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory.h
@@ -16,11 +16,13 @@
 
 namespace gpu {
 class SharedImageBacking;
+struct Mailbox;
 
 class SharedImageBackingFactory {
  public:
   virtual ~SharedImageBackingFactory() = default;
   virtual std::unique_ptr<SharedImageBacking> CreateSharedImage(
+      const Mailbox& mailbox,
       viz::ResourceFormat format,
       const gfx::Size& size,
       const gfx::ColorSpace& color_space,
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
index 53f314eb..a817d61 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -4,11 +4,9 @@
 
 #include "gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h"
 
-#include <inttypes.h>
-
 #include "base/feature_list.h"
-#include "base/strings/stringprintf.h"
 #include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/trace_event.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 #include "gpu/command_buffer/common/shared_image_trace_utils.h"
@@ -32,22 +30,22 @@
 // as a gles2::Texture. Can be used with the legacy mailbox implementation.
 class SharedImageBackingGLTexture : public SharedImageBacking {
  public:
-  SharedImageBackingGLTexture(viz::ResourceFormat format,
+  SharedImageBackingGLTexture(const Mailbox& mailbox,
+                              viz::ResourceFormat format,
                               const gfx::Size& size,
                               const gfx::ColorSpace& color_space,
                               uint32_t usage,
                               gles2::Texture* texture)
-      : SharedImageBacking(format, size, color_space, usage),
+      : SharedImageBacking(mailbox, format, size, color_space, usage),
         texture_(texture) {
     DCHECK(texture_);
   }
 
   ~SharedImageBackingGLTexture() override { DCHECK(!texture_); }
 
-  bool ProduceLegacyMailbox(const Mailbox& mailbox,
-                            MailboxManager* mailbox_manager) override {
+  bool ProduceLegacyMailbox(MailboxManager* mailbox_manager) override {
     DCHECK(texture_);
-    mailbox_manager->ProduceTexture(mailbox, texture_);
+    mailbox_manager->ProduceTexture(mailbox(), texture_);
     return true;
   }
 
@@ -57,6 +55,28 @@
     texture_ = nullptr;
   }
 
+  size_t EstimatedSize() const override { return texture_->estimated_size(); }
+
+  void OnMemoryDump(const std::string& dump_name,
+                    base::trace_event::MemoryAllocatorDump* dump,
+                    base::trace_event::ProcessMemoryDump* pmd,
+                    uint64_t client_tracing_id) override {
+    // Add a |service_guid| which expresses shared ownership between the
+    // various GPU dumps.
+    auto client_guid = GetSharedImageGUIDForTracing(mailbox());
+    auto service_guid =
+        gl::GetGLTextureServiceGUIDForTracing(texture_->service_id());
+    pmd->CreateSharedGlobalAllocatorDump(service_guid);
+    // TODO(piman): coalesce constant with TextureManager::DumpTextureRef.
+    int importance = 2;  // This client always owns the ref.
+
+    pmd->AddOwnershipEdge(client_guid, service_guid, importance);
+
+    // Dump all sub-levels held by the texture. They will appear below the
+    // main gl/textures/client_X/mailbox_Y dump.
+    texture_->DumpLevelMemory(pmd, client_tracing_id, dump_name);
+  }
+
  private:
   gles2::Texture* texture_ = nullptr;
 };
@@ -67,12 +87,13 @@
 class SharedImageBackingPassthroughGLTexture : public SharedImageBacking {
  public:
   SharedImageBackingPassthroughGLTexture(
+      const Mailbox& mailbox,
       viz::ResourceFormat format,
       const gfx::Size& size,
       const gfx::ColorSpace& color_space,
       uint32_t usage,
       scoped_refptr<gles2::TexturePassthrough> passthrough_texture)
-      : SharedImageBacking(format, size, color_space, usage),
+      : SharedImageBacking(mailbox, format, size, color_space, usage),
         passthrough_texture_(std::move(passthrough_texture)) {
     DCHECK(passthrough_texture_);
   }
@@ -81,10 +102,9 @@
     DCHECK(!passthrough_texture_);
   }
 
-  bool ProduceLegacyMailbox(const Mailbox& mailbox,
-                            MailboxManager* mailbox_manager) override {
+  bool ProduceLegacyMailbox(MailboxManager* mailbox_manager) override {
     DCHECK(passthrough_texture_);
-    mailbox_manager->ProduceTexture(mailbox, passthrough_texture_.get());
+    mailbox_manager->ProduceTexture(mailbox(), passthrough_texture_.get());
     return true;
   }
 
@@ -200,6 +220,7 @@
 
 std::unique_ptr<SharedImageBacking>
 SharedImageBackingFactoryGLTexture::CreateSharedImage(
+    const Mailbox& mailbox,
     viz::ResourceFormat format,
     const gfx::Size& size,
     const gfx::ColorSpace& color_space,
@@ -307,7 +328,8 @@
     if (image)
       passthrough_texture->SetLevelImage(target, 0, image.get());
     backing = std::make_unique<SharedImageBackingPassthroughGLTexture>(
-        format, size, color_space, usage, std::move(passthrough_texture));
+        mailbox, format, size, color_space, usage,
+        std::move(passthrough_texture));
   } else {
     gles2::Texture* texture = new gles2::Texture(service_id);
     texture->SetLightweightRef(memory_tracker_.get());
@@ -328,7 +350,7 @@
       texture->SetLevelImage(target, 0, image.get(), gles2::Texture::BOUND);
     texture->SetImmutable(true);
     backing = std::make_unique<SharedImageBackingGLTexture>(
-        format, size, color_space, usage, texture);
+        mailbox, format, size, color_space, usage, texture);
   }
 
   api->glBindTextureFn(target, old_texture_binding);
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
index 4ac6d483..76b463ca 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
@@ -25,6 +25,7 @@
 class GpuDriverBugWorkarounds;
 struct GpuFeatureInfo;
 struct GpuPreferences;
+struct Mailbox;
 class ImageFactory;
 
 namespace gles2 {
@@ -47,6 +48,7 @@
 
   // SharedImageBackingFactory implementation.
   std::unique_ptr<SharedImageBacking> CreateSharedImage(
+      const Mailbox& mailbox,
       viz::ResourceFormat format,
       const gfx::Size& size,
       const gfx::ColorSpace& color_space,
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
index 56bc8f7..81b637d 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
@@ -58,18 +58,18 @@
 };
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, Basic) {
+  auto mailbox = Mailbox::Generate();
   auto format = viz::ResourceFormat::RGBA_8888;
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
-  auto backing =
-      backing_factory_->CreateSharedImage(format, size, color_space, usage);
+  auto backing = backing_factory_->CreateSharedImage(mailbox, format, size,
+                                                     color_space, usage);
   EXPECT_TRUE(backing);
 
   // TODO(ericrk): Validate via a SharedImageRepresentation. For now use legacy
   // mailbox.
-  auto mailbox = Mailbox::Generate();
-  EXPECT_TRUE(backing->ProduceLegacyMailbox(mailbox, &mailbox_manager_));
+  EXPECT_TRUE(backing->ProduceLegacyMailbox(&mailbox_manager_));
   TextureBase* texture_base = mailbox_manager_.ConsumeTexture(mailbox);
   ASSERT_TRUE(texture_base);
   GLenum expected_target = GL_TEXTURE_2D;
@@ -90,18 +90,18 @@
 }
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, Image) {
+  auto mailbox = Mailbox::Generate();
   auto format = viz::ResourceFormat::RGBA_8888;
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_SCANOUT;
-  auto backing =
-      backing_factory_->CreateSharedImage(format, size, color_space, usage);
+  auto backing = backing_factory_->CreateSharedImage(mailbox, format, size,
+                                                     color_space, usage);
   EXPECT_TRUE(backing);
 
   // TODO(ericrk): Validate via a SharedImageRepresentation. For now use legacy
   // mailbox.
-  auto mailbox = Mailbox::Generate();
-  EXPECT_TRUE(backing->ProduceLegacyMailbox(mailbox, &mailbox_manager_));
+  EXPECT_TRUE(backing->ProduceLegacyMailbox(&mailbox_manager_));
   TextureBase* texture_base = mailbox_manager_.ConsumeTexture(mailbox);
   ASSERT_TRUE(texture_base);
   GLenum target = texture_base->target();
@@ -121,27 +121,29 @@
 }
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, InvalidFormat) {
+  auto mailbox = Mailbox::Generate();
   auto format = viz::ResourceFormat::UYVY_422;
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
-  auto backing =
-      backing_factory_->CreateSharedImage(format, size, color_space, usage);
+  auto backing = backing_factory_->CreateSharedImage(mailbox, format, size,
+                                                     color_space, usage);
   EXPECT_FALSE(backing);
 }
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, InvalidSize) {
+  auto mailbox = Mailbox::Generate();
   auto format = viz::ResourceFormat::RGBA_8888;
   gfx::Size size(0, 0);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
-  auto backing =
-      backing_factory_->CreateSharedImage(format, size, color_space, usage);
+  auto backing = backing_factory_->CreateSharedImage(mailbox, format, size,
+                                                     color_space, usage);
   EXPECT_FALSE(backing);
 
   size = gfx::Size(INT_MAX, INT_MAX);
-  backing =
-      backing_factory_->CreateSharedImage(format, size, color_space, usage);
+  backing = backing_factory_->CreateSharedImage(mailbox, format, size,
+                                                color_space, usage);
   EXPECT_FALSE(backing);
 }
 
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc
index 70293f4c..19c0acf 100644
--- a/gpu/command_buffer/service/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -34,9 +34,7 @@
     SharedImageManager* shared_image_manager,
     ImageFactory* image_factory,
     gles2::MemoryTracker* tracker)
-    : use_passthrough_(gpu_preferences.use_passthrough_cmd_decoder &&
-                       gles2::PassthroughCommandDecoderSupported()),
-      mailbox_manager_(mailbox_manager),
+    : mailbox_manager_(mailbox_manager),
       shared_image_manager_(shared_image_manager),
       backing_factory_(
           std::make_unique<SharedImageBackingFactoryGLTexture>(gpu_preferences,
@@ -66,11 +64,11 @@
   std::unique_ptr<SharedImageBacking> backing;
   if (wrapped_sk_image_factory_ &&
       (usage & SHARED_IMAGE_USAGE_OOP_RASTERIZATION)) {
-    backing = wrapped_sk_image_factory_->CreateSharedImage(format, size,
-                                                           color_space, usage);
+    backing = wrapped_sk_image_factory_->CreateSharedImage(
+        mailbox, format, size, color_space, usage);
   } else {
-    backing =
-        backing_factory_->CreateSharedImage(format, size, color_space, usage);
+    backing = backing_factory_->CreateSharedImage(mailbox, format, size,
+                                                  color_space, usage);
   }
 
   if (!backing) {
@@ -79,14 +77,14 @@
   }
 
   // TODO(ericrk): Handle the non-legacy case.
-  if (!backing->ProduceLegacyMailbox(mailbox, mailbox_manager_)) {
+  if (!backing->ProduceLegacyMailbox(mailbox_manager_)) {
     LOG(ERROR)
         << "CreateSharedImage: could not convert backing to legacy mailbox.";
     backing->Destroy(true /* have_context */);
     return false;
   }
 
-  if (!shared_image_manager_->Register(mailbox, std::move(backing))) {
+  if (!shared_image_manager_->Register(std::move(backing))) {
     LOG(ERROR) << "CreateSharedImage: Could not register backing with "
                   "SharedImageManager.";
     backing->Destroy(true /* have_context */);
@@ -120,59 +118,9 @@
     base::trace_event::ProcessMemoryDump* pmd,
     int client_id,
     uint64_t client_tracing_id) {
-  if (use_passthrough_)
-    return true;
-
-  // TODO(ericrk): Move some of this to SharedImageBacking.
   for (const auto& mailbox : mailboxes_) {
-    uint64_t estimated_size = 0;
-    TextureBase* texture_base = mailbox_manager_->ConsumeTexture(mailbox);
-
-    gles2::Texture* texture = nullptr;
-    switch (texture_base->GetType()) {
-      case TextureBase::Type::kValidated:
-        texture = gles2::Texture::CheckedCast(texture_base);
-        estimated_size = texture->estimated_size();
-        break;
-      case TextureBase::Type::kSkImage:
-        estimated_size =
-            raster::WrappedSkImage::CheckedCast(texture_base)->estimated_size();
-        break;
-      default:
-        NOTREACHED();
-        continue;
-    }
-
-    // Unique name in the process.
-    std::string dump_name =
-        base::StringPrintf("gpu/shared-images/client_0x%" PRIX32 "/mailbox_%s",
-                           client_id, mailbox.ToDebugString().c_str());
-
-    base::trace_event::MemoryAllocatorDump* dump =
-        pmd->CreateAllocatorDump(dump_name);
-    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
-                    base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                    estimated_size);
-    // Add a mailbox guid which expresses shared ownership with the client
-    // process.
-    // This must match the client-side.
-    auto client_guid = GetSharedImageGUIDForTracing(mailbox);
-    pmd->CreateSharedGlobalAllocatorDump(client_guid);
-    pmd->AddOwnershipEdge(dump->guid(), client_guid);
-    // Add a |service_guid| which expresses shared ownership between the
-    // various GPU dumps.
-    auto service_guid =
-        gl::GetGLTextureServiceGUIDForTracing(texture->GetTracingId());
-    pmd->CreateSharedGlobalAllocatorDump(service_guid);
-    // TODO(piman): coalesce constant with TextureManager::DumpTextureRef.
-    int importance = 2;  // This client always owns the ref.
-
-    pmd->AddOwnershipEdge(client_guid, service_guid, importance);
-
-    // Dump all sub-levels held by the texture. They will appear below the
-    // main gl/textures/client_X/mailbox_Y dump.
-    if (texture)
-      texture->DumpLevelMemory(pmd, client_tracing_id, dump_name);
+    shared_image_manager_->OnMemoryDump(mailbox, pmd, client_id,
+                                        client_tracing_id);
   }
 
   return true;
diff --git a/gpu/command_buffer/service/shared_image_factory.h b/gpu/command_buffer/service/shared_image_factory.h
index f3ff67e..9f0f44cb 100644
--- a/gpu/command_buffer/service/shared_image_factory.h
+++ b/gpu/command_buffer/service/shared_image_factory.h
@@ -60,7 +60,6 @@
                     uint64_t client_tracing_id);
 
  private:
-  bool use_passthrough_;
   MailboxManager* mailbox_manager_;
   SharedImageManager* shared_image_manager_;
 
diff --git a/gpu/command_buffer/service/shared_image_manager.cc b/gpu/command_buffer/service/shared_image_manager.cc
index b2211282..e0dec4f 100644
--- a/gpu/command_buffer/service/shared_image_manager.cc
+++ b/gpu/command_buffer/service/shared_image_manager.cc
@@ -4,7 +4,32 @@
 
 #include "gpu/command_buffer/service/shared_image_manager.h"
 
+#include <inttypes.h>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
+#include "gpu/command_buffer/common/shared_image_trace_utils.h"
+#include "ui/gl/trace_util.h"
+
 namespace gpu {
+// Overrides for flat_set lookups:
+bool operator<(const gpu::SharedImageManager::BackingAndRefCount& lhs,
+               const gpu::SharedImageManager::BackingAndRefCount& rhs) {
+  return lhs.backing->mailbox() < rhs.backing->mailbox();
+}
+
+bool operator<(const gpu::Mailbox& lhs,
+               const gpu::SharedImageManager::BackingAndRefCount& rhs) {
+  return lhs < rhs.backing->mailbox();
+}
+
+bool operator<(const gpu::SharedImageManager::BackingAndRefCount& lhs,
+               const gpu::Mailbox& rhs) {
+  return lhs.backing->mailbox() < rhs;
+}
 
 SharedImageManager::SharedImageManager() = default;
 
@@ -12,14 +37,12 @@
   DCHECK(images_.empty());
 }
 
-bool SharedImageManager::Register(const Mailbox& mailbox,
-                                  std::unique_ptr<SharedImageBacking> backing) {
-  auto found = images_.find(mailbox);
+bool SharedImageManager::Register(std::unique_ptr<SharedImageBacking> backing) {
+  auto found = images_.find(backing->mailbox());
   if (found != images_.end())
     return false;
 
-  images_.emplace(mailbox,
-                  BackingAndRefCount(std::move(backing), 1 /* ref_count */));
+  images_.emplace(std::move(backing), 1 /* ref_count */);
   return true;
 }
 
@@ -31,13 +54,51 @@
     return;
   }
 
-  found->second.ref_count--;
-  if (found->second.ref_count == 0) {
-    found->second.backing->Destroy(have_context);
+  found->ref_count--;
+  if (found->ref_count == 0) {
+    found->backing->Destroy(have_context);
     images_.erase(found);
   }
 }
 
+void SharedImageManager::OnMemoryDump(const Mailbox& mailbox,
+                                      base::trace_event::ProcessMemoryDump* pmd,
+                                      int client_id,
+                                      uint64_t client_tracing_id) {
+  auto found = images_.find(mailbox);
+  if (found == images_.end()) {
+    LOG(ERROR) << "SharedImageManager::OnMemoryDump: Trying to dump memory for "
+                  "a non existent mailbox.";
+    return;
+  }
+
+  auto* backing = found->backing.get();
+  size_t estimated_size = backing->EstimatedSize();
+  if (estimated_size == 0)
+    return;
+
+  // Unique name in the process.
+  std::string dump_name =
+      base::StringPrintf("gpu/shared-images/client_0x%" PRIX32 "/mailbox_%s",
+                         client_id, mailbox.ToDebugString().c_str());
+
+  base::trace_event::MemoryAllocatorDump* dump =
+      pmd->CreateAllocatorDump(dump_name);
+  dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                  base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                  estimated_size);
+  // Add a mailbox guid which expresses shared ownership with the client
+  // process.
+  // This must match the client-side.
+  auto client_guid = GetSharedImageGUIDForTracing(mailbox);
+  pmd->CreateSharedGlobalAllocatorDump(client_guid);
+  pmd->AddOwnershipEdge(dump->guid(), client_guid);
+
+  // Allow the SharedImageBacking to attach additional data to the dump
+  // or dump additional sub-paths.
+  backing->OnMemoryDump(dump_name, dump, pmd, client_tracing_id);
+}
+
 SharedImageManager::BackingAndRefCount::BackingAndRefCount(
     std::unique_ptr<SharedImageBacking> backing,
     uint32_t ref_count)
diff --git a/gpu/command_buffer/service/shared_image_manager.h b/gpu/command_buffer/service/shared_image_manager.h
index 10a1263..2b1ff67f 100644
--- a/gpu/command_buffer/service/shared_image_manager.h
+++ b/gpu/command_buffer/service/shared_image_manager.h
@@ -5,7 +5,7 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_MANAGER_H_
 #define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_MANAGER_H_
 
-#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/service/shared_image_backing.h"
 #include "gpu/gpu_gles2_export.h"
@@ -20,8 +20,7 @@
   // Registers a SharedImageBacking with the manager and returns true on
   // success. On success, the backing has one ref which may be released by
   // calling Unregister.
-  bool Register(const Mailbox& mailbox,
-                std::unique_ptr<SharedImageBacking> backing);
+  bool Register(std::unique_ptr<SharedImageBacking> backing);
 
   // Releases the registration ref. If a backing reaches zero refs, it is
   // destroyed.
@@ -31,6 +30,12 @@
   // SharedImageRepresentation. Representations also take a ref on the
   // mailbox, releasing it when the representation is destroyed.
 
+  // Dump memory for the given mailbox.
+  void OnMemoryDump(const Mailbox& mailbox,
+                    base::trace_event::ProcessMemoryDump* pmd,
+                    int client_id,
+                    uint64_t client_tracing_id);
+
  private:
   struct BackingAndRefCount {
     BackingAndRefCount(std::unique_ptr<SharedImageBacking> backing,
@@ -41,7 +46,12 @@
     std::unique_ptr<SharedImageBacking> backing;
     uint32_t ref_count = 0;
   };
-  base::flat_map<Mailbox, BackingAndRefCount> images_;
+  friend bool operator<(const BackingAndRefCount& lhs,
+                        const BackingAndRefCount& rhs);
+  friend bool operator<(const Mailbox& lhs, const BackingAndRefCount& rhs);
+  friend bool operator<(const BackingAndRefCount& lhs, const Mailbox& rhs);
+
+  base::flat_set<BackingAndRefCount> images_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageManager);
 };
diff --git a/gpu/command_buffer/service/wrapped_sk_image.cc b/gpu/command_buffer/service/wrapped_sk_image.cc
index 0b6c0ba..aa44af6 100644
--- a/gpu/command_buffer/service/wrapped_sk_image.cc
+++ b/gpu/command_buffer/service/wrapped_sk_image.cc
@@ -6,7 +6,11 @@
 
 #include "base/hash.h"
 #include "base/logging.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
 #include "components/viz/common/resources/resource_format_utils.h"
+#include "gpu/command_buffer/common/shared_image_trace_utils.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/raster_decoder_context_state.h"
 #include "gpu/command_buffer/service/shared_image_backing.h"
@@ -15,6 +19,7 @@
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/GrTypes.h"
 #include "ui/gl/gl_context.h"
+#include "ui/gl/trace_util.h"
 
 namespace gpu {
 namespace raster {
@@ -23,22 +28,22 @@
 
 class WrappedSkImageBacking : public SharedImageBacking {
  public:
-  WrappedSkImageBacking(viz::ResourceFormat format,
+  WrappedSkImageBacking(const Mailbox& mailbox,
+                        viz::ResourceFormat format,
                         const gfx::Size& size,
                         const gfx::ColorSpace& color_space,
                         uint32_t usage,
                         std::unique_ptr<WrappedSkImage> wrapped_sk_image)
-      : SharedImageBacking(format, size, color_space, usage),
+      : SharedImageBacking(mailbox, format, size, color_space, usage),
         wrapped_sk_image_(std::move(wrapped_sk_image)) {
     DCHECK(!!wrapped_sk_image_);
   }
 
   ~WrappedSkImageBacking() override { DCHECK(!wrapped_sk_image_); }
 
-  bool ProduceLegacyMailbox(const Mailbox& mailbox,
-                            MailboxManager* mailbox_manager) override {
+  bool ProduceLegacyMailbox(MailboxManager* mailbox_manager) override {
     DCHECK(!!wrapped_sk_image_);
-    mailbox_manager->ProduceTexture(mailbox, wrapped_sk_image_.get());
+    mailbox_manager->ProduceTexture(mailbox(), wrapped_sk_image_.get());
     return true;
   }
 
@@ -47,6 +52,26 @@
     wrapped_sk_image_.reset();
   }
 
+  size_t EstimatedSize() const override {
+    return wrapped_sk_image_->estimated_size();
+  }
+
+  void OnMemoryDump(const std::string& dump_name,
+                    base::trace_event::MemoryAllocatorDump* dump,
+                    base::trace_event::ProcessMemoryDump* pmd,
+                    uint64_t client_tracing_id) override {
+    // Add a |service_guid| which expresses shared ownership between the
+    // various GPU dumps.
+    auto client_guid = GetSharedImageGUIDForTracing(mailbox());
+    auto service_guid = gl::GetGLTextureServiceGUIDForTracing(
+        wrapped_sk_image_->GetTracingId());
+    pmd->CreateSharedGlobalAllocatorDump(service_guid);
+    // TODO(piman): coalesce constant with TextureManager::DumpTextureRef.
+    int importance = 2;  // This client always owns the ref.
+
+    pmd->AddOwnershipEdge(client_guid, service_guid, importance);
+  }
+
  private:
   std::unique_ptr<WrappedSkImage> wrapped_sk_image_;
 
@@ -62,6 +87,7 @@
 WrappedSkImageFactory::~WrappedSkImageFactory() = default;
 
 std::unique_ptr<SharedImageBacking> WrappedSkImageFactory::CreateSharedImage(
+    const Mailbox& mailbox,
     viz::ResourceFormat format,
     const gfx::Size& size,
     const gfx::ColorSpace& color_space,
@@ -69,8 +95,8 @@
   std::unique_ptr<WrappedSkImage> texture(new WrappedSkImage(context_state_));
   if (!texture->Initialize(size, format))
     return nullptr;
-  return std::make_unique<WrappedSkImageBacking>(format, size, color_space,
-                                                 usage, std::move(texture));
+  return std::make_unique<WrappedSkImageBacking>(
+      mailbox, format, size, color_space, usage, std::move(texture));
 }
 
 WrappedSkImage::WrappedSkImage(raster::RasterDecoderContextState* context_state)
diff --git a/gpu/command_buffer/service/wrapped_sk_image.h b/gpu/command_buffer/service/wrapped_sk_image.h
index 3c6b98d..faa5a7e 100644
--- a/gpu/command_buffer/service/wrapped_sk_image.h
+++ b/gpu/command_buffer/service/wrapped_sk_image.h
@@ -31,6 +31,7 @@
 
   // SharedImageBackingFactory implementation:
   std::unique_ptr<SharedImageBacking> CreateSharedImage(
+      const Mailbox& mailbox,
       viz::ResourceFormat format,
       const gfx::Size& size,
       const gfx::ColorSpace& color_space,
diff --git a/gpu/command_buffer/tests/decoder_perftest.cc b/gpu/command_buffer/tests/decoder_perftest.cc
index cbe76c4..2ee83f4c 100644
--- a/gpu/command_buffer/tests/decoder_perftest.cc
+++ b/gpu/command_buffer/tests/decoder_perftest.cc
@@ -264,61 +264,59 @@
                       size_t width,
                       size_t height,
                       unsigned internalformat) override {
-    NOTREACHED();
+    NOTIMPLEMENTED();
     return -1;
   }
 
-  void DestroyImage(int32_t id) override { NOTREACHED(); }
+  void DestroyImage(int32_t id) override { NOTIMPLEMENTED(); }
 
   void SignalQuery(uint32_t query, base::OnceClosure callback) override {
-    NOTREACHED();
+    NOTIMPLEMENTED();
   }
 
   void CreateGpuFence(uint32_t gpu_fence_id, ClientGpuFence source) override {
-    NOTREACHED();
+    NOTIMPLEMENTED();
   }
 
   void GetGpuFence(uint32_t gpu_fence_id,
                    base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)>
                        callback) override {
-    NOTREACHED();
+    NOTIMPLEMENTED();
   }
 
-  void SetLock(base::Lock*) override { NOTREACHED(); }
+  void SetLock(base::Lock*) override { NOTIMPLEMENTED(); }
 
-  void EnsureWorkVisible() override { NOTREACHED(); }
+  void EnsureWorkVisible() override {}
 
   gpu::CommandBufferNamespace GetNamespaceID() const override {
-    return gpu::CommandBufferNamespace::INVALID;
+    return command_buffer_->GetNamespaceID();
   }
 
   CommandBufferId GetCommandBufferID() const override {
-    return gpu::CommandBufferId();
+    return command_buffer_->GetCommandBufferID();
   }
 
-  void FlushPendingWork() override { NOTREACHED(); }
+  void FlushPendingWork() override {}
 
   uint64_t GenerateFenceSyncRelease() override {
-    NOTREACHED();
+    NOTIMPLEMENTED();
     return 0;
   }
 
   bool IsFenceSyncReleased(uint64_t release) override {
-    NOTREACHED();
+    NOTIMPLEMENTED();
     return true;
   }
 
   void SignalSyncToken(const gpu::SyncToken& sync_token,
                        base::OnceClosure callback) override {
-    NOTREACHED();
+    NOTIMPLEMENTED();
   }
 
-  void WaitSyncToken(const gpu::SyncToken& sync_token) override {
-    NOTREACHED();
-  }
+  void WaitSyncTokenHint(const gpu::SyncToken& sync_token) override {}
 
   bool CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) override {
-    NOTREACHED();
+    NOTIMPLEMENTED();
     return true;
   }
 
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc
index 4a17622..1559138 100644
--- a/gpu/command_buffer/tests/fuzzer_main.cc
+++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -17,6 +17,7 @@
 #include "build/build_config.h"
 #include "gpu/command_buffer/common/constants.h"
 #include "gpu/command_buffer/common/context_creation_attribs.h"
+#include "gpu/command_buffer/common/sync_token.h"
 #include "gpu/command_buffer/service/buffer_manager.h"
 #include "gpu/command_buffer/service/command_buffer_direct.h"
 #include "gpu/command_buffer/service/context_group.h"
@@ -30,6 +31,7 @@
 #include "gpu/command_buffer/service/raster_decoder_context_state.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shared_image_manager.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gl/gl_context.h"
@@ -349,8 +351,8 @@
         config_.attrib_helper.bind_generates_resource, &image_manager_,
         nullptr /* image_factory */, nullptr /* progress_reporter */,
         gpu_feature_info, discardable_manager_.get(), &shared_image_manager_);
-    command_buffer_.reset(
-        new CommandBufferDirect(context_group->transfer_buffer_manager()));
+    command_buffer_.reset(new CommandBufferDirect(
+        context_group->transfer_buffer_manager(), &sync_point_manager_));
 
 #if defined(GPU_FUZZER_USE_RASTER_DECODER)
     CHECK(feature_info->feature_flags().chromium_raster_transport);
@@ -506,6 +508,7 @@
   gles2::MailboxManagerImpl mailbox_manager_;
   gles2::TraceOutputter outputter_;
   scoped_refptr<gl::GLShareGroup> share_group_;
+  SyncPointManager sync_point_manager_;
   gles2::ImageManager image_manager_;
   std::unique_ptr<ServiceDiscardableManager> discardable_manager_;
   SharedImageManager shared_image_manager_;
diff --git a/gpu/command_buffer/tests/gl_fence_sync_unittest.cc b/gpu/command_buffer/tests/gl_fence_sync_unittest.cc
new file mode 100644
index 0000000..5695997
--- /dev/null
+++ b/gpu/command_buffer/tests/gl_fence_sync_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/command_buffer/tests/gl_manager.h"
+#include "gpu/command_buffer/tests/gl_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define SHADER(Src) #Src
+
+namespace gpu {
+
+class GLFenceSyncTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    sync_point_manager_.reset(new SyncPointManager());
+
+    GLManager::Options options;
+    options.sync_point_manager = sync_point_manager_.get();
+    gl1_.Initialize(options);
+    gl2_.Initialize(options);
+  }
+
+  void TearDown() override {
+    gl2_.Destroy();
+    gl1_.Destroy();
+
+    sync_point_manager_.reset();
+  }
+
+  std::unique_ptr<SyncPointManager> sync_point_manager_;
+  GLManager gl1_;
+  GLManager gl2_;
+};
+
+TEST_F(GLFenceSyncTest, SimpleReleaseWait) {
+  gl1_.MakeCurrent();
+
+  SyncToken sync_token;
+  glFlush();
+  glGenSyncTokenCHROMIUM(sync_token.GetData());
+  ASSERT_TRUE(GL_NO_ERROR == glGetError());
+
+  // Make sure it is actually released.
+  EXPECT_TRUE(sync_point_manager_->IsSyncTokenReleased(sync_token));
+
+  gl2_.MakeCurrent();
+  glWaitSyncTokenCHROMIUM(sync_token.GetConstData());
+  glFinish();
+}
+
+static void TestCallback(int* storage, int assign) {
+  *storage = assign;
+}
+
+TEST_F(GLFenceSyncTest, SimpleReleaseSignal) {
+  gl1_.MakeCurrent();
+
+  // Pause the command buffer so the fence sync does not immediately trigger.
+  gl1_.SetCommandsPaused(true);
+
+  SyncToken sync_token;
+  glGenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+  glFlush();
+  ASSERT_TRUE(sync_token.HasData());
+
+  gl2_.MakeCurrent();
+  int callback_called = 0;
+  gl2_.SignalSyncToken(sync_token,
+                       base::Bind(TestCallback, &callback_called, 1));
+
+  gl1_.MakeCurrent();
+  EXPECT_EQ(0, callback_called);
+
+  gl1_.SetCommandsPaused(false);
+  glFinish();
+
+  EXPECT_EQ(1, callback_called);
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 11478b10..60ddd3c1 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -196,8 +196,9 @@
 class CommandBufferCheckLostContext : public CommandBufferDirect {
  public:
   CommandBufferCheckLostContext(TransferBufferManager* transfer_buffer_manager,
+                                SyncPointManager* sync_point_manager,
                                 bool context_lost_allowed)
-      : CommandBufferDirect(transfer_buffer_manager),
+      : CommandBufferDirect(transfer_buffer_manager, sync_point_manager),
         context_lost_allowed_(context_lost_allowed) {}
 
   ~CommandBufferCheckLostContext() override = default;
@@ -356,7 +357,8 @@
   }
 
   command_buffer_.reset(new CommandBufferCheckLostContext(
-      context_group->transfer_buffer_manager(), options.context_lost_allowed));
+      context_group->transfer_buffer_manager(), options.sync_point_manager,
+      options.context_lost_allowed));
 
   decoder_.reset(::gpu::gles2::GLES2Decoder::Create(
       command_buffer_.get(), command_buffer_->service(), &outputter_,
@@ -463,6 +465,10 @@
   decoder_->PerformIdleWork();
 }
 
+void GLManager::SetCommandsPaused(bool paused) {
+  command_buffer_->SetCommandsPaused(paused);
+}
+
 void GLManager::Destroy() {
   if (gles2_implementation_.get()) {
     MakeCurrent();
@@ -557,60 +563,56 @@
 }
 
 void GLManager::SignalQuery(uint32_t query, base::OnceClosure callback) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void GLManager::CreateGpuFence(uint32_t gpu_fence_id, ClientGpuFence source) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void GLManager::GetGpuFence(
     uint32_t gpu_fence_id,
     base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void GLManager::SetLock(base::Lock*) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void GLManager::EnsureWorkVisible() {
-  NOTREACHED();
+  // This is only relevant for out-of-process command buffers.
 }
 
 gpu::CommandBufferNamespace GLManager::GetNamespaceID() const {
-  return CommandBufferNamespace::INVALID;
+  return command_buffer_->GetNamespaceID();
 }
 
 CommandBufferId GLManager::GetCommandBufferID() const {
-  return CommandBufferId();
+  return command_buffer_->GetCommandBufferID();
 }
 
 void GLManager::FlushPendingWork() {
-  NOTREACHED();
+  // This is only relevant for out-of-process command buffers.
 }
 
 uint64_t GLManager::GenerateFenceSyncRelease() {
-  NOTREACHED();
-  return 0;
+  return next_fence_sync_release_++;
 }
 
 bool GLManager::IsFenceSyncReleased(uint64_t release) {
-  NOTREACHED();
-  return false;
+  return release <= command_buffer_->GetLastState().release_count;
 }
 
 void GLManager::SignalSyncToken(const gpu::SyncToken& sync_token,
                                 base::OnceClosure callback) {
-  NOTREACHED();
+  command_buffer_->SignalSyncToken(
+      sync_token, base::AdaptCallbackForRepeating(std::move(callback)));
 }
 
-void GLManager::WaitSyncToken(const gpu::SyncToken& sync_token) {
-  NOTREACHED();
-}
+void GLManager::WaitSyncTokenHint(const gpu::SyncToken& sync_token) {}
 
 bool GLManager::CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) {
-  NOTREACHED();
   return false;
 }
 
diff --git a/gpu/command_buffer/tests/gl_manager.h b/gpu/command_buffer/tests/gl_manager.h
index 76c71ca..c8d6c3b 100644
--- a/gpu/command_buffer/tests/gl_manager.h
+++ b/gpu/command_buffer/tests/gl_manager.h
@@ -38,6 +38,7 @@
 class GpuMemoryBufferFactory;
 class ImageFactory;
 class MailboxManager;
+class SyncPointManager;
 class TransferBuffer;
 
 namespace gles2 {
@@ -53,6 +54,8 @@
     Options();
     // The size of the backbuffer.
     gfx::Size size = gfx::Size(4, 4);
+    // If not null will have a corresponding sync point manager.
+    SyncPointManager* sync_point_manager = nullptr;
     // If not null will share resources with this context.
     GLManager* share_group_manager = nullptr;
     // If not null will share a mailbox manager with this context.
@@ -111,6 +114,8 @@
     use_native_pixmap_memory_buffers_ = use_native_pixmap_memory_buffers;
   }
 
+  void SetCommandsPaused(bool paused);
+
   gles2::GLES2Decoder* decoder() const {
     return decoder_.get();
   }
@@ -152,7 +157,7 @@
   bool IsFenceSyncReleased(uint64_t release) override;
   void SignalSyncToken(const gpu::SyncToken& sync_token,
                        base::OnceClosure callback) override;
-  void WaitSyncToken(const gpu::SyncToken& sync_token) override;
+  void WaitSyncTokenHint(const gpu::SyncToken& sync_token) override;
   bool CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) override;
 
   size_t GetSharedMemoryBytesAllocated() const;
@@ -187,6 +192,8 @@
   std::unique_ptr<gpu::GpuMemoryBufferFactory> gpu_memory_buffer_factory_;
   SharedImageManager shared_image_manager_;
 
+  uint64_t next_fence_sync_release_ = 1;
+
   bool use_iosurface_memory_buffers_ = false;
   bool use_native_pixmap_memory_buffers_ = false;
 
diff --git a/gpu/gles2_conform_support/egl/context.cc b/gpu/gles2_conform_support/egl/context.cc
index 1545fb1..23c3cd9 100644
--- a/gpu/gles2_conform_support/egl/context.cc
+++ b/gpu/gles2_conform_support/egl/context.cc
@@ -175,38 +175,38 @@
                              size_t width,
                              size_t height,
                              unsigned internalformat) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
   return -1;
 }
 
 void Context::DestroyImage(int32_t id) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void Context::SignalQuery(uint32_t query, base::OnceClosure callback) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void Context::CreateGpuFence(uint32_t gpu_fence_id, ClientGpuFence source) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void Context::GetGpuFence(
     uint32_t gpu_fence_id,
     base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void Context::SetLock(base::Lock*) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void Context::EnsureWorkVisible() {
-  NOTREACHED();
+  // This is only relevant for out-of-process command buffers.
 }
 
 gpu::CommandBufferNamespace Context::GetNamespaceID() const {
-  return gpu::CommandBufferNamespace::INVALID;
+  return gpu::CommandBufferNamespace::IN_PROCESS;
 }
 
 gpu::CommandBufferId Context::GetCommandBufferID() const {
@@ -214,27 +214,24 @@
 }
 
 void Context::FlushPendingWork() {
-  NOTREACHED();
+  // This is only relevant for out-of-process command buffers.
 }
 
 uint64_t Context::GenerateFenceSyncRelease() {
-  NOTREACHED();
-  return 0;
+  return display_->GenerateFenceSyncRelease();
 }
 
 bool Context::IsFenceSyncReleased(uint64_t release) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
   return false;
 }
 
 void Context::SignalSyncToken(const gpu::SyncToken& sync_token,
                               base::OnceClosure callback) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
-void Context::WaitSyncToken(const gpu::SyncToken& sync_token) {
-  NOTREACHED();
-}
+void Context::WaitSyncTokenHint(const gpu::SyncToken& sync_token) {}
 
 bool Context::CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) {
   return false;
diff --git a/gpu/gles2_conform_support/egl/context.h b/gpu/gles2_conform_support/egl/context.h
index 60917fb..42b432aa 100644
--- a/gpu/gles2_conform_support/egl/context.h
+++ b/gpu/gles2_conform_support/egl/context.h
@@ -80,7 +80,7 @@
   bool IsFenceSyncReleased(uint64_t release) override;
   void SignalSyncToken(const gpu::SyncToken& sync_token,
                        base::OnceClosure callback) override;
-  void WaitSyncToken(const gpu::SyncToken& sync_token) override;
+  void WaitSyncTokenHint(const gpu::SyncToken& sync_token) override;
   bool CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) override;
 
   // Called by ThreadState to set the needed global variables when this context
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.cc b/gpu/ipc/client/command_buffer_proxy_impl.cc
index 4702a07..31cc9b0 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.cc
+++ b/gpu/ipc/client/command_buffer_proxy_impl.cc
@@ -269,6 +269,10 @@
   last_put_offset_ = put_offset;
   last_flush_id_ = channel_->OrderingBarrier(
       route_id_, put_offset, std::move(pending_sync_token_fences_));
+
+  pending_sync_token_fences_.clear();
+
+  flushed_fence_sync_release_ = next_fence_sync_release_ - 1;
 }
 
 void CommandBufferProxyImpl::SetUpdateVSyncParametersCallback(
@@ -432,9 +436,13 @@
   bool requires_sync_token = handle.type == gfx::IO_SURFACE_BUFFER;
 
   uint64_t image_fence_sync = 0;
-  if (requires_sync_token)
+  if (requires_sync_token) {
     image_fence_sync = GenerateFenceSyncRelease();
 
+    // Make sure fence syncs were flushed before CreateImage() was called.
+    DCHECK_EQ(image_fence_sync, flushed_fence_sync_release_ + 1);
+  }
+
   DCHECK(gpu::IsImageFromGpuMemoryBufferFormatSupported(
       gpu_memory_buffer->GetFormat(), capabilities_));
   DCHECK(gpu::IsImageSizeValidForGpuMemoryBufferFormat(
@@ -543,7 +551,8 @@
   signal_tasks_.insert(std::make_pair(signal_id, std::move(callback)));
 }
 
-void CommandBufferProxyImpl::WaitSyncToken(const gpu::SyncToken& sync_token) {
+void CommandBufferProxyImpl::WaitSyncTokenHint(
+    const gpu::SyncToken& sync_token) {
   CheckLock();
   base::AutoLock lock(last_state_lock_);
   if (last_state_.error != gpu::error::kNoError)
@@ -650,8 +659,8 @@
   if (last_state_.error != gpu::error::kNoError)
     return;
 
-  Send(new GpuCommandBufferMsg_ReturnFrontBuffer(route_id_, mailbox, sync_token,
-                                                 is_lost));
+  Send(new GpuCommandBufferMsg_WaitSyncToken(route_id_, sync_token));
+  Send(new GpuCommandBufferMsg_ReturnFrontBuffer(route_id_, mailbox, is_lost));
 }
 
 bool CommandBufferProxyImpl::Send(IPC::Message* msg) {
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.h b/gpu/ipc/client/command_buffer_proxy_impl.h
index 3df4dd8..c71dbe1 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.h
+++ b/gpu/ipc/client/command_buffer_proxy_impl.h
@@ -129,7 +129,7 @@
   bool IsFenceSyncReleased(uint64_t release) override;
   void SignalSyncToken(const gpu::SyncToken& sync_token,
                        base::OnceClosure callback) override;
-  void WaitSyncToken(const gpu::SyncToken& sync_token) override;
+  void WaitSyncTokenHint(const gpu::SyncToken& sync_token) override;
   bool CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) override;
   void TakeFrontBuffer(const gpu::Mailbox& mailbox);
   void ReturnFrontBuffer(const gpu::Mailbox& mailbox,
@@ -263,6 +263,12 @@
   // Sync token waits that haven't been flushed yet.
   std::vector<SyncToken> pending_sync_token_fences_;
 
+  // Last flushed fence sync release, same as last item in queue if not empty.
+  uint64_t flushed_fence_sync_release_ = 0;
+
+  // Last verified fence sync.
+  uint64_t verified_fence_sync_release_ = 0;
+
   GpuConsoleMessageCallback console_message_callback_;
 
   // Tasks to be invoked in SignalSyncPoint responses.
diff --git a/gpu/ipc/client/gpu_channel_host.cc b/gpu/ipc/client/gpu_channel_host.cc
index 583cd7d..6ecc095 100644
--- a/gpu/ipc/client/gpu_channel_host.cc
+++ b/gpu/ipc/client/gpu_channel_host.cc
@@ -156,8 +156,7 @@
   deferred_message.message = GpuCommandBufferMsg_AsyncFlush(
       pending_ordering_barrier_->route_id,
       pending_ordering_barrier_->put_offset,
-      pending_ordering_barrier_->deferred_message_id,
-      pending_ordering_barrier_->sync_token_fences);
+      pending_ordering_barrier_->deferred_message_id);
   deferred_message.sync_token_fences =
       std::move(pending_ordering_barrier_->sync_token_fences);
   deferred_messages_.push_back(std::move(deferred_message));
diff --git a/gpu/ipc/command_buffer_task_executor.h b/gpu/ipc/command_buffer_task_executor.h
index a0c6714..42f33ef 100644
--- a/gpu/ipc/command_buffer_task_executor.h
+++ b/gpu/ipc/command_buffer_task_executor.h
@@ -56,6 +56,9 @@
     // Returns true if sequence should yield while running its current task.
     virtual bool ShouldYield() = 0;
 
+    // Enables or disables further execution of tasks in this sequence.
+    virtual void SetEnabled(bool enabled) = 0;
+
     // Schedule a task with provided sync token dependencies. The dependencies
     // are hints for sync token waits within the task, and can be ignored by the
     // implementation.
@@ -79,6 +82,10 @@
   // Creates a memory tracker for the context group if this returns true.
   virtual bool ShouldCreateMemoryTracker() const = 0;
 
+  // Block thread when a WaitSyncToken command is encountered instead of calling
+  // OnWaitSyncToken().
+  virtual bool BlockThreadOnWaitSyncToken() const = 0;
+
   // Schedules |task| to run out of order with respect to other sequenced tasks.
   virtual void ScheduleOutOfOrderTask(base::OnceClosure task) = 0;
 
diff --git a/gpu/ipc/common/gpu_messages.h b/gpu/ipc/common/gpu_messages.h
index 6b6470c4..b563322 100644
--- a/gpu/ipc/common/gpu_messages.h
+++ b/gpu/ipc/common/gpu_messages.h
@@ -159,9 +159,8 @@
 
 // Returns a front buffer taken with GpuCommandBufferMsg_TakeFrontBuffer. This
 // allows it to be reused.
-IPC_MESSAGE_ROUTED3(GpuCommandBufferMsg_ReturnFrontBuffer,
+IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_ReturnFrontBuffer,
                     gpu::Mailbox /* mailbox */,
-                    gpu::SyncToken /* sync_token */,
                     bool /* is_lost */)
 
 // Wait until the token is in a specific range, inclusive.
@@ -183,10 +182,9 @@
 // TODO(sunnyps): This is an internal implementation detail of the gpu service
 // and is not sent by the client. Remove this once the non-scheduler code path
 // is removed.
-IPC_MESSAGE_ROUTED3(GpuCommandBufferMsg_AsyncFlush,
+IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_AsyncFlush,
                     int32_t /* put_offset */,
-                    uint32_t /* flush_id */,
-                    std::vector<gpu::SyncToken> /* sync_token_fences */)
+                    uint32_t /* flush_id */)
 
 // Sent by the GPU process to display messages in the console.
 IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_ConsoleMsg,
@@ -216,6 +214,10 @@
                     uint64_t, /* swap_id */
                     gfx::PresentationFeedback /* feedback */)
 
+// The receiver will stop processing messages until the Synctoken is signaled.
+IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_WaitSyncToken,
+                    gpu::SyncToken /* sync_token */)
+
 // The receiver will asynchronously wait until the SyncToken is signaled, and
 // then return a GpuCommandBufferMsg_SignalAck message.
 IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_SignalSyncToken,
diff --git a/gpu/ipc/gpu_in_process_thread_service.cc b/gpu/ipc/gpu_in_process_thread_service.cc
index c0d6edd..3d3c773 100644
--- a/gpu/ipc/gpu_in_process_thread_service.cc
+++ b/gpu/ipc/gpu_in_process_thread_service.cc
@@ -28,6 +28,13 @@
 
   bool ShouldYield() override { return scheduler_->ShouldYield(sequence_id_); }
 
+  void SetEnabled(bool enabled) override {
+    if (enabled)
+      scheduler_->EnableSequence(sequence_id_);
+    else
+      scheduler_->DisableSequence(sequence_id_);
+  }
+
   void ScheduleTask(base::OnceClosure task,
                     std::vector<SyncToken> sync_token_fences) override {
     scheduler_->ScheduleTask(Scheduler::Task(sequence_id_, std::move(task),
@@ -75,6 +82,10 @@
   return true;
 }
 
+bool GpuInProcessThreadService::BlockThreadOnWaitSyncToken() const {
+  return false;
+}
+
 std::unique_ptr<CommandBufferTaskExecutor::Sequence>
 GpuInProcessThreadService::CreateSequence() {
   return std::make_unique<SchedulerSequence>(scheduler_);
diff --git a/gpu/ipc/gpu_in_process_thread_service.h b/gpu/ipc/gpu_in_process_thread_service.h
index df52823..1527230 100644
--- a/gpu/ipc/gpu_in_process_thread_service.h
+++ b/gpu/ipc/gpu_in_process_thread_service.h
@@ -35,6 +35,7 @@
   // CommandBufferTaskExecutor implementation.
   bool ForceVirtualizedGLContexts() const override;
   bool ShouldCreateMemoryTracker() const override;
+  bool BlockThreadOnWaitSyncToken() const override;
   std::unique_ptr<CommandBufferTaskExecutor::Sequence> CreateSequence()
       override;
   void ScheduleOutOfOrderTask(base::OnceClosure task) override;
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index ec0a9bf4..922423b 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -807,29 +807,16 @@
   return false;
 }
 
-void InProcessCommandBuffer::FlushOnGpuThread(
-    int32_t put_offset,
-    const std::vector<SyncToken>& sync_token_fences) {
+void InProcessCommandBuffer::FlushOnGpuThread(int32_t put_offset) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
   TRACE_EVENT1("gpu", "InProcessCommandBuffer::FlushOnGpuThread", "put_offset",
                put_offset);
 
   ScopedEvent handle_flush(&flush_event_);
-  // Check if sync token waits are invalid or already complete. Do not use
-  // SyncPointManager::IsSyncTokenReleased() as it can't say if the wait is
-  // invalid.
-  for (const auto& sync_token : sync_token_fences)
-    DCHECK(!sync_point_client_state_->Wait(sync_token, base::DoNothing()));
 
   if (!MakeCurrent())
     return;
 
-  MailboxManager* mailbox_manager = context_group_->mailbox_manager();
-  if (mailbox_manager->UsesSync()) {
-    for (const auto& sync_token : sync_token_fences)
-      mailbox_manager->PullTextureUpdates(sync_token);
-  }
-
   {
     base::Optional<raster::GrShaderCache::ScopedCacheUse> cache_use;
     if (gr_shader_cache_)
@@ -842,9 +829,10 @@
   bool has_unprocessed_commands = HasUnprocessedCommandsOnGpuThread();
 
   if (!command_buffer_->scheduled() || has_unprocessed_commands) {
+    DCHECK(!task_executor_->BlockThreadOnWaitSyncToken());
     ContinueGpuTask(base::BindOnce(&InProcessCommandBuffer::FlushOnGpuThread,
                                    gpu_thread_weak_ptr_factory_.GetWeakPtr(),
-                                   put_offset, sync_token_fences));
+                                   put_offset));
   }
 
   // If we've processed all pending commands but still have pending queries,
@@ -891,16 +879,15 @@
                put_offset);
 
   last_put_offset_ = put_offset;
+  flushed_fence_sync_release_ = next_fence_sync_release_ - 1;
 
   std::vector<SyncToken> sync_token_fences;
   next_flush_sync_token_fences_.swap(sync_token_fences);
 
-  // Don't use std::move() for |sync_token_fences| because evaluation order for
-  // arguments is not defined.
-  ScheduleGpuTask(base::BindOnce(&InProcessCommandBuffer::FlushOnGpuThread,
-                                 gpu_thread_weak_ptr_factory_.GetWeakPtr(),
-                                 put_offset, sync_token_fences),
-                  sync_token_fences);
+  ScheduleGpuTask(
+      base::BindOnce(&InProcessCommandBuffer::FlushOnGpuThread,
+                     gpu_thread_weak_ptr_factory_.GetWeakPtr(), put_offset),
+      std::move(sync_token_fences));
 }
 
 void InProcessCommandBuffer::OrderingBarrier(int32_t put_offset) {
@@ -1027,9 +1014,13 @@
   bool requires_sync_point = handle.type == gfx::IO_SURFACE_BUFFER;
 
   uint64_t fence_sync = 0;
-  if (requires_sync_point)
+  if (requires_sync_point) {
     fence_sync = GenerateFenceSyncRelease();
 
+    // Previous fence syncs should be flushed already.
+    DCHECK_EQ(fence_sync - 1, flushed_fence_sync_release_);
+  }
+
   ScheduleGpuTask(base::BindOnce(
       &InProcessCommandBuffer::CreateImageOnGpuThread,
       gpu_thread_weak_ptr_factory_.GetWeakPtr(), new_id, std::move(handle),
@@ -1039,6 +1030,7 @@
       base::checked_cast<uint32_t>(internalformat), fence_sync));
 
   if (fence_sync) {
+    flushed_fence_sync_release_ = fence_sync;
     SyncToken sync_token(GetNamespaceID(), GetCommandBufferID(), fence_sync);
     sync_token.SetVerifyFlush();
     gpu_memory_buffer_manager_->SetDestructionSyncToken(gpu_memory_buffer,
@@ -1139,13 +1131,58 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
 
   SyncToken sync_token(GetNamespaceID(), GetCommandBufferID(), release);
+  context_group_->mailbox_manager()->PushTextureUpdates(sync_token);
+  sync_point_client_state_->ReleaseFenceSync(release);
+}
+
+// TODO(sunnyps): Remove the wait command once all sync tokens are passed as
+// task dependencies.
+bool InProcessCommandBuffer::OnWaitSyncToken(const SyncToken& sync_token) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+  DCHECK(!waiting_for_sync_point_);
+  TRACE_EVENT0("gpu", "InProcessCommandBuffer::OnWaitSyncToken");
+
+  SyncPointManager* sync_point_manager = task_executor_->sync_point_manager();
+  DCHECK(sync_point_manager);
 
   MailboxManager* mailbox_manager = context_group_->mailbox_manager();
-  if (mailbox_manager->UsesSync())
-    mailbox_manager->PushTextureUpdates(sync_token);
+  DCHECK(mailbox_manager);
 
-  command_buffer_->SetReleaseCount(release);
-  sync_point_client_state_->ReleaseFenceSync(release);
+  if (task_executor_->BlockThreadOnWaitSyncToken()) {
+    // Wait if sync point wait is valid.
+    if (sync_point_client_state_->Wait(
+            sync_token,
+            base::Bind(&base::WaitableEvent::Signal,
+                       base::Unretained(&fence_sync_wait_event_)))) {
+      fence_sync_wait_event_.Wait();
+    }
+
+    mailbox_manager->PullTextureUpdates(sync_token);
+    return false;
+  }
+
+  waiting_for_sync_point_ = sync_point_client_state_->Wait(
+      sync_token,
+      base::Bind(&InProcessCommandBuffer::OnWaitSyncTokenCompleted,
+                 gpu_thread_weak_ptr_factory_.GetWeakPtr(), sync_token));
+  if (!waiting_for_sync_point_) {
+    mailbox_manager->PullTextureUpdates(sync_token);
+    return false;
+  }
+
+  command_buffer_->SetScheduled(false);
+  task_sequence_->SetEnabled(false);
+  return true;
+}
+
+void InProcessCommandBuffer::OnWaitSyncTokenCompleted(
+    const SyncToken& sync_token) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+  DCHECK(waiting_for_sync_point_);
+  context_group_->mailbox_manager()->PullTextureUpdates(sync_token);
+  waiting_for_sync_point_ = false;
+  command_buffer_->SetScheduled(true);
+  task_sequence_->SetEnabled(true);
 }
 
 void InProcessCommandBuffer::OnDescheduleUntilFinished() {
@@ -1357,7 +1394,7 @@
   return release <= GetLastState().release_count;
 }
 
-void InProcessCommandBuffer::WaitSyncToken(const SyncToken& sync_token) {
+void InProcessCommandBuffer::WaitSyncTokenHint(const SyncToken& sync_token) {
   next_flush_sync_token_fences_.push_back(sync_token);
 }
 
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index 57bdefb0..243f330 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -140,7 +140,7 @@
   bool IsFenceSyncReleased(uint64_t release) override;
   void SignalSyncToken(const SyncToken& sync_token,
                        base::OnceClosure callback) override;
-  void WaitSyncToken(const SyncToken& sync_token) override;
+  void WaitSyncTokenHint(const SyncToken& sync_token) override;
   bool CanWaitUnverifiedSyncToken(const SyncToken& sync_token) override;
 
   // CommandBufferServiceClient implementation (called on gpu thread):
@@ -151,6 +151,7 @@
   void OnConsoleMessage(int32_t id, const std::string& message) override;
   void CacheShader(const std::string& key, const std::string& shader) override;
   void OnFenceSyncRelease(uint64_t release) override;
+  bool OnWaitSyncToken(const SyncToken& sync_token) override;
   void OnDescheduleUntilFinished() override;
   void OnRescheduleAfterFinished() override;
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
@@ -234,8 +235,7 @@
 
   // Flush up to put_offset. If execution is deferred either by yielding, or due
   // to a sync token wait, HasUnprocessedCommandsOnGpuThread() returns true.
-  void FlushOnGpuThread(int32_t put_offset,
-                        const std::vector<SyncToken>& sync_token_fences);
+  void FlushOnGpuThread(int32_t put_offset);
   bool HasUnprocessedCommandsOnGpuThread();
   void UpdateLastStateOnGpuThread();
 
@@ -287,6 +287,7 @@
 
   // Callbacks on the gpu thread.
   void PerformDelayedWorkOnGpuThread();
+  void OnWaitSyncTokenCompleted(const SyncToken& sync_token);
 
   // Callback implementations on the client thread.
   void OnContextLost();
@@ -299,6 +300,7 @@
 
   // Members accessed on the gpu thread (possibly with the exception of
   // creation):
+  bool waiting_for_sync_point_ = false;
   bool use_virtualized_gl_context_ = false;
   raster::GrShaderCache* gr_shader_cache_ = nullptr;
   scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
@@ -330,6 +332,7 @@
   Capabilities capabilities_;
   GpuMemoryBufferManager* gpu_memory_buffer_manager_ = nullptr;
   uint64_t next_fence_sync_release_ = 1;
+  uint64_t flushed_fence_sync_release_ = 0;
   std::vector<SyncToken> next_flush_sync_token_fences_;
   // Sequence checker for client sequence used for initialization, destruction,
   // callbacks, such as context loss, and methods which provide such callbacks,
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index b3db1be..ed7e19a 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -150,6 +150,7 @@
       stream_id_(stream_id),
       route_id_(route_id),
       last_flush_id_(0),
+      waiting_for_sync_point_(false),
       previous_processed_num_(0),
       wait_set_get_buffer_count_(0) {}
 
@@ -175,6 +176,7 @@
       message.type() != GpuCommandBufferMsg_WaitForGetOffsetInRange::ID &&
       message.type() != GpuCommandBufferMsg_RegisterTransferBuffer::ID &&
       message.type() != GpuCommandBufferMsg_DestroyTransferBuffer::ID &&
+      message.type() != GpuCommandBufferMsg_WaitSyncToken::ID &&
       message.type() != GpuCommandBufferMsg_SignalSyncToken::ID &&
       message.type() != GpuCommandBufferMsg_SignalQuery::ID) {
     if (!MakeCurrent())
@@ -199,6 +201,7 @@
                         OnRegisterTransferBuffer);
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_DestroyTransferBuffer,
                         OnDestroyTransferBuffer);
+    IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_WaitSyncToken, OnWaitSyncToken)
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalSyncToken, OnSignalSyncToken)
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalQuery, OnSignalQuery)
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateImage, OnCreateImage);
@@ -514,10 +517,7 @@
   }
 }
 
-void CommandBufferStub::OnAsyncFlush(
-    int32_t put_offset,
-    uint32_t flush_id,
-    const std::vector<SyncToken>& sync_token_fences) {
+void CommandBufferStub::OnAsyncFlush(int32_t put_offset, uint32_t flush_id) {
   TRACE_EVENT1("gpu", "CommandBufferStub::OnAsyncFlush", "put_offset",
                put_offset);
   DCHECK(command_buffer_);
@@ -525,22 +525,11 @@
   // to catch regressions. Ignore the message.
   DVLOG_IF(0, flush_id - last_flush_id_ >= 0x8000000U)
       << "Received a Flush message out-of-order";
-  // Check if sync token waits are invalid or already complete. Do not use
-  // SyncPointManager::IsSyncTokenReleased() as it can't say if the wait is
-  // invalid.
-  for (const auto& sync_token : sync_token_fences)
-    DCHECK(!sync_point_client_state_->Wait(sync_token, base::DoNothing()));
 
   last_flush_id_ = flush_id;
   CommandBuffer::State pre_state = command_buffer_->GetState();
   FastSetActiveURL(active_url_, active_url_hash_, channel_);
 
-  MailboxManager* mailbox_manager = context_group_->mailbox_manager();
-  if (mailbox_manager->UsesSync()) {
-    for (const auto& sync_token : sync_token_fences)
-      mailbox_manager->PullTextureUpdates(sync_token);
-  }
-
   {
     auto* gr_shader_cache = channel_->gpu_channel_manager()->gr_shader_cache();
     base::Optional<raster::GrShaderCache::ScopedCacheUse> cache_use;
@@ -691,6 +680,43 @@
   channel_->gpu_channel_manager()->ScheduleGrContextCleanup();
 }
 
+// TODO(sunnyps): Remove the wait command once all sync tokens are passed as
+// task dependencies.
+bool CommandBufferStub::OnWaitSyncToken(const SyncToken& sync_token) {
+  DCHECK(!waiting_for_sync_point_);
+  DCHECK(command_buffer_->scheduled());
+  TRACE_EVENT_ASYNC_BEGIN1("gpu", "WaitSyncToken", this, "CommandBufferStub",
+                           this);
+
+  waiting_for_sync_point_ = sync_point_client_state_->WaitNonThreadSafe(
+      sync_token, channel_->task_runner(),
+      base::Bind(&CommandBufferStub::OnWaitSyncTokenCompleted, AsWeakPtr(),
+                 sync_token));
+
+  if (waiting_for_sync_point_) {
+    command_buffer_->SetScheduled(false);
+    channel_->OnCommandBufferDescheduled(this);
+    return true;
+  }
+
+  MailboxManager* mailbox_manager = context_group_->mailbox_manager();
+  if (mailbox_manager->UsesSync() && MakeCurrent())
+    mailbox_manager->PullTextureUpdates(sync_token);
+  return false;
+}
+
+void CommandBufferStub::OnWaitSyncTokenCompleted(const SyncToken& sync_token) {
+  DCHECK(waiting_for_sync_point_);
+  TRACE_EVENT_ASYNC_END1("gpu", "WaitSyncToken", this, "CommandBufferStub",
+                         this);
+  // Don't call PullTextureUpdates here because we can't MakeCurrent if we're
+  // executing commands on another context. The WaitSyncToken command will run
+  // again and call PullTextureUpdates once this command buffer gets scheduled.
+  waiting_for_sync_point_ = false;
+  command_buffer_->SetScheduled(true);
+  channel_->OnCommandBufferScheduled(this);
+}
+
 void CommandBufferStub::OnCreateImage(
     GpuCommandBufferMsg_CreateImage_Params params) {
   TRACE_EVENT0("gpu", "CommandBufferStub::OnCreateImage");
diff --git a/gpu/ipc/service/command_buffer_stub.h b/gpu/ipc/service/command_buffer_stub.h
index 5e8d3e99..dfd7ebb6 100644
--- a/gpu/ipc/service/command_buffer_stub.h
+++ b/gpu/ipc/service/command_buffer_stub.h
@@ -97,6 +97,7 @@
   void OnConsoleMessage(int32_t id, const std::string& message) override;
   void CacheShader(const std::string& key, const std::string& shader) override;
   void OnFenceSyncRelease(uint64_t release) override;
+  bool OnWaitSyncToken(const SyncToken& sync_token) override;
   void OnDescheduleUntilFinished() override;
   void OnRescheduleAfterFinished() override;
   void ScheduleGrContextCleanup() override;
@@ -179,9 +180,7 @@
   // Message handlers:
   void OnSetGetBuffer(int32_t shm_id);
   virtual void OnTakeFrontBuffer(const Mailbox& mailbox) = 0;
-  virtual void OnReturnFrontBuffer(const Mailbox& mailbox,
-                                   const SyncToken& sync_token,
-                                   bool is_lost) = 0;
+  virtual void OnReturnFrontBuffer(const Mailbox& mailbox, bool is_lost) = 0;
   void OnGetState(IPC::Message* reply_message);
   void OnWaitForTokenInRange(int32_t start,
                              int32_t end,
@@ -190,9 +189,7 @@
                                  int32_t start,
                                  int32_t end,
                                  IPC::Message* reply_message);
-  void OnAsyncFlush(int32_t put_offset,
-                    uint32_t flush_id,
-                    const std::vector<SyncToken>& sync_token_fences);
+  void OnAsyncFlush(int32_t put_offset, uint32_t flush_id);
   void OnRegisterTransferBuffer(int32_t id,
                                 base::UnsafeSharedMemoryRegion transfer_buffer);
   void OnDestroyTransferBuffer(int32_t id);
@@ -207,6 +204,8 @@
                                   const gfx::GpuFenceHandle& handle);
   void OnGetGpuFenceHandle(uint32_t gpu_fence_id);
 
+  void OnWaitSyncTokenCompleted(const SyncToken& sync_token);
+
   void OnCreateImage(GpuCommandBufferMsg_CreateImage_Params params);
   void OnDestroyImage(int32_t id);
   void OnCreateStreamTexture(uint32_t texture_id,
@@ -238,6 +237,8 @@
 
   base::ObserverList<DestructionObserver>::Unchecked destruction_observers_;
 
+  bool waiting_for_sync_point_;
+
   base::TimeTicks process_delayed_work_time_;
   uint32_t previous_processed_num_;
   base::TimeTicks last_idle_time_;
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index 211aa42b..a5c624a 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -426,14 +426,7 @@
 }
 
 void GLES2CommandBufferStub::OnReturnFrontBuffer(const Mailbox& mailbox,
-                                                 const SyncToken& sync_token,
                                                  bool is_lost) {
-  // Check if sync token wait is invalid or already complete. Do not use
-  // SyncPointManager::IsSyncTokenReleased() as it can't say if the wait is
-  // invalid.
-  DCHECK(!sync_point_client_state_->Wait(sync_token, base::DoNothing()));
-  // No need to pull texture updates.
-  DCHECK(!context_group_->mailbox_manager()->UsesSync());
   gles2_decoder_->ReturnFrontBuffer(mailbox, is_lost);
 }
 
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.h b/gpu/ipc/service/gles2_command_buffer_stub.h
index ae0cd39..4d4530ba 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.h
+++ b/gpu/ipc/service/gles2_command_buffer_stub.h
@@ -51,9 +51,7 @@
 
  private:
   void OnTakeFrontBuffer(const Mailbox& mailbox) override;
-  void OnReturnFrontBuffer(const Mailbox& mailbox,
-                           const SyncToken& sync_token,
-                           bool is_lost) override;
+  void OnReturnFrontBuffer(const Mailbox& mailbox, bool is_lost) override;
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
 
   // Keep a more specifically typed reference to the decoder to avoid
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index 59f1a96..af29d8b3 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -104,11 +104,6 @@
  private:
   ~GpuChannelMessageFilter() override;
 
-  SequenceId GetSequenceId(int32_t route_id) const;
-
-  bool HandleFlushMessage(const IPC::Message& message);
-  bool HandleReturnFrontBufferMessage(const IPC::Message& message);
-
   bool MessageErrorHandler(const IPC::Message& message, const char* error_msg);
 
   IPC::Channel* ipc_channel_ = nullptr;
@@ -223,16 +218,6 @@
   if (message.should_unblock() || message.is_reply())
     return MessageErrorHandler(message, "Unexpected message type");
 
-  switch (message.type()) {
-    case GpuCommandBufferMsg_AsyncFlush::ID:
-    case GpuCommandBufferMsg_DestroyTransferBuffer::ID:
-    case GpuChannelMsg_CreateSharedImage::ID:
-    case GpuChannelMsg_DestroySharedImage::ID:
-      return MessageErrorHandler(message, "Invalid message");
-    default:
-      break;
-  }
-
   if (message.type() == GpuChannelMsg_Nop::ID) {
     IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
     ipc_channel_->Send(reply);
@@ -248,96 +233,64 @@
   if (!gpu_channel_)
     return MessageErrorHandler(message, "Channel destroyed");
 
-  // Handle flush first so that it doesn't get handled out of order.
-  if (message.type() == GpuChannelMsg_FlushDeferredMessages::ID)
-    return HandleFlushMessage(message);
+  switch (message.type()) {
+    case GpuCommandBufferMsg_AsyncFlush::ID:
+    case GpuCommandBufferMsg_DestroyTransferBuffer::ID:
+    case GpuChannelMsg_CreateSharedImage::ID:
+    case GpuChannelMsg_DestroySharedImage::ID:
+      return MessageErrorHandler(message, "Invalid message");
+    default:
+      break;
+  }
 
-  if (message.type() == GpuCommandBufferMsg_ReturnFrontBuffer::ID)
-    return HandleReturnFrontBufferMessage(message);
+  if (message.type() == GpuChannelMsg_FlushDeferredMessages::ID) {
+    GpuChannelMsg_FlushDeferredMessages::Param params;
 
-  bool handle_out_of_order =
-      message.routing_id() == MSG_ROUTING_CONTROL ||
-      message.type() == GpuCommandBufferMsg_WaitForTokenInRange::ID ||
-      message.type() == GpuCommandBufferMsg_WaitForGetOffsetInRange::ID;
+    if (!GpuChannelMsg_FlushDeferredMessages::Read(&message, &params))
+      return MessageErrorHandler(message, "Invalid flush message");
 
-  if (handle_out_of_order) {
+    std::vector<GpuDeferredMessage> deferred_messages =
+        std::get<0>(std::move(params));
+    std::vector<Scheduler::Task> tasks;
+    tasks.reserve(deferred_messages.size());
+
+    for (auto& deferred_message : deferred_messages) {
+      auto it = route_sequences_.find(deferred_message.message.routing_id());
+      if (it == route_sequences_.end()) {
+        DLOG(ERROR) << "Invalid route id in flush list";
+        continue;
+      }
+
+      tasks.emplace_back(
+          it->second /* sequence_id */,
+          base::BindOnce(&GpuChannel::HandleMessage, gpu_channel_->AsWeakPtr(),
+                         std::move(deferred_message.message)),
+          std::move(deferred_message.sync_token_fences));
+    }
+
+    scheduler_->ScheduleTasks(std::move(tasks));
+
+  } else if (message.routing_id() == MSG_ROUTING_CONTROL ||
+             message.type() == GpuCommandBufferMsg_WaitForTokenInRange::ID ||
+             message.type() ==
+                 GpuCommandBufferMsg_WaitForGetOffsetInRange::ID) {
     // It's OK to post task that may never run even for sync messages, because
     // if the channel is destroyed, the client Send will fail.
-    main_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&GpuChannel::HandleOutOfOrderMessage,
-                                  gpu_channel_->AsWeakPtr(), message));
-    return true;
+    main_task_runner_->PostTask(FROM_HERE,
+                                base::Bind(&GpuChannel::HandleOutOfOrderMessage,
+                                           gpu_channel_->AsWeakPtr(), message));
+  } else {
+    auto it = route_sequences_.find(message.routing_id());
+    if (it == route_sequences_.end())
+      return MessageErrorHandler(message, "Invalid route id");
+
+    scheduler_->ScheduleTask(
+        Scheduler::Task(it->second /* sequence_id */,
+                        base::BindOnce(&GpuChannel::HandleMessage,
+                                       gpu_channel_->AsWeakPtr(), message),
+                        std::vector<SyncToken>()));
   }
 
-  // Messages which do not have sync token dependencies.
-  SequenceId sequence_id = GetSequenceId(message.routing_id());
-  if (sequence_id.is_null())
-    return MessageErrorHandler(message, "Invalid route id");
-
-  scheduler_->ScheduleTask(
-      Scheduler::Task(sequence_id,
-                      base::BindOnce(&GpuChannel::HandleMessage,
-                                     gpu_channel_->AsWeakPtr(), message),
-                      std::vector<SyncToken>()));
-  return true;
-}
-
-SequenceId GpuChannelMessageFilter::GetSequenceId(int32_t route_id) const {
-  gpu_channel_lock_.AssertAcquired();
-  auto it = route_sequences_.find(route_id);
-  if (it == route_sequences_.end())
-    return SequenceId();
-  return it->second;
-}
-
-bool GpuChannelMessageFilter::HandleFlushMessage(const IPC::Message& message) {
-  DCHECK_EQ(message.type(), GpuChannelMsg_FlushDeferredMessages::ID);
-  gpu_channel_lock_.AssertAcquired();
-
-  GpuChannelMsg_FlushDeferredMessages::Param params;
-  if (!GpuChannelMsg_FlushDeferredMessages::Read(&message, &params))
-    return MessageErrorHandler(message, "Invalid flush message");
-
-  std::vector<GpuDeferredMessage> deferred_messages =
-      std::get<0>(std::move(params));
-
-  std::vector<Scheduler::Task> tasks;
-  tasks.reserve(deferred_messages.size());
-  for (auto& deferred_message : deferred_messages) {
-    auto it = route_sequences_.find(deferred_message.message.routing_id());
-    if (it == route_sequences_.end()) {
-      DLOG(ERROR) << "Invalid route id in flush list";
-      continue;
-    }
-    tasks.emplace_back(
-        it->second /* sequence_id */,
-        base::BindOnce(&GpuChannel::HandleMessage, gpu_channel_->AsWeakPtr(),
-                       std::move(deferred_message.message)),
-        std::move(deferred_message.sync_token_fences));
-  }
-  scheduler_->ScheduleTasks(std::move(tasks));
-  return true;
-}
-
-bool GpuChannelMessageFilter::HandleReturnFrontBufferMessage(
-    const IPC::Message& message) {
-  DCHECK_EQ(message.type(), GpuCommandBufferMsg_ReturnFrontBuffer::ID);
-  gpu_channel_lock_.AssertAcquired();
-
-  GpuCommandBufferMsg_ReturnFrontBuffer::Param params;
-  if (!GpuCommandBufferMsg_ReturnFrontBuffer::Read(&message, &params))
-    return MessageErrorHandler(message, "Invalid ReturnFrontBuffer message");
-
-  SequenceId sequence_id = GetSequenceId(message.routing_id());
-  if (sequence_id.is_null())
-    return MessageErrorHandler(message, "Invalid route id");
-
-  SyncToken sync_token = std::get<1>(params);
-  scheduler_->ScheduleTask(
-      Scheduler::Task(sequence_id,
-                      base::BindOnce(&GpuChannel::HandleMessage,
-                                     gpu_channel_->AsWeakPtr(), message),
-                      std::vector<SyncToken>{sync_token}));
   return true;
 }
 
@@ -538,7 +491,8 @@
 
   // If we get descheduled or yield while processing a message.
   if (stub && (stub->HasUnprocessedCommands() || !stub->IsScheduled())) {
-    DCHECK_EQ(GpuCommandBufferMsg_AsyncFlush::ID, msg.type());
+    DCHECK((uint32_t)GpuCommandBufferMsg_AsyncFlush::ID == msg.type() ||
+           (uint32_t)GpuCommandBufferMsg_WaitSyncToken::ID == msg.type());
     scheduler_->ContinueTask(
         stub->sequence_id(),
         base::BindOnce(&GpuChannel::HandleMessage, AsWeakPtr(), msg));
diff --git a/gpu/ipc/service/raster_command_buffer_stub.cc b/gpu/ipc/service/raster_command_buffer_stub.cc
index 3f8a69d..dda58ab 100644
--- a/gpu/ipc/service/raster_command_buffer_stub.cc
+++ b/gpu/ipc/service/raster_command_buffer_stub.cc
@@ -233,7 +233,6 @@
   NOTREACHED();
 }
 void RasterCommandBufferStub::OnReturnFrontBuffer(const Mailbox& mailbox,
-                                                  const SyncToken& sync_token,
                                                   bool is_lost) {
   NOTREACHED();
 }
diff --git a/gpu/ipc/service/raster_command_buffer_stub.h b/gpu/ipc/service/raster_command_buffer_stub.h
index 28989f1..b1b0547 100644
--- a/gpu/ipc/service/raster_command_buffer_stub.h
+++ b/gpu/ipc/service/raster_command_buffer_stub.h
@@ -30,9 +30,7 @@
 
  private:
   void OnTakeFrontBuffer(const Mailbox& mailbox) override;
-  void OnReturnFrontBuffer(const Mailbox& mailbox,
-                           const SyncToken& sync_token,
-                           bool is_lost) override;
+  void OnReturnFrontBuffer(const Mailbox& mailbox, bool is_lost) override;
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
   void SetActiveURL(GURL url) override;
   void ResetActiveURL() override;
diff --git a/gpu/ipc/service/webgpu_command_buffer_stub.cc b/gpu/ipc/service/webgpu_command_buffer_stub.cc
index 9dafcd2..99e089e6 100644
--- a/gpu/ipc/service/webgpu_command_buffer_stub.cc
+++ b/gpu/ipc/service/webgpu_command_buffer_stub.cc
@@ -171,7 +171,6 @@
   LOG(ERROR) << "Called WebGPUCommandBufferStub::OnTakeFrontBuffer";
 }
 void WebGPUCommandBufferStub::OnReturnFrontBuffer(const Mailbox& mailbox,
-                                                  const SyncToken& sync_token,
                                                   bool is_lost) {
   LOG(ERROR) << "Called WebGPUCommandBufferStub::OnReturnFrontBuffer";
 }
diff --git a/gpu/ipc/service/webgpu_command_buffer_stub.h b/gpu/ipc/service/webgpu_command_buffer_stub.h
index 866ae1f..cc74b5d 100644
--- a/gpu/ipc/service/webgpu_command_buffer_stub.h
+++ b/gpu/ipc/service/webgpu_command_buffer_stub.h
@@ -30,9 +30,7 @@
 
  private:
   void OnTakeFrontBuffer(const Mailbox& mailbox) override;
-  void OnReturnFrontBuffer(const Mailbox& mailbox,
-                           const SyncToken& sync_token,
-                           bool is_lost) override;
+  void OnReturnFrontBuffer(const Mailbox& mailbox, bool is_lost) override;
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
 
   DISALLOW_COPY_AND_ASSIGN(WebGPUCommandBufferStub);
diff --git a/gpu/vulkan/features.gni b/gpu/vulkan/features.gni
index 315646cb4..d97fadb7 100644
--- a/gpu/vulkan/features.gni
+++ b/gpu/vulkan/features.gni
@@ -8,11 +8,5 @@
 # For details see declare_args() in build/config/BUILDCONFIG.gn.
 declare_args() {
   # Enable experimental vulkan backend.
-  enable_vulkan = is_linux || is_android || is_fuchsia
-
-  # We want to temporarily disable Vulkan on Android to give us time to
-  # investigate ways to reduce its binary size.
-  if (is_android) {
-    enable_vulkan = false
-  }
+  enable_vulkan = is_linux || is_fuchsia
 }
diff --git a/gpu/vulkan/init/BUILD.gn b/gpu/vulkan/init/BUILD.gn
index 5d8f2aa..7800c20 100644
--- a/gpu/vulkan/init/BUILD.gn
+++ b/gpu/vulkan/init/BUILD.gn
@@ -32,4 +32,7 @@
   if (use_ozone) {
     deps += [ "//ui/ozone" ]
   }
+  if (is_win) {
+    deps += [ "//gpu/vulkan/win32" ]
+  }
 }
diff --git a/gpu/vulkan/init/vulkan_factory.cc b/gpu/vulkan/init/vulkan_factory.cc
index fd27ac3..4b842bc 100644
--- a/gpu/vulkan/init/vulkan_factory.cc
+++ b/gpu/vulkan/init/vulkan_factory.cc
@@ -11,6 +11,10 @@
 #include "gpu/vulkan/android/vulkan_implementation_android.h"
 #endif
 
+#if defined(OS_WIN)
+#include "gpu/vulkan/win32/vulkan_implementation_win32.h"
+#endif
+
 #if defined(USE_X11)
 #include "gpu/vulkan/x/vulkan_implementation_x11.h"  // nogncheck
 #endif
@@ -31,6 +35,8 @@
   return ui::OzonePlatform::GetInstance()
       ->GetSurfaceFactoryOzone()
       ->CreateVulkanImplementation();
+#elif defined(OS_WIN)
+  return std::make_unique<VulkanImplementationWin32>();
 #else
 #error Unsupported Vulkan Platform.
 #endif
diff --git a/gpu/vulkan/win32/BUILD.gn b/gpu/vulkan/win32/BUILD.gn
new file mode 100644
index 0000000..d1834ff8
--- /dev/null
+++ b/gpu/vulkan/win32/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ui.gni")
+import("//gpu/vulkan/features.gni")
+
+assert(enable_vulkan)
+assert(is_win)
+
+config("vulkan_win32") {
+  defines = [ "VK_USE_PLATFORM_WIN32_KHR" ]
+}
+
+component("win32") {
+  output_name = "vulkan_win32"
+
+  sources = [
+    "vulkan_implementation_win32.cc",
+    "vulkan_implementation_win32.h",
+  ]
+
+  defines = [ "IS_VULKAN_WIN32_IMPL" ]
+
+  public_configs = [ ":vulkan_win32" ]
+
+  deps = [
+    "//ui/gfx",
+  ]
+
+  public_deps = [
+    "//base",
+    "//gpu/vulkan",
+  ]
+}
diff --git a/gpu/vulkan/win32/vulkan_implementation_win32.cc b/gpu/vulkan/win32/vulkan_implementation_win32.cc
new file mode 100644
index 0000000..0e3fa1a
--- /dev/null
+++ b/gpu/vulkan/win32/vulkan_implementation_win32.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2018 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 "gpu/vulkan/win32/vulkan_implementation_win32.h"
+
+#include <Windows.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "gpu/vulkan/vulkan_function_pointers.h"
+#include "gpu/vulkan/vulkan_instance.h"
+#include "gpu/vulkan/vulkan_surface.h"
+#include "ui/gfx/gpu_fence.h"
+
+namespace gpu {
+
+VulkanImplementationWin32::~VulkanImplementationWin32() = default;
+
+bool VulkanImplementationWin32::InitializeVulkanInstance() {
+  std::vector<const char*> required_extensions = {
+      VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME};
+
+  VulkanFunctionPointers* vulkan_function_pointers =
+      gpu::GetVulkanFunctionPointers();
+
+  base::NativeLibraryLoadError native_library_load_error;
+  vulkan_function_pointers->vulkan_loader_library_ = base::LoadNativeLibrary(
+      base::FilePath(L"vulkan-1.dll"), &native_library_load_error);
+  if (!vulkan_function_pointers->vulkan_loader_library_)
+    return false;
+
+  if (!vulkan_instance_.Initialize(required_extensions, {})) {
+    vulkan_instance_.Destroy();
+    return false;
+  }
+
+  // Initialize platform function pointers
+  vkGetPhysicalDeviceWin32PresentationSupportKHR_ =
+      reinterpret_cast<PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR>(
+          vkGetInstanceProcAddr(
+              vulkan_instance_.vk_instance(),
+              "vkGetPhysicalDeviceWin32PresentationSupportKHR"));
+  if (!vkGetPhysicalDeviceWin32PresentationSupportKHR_) {
+    LOG(ERROR) << "vkGetPhysicalDeviceWin32PresentationSupportKHR not found";
+    vulkan_instance_.Destroy();
+    return false;
+  }
+
+  vkCreateWin32SurfaceKHR_ =
+      reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(vkGetInstanceProcAddr(
+          vulkan_instance_.vk_instance(), "vkCreateWin32SurfaceKHR"));
+  if (!vkCreateWin32SurfaceKHR_) {
+    LOG(ERROR) << "vkCreateWin32SurfaceKHR not found";
+    vulkan_instance_.Destroy();
+    return false;
+  }
+
+  return true;
+}
+
+VkInstance VulkanImplementationWin32::GetVulkanInstance() {
+  return vulkan_instance_.vk_instance();
+}
+
+std::unique_ptr<VulkanSurface> VulkanImplementationWin32::CreateViewSurface(
+    gfx::AcceleratedWidget window) {
+  VkSurfaceKHR surface;
+  VkWin32SurfaceCreateInfoKHR surface_create_info = {};
+  surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+  surface_create_info.hinstance =
+      reinterpret_cast<HINSTANCE>(GetWindowLongPtr(window, GWLP_HINSTANCE));
+  surface_create_info.hwnd = window;
+  VkResult result = vkCreateWin32SurfaceKHR_(
+      GetVulkanInstance(), &surface_create_info, nullptr, &surface);
+  if (VK_SUCCESS != result) {
+    DLOG(ERROR) << "vkCreatWin32SurfaceKHR() failed: " << result;
+    return nullptr;
+  }
+
+  return std::make_unique<VulkanSurface>(GetVulkanInstance(), surface);
+}
+
+bool VulkanImplementationWin32::GetPhysicalDevicePresentationSupport(
+    VkPhysicalDevice device,
+    const std::vector<VkQueueFamilyProperties>& queue_family_properties,
+    uint32_t queue_family_index) {
+  return vkGetPhysicalDeviceWin32PresentationSupportKHR_(device,
+                                                         queue_family_index);
+}
+
+std::vector<const char*>
+VulkanImplementationWin32::GetRequiredDeviceExtensions() {
+  return {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
+}
+
+VkFence VulkanImplementationWin32::CreateVkFenceForGpuFence(
+    VkDevice vk_device) {
+  NOTREACHED();
+  return VK_NULL_HANDLE;
+}
+
+std::unique_ptr<gfx::GpuFence>
+VulkanImplementationWin32::ExportVkFenceToGpuFence(VkDevice vk_device,
+                                                   VkFence vk_fence) {
+  NOTREACHED();
+  return nullptr;
+}
+
+}  // namespace gpu
diff --git a/gpu/vulkan/win32/vulkan_implementation_win32.h b/gpu/vulkan/win32/vulkan_implementation_win32.h
new file mode 100644
index 0000000..43c3352
--- /dev/null
+++ b/gpu/vulkan/win32/vulkan_implementation_win32.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2018 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 GPU_VULKAN_WIN32_VULKAN_IMPLEMENTATION_WIN32_H_
+#define GPU_VULKAN_WIN32_VULKAN_IMPLEMENTATION_WIN32_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "gpu/vulkan/vulkan_implementation.h"
+#include "gpu/vulkan/vulkan_instance.h"
+
+namespace gpu {
+
+class COMPONENT_EXPORT(VULKAN_WIN32) VulkanImplementationWin32
+    : public VulkanImplementation {
+ public:
+  VulkanImplementationWin32() = default;
+  ~VulkanImplementationWin32() override;
+
+  // VulkanImplementation:
+  bool InitializeVulkanInstance() override;
+  VkInstance GetVulkanInstance() override;
+  std::unique_ptr<VulkanSurface> CreateViewSurface(
+      gfx::AcceleratedWidget window) override;
+  bool GetPhysicalDevicePresentationSupport(
+      VkPhysicalDevice device,
+      const std::vector<VkQueueFamilyProperties>& queue_family_properties,
+      uint32_t queue_family_index) override;
+  std::vector<const char*> GetRequiredDeviceExtensions() override;
+  VkFence CreateVkFenceForGpuFence(VkDevice vk_device) override;
+  std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence(
+      VkDevice vk_device,
+      VkFence vk_fence) override;
+
+ private:
+  VulkanInstance vulkan_instance_;
+
+  PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR
+      vkGetPhysicalDeviceWin32PresentationSupportKHR_ = nullptr;
+  PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(VulkanImplementationWin32);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_VULKAN_WIN32_VULKAN_IMPLEMENTATION_WIN32_H_
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index fa9ca30..4603850f 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -564,6 +564,7 @@
 
 test("headless_unittests") {
   sources = [
+    "lib/browser/protocol/protocol_unittest.cc",
     "public/domains/types_unittest.cc",
     "public/util/error_reporter_unittest.cc",
   ]
diff --git a/headless/lib/browser/headless_request_context_manager.cc b/headless/lib/browser/headless_request_context_manager.cc
index b859494..653f587 100644
--- a/headless/lib/browser/headless_request_context_manager.cc
+++ b/headless/lib/browser/headless_request_context_manager.cc
@@ -357,6 +357,11 @@
     builder->SetCreateHttpTransactionFactoryCallback(
         base::BindOnce(&content::CreateDevToolsNetworkTransactionFactory));
     builder->SetInterceptors(std::move(request_interceptors_));
+    for (auto& protocol_handler : protocol_handlers_) {
+      builder->SetProtocolHandler(protocol_handler.first,
+                                  std::move(protocol_handler.second));
+    }
+    protocol_handlers_.clear();
 
     net::URLRequestContext* url_request_context = nullptr;
     network_context_owner_ =
diff --git a/headless/lib/browser/protocol/protocol_string.cc b/headless/lib/browser/protocol/protocol_string.cc
index c102f2fe..5e82611 100644
--- a/headless/lib/browser/protocol/protocol_string.cc
+++ b/headless/lib/browser/protocol/protocol_string.cc
@@ -4,6 +4,8 @@
 
 #include "headless/lib/browser/protocol/protocol_string.h"
 
+#include <utility>
+#include "base/base64.h"
 #include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
@@ -159,5 +161,54 @@
   string_.reserve(capacity);
 }
 
+Binary::Binary() : bytes_(new base::RefCountedBytes) {}
+Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {}
+Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {}
+Binary::~Binary() {}
+
+String Binary::toBase64() const {
+  std::string encoded;
+  base::Base64Encode(
+      base::StringPiece(reinterpret_cast<const char*>(data()), size()),
+      &encoded);
+  return encoded;
+}
+
+// static
+Binary Binary::fromBase64(const String& base64, bool* success) {
+  std::string decoded;
+  *success = base::Base64Decode(base::StringPiece(base64), &decoded);
+  if (*success) {
+    return Binary::fromString(std::move(decoded));
+  }
+  return Binary();
+}
+
+// static
+Binary Binary::fromRefCounted(scoped_refptr<base::RefCountedMemory> memory) {
+  return Binary(memory);
+}
+
+// static
+Binary Binary::fromVector(std::vector<uint8_t>&& data) {
+  return Binary(base::RefCountedBytes::TakeVector(&data));
+}
+
+// static
+Binary Binary::fromVector(const std::vector<uint8_t>& data) {
+  std::vector<uint8_t> copied_data(data);
+  return Binary(base::RefCountedBytes::TakeVector(&copied_data));
+}
+
+// static
+Binary Binary::fromString(std::string&& data) {
+  return Binary(base::RefCountedString::TakeString(&data));
+}
+
+// static
+Binary Binary::fromString(const std::string& data) {
+  std::string copied_data(data);
+  return Binary(base::RefCountedString::TakeString(&copied_data));
+}
 }  // namespace protocol
 }  // namespace headless
diff --git a/headless/lib/browser/protocol/protocol_string.h b/headless/lib/browser/protocol/protocol_string.h
index fd77f7e..f6d988ca 100644
--- a/headless/lib/browser/protocol/protocol_string.h
+++ b/headless/lib/browser/protocol/protocol_string.h
@@ -7,9 +7,11 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/strings/string_number_conversions.h"
 #include "headless/public/headless_export.h"
 
@@ -83,6 +85,29 @@
   static std::unique_ptr<Value> parseJSON(const String&);
 };
 
+// A read-only sequence of uninterpreted bytes with reference-counted storage.
+class HEADLESS_EXPORT Binary {
+ public:
+  Binary(const Binary&);
+  Binary();
+  ~Binary();
+
+  const uint8_t* data() const { return bytes_->front(); }
+  size_t size() const { return bytes_->size(); }
+
+  String toBase64() const;
+  static Binary fromBase64(const String& base64, bool* success);
+  static Binary fromRefCounted(scoped_refptr<base::RefCountedMemory> memory);
+  static Binary fromVector(std::vector<uint8_t>&& data);
+  static Binary fromVector(const std::vector<uint8_t>& data);
+  static Binary fromString(std::string&& data);
+  static Binary fromString(const std::string& data);
+
+ private:
+  explicit Binary(scoped_refptr<base::RefCountedMemory> bytes);
+  scoped_refptr<base::RefCountedMemory> bytes_;
+};
+
 std::unique_ptr<Value> toProtocolValue(const base::Value* value, int depth);
 std::unique_ptr<base::Value> toBaseValue(Value* value, int depth);
 
diff --git a/headless/lib/browser/protocol/protocol_unittest.cc b/headless/lib/browser/protocol/protocol_unittest.cc
new file mode 100644
index 0000000..3d5419f9
--- /dev/null
+++ b/headless/lib/browser/protocol/protocol_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2018 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 "headless/lib/browser/protocol/protocol_string.h"
+
+#include <vector>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace headless {
+namespace protocol {
+namespace {
+TEST(ProtocolBinaryTest, base64EmptyArgs) {
+  EXPECT_EQ(protocol::String(), Binary().toBase64());
+
+  bool success = false;
+  Binary decoded = Binary::fromBase64("", &success);
+  EXPECT_TRUE(success);
+  EXPECT_EQ(
+      std::vector<uint8_t>(),
+      std::vector<uint8_t>(decoded.data(), decoded.data() + decoded.size()));
+}
+
+TEST(ProtocolStringTest, AllBytesBase64Roundtrip) {
+  std::vector<uint8_t> all_bytes;
+  for (int ii = 0; ii < 255; ++ii)
+    all_bytes.push_back(ii);
+  Binary binary = Binary::fromVector(all_bytes);
+  bool success = false;
+  Binary decoded = Binary::fromBase64(binary.toBase64(), &success);
+  EXPECT_TRUE(success);
+  std::vector<uint8_t> decoded_bytes(decoded.data(),
+                                     decoded.data() + decoded.size());
+  EXPECT_EQ(all_bytes, decoded_bytes);
+}
+
+TEST(ProtocolStringTest, HelloWorldBase64Roundtrip) {
+  const char* kMsg = "Hello, world.";
+  std::vector<uint8_t> msg(kMsg, kMsg + strlen(kMsg));
+  EXPECT_EQ(strlen(kMsg), msg.size());
+
+  protocol::String encoded = Binary::fromVector(msg).toBase64();
+  EXPECT_EQ("SGVsbG8sIHdvcmxkLg==", encoded);
+  bool success = false;
+  Binary decoded_binary = Binary::fromBase64(encoded, &success);
+  EXPECT_TRUE(success);
+  std::vector<uint8_t> decoded(decoded_binary.data(),
+                               decoded_binary.data() + decoded_binary.size());
+  EXPECT_EQ(msg, decoded);
+}
+
+TEST(ProtocolBinaryTest, InvalidBase64Decode) {
+  bool success = true;
+  Binary binary = Binary::fromBase64("This is not base64.", &success);
+  EXPECT_FALSE(success);
+}
+}  // namespace
+}  // namespace protocol
+}  // namespace headless
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 1925860..40f9f7c 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -2159,12 +2159,6 @@
       mixins: "libfuzzer"
     }
     builders {
-      name: "WebKit Win Builder"
-      # This may be obsolete (WebKit Win10 is triggered by Win Builder).
-      dimensions: "os:Windows-10"
-      mixins: "webkit-ci"
-    }
-    builders {
       name: "VR Linux"
       dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
@@ -2364,12 +2358,6 @@
       mixins: "android-fyi-ci"
     }
     builders {
-      name: "WebKit Mac Builder"
-      # This may be obsolete (WebKit Mac testers are triggered by Mac Builder).
-      dimensions: "os:Mac-10.12.2"
-      mixins: "webkit-ci"
-    }
-    builders {
       name: "Linux ChromiumOS MSan Builder"
       dimensions: "os:Ubuntu-14.04"
       mixins: "memory-ci"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 552a6cd..49656b8 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -695,23 +695,11 @@
     short_name: "cfi"
   }
   builders {
-    name: "buildbot/chromium.webkit/WebKit Win Builder"
-    category: "chromium.webkit|win rel|builder"
-    short_name: "32"
-  }
-  builders {
-    name: "buildbot/chromium.webkit/WebKit Win10"
     name: "buildbucket/luci.chromium.ci/WebKit Win10"
     category: "chromium.webkit|win rel|tester"
     short_name: "10"
   }
   builders {
-    name: "buildbot/chromium.webkit/WebKit Mac Builder"
-    category: "chromium.webkit|mac|release"
-    short_name: "bld"
-  }
-  builders {
-    name: "buildbot/chromium.webkit/WebKit Mac10.13 (retina)"
     name: "buildbucket/luci.chromium.ci/WebKit Mac10.13 (retina)"
     category: "chromium.webkit|mac|release"
     short_name: "13r"
@@ -1150,7 +1138,6 @@
     short_name: "32"
   }
   builders {
-    name: "buildbot/chromium.webkit/WebKit Win10"
     name: "buildbucket/luci.chromium.ci/WebKit Win10"
     category: "win|release|tester"
     short_name: "10"
@@ -1162,7 +1149,6 @@
     short_name: "bld"
   }
   builders {
-    name: "buildbot/chromium.webkit/WebKit Mac10.13 (retina)"
     name: "buildbucket/luci.chromium.ci/WebKit Mac10.13 (retina)"
     category: "mac|release"
     short_name: "13r"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 61532c6..8bd4650 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -3681,17 +3681,6 @@
 }
 
 job {
-  # This may be obsolete (WebKit Mac testers are triggered by Mac Builder).
-  id: "WebKit Mac Builder"
-  acl_sets: "default"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "WebKit Mac Builder"
-  }
-}
-
-job {
   id: "WebKit Mac10.13 (retina)"
   # Triggered by "Mac Builder"
   acl_sets: "triggered-by-parent-builders"
@@ -3703,17 +3692,6 @@
 }
 
 job {
-  id: "WebKit Win Builder"
-  # This may be obsolete (WebKit Win10 is triggered by Win Builder).
-  acl_sets: "default"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "WebKit Win Builder"
-  }
-}
-
-job {
   id: "WebKit Win10"
   # Triggered by "Win Builder"
   acl_sets: "triggered-by-parent-builders"
diff --git a/ios/build/bots/tests/eg_tests.json b/ios/build/bots/tests/eg_tests.json
index 7ea59225..e6249baa 100644
--- a/ios/build/bots/tests/eg_tests.json
+++ b/ios/build/bots/tests/eg_tests.json
@@ -23,13 +23,6 @@
       "xctest": true
     },
     {
-      "app": "ios_chrome_payments_egtests",
-      "test args": [
-        "--enable-features=WebPayments"
-      ],
-      "xctest": true
-    },
-    {
       "app": "ios_chrome_reading_list_egtests",
       "test args": [
         "--enable-reading-list"
diff --git a/ios/chrome/browser/autofill/automation/BUILD.gn b/ios/chrome/browser/autofill/automation/BUILD.gn
index b2ae8079..2336f40 100644
--- a/ios/chrome/browser/autofill/automation/BUILD.gn
+++ b/ios/chrome/browser/autofill/automation/BUILD.gn
@@ -15,6 +15,7 @@
   deps = [
     "//base",
     "//components/autofill/core/browser:browser",
+    "//components/autofill/ios/browser:autofill_test_bundle_data",
     "//components/autofill/ios/browser:browser",
     "//components/strings",
     "//ios/chrome/app/strings",
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
index d0fa79a..44be9da 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
@@ -124,19 +124,20 @@
       return;
     }
 
-    // If this is a form suggestion view and no suggestions have been triggered
-    // yet, don't show the custom view.
-    FormSuggestionView* formSuggestionView =
-        base::mac::ObjCCast<FormSuggestionView>(view);
-    if (formSuggestionView) {
-      int numSuggestions = [[formSuggestionView suggestions] count];
-      if (!_suggestionsHaveBeenShown && numSuggestions == 0) {
-        self.customAccessoryView = nil;
-        return;
+    if (!autofill::features::IsPasswordManualFallbackEnabled()) {
+      // If this is a form suggestion view and no suggestions have been
+      // triggered yet, don't show the custom view.
+      FormSuggestionView* formSuggestionView =
+          base::mac::ObjCCast<FormSuggestionView>(view);
+      if (formSuggestionView) {
+        int numSuggestions = [[formSuggestionView suggestions] count];
+        if (!_suggestionsHaveBeenShown && numSuggestions == 0) {
+          self.customAccessoryView = nil;
+          return;
+        }
       }
+      _suggestionsHaveBeenShown = YES;
     }
-    _suggestionsHaveBeenShown = YES;
-
     self.customAccessoryView = [[FormInputAccessoryView alloc] init];
     [self.customAccessoryView setUpWithCustomView:view];
     [self addCustomAccessoryViewIfNeeded];
@@ -249,7 +250,6 @@
   // keyboard view is created by the system, i.e. the first time the keyboard
   // will appear.
   if (!IsIPadIdiom()) {
-    [self addCustomAccessoryViewIfNeeded];
     [self addCustomKeyboardViewIfNeeded];
   }
 }
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index 7a5fbcb..11940562 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -105,6 +105,7 @@
   deps = [
     ":autofill_ui",
     "//components/autofill/core/browser",
+    "//components/autofill/ios/browser:autofill_test_bundle_data",
     "//components/autofill/ios/browser:test_support",
     "//components/strings:components_strings_grit",
     "//ios/chrome/browser/autofill",
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.h b/ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.h
index 3dbb98d..bc307c20 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.h
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.h
@@ -24,7 +24,7 @@
 // accessory view elements.
 @interface FormInputAccessoryCoordinator : ChromeCoordinator
 
-// The delegate for the password coordinator. Must be set before it starts.
+// The delegate for the coordinator. Must be set before it starts.
 @property(nonatomic, weak) id<FormInputAccessoryCoordinatorDelegate> delegate;
 
 // Creates a coordinator that uses a |viewController| a |browserState| and
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.mm
index 3e49373..34fbd58 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.mm
@@ -11,6 +11,7 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h"
+#include "ios/chrome/browser/ui/ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -97,7 +98,7 @@
   [self.childCoordinators removeAllObjects];
 }
 
-- (void)startPasswords {
+- (void)startPasswordsFromButton:(UIButton*)button {
   ManualFillPasswordCoordinator* passwordCoordinator =
       [[ManualFillPasswordCoordinator alloc]
           initWithBaseViewController:self.baseViewController
@@ -105,10 +106,14 @@
                         webStateList:self.webStateList
                     injectionHandler:self.manualFillInjectionHandler];
   passwordCoordinator.delegate = self;
-  [self.formInputAccessoryViewController
-      presentView:passwordCoordinator.viewController.view];
-  [self.childCoordinators addObject:passwordCoordinator];
+  if (IsIPadIdiom()) {
+    [passwordCoordinator presentFromButton:button];
+  } else {
+    [self.formInputAccessoryViewController
+        presentView:passwordCoordinator.viewController.view];
+  }
 
+  [self.childCoordinators addObject:passwordCoordinator];
   [self.formInputAccessoryMediator disableSuggestions];
 }
 
@@ -129,9 +134,9 @@
   // TODO(crbug.com/845472): implement.
 }
 
-- (void)passwordButtonPressed {
+- (void)passwordButtonPressed:(UIButton*)sender {
   [self stopChildren];
-  [self startPasswords];
+  [self startPasswordsFromButton:sender];
 }
 
 #pragma mark - PasswordCoordinatorDelegate
@@ -140,4 +145,8 @@
   [self.delegate openPasswordSettings];
 }
 
+- (void)resetAccessoryView {
+  [self.manualFillAccessoryViewController reset];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
index 9ed640a..3c2293ed 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
@@ -131,6 +131,14 @@
                       selector:@selector(handleTextInputDidBeginEditing:)
                           name:UITextFieldTextDidBeginEditingNotification
                         object:nil];
+    [defaultCenter addObserver:self
+                      selector:@selector(handleTextInputDidEndEditing:)
+                          name:UITextFieldTextDidEndEditingNotification
+                        object:nil];
+    [defaultCenter addObserver:self
+                      selector:@selector(handleKeyboardWillShow:)
+                          name:UIKeyboardWillShowNotification
+                        object:nil];
     _keyboardObserver = [[KeyboardObserverHelper alloc] init];
     _keyboardObserver.delegate = self;
   }
@@ -416,13 +424,29 @@
                            navigationDelegate:self.formInputAccessoryHandler];
 }
 
-// When any text field or text view (e.g. omnibox, settings, card unmask dialog)
-// begins editing, reset ourselves so that we don't present our custom view over
+#pragma mark - Keyboard Notifications
+
+// When the keyboard is shown, send the last suggestions to the consumer.
+- (void)handleKeyboardWillShow:(NSNotification*)notification {
+  if (self.lastSuggestionView) {
+    [self updateWithProvider:self.lastProvider
+              suggestionView:self.lastSuggestionView];
+  }
+}
+
+// When any text field or text view (e.g. omnibox, settings search bar)
+// begins editing, pause the consumer so it doesn't present the custom view over
 // the keyboard.
 - (void)handleTextInputDidBeginEditing:(NSNotification*)notification {
   [self.consumer pauseCustomKeyboardView];
 }
 
+// When any text field or text view (e.g. omnibox, settings, card unmask dialog)
+// ends editing, continue presenting.
+- (void)handleTextInputDidEndEditing:(NSNotification*)notification {
+  [self.consumer continueCustomKeyboardView];
+}
+
 #pragma mark - Tests
 
 - (void)injectWebState:(web::WebState*)webState {
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
index ad9f099..55b52628 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
@@ -28,9 +28,11 @@
     "//ios/chrome/browser/autofill:autofill_shared",
     "//ios/chrome/browser/autofill/manual_fill:manual_fill",
     "//ios/chrome/browser/passwords",
+    "//ios/chrome/browser/ui:ui_util",
     "//ios/chrome/browser/ui/autofill/manual_fill:manual_fill_ui",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/list_model:list_model",
+    "//ios/chrome/browser/ui/table_view:presentation",
     "//ios/chrome/browser/ui/table_view:table_view",
     "//ios/chrome/browser/web_state_list:web_state_list",
     "//ios/web/public:public",
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h
index bae9fc4..935e1f6 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h
@@ -33,7 +33,7 @@
 - (void)keyboardButtonPressed;
 
 // Invoked after the user touches the `passwords` button.
-- (void)passwordButtonPressed;
+- (void)passwordButtonPressed:(UIButton*)sender;
 
 @end
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
index 6d2065e..e685cda 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
@@ -6,6 +6,7 @@
 
 #include "components/autofill/core/common/autofill_features.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/uicolor_manualfill.h"
+#include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -62,17 +63,21 @@
   self.view.translatesAutoresizingMaskIntoConstraints = NO;
 
   UIColor* tintColor = [self activeTintColor];
+  NSMutableArray<UIView*>* icons = [[NSMutableArray alloc] init];
 
-  self.keyboardButton = [UIButton buttonWithType:UIButtonTypeSystem];
-  UIImage* keyboardImage = [UIImage imageNamed:@"ic_keyboard"];
-  [self.keyboardButton setImage:keyboardImage forState:UIControlStateNormal];
-  self.keyboardButton.tintColor = tintColor;
-  self.keyboardButton.translatesAutoresizingMaskIntoConstraints = NO;
-  [self.keyboardButton addTarget:self
-                          action:@selector(keyboardButtonPressed)
-                forControlEvents:UIControlEventTouchUpInside];
-  self.keyboardButton.accessibilityIdentifier =
-      manual_fill::AccessoryKeyboardAccessibilityIdentifier;
+  if (!IsIPadIdiom()) {
+    self.keyboardButton = [UIButton buttonWithType:UIButtonTypeSystem];
+    UIImage* keyboardImage = [UIImage imageNamed:@"ic_keyboard"];
+    [self.keyboardButton setImage:keyboardImage forState:UIControlStateNormal];
+    self.keyboardButton.tintColor = tintColor;
+    self.keyboardButton.translatesAutoresizingMaskIntoConstraints = NO;
+    [self.keyboardButton addTarget:self
+                            action:@selector(keyboardButtonPressed)
+                  forControlEvents:UIControlEventTouchUpInside];
+    self.keyboardButton.accessibilityIdentifier =
+        manual_fill::AccessoryKeyboardAccessibilityIdentifier;
+    [icons addObject:self.keyboardButton];
+  }
 
   self.passwordButton = [UIButton buttonWithType:UIButtonTypeSystem];
   UIImage* keyImage = [UIImage imageNamed:@"ic_vpn_key"];
@@ -80,12 +85,12 @@
   self.passwordButton.tintColor = tintColor;
   self.passwordButton.translatesAutoresizingMaskIntoConstraints = NO;
   [self.passwordButton addTarget:self
-                          action:@selector(passwordButtonPressed)
+                          action:@selector(passwordButtonPressed:)
                 forControlEvents:UIControlEventTouchUpInside];
   self.passwordButton.accessibilityIdentifier =
       manual_fill::AccessoryPasswordAccessibilityIdentifier;
+  [icons addObject:self.passwordButton];
 
-  NSArray* views;
   if (autofill::features::IsAutofillManualFallbackEnabled()) {
     self.cardsButton = [UIButton buttonWithType:UIButtonTypeSystem];
     UIImage* cardImage = [UIImage imageNamed:@"ic_credit_card"];
@@ -97,6 +102,7 @@
                forControlEvents:UIControlEventTouchUpInside];
     self.cardsButton.accessibilityIdentifier =
         manual_fill::AccessoryCreditCardAccessibilityIdentifier;
+    [icons addObject:self.cardsButton];
 
     self.accountButton = [UIButton buttonWithType:UIButtonTypeSystem];
     UIImage* accountImage = [UIImage imageNamed:@"addresses"];
@@ -108,15 +114,9 @@
                  forControlEvents:UIControlEventTouchUpInside];
     self.accountButton.accessibilityIdentifier =
         manual_fill::AccessoryAddressAccessibilityIdentifier;
-
-    views = @[
-      self.keyboardButton, self.passwordButton, self.accountButton,
-      self.cardsButton
-    ];
-  } else {
-    views = @[ self.keyboardButton, self.passwordButton ];
+    [icons addObject:self.accountButton];
   }
-  UIStackView* stackView = [[UIStackView alloc] initWithArrangedSubviews:views];
+  UIStackView* stackView = [[UIStackView alloc] initWithArrangedSubviews:icons];
   stackView.spacing = 10;
   stackView.axis = UILayoutConstraintAxisHorizontal;
   stackView.translatesAutoresizingMaskIntoConstraints = NO;
@@ -177,11 +177,11 @@
   [self.delegate keyboardButtonPressed];
 }
 
-- (void)passwordButtonPressed {
+- (void)passwordButtonPressed:(UIButton*)sender {
   [self animateKeyboardButtonHidden:NO];
   [self resetTintColors];
   [self.passwordButton setTintColor:UIColor.cr_manualFillTintColor];
-  [self.delegate passwordButtonPressed];
+  [self.delegate passwordButtonPressed:sender];
 }
 
 - (void)cardButtonPressed {
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h b/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h
index 914fa26..ac218d5 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h
@@ -10,9 +10,18 @@
 @class ManualFillInjectionHandler;
 class WebStateList;
 
+namespace manual_fill {
+
+extern NSString* const PasswordDoneButtonAccessibilityIdentifier;
+
+}  // namespace manual_fill
+
 // Delegate for the coordinator actions.
 @protocol PasswordCoordinatorDelegate<NSObject>
 
+// Resets the accessory view.
+- (void)resetAccessoryView;
+
 // Opens the passwords settings.
 - (void)openPasswordSettings;
 
@@ -43,6 +52,9 @@
                                   (ios::ChromeBrowserState*)browserState
     NS_UNAVAILABLE;
 
+// Presents the password view controller as a popover from the passed button.
+- (void)presentFromButton:(UIButton*)button;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_PASSWORD_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.mm
index fe37eb30..d8b7b6c 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.mm
@@ -11,12 +11,26 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
+#import "ios/chrome/browser/ui/table_view/table_view_animator.h"
+#import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h"
+#import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h"
+#include "ios/chrome/browser/ui/ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface ManualFillPasswordCoordinator ()<PasswordListDelegate>
+namespace manual_fill {
+
+NSString* const PasswordDoneButtonAccessibilityIdentifier =
+    @"kManualFillPasswordDoneButtonAccessibilityIdentifier";
+
+}  // namespace manual_fill
+
+@interface ManualFillPasswordCoordinator ()<
+    PasswordListDelegate,
+    UIViewControllerTransitioningDelegate,
+    UIPopoverPresentationControllerDelegate>
 
 // Fetches and filters the passwords for the view controller.
 @property(nonatomic, strong) ManualFillPasswordMediator* passwordMediator;
@@ -34,6 +48,10 @@
 @property(nonatomic, strong)
     ManualFillInjectionHandler* manualFillInjectionHandler;
 
+// Button presenting this coordinator in a popover. Used for continuation after
+// dismissing any presented view controller. iPad only.
+@property(nonatomic, weak) UIButton* presentingButton;
+
 @end
 
 @implementation ManualFillPasswordCoordinator
@@ -69,7 +87,12 @@
 }
 
 - (void)stop {
-  [self.passwordViewController.view removeFromSuperview];
+  if (IsIPadIdiom() && self.passwordViewController.presentingViewController) {
+    [self.passwordViewController dismissViewControllerAnimated:true
+                                                    completion:nil];
+  } else {
+    [self.passwordViewController.view removeFromSuperview];
+  }
   [self.allPasswordsViewController dismissViewControllerAnimated:YES
                                                       completion:nil];
 }
@@ -78,9 +101,88 @@
   return self.passwordViewController;
 }
 
+- (void)presentFromButton:(UIButton*)button {
+  self.presentingButton = button;
+  self.passwordViewController.modalPresentationStyle =
+      UIModalPresentationPopover;
+
+  // The |button.window.rootViewController| is used in order to present above
+  // the keyboard. This way the popover will be dismissed on keyboard
+  // interaction and it won't be covered when the keyboard is near the top of
+  // the screen.
+  [button.window.rootViewController
+      presentViewController:self.passwordViewController
+                   animated:YES
+                 completion:nil];
+
+  UIPopoverPresentationController* popoverPresentationController =
+      self.passwordViewController.popoverPresentationController;
+  popoverPresentationController.sourceView = button;
+  popoverPresentationController.sourceRect = button.bounds;
+  popoverPresentationController.permittedArrowDirections =
+      UIPopoverArrowDirectionUp | UIMenuControllerArrowDown;
+  popoverPresentationController.delegate = self;
+}
+
+#pragma mark - UIViewControllerTransitioningDelegate
+
+- (UIPresentationController*)
+presentationControllerForPresentedViewController:(UIViewController*)presented
+                        presentingViewController:(UIViewController*)presenting
+                            sourceViewController:(UIViewController*)source {
+  TableViewPresentationController* presentationController =
+      [[TableViewPresentationController alloc]
+          initWithPresentedViewController:presented
+                 presentingViewController:presenting];
+  presentationController.position = TablePresentationPositionLeading;
+  return presentationController;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)
+animationControllerForPresentedController:(UIViewController*)presented
+                     presentingController:(UIViewController*)presenting
+                         sourceController:(UIViewController*)source {
+  UITraitCollection* traitCollection = presenting.traitCollection;
+  if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
+      traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
+    // Use the default animator for fullscreen presentations.
+    return nil;
+  }
+
+  TableViewAnimator* animator = [[TableViewAnimator alloc] init];
+  animator.presenting = YES;
+  animator.direction = TableAnimatorDirectionFromLeading;
+  return animator;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)
+animationControllerForDismissedController:(UIViewController*)dismissed {
+  UITraitCollection* traitCollection = dismissed.traitCollection;
+  if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
+      traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
+    // Use the default animator for fullscreen presentations.
+    return nil;
+  }
+
+  TableViewAnimator* animator = [[TableViewAnimator alloc] init];
+  animator.presenting = NO;
+  animator.direction = TableAnimatorDirectionFromLeading;
+  return animator;
+}
+
 #pragma mark - PasswordListDelegate
 
 - (void)openAllPasswordsList {
+  // On iPad, first dismiss the popover before the new view is presented.
+  __weak __typeof(self) weakSelf = self;
+  if (IsIPadIdiom() && self.passwordViewController.presentingViewController) {
+    [self.passwordViewController
+        dismissViewControllerAnimated:true
+                           completion:^{
+                             [weakSelf openAllPasswordsList];
+                           }];
+    return;
+  }
   UISearchController* searchController =
       [[UISearchController alloc] initWithSearchResultsController:nil];
   searchController.searchResultsUpdater = self.passwordMediator;
@@ -89,26 +191,57 @@
       [PasswordViewController alloc] initWithSearchController:searchController];
   self.passwordMediator.disableFilter = YES;
   self.passwordMediator.consumer = allPasswordsViewController;
-
-  UINavigationController* navigationController = [[UINavigationController alloc]
-      initWithRootViewController:allPasswordsViewController];
-  if (@available(iOS 11, *)) {
-    navigationController.navigationBar.prefersLargeTitles = YES;
-  }
+  UIBarButtonItem* doneButton = [[UIBarButtonItem alloc]
+      initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+                           target:self
+                           action:@selector(dismissPresentedViewController)];
+  doneButton.accessibilityIdentifier =
+      manual_fill::PasswordDoneButtonAccessibilityIdentifier;
+  allPasswordsViewController.navigationItem.rightBarButtonItem = doneButton;
   self.allPasswordsViewController = allPasswordsViewController;
+
+  TableViewNavigationController* navigationController =
+      [[TableViewNavigationController alloc]
+          initWithTable:allPasswordsViewController];
+  navigationController.transitioningDelegate = self;
+  [navigationController setModalPresentationStyle:UIModalPresentationCustom];
+
   [self.baseViewController presentViewController:navigationController
                                         animated:YES
                                       completion:nil];
 }
 
 - (void)dismissPresentedViewController {
+  // Dismiss the full screen view controller and present the pop over.
+  __weak __typeof(self) weakSelf = self;
   [self.allPasswordsViewController.presentingViewController
       dismissViewControllerAnimated:YES
-                         completion:nil];
+                         completion:^{
+                           if (weakSelf.presentingButton) {
+                             [weakSelf
+                                 presentFromButton:weakSelf.presentingButton];
+                           }
+                         }];
 }
 
 - (void)openPasswordSettings {
+  // On iPad, dismiss the popover before the settings are presented.
+  if (IsIPadIdiom() && self.passwordViewController.presentingViewController) {
+    [self.passwordViewController
+        dismissViewControllerAnimated:true
+                           completion:^{
+                             [self openPasswordSettings];
+                           }];
+    return;
+  }
   [self.delegate openPasswordSettings];
 }
 
+#pragma mark - UIPopoverPresentationControllerDelegate
+
+- (void)popoverPresentationControllerDidDismissPopover:
+    (UIPopoverPresentationController*)popoverPresentationController {
+  [self.delegate resetAccessoryView];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h b/ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h
index 9e7fc6b6..ed1ffc6 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h
@@ -7,12 +7,17 @@
 
 // Delegate for actions in manual fallback's passwords list.
 @protocol PasswordListDelegate
-// Dismisses the presented view controller.
+
+// Dismisses the presented view controller and continues as pop over on iPads
+// or above the keyboard else.
 - (void)dismissPresentedViewController;
+
 // Requests to open the list of all passwords.
 - (void)openAllPasswordsList;
+
 // Opens passwords settings.
 - (void)openPasswordSettings;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_PASSWORD_LIST_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h
index c1539a0..83d9d7771 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h
@@ -14,6 +14,7 @@
 
 extern NSString* const PasswordSearchBarAccessibilityIdentifier;
 extern NSString* const PasswordTableViewAccessibilityIdentifier;
+extern NSString* const PasswordDoneButtonAccessibilityIdentifier;
 
 }  // namespace manual_fill
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
index e5a60c9..9b498ea 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/action_cell.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -31,6 +32,12 @@
   ActionsSectionIdentifier,
 };
 
+// This is the width used for |self.preferredContentSize|.
+constexpr float PopoverPreferredWidth = 320;
+
+// This is the maximum height used for |self.preferredContentSize|.
+constexpr float PopoverMaxHeight = 250;
+
 }  // namespace
 
 @interface PasswordViewController ()
@@ -80,10 +87,6 @@
   NSString* titleString =
       l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_USE_OTHER_PASSWORD);
   self.title = titleString;
-  self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
-      initWithBarButtonSystemItem:UIBarButtonSystemItemDone
-                           target:self
-                           action:@selector(handleDoneNavigationItemTap)];
 
   if (!base::ios::IsRunningOnIOS11OrLater()) {
     // On iOS 11 this is not needed since the cell constrains are updated by the
@@ -108,11 +111,6 @@
 
 #pragma mark - Private
 
-// Callback for the "Done" navigation item.
-- (void)handleDoneNavigationItemTap {
-  [self dismissViewControllerAnimated:YES completion:nil];
-}
-
 // Presents |items| in the respective section. Handles creating or deleting the
 // section accordingly.
 - (void)presentItems:(NSArray<TableViewItem*>*)items
@@ -145,6 +143,15 @@
     }
   }
   [self.tableView reloadData];
+  if (IsIPadIdiom()) {
+    // Update the preffered content size on iPad so the popover shows the right
+    // size.
+    [self.tableView layoutIfNeeded];
+    CGSize systemLayoutSize = self.tableView.contentSize;
+    CGFloat preferredHeight = MIN(systemLayoutSize.height, PopoverMaxHeight);
+    self.preferredContentSize =
+        CGSizeMake(PopoverPreferredWidth, preferredHeight);
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
index 4d8b1f88..6ee50dd 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
@@ -16,6 +16,7 @@
 #include "components/password_manager/core/browser/password_store_consumer.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
 #import "ios/chrome/browser/ui/ui_util.h"
@@ -48,6 +49,11 @@
       manual_fill::AccessoryPasswordAccessibilityIdentifier);
 }
 
+id<GREYMatcher> KeyboardIconMatcher() {
+  return grey_accessibilityID(
+      manual_fill::AccessoryKeyboardAccessibilityIdentifier);
+}
+
 // Returns a matcher for the password table view in manual fallback.
 id<GREYMatcher> PasswordTableViewMatcher() {
   return grey_accessibilityID(
@@ -73,6 +79,11 @@
       manual_fill::OtherPasswordsAccessibilityIdentifier);
 }
 
+id<GREYMatcher> OtherPasswordsDismissMatcher() {
+  return grey_accessibilityID(
+      manual_fill::PasswordDoneButtonAccessibilityIdentifier);
+}
+
 // Returns a matcher for the example username in the list.
 id<GREYMatcher> UsernameButtonMatcher() {
   return grey_buttonTitle(base::SysUTF8ToNSString(kExampleUsername));
@@ -88,6 +99,13 @@
   return grey_accessibilityID(@"SettingsSearchCellTextField");
 }
 
+// Returns a matcher for the PasswordTableView window.
+id<GREYMatcher> PasswordTableViewWindowMatcher() {
+  id<GREYMatcher> classMatcher = grey_kindOfClass([UIWindow class]);
+  id<GREYMatcher> parentMatcher = grey_descendant(PasswordTableViewMatcher());
+  return grey_allOf(classMatcher, parentMatcher, nil);
+}
+
 // Gets the current password store.
 scoped_refptr<password_manager::PasswordStore> GetPasswordStore() {
   // ServiceAccessType governs behaviour in Incognito: only modifications with
@@ -208,12 +226,8 @@
   [super tearDown];
 }
 
-// Test that the passwords view controller appears on screen.
+// Tests that the passwords view controller appears on screen.
 - (void)testPasswordsViewControllerIsPresented {
-  // TODO:(https://crbug.com/878388) Enable on iPad when supported.
-  if (IsIPadIdiom())
-    return;
-
   // Bring up the keyboard.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
@@ -227,13 +241,9 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-// Test that the passwords view controller contains the "Manage Passwords..."
+// Tests that the passwords view controller contains the "Manage Passwords..."
 // action.
 - (void)testPasswordsViewControllerContainsManagePasswordsAction {
-  // TODO:(https://crbug.com/878388) Enable on iPad when supported.
-  if (IsIPadIdiom())
-    return;
-
   // Bring up the keyboard.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
@@ -247,12 +257,8 @@
       assertWithMatcher:grey_interactable()];
 }
 
-// Test that the "Manage Passwords..." action works.
+// Tests that the "Manage Passwords..." action works.
 - (void)testManagePasswordsActionOpensPasswordSettings {
-  // TODO:(https://crbug.com/878388) Enable on iPad when supported.
-  if (IsIPadIdiom())
-    return;
-
   // Bring up the keyboard.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
@@ -270,12 +276,8 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-// Test that the Password View Controller is not present when presenting UI.
+// Tests that the Password View Controller is not present when presenting UI.
 - (void)testPasswordControllerPauses {
-  // TODO:(https://crbug.com/878388) Enable on iPad when supported.
-  if (IsIPadIdiom())
-    return;
-
   // For the search bar to appear in password settings at least one password is
   // needed.
   SaveExamplePasswordForm();
@@ -293,7 +295,6 @@
       performAction:grey_tap()];
 
   // Tap the password search.
-
   [[EarlGrey selectElementWithMatcher:PasswordSettingsSearchMatcher()]
       performAction:grey_tap()];
 
@@ -303,13 +304,9 @@
       assertWithMatcher:grey_notVisible()];
 }
 
-// Test that the Password View Controller is resumed after selecting other
+// Tests that the Password View Controller is resumed after selecting other
 // password.
 - (void)testPasswordControllerResumes {
-  // TODO:(https://crbug.com/878388) Enable on iPad when supported.
-  if (IsIPadIdiom())
-    return;
-
   // For this test one password is needed.
   SaveExamplePasswordForm();
 
@@ -321,7 +318,7 @@
   [[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
       performAction:grey_tap()];
 
-  // Tap the "Manage Passwords..." action.
+  // Tap the "Other Passwords..." action.
   [[EarlGrey selectElementWithMatcher:OtherPasswordsMatcher()]
       performAction:grey_tap()];
 
@@ -334,18 +331,145 @@
   [[EarlGrey selectElementWithMatcher:UsernameButtonMatcher()]
       performAction:grey_tap()];
 
-  // Only on iOS 12 it is certain that on iPhones the keyboard is back. On iOS
-  // 11, it varies by device and version.
-  if (base::ios::IsRunningOnIOS12OrLater()) {
-    // Verify the password controller table view and the keyboard are visible.
-    GREYAssertTrue([GREYKeyboard isKeyboardShown], @"Keyboard Should be Shown");
+  // Wait for the password list to disappear. Using the search bar, since the
+  // popover doesn't have it.
+  [[EarlGrey selectElementWithMatcher:PasswordSearchBarMatcher()]
+      assertWithMatcher:grey_notVisible()];
+
+  // Only on iOS 11.3 it is certain that on iPhones the keyboard is back. On iOS
+  // 11.0-11.2, it varies by device and version.
+  if ([GREYKeyboard isKeyboardShown]) {
     [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
         assertWithMatcher:grey_sufficientlyVisible()];
-  } else if (!base::ios::IsRunningOnIOS11OrLater()) {
-    // On iOS 10 the keyboard is hidden.
-    GREYAssertFalse([GREYKeyboard isKeyboardShown],
-                    @"Keyboard Should be Hidden");
+    [[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
+        assertWithMatcher:grey_sufficientlyVisible()];
   }
 }
 
+// Tests that the Password View Controller is resumed after dismissing "Other
+// Passwords".
+- (void)testPasswordControllerResumesWhenOtherPasswordsDismiss {
+  // Bring up the keyboard.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
+
+  // Tap on the passwords icon.
+  [[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
+      performAction:grey_tap()];
+
+  // Tap the "Other Passwords..." action.
+  [[EarlGrey selectElementWithMatcher:OtherPasswordsMatcher()]
+      performAction:grey_tap()];
+
+  // Dismiss the Other Passwords view.
+  [[EarlGrey selectElementWithMatcher:OtherPasswordsDismissMatcher()]
+      performAction:grey_tap()];
+
+  // Wait for the password list to disappear. Using the search bar, since the
+  // popover doesn't have it.
+  [[EarlGrey selectElementWithMatcher:PasswordSearchBarMatcher()]
+      assertWithMatcher:grey_notVisible()];
+
+  // Only on iOS 11.3 it is certain that on iPhones the keyboard is back. On iOS
+  // 11.0-11.2, it varies by device and version.
+  if ([GREYKeyboard isKeyboardShown]) {
+    [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
+        assertWithMatcher:grey_sufficientlyVisible()];
+    [[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
+        assertWithMatcher:grey_sufficientlyVisible()];
+  }
+}
+
+// Tests that the Password View Controller is dismissed when tapping the
+// keyboard icon.
+- (void)testKeyboardIconDismissPasswordController {
+  if (IsIPadIdiom()) {
+    // The keyboard icon is never present in iPads.
+    return;
+  }
+  // Bring up the keyboard.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
+
+  // Tap on the passwords icon.
+  [[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
+      performAction:grey_tap()];
+
+  // Verify the password controller table view is visible.
+  [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
+  // Tap on the keyboard icon.
+  [[EarlGrey selectElementWithMatcher:KeyboardIconMatcher()]
+      performAction:grey_tap()];
+
+  // Verify the password controller table view and the password icon is NOT
+  // visible.
+  [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
+      assertWithMatcher:grey_notVisible()];
+  [[EarlGrey selectElementWithMatcher:KeyboardIconMatcher()]
+      assertWithMatcher:grey_notVisible()];
+}
+
+// Tests that the Password View Controller is dismissed when tapping the outside
+// the popover on iPad.
+- (void)testIPadTappingOutsidePopOverDismissPasswordController {
+  if (!IsIPadIdiom()) {
+    return;
+  }
+  // Bring up the keyboard.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
+
+  // Tap on the passwords icon.
+  [[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
+      performAction:grey_tap()];
+
+  // Verify the password controller table view is visible.
+  [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
+  // Tap on a point outside of the popover.
+  // The way EarlGrey taps doesn't go through the window hierarchy. Because of
+  // this, the tap needs to be done in the same window as the popover.
+  [[EarlGrey selectElementWithMatcher:PasswordTableViewWindowMatcher()]
+      performAction:grey_tapAtPoint(CGPointMake(0, 0))];
+
+  // Verify the password controller table view and the password icon is NOT
+  // visible.
+  [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
+      assertWithMatcher:grey_notVisible()];
+  [[EarlGrey selectElementWithMatcher:KeyboardIconMatcher()]
+      assertWithMatcher:grey_notVisible()];
+}
+
+// Tests that the Password View Controller is dismissed when tapping the
+// keyboard.
+- (void)testTappingKeyboardDismissPasswordControllerPopOver {
+  if (!IsIPadIdiom()) {
+    return;
+  }
+  // Bring up the keyboard.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
+
+  // Tap on the passwords icon.
+  [[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
+      performAction:grey_tap()];
+
+  // Verify the password controller table view is visible.
+  [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
+  [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
+      performAction:grey_typeText(@"text")];
+
+  // Verify the password controller table view and the password icon is NOT
+  // visible.
+  [[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
+      assertWithMatcher:grey_notVisible()];
+  [[EarlGrey selectElementWithMatcher:KeyboardIconMatcher()]
+      assertWithMatcher:grey_notVisible()];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm b/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
index 985c134..af1f525 100644
--- a/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
@@ -157,6 +157,11 @@
     personal_data_manager_->RemoveByGUID(creditCard->guid());
   }
 
+  // Clear existing profiles.
+  for (const auto* profile : personal_data_manager_->GetProfiles()) {
+    personal_data_manager_->RemoveByGUID(profile->guid());
+  }
+
   [super tearDown];
 }
 
@@ -409,6 +414,10 @@
 // Google Payments, and UMA metrics are correctly logged if the user accepts
 // upload.
 - (void)testUMA_Upstream_UserAccepts {
+  // TODO(crbug.com/895687): Re-enable this test after eliminating the need for
+  // disabling EarlGrey synchronization.
+  EARL_GREY_TEST_DISABLED(@"Test disabled.");
+
   base::HistogramTester histogram_tester;
 
   [ChromeEarlGrey
diff --git a/ios/chrome/browser/ui/payments/BUILD.gn b/ios/chrome/browser/ui/payments/BUILD.gn
index 64aa0fd..3ecf90f 100644
--- a/ios/chrome/browser/ui/payments/BUILD.gn
+++ b/ios/chrome/browser/ui/payments/BUILD.gn
@@ -258,6 +258,7 @@
     "//components/autofill/core/browser:test_support",
     "//components/image_fetcher/core",
     "//components/payments/core",
+    "//components/payments/core:payments_test_bundle_data",
     "//components/payments/mojom",
     "//components/prefs",
     "//components/strings",
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index deb8ccc..294c12b 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -8,7 +8,6 @@
   testonly = true
   deps = [
     ":ios_chrome_autofill_automation_egtests",
-    ":ios_chrome_autofill_egtests",
     ":ios_chrome_bookmarks_egtests",
     ":ios_chrome_device_check_egtests",
     ":ios_chrome_external_url_egtests",
@@ -16,7 +15,6 @@
     ":ios_chrome_integration_egtests",
     ":ios_chrome_manual_fill_egtests",
     ":ios_chrome_multitasking_egtests",
-    ":ios_chrome_payments_egtests",
     ":ios_chrome_perf_egtests",
     ":ios_chrome_reading_list_egtests",
     ":ios_chrome_settings_egtests",
@@ -43,18 +41,8 @@
   ]
 }
 
-chrome_ios_eg_test("ios_chrome_autofill_egtests") {
-  deps = [
-    ":test_support",
-    "//components/autofill/ios/browser:autofill_test_bundle_data",
-    "//ios/chrome/browser/ui/autofill:eg_tests",
-  ]
-}
-
 chrome_ios_eg_test("ios_chrome_autofill_automation_egtests") {
   deps = [
-    ":test_support",
-    "//components/autofill/ios/browser:autofill_test_bundle_data",
     "//ios/chrome/browser/autofill/automation:eg_tests",
   ]
 }
@@ -66,14 +54,6 @@
   ]
 }
 
-chrome_ios_eg_test("ios_chrome_payments_egtests") {
-  deps = [
-    ":test_support",
-    "//components/payments/core:payments_test_bundle_data",
-    "//ios/chrome/browser/ui/payments:eg_tests",
-  ]
-}
-
 chrome_ios_eg_test("ios_chrome_reading_list_egtests") {
   deps = [
     ":test_support",
@@ -106,6 +86,7 @@
     "//ios/chrome/browser/ui:eg_tests",
     "//ios/chrome/browser/ui/activity_services:eg_tests",
     "//ios/chrome/browser/ui/alert_coordinator:eg_tests",
+    "//ios/chrome/browser/ui/autofill:eg_tests",
     "//ios/chrome/browser/ui/content_suggestions:eg_tests",
     "//ios/chrome/browser/ui/dialogs:eg_tests",
     "//ios/chrome/browser/ui/download:eg_tests",
@@ -115,6 +96,7 @@
     "//ios/chrome/browser/ui/history:eg_tests",
     "//ios/chrome/browser/ui/infobars:eg_tests",
     "//ios/chrome/browser/ui/ntp:eg_tests",
+    "//ios/chrome/browser/ui/payments:eg_tests",
     "//ios/chrome/browser/ui/popup_menu:eg_tests",
     "//ios/chrome/browser/ui/print:eg_tests",
     "//ios/chrome/browser/ui/qr_scanner:eg_tests",
diff --git a/ios/web/net/cookies/wk_http_system_cookie_store.mm b/ios/web/net/cookies/wk_http_system_cookie_store.mm
index dfbb178..205c2d1 100644
--- a/ios/web/net/cookies/wk_http_system_cookie_store.mm
+++ b/ios/web/net/cookies/wk_http_system_cookie_store.mm
@@ -48,6 +48,22 @@
   return canonical_cookie.IncludeForRequestURL(url, options);
 }
 
+// Prioritizes queued WKHTTPCookieStore completion handlers to run as soon as
+// possible. This function is needed because some of WKHTTPCookieStore methods
+// completion handlers are not called until there is a WKWebView on the view
+// hierarchy.
+void PrioritizeWKHTTPCookieStoreCallbacks() {
+  // TODO(crbug.com/885218): Currently this hack is needed to fix
+  // crbug.com/885218. Remove when the behavior of
+  // [WKHTTPCookieStore getAllCookies:] changes.
+  NSSet* data_types = [NSSet setWithObject:WKWebsiteDataTypeCookies];
+  [[WKWebsiteDataStore defaultDataStore]
+      removeDataOfTypes:data_types
+          modifiedSince:[NSDate distantFuture]
+      completionHandler:^{
+      }];
+}
+
 }  // namespace
 
 WKHTTPSystemCookieStore::WKHTTPSystemCookieStore(
@@ -89,6 +105,7 @@
                 RunSystemCookieCallbackForCookies(std::move(shared_callback),
                                                   weak_time_manager, result);
               }];
+          PrioritizeWKHTTPCookieStoreCallbacks();
         } else {
           net::ReportGetCookiesForURLResult(
               net::SystemCookieStoreType::kWKHTTPSystemCookieStore, false);
@@ -115,6 +132,7 @@
                 RunSystemCookieCallbackForCookies(std::move(shared_callback),
                                                   weak_time_manager, cookies);
               }];
+          PrioritizeWKHTTPCookieStoreCallbacks();
         } else {
           RunSystemCookieCallbackForCookies(std::move(shared_callback),
                                             weak_time_manager, @[]);
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index b616e028..2e862882 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -84,6 +84,8 @@
     "public/cwv_autofill_suggestion.h",
     "public/cwv_credit_card.h",
     "public/cwv_credit_card_verifier.h",
+    "public/cwv_credit_card_verifier_data_source.h",
+    "public/cwv_credit_card_verifier_delegate.h",
     "public/cwv_preferences_autofill.h",
     "public/cwv_web_view_autofill.h",
     "public/cwv_web_view_configuration_autofill.h",
diff --git a/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h b/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
index 9d7bde75..addb2131 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
+++ b/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
@@ -33,6 +33,9 @@
 - (void)didReceiveUnmaskVerificationResult:
     (autofill::AutofillClient::PaymentsRpcResult)result;
 
+// Bridge for AutofillClient's method |LoadRiskData|.
+- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback;
+
 @end
 
 #endif  // IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_AUTOFILL_CLIENT_IOS_BRIDGE_H_
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
index de2c3bd7..b46ea20 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -368,6 +368,10 @@
   [_verifier didReceiveUnmaskVerificationResult:result];
 }
 
+- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback {
+  [_verifier loadRiskData:std::move(callback)];
+}
+
 #pragma mark - AutofillDriverIOSBridge
 
 - (void)onFormDataFilled:(uint16_t)query_id
diff --git a/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm b/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
index ea3417f..c9ec2de6 100644
--- a/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
+++ b/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
@@ -11,12 +11,39 @@
 #include "components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h"
 #include "components/autofill/core/browser/ui/card_unmask_prompt_view.h"
 #import "ios/web_view/internal/autofill/cwv_credit_card_internal.h"
+#import "ios/web_view/public/cwv_credit_card_verifier_data_source.h"
+#import "ios/web_view/public/cwv_credit_card_verifier_delegate.h"
 #include "ui/base/resource/resource_bundle.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+NSErrorDomain const CWVCreditCardVerifierErrorDomain =
+    @"org.chromium.chromewebview.CreditCardVerifierErrorDomain";
+NSString* const CWVCreditCardVerifierErrorMessageKey = @"error_message";
+NSString* const CWVCreditCardVerifierRetryAllowedKey = @"retry_allowed";
+
+namespace {
+// Converts an autofill::AutofillClient::PaymentsRpcResult to a
+// CWVCreditCardVerificationError.
+CWVCreditCardVerificationError CWVConvertPaymentsRPCResult(
+    autofill::AutofillClient::PaymentsRpcResult result) {
+  switch (result) {
+    case autofill::AutofillClient::NONE:
+    case autofill::AutofillClient::SUCCESS:
+      NOTREACHED();
+      return CWVCreditCardVerificationErrorNone;
+    case autofill::AutofillClient::TRY_AGAIN_FAILURE:
+      return CWVCreditCardVerificationErrorTryAgainFailure;
+    case autofill::AutofillClient::PERMANENT_FAILURE:
+      return CWVCreditCardVerificationErrorPermanentFailure;
+    case autofill::AutofillClient::NETWORK_ERROR:
+      return CWVCreditCardVerificationErrorNetworkFailure;
+  }
+}
+}  // namespace
+
 @interface CWVCreditCardVerifier ()
 
 // Used to receive |GotVerificationResult| from WebViewCardUnmaskPromptView.
@@ -60,10 +87,10 @@
       _unmaskingController;
   // Used to interface with |_unmaskingController|.
   std::unique_ptr<ios_web_view::WebViewCardUnmaskPromptView> _unmaskingView;
-  // Completion handler for verification results. This is provided by the
-  // client and copied internally to be invoked later from
-  // |didReceiveVerificationResultWithErrorMessage:retryAllowed:|.
-  void (^_completionHandler)(NSString* errorMessage, BOOL retryAllowed);
+  // Data source to provide risk data.
+  __weak id<CWVCreditCardVerifierDataSource> _dataSource;
+  // Delegate to receive callbacks.
+  __weak id<CWVCreditCardVerifierDelegate> _delegate;
 }
 
 @synthesize creditCard = _creditCard;
@@ -131,16 +158,17 @@
 }
 
 - (void)verifyWithCVC:(NSString*)CVC
-      expirationMonth:(NSString*)expirationMonth
-       expirationYear:(NSString*)expirationYear
+      expirationMonth:(nullable NSString*)expirationMonth
+       expirationYear:(nullable NSString*)expirationYear
          storeLocally:(BOOL)storeLocally
-    completionHandler:
-        (void (^)(NSString* errorMessage, BOOL retryAllowed))completionHandler {
-  DCHECK(!_completionHandler);
+           dataSource:(__weak id<CWVCreditCardVerifierDataSource>)dataSource
+             delegate:
+                 (nullable __weak id<CWVCreditCardVerifierDelegate>)delegate {
+  _dataSource = dataSource;
+  _delegate = delegate;
   _unmaskingController->OnUnmaskResponse(
       base::SysNSStringToUTF16(CVC), base::SysNSStringToUTF16(expirationMonth),
       base::SysNSStringToUTF16(expirationYear), storeLocally);
-  _completionHandler = completionHandler;
 }
 
 - (BOOL)isCVCValid:(NSString*)CVC {
@@ -156,9 +184,24 @@
 
 - (void)didReceiveVerificationResultWithErrorMessage:(NSString*)errorMessage
                                         retryAllowed:(BOOL)retryAllowed {
-  DCHECK(_completionHandler);
-  _completionHandler(errorMessage, retryAllowed);
-  _completionHandler = nil;
+  if ([_delegate respondsToSelector:@selector
+                 (creditCardVerifier:didFinishVerificationWithError:)]) {
+    NSError* error;
+    autofill::AutofillClient::PaymentsRpcResult result =
+        _unmaskingController->GetVerificationResult();
+    if (errorMessage.length > 0 && result != autofill::AutofillClient::NONE &&
+        result != autofill::AutofillClient::SUCCESS) {
+      NSDictionary* userInfo = @{
+        CWVCreditCardVerifierErrorMessageKey : errorMessage,
+        CWVCreditCardVerifierRetryAllowedKey : @(retryAllowed),
+      };
+      error = [NSError errorWithDomain:CWVCreditCardVerifierErrorDomain
+                                  code:CWVConvertPaymentsRPCResult(result)
+                              userInfo:userInfo];
+    }
+
+    [_delegate creditCardVerifier:self didFinishVerificationWithError:error];
+  }
 }
 
 #pragma mark - Internal Methods
@@ -168,4 +211,13 @@
   _unmaskingController->OnVerificationResult(result);
 }
 
+- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback {
+  __block base::OnceCallback<void(const std::string&)> blockCallback =
+      std::move(callback);
+  [_dataSource creditCardVerifier:self
+      getRiskDataWithCompletionHandler:^(NSString* _Nonnull riskData) {
+        std::move(blockCallback).Run(base::SysNSStringToUTF8(riskData));
+      }];
+}
+
 @end
diff --git a/ios/web_view/internal/autofill/cwv_credit_card_verifier_internal.h b/ios/web_view/internal/autofill/cwv_credit_card_verifier_internal.h
index ba8c707..76c242d 100644
--- a/ios/web_view/internal/autofill/cwv_credit_card_verifier_internal.h
+++ b/ios/web_view/internal/autofill/cwv_credit_card_verifier_internal.h
@@ -38,6 +38,10 @@
 - (void)didReceiveUnmaskVerificationResult:
     (autofill::AutofillClient::PaymentsRpcResult)result;
 
+// Use to notify CWVCreditCardVerifier that it needs to obtain risk data for
+// credit card verification and to pass it back in |callback|.
+- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback;
+
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm b/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm
index 4dce241..a27b2e2 100644
--- a/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm
@@ -9,6 +9,7 @@
 
 #include "base/base_paths.h"
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
@@ -25,9 +26,12 @@
 #include "ios/web/public/web_task_traits.h"
 #include "ios/web/public/web_thread.h"
 #import "ios/web_view/internal/autofill/cwv_credit_card_internal.h"
+#import "ios/web_view/public/cwv_credit_card_verifier_data_source.h"
+#import "ios/web_view/public/cwv_credit_card_verifier_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/resource/resource_bundle.h"
 
@@ -171,24 +175,17 @@
 
 // Tests CWVCreditCardVerifier's verification method.
 TEST_F(CWVCreditCardVerifierTest, VerifyCard) {
-  __block bool completion_handler_called = false;
+  id unused_data_source =
+      OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
   NSString* cvc = @"123";
   BOOL store_locally = YES;
   [credit_card_verifier_
-          verifyWithCVC:cvc
-        expirationMonth:@""  // Expiration dates are ignored here because
-         expirationYear:@""  // |needsUpdateForExpirationDate| is NO.
-           storeLocally:store_locally
-      completionHandler:^(NSString* errorMessage, BOOL retryAllowed) {
-        EXPECT_FALSE(errorMessage.length > 0);
-        EXPECT_TRUE(retryAllowed);
-        completion_handler_called = true;
-      }];
-
-  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
-    base::RunLoop().RunUntilIdle();
-    return completion_handler_called;
-  }));
+        verifyWithCVC:cvc
+      expirationMonth:@""  // Expiration dates are ignored here because
+       expirationYear:@""  // |needsUpdateForExpirationDate| is NO.
+         storeLocally:store_locally
+           dataSource:unused_data_source
+             delegate:nil];
   EXPECT_TRUE(credit_card_verifier_.lastStoreLocallyValue);
 
   const FakeCardUnmaskDelegate::UnmaskResponse& unmask_response_ =
@@ -197,4 +194,66 @@
   EXPECT_EQ(store_locally, unmask_response_.should_store_pan);
 }
 
+// Tests CWVCreditCardVerifier properly invokes its delegate.
+TEST_F(CWVCreditCardVerifierTest, DelegateCallbacks) {
+  id unused_data_source =
+      OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
+  id delegate = OCMProtocolMock(@protocol(CWVCreditCardVerifierDelegate));
+  [credit_card_verifier_ verifyWithCVC:@"123"
+                       expirationMonth:@""
+                        expirationYear:@""
+                          storeLocally:NO
+                            dataSource:unused_data_source
+                              delegate:delegate];
+
+  [[delegate expect]
+                  creditCardVerifier:credit_card_verifier_
+      didFinishVerificationWithError:[OCMArg checkWithBlock:^BOOL(
+                                                 NSError* error) {
+        return
+            [error.domain isEqualToString:CWVCreditCardVerifierErrorDomain] &&
+            error.code == CWVCreditCardVerificationErrorTryAgainFailure &&
+            error.userInfo[CWVCreditCardVerifierErrorMessageKey] != nil &&
+            error.userInfo[CWVCreditCardVerifierRetryAllowedKey] &&
+            [error.userInfo[CWVCreditCardVerifierRetryAllowedKey] boolValue];
+      }]];
+  [credit_card_verifier_ didReceiveUnmaskVerificationResult:
+                             autofill::AutofillClient::TRY_AGAIN_FAILURE];
+  [delegate verify];
+}
+
+// Tests CWVCreditCardVerifier properly invokes its data source.
+TEST_F(CWVCreditCardVerifierTest, DataSourceCallbacks) {
+  id data_source = OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
+  [credit_card_verifier_ verifyWithCVC:@"123"
+                       expirationMonth:@""
+                        expirationYear:@""
+                          storeLocally:NO
+                            dataSource:data_source
+                              delegate:nil];
+
+  [[data_source expect]
+                    creditCardVerifier:credit_card_verifier_
+      getRiskDataWithCompletionHandler:[OCMArg checkWithBlock:^BOOL(id arg) {
+        void (^completionHandler)(NSString*) = arg;
+        completionHandler(@"dummy-risk-data");
+        return YES;
+      }]];
+  __block bool callback_called = false;
+  base::OnceCallback<void(const std::string&)> callback = base::BindOnce(
+      [](bool* callback_called, const std::string& risk_data) -> void {
+        *callback_called = true;
+        EXPECT_EQ("dummy-risk-data", risk_data);
+      },
+      &callback_called);
+  [credit_card_verifier_ loadRiskData:std::move(callback)];
+
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
+    base::RunLoop().RunUntilIdle();
+    return callback_called;
+  }));
+
+  [data_source verify];
+}
+
 }  // namespace ios_web_view
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
index c5806e5a..ef11a0c 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
@@ -139,7 +139,9 @@
     const base::Closure& callback) {}
 
 void WebViewAutofillClientIOS::LoadRiskData(
-    base::OnceCallback<void(const std::string&)> callback) {}
+    base::OnceCallback<void(const std::string&)> callback) {
+  [bridge_ loadRiskData:std::move(callback)];
+}
 
 bool WebViewAutofillClientIOS::HasCreditCardScanFeature() {
   return false;
diff --git a/ios/web_view/public/cwv_credit_card_verifier.h b/ios/web_view/public/cwv_credit_card_verifier.h
index 5445aed4..d3f47d45 100644
--- a/ios/web_view/public/cwv_credit_card_verifier.h
+++ b/ios/web_view/public/cwv_credit_card_verifier.h
@@ -12,6 +12,31 @@
 NS_ASSUME_NONNULL_BEGIN
 
 @class CWVCreditCard;
+@protocol CWVCreditCardVerifierDataSource;
+@protocol CWVCreditCardVerifierDelegate;
+
+// The error domain for credit card verification errors.
+FOUNDATION_EXPORT CWV_EXPORT
+    NSErrorDomain const CWVCreditCardVerifierErrorDomain;
+// The key for the error message value in the error's |userInfo| dictionary.
+FOUNDATION_EXPORT CWV_EXPORT
+    NSString* const CWVCreditCardVerifierErrorMessageKey;
+// The key for the retry allowed value in the error's |userInfo| dictionary.
+FOUNDATION_EXPORT CWV_EXPORT
+    NSString* const CWVCreditCardVerifierRetryAllowedKey;
+
+// Possible error codes during credit card verification.
+typedef NS_ENUM(NSInteger, CWVCreditCardVerificationError) {
+  // No errors.
+  CWVCreditCardVerificationErrorNone = 0,
+  // Request failed; try again.
+  CWVCreditCardVerificationErrorTryAgainFailure = -100,
+  // Request failed; don't try again.
+  CWVCreditCardVerificationErrorPermanentFailure = -200,
+  // Unable to connect to Payments servers. Prompt user to check internet
+  // connection.
+  CWVCreditCardVerificationErrorNetworkFailure = -300,
+};
 
 CWV_EXPORT
 // Helps with verifying credit cards for autofill, updating expired expiration
@@ -53,22 +78,22 @@
 // Attempts |creditCard| verification.
 // |CVC| Card verification code. e.g. 3 digit code on the back of Visa cards or
 // 4 digit code in the front of American Express cards.
-// |month| 1 or 2 digit expiration month. e.g. 8 or 08 for August. Ignored if
+// |month| 1 or 2 digit expiration month. e.g. 8 or 08 for August. Can be nil if
 // |needsUpdateForExpirationDate| is NO.
-// |year| 2 or 4 digit expiration year. e.g. 19 or 2019. Ignored if
+// |year| 2 or 4 digit expiration year. e.g. 19 or 2019. Can be nil if
 // |needsUpdateForExpirationDate| is NO.
 // |storeLocally| Whether or not to save |creditCard| locally. If YES, user will
 // not be asked again to verify this card. Ignored if |canSaveLocally| is NO.
-// |completionHandler| Use to receive verification results. Must wait for
-// handler to return before attempting another verification.
-// |error| Contains the error message if unsuccessful. Empty if successful.
-// |retryAllowed| YES if user may attempt verification again.
+// |dataSource| will be asked to return risk data needed for verification.
+// |delegate| will be passed the verification result. Must wait for |delegate|
+// methods before attempting to verify again.
 - (void)verifyWithCVC:(NSString*)CVC
-      expirationMonth:(NSString*)expirationMonth
-       expirationYear:(NSString*)expirationYear
+      expirationMonth:(nullable NSString*)expirationMonth
+       expirationYear:(nullable NSString*)expirationYear
          storeLocally:(BOOL)storeLocally
-    completionHandler:
-        (void (^)(NSString* errorMessage, BOOL retryAllowed))completionHandler;
+           dataSource:(__weak id<CWVCreditCardVerifierDataSource>)dataSource
+             delegate:
+                 (nullable __weak id<CWVCreditCardVerifierDelegate>)delegate;
 
 // Returns YES if |CVC| is all digits and matches |expectedCVCLength|.
 - (BOOL)isCVCValid:(NSString*)CVC;
diff --git a/ios/web_view/public/cwv_credit_card_verifier_data_source.h b/ios/web_view/public/cwv_credit_card_verifier_data_source.h
new file mode 100644
index 0000000..ef4f5c58
--- /dev/null
+++ b/ios/web_view/public/cwv_credit_card_verifier_data_source.h
@@ -0,0 +1,29 @@
+// Copyright 2018 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_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DATA_SOURCE_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DATA_SOURCE_H_
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class CWVCreditCardVerifier;
+
+// Data source for CWVCreditCardVerifer.
+@protocol CWVCreditCardVerifierDataSource<NSObject>
+
+// Called when CWVCreditCardVerifier needs risk data before it can continue with
+// credit card verification. The client must obtain and return the risk data
+// needed for 1st party integration with the internal payments API.
+// See go/risk-eng.g3doc for more details.
+- (void)creditCardVerifier:(CWVCreditCardVerifier*)creditCardVerifier
+    getRiskDataWithCompletionHandler:
+        (void (^)(NSString* riskData))completionHandler;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DATA_SOURCE_H_
diff --git a/ios/web_view/public/cwv_credit_card_verifier_delegate.h b/ios/web_view/public/cwv_credit_card_verifier_delegate.h
new file mode 100644
index 0000000..edb71d3
--- /dev/null
+++ b/ios/web_view/public/cwv_credit_card_verifier_delegate.h
@@ -0,0 +1,30 @@
+// Copyright 2018 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_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DELEGATE_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class CWVCreditCardVerifier;
+
+// Delegate of CWVCreditCardVerifier.
+@protocol CWVCreditCardVerifierDelegate<NSObject>
+
+@optional
+
+// Called when CWVCreditCardVerifier could not verify the credit card.
+// |error| nil if successful, non-nil if unsuccessful. User info will contain
+// key CWVCreditCardVerifierErrorMessageKey indicating the reason and
+// CWVCreditCardVerifierRetryAllowedKey indicating if user can try again.
+- (void)creditCardVerifier:(CWVCreditCardVerifier*)creditCardVerifier
+    didFinishVerificationWithError:(nullable NSError*)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DELEGATE_H_
diff --git a/mojo/core/multiprocess_message_pipe_unittest.cc b/mojo/core/multiprocess_message_pipe_unittest.cc
index b69af36..df3d5924 100644
--- a/mojo/core/multiprocess_message_pipe_unittest.cc
+++ b/mojo/core/multiprocess_message_pipe_unittest.cc
@@ -1331,9 +1331,13 @@
     ,
     MultiprocessMessagePipeTestWithPeerSupport,
     testing::Values(test::MojoTestBase::LaunchType::CHILD,
-                    test::MojoTestBase::LaunchType::PEER,
+                    test::MojoTestBase::LaunchType::PEER
+#if !defined(OS_FUCHSIA)
+                    ,
                     test::MojoTestBase::LaunchType::NAMED_CHILD,
-                    test::MojoTestBase::LaunchType::NAMED_PEER));
+                    test::MojoTestBase::LaunchType::NAMED_PEER
+#endif  // !defined(OS_FUCHSIA)
+                    ));
 }  // namespace
 }  // namespace core
 }  // namespace mojo
diff --git a/mojo/core/test/multiprocess_test_helper.cc b/mojo/core/test/multiprocess_test_helper.cc
index 7181520..df7f4eb 100644
--- a/mojo/core/test/multiprocess_test_helper.cc
+++ b/mojo/core/test/multiprocess_test_helper.cc
@@ -109,24 +109,29 @@
   mojo::PlatformChannel channel;
   mojo::NamedPlatformChannel::ServerName server_name;
   base::LaunchOptions options;
-  if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) {
-    channel.PrepareToPassRemoteEndpoint(&options, &command_line);
-  } else if (launch_type == LaunchType::NAMED_CHILD ||
-             launch_type == LaunchType::NAMED_PEER) {
-#if defined(OS_FUCHSIA)
-    // TODO(fuchsia): Implement named channels. See crbug.com/754038.
-    NOTREACHED();
-#elif defined(OS_POSIX)
-    base::FilePath temp_dir;
-    CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir));
-    server_name =
-        temp_dir.AppendASCII(base::NumberToString(base::RandUint64())).value();
+  switch (launch_type) {
+    case LaunchType::CHILD:
+    case LaunchType::PEER:
+      channel.PrepareToPassRemoteEndpoint(&options, &command_line);
+      break;
+#if !defined(OS_FUCHSIA)
+    case LaunchType::NAMED_CHILD:
+    case LaunchType::NAMED_PEER: {
+#if defined(OS_POSIX)
+      base::FilePath temp_dir;
+      CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir));
+      server_name =
+          temp_dir.AppendASCII(base::NumberToString(base::RandUint64()))
+              .value();
 #elif defined(OS_WIN)
-    server_name = base::NumberToString16(base::RandUint64());
+      server_name = base::NumberToString16(base::RandUint64());
 #else
 #error "Platform not yet supported."
 #endif
-    command_line.AppendSwitchNative(kNamedPipeName, server_name);
+      command_line.AppendSwitchNative(kNamedPipeName, server_name);
+      break;
+    }
+#endif  // !defined(OS_FUCHSIA)
   }
 
   if (!switch_string.empty()) {
@@ -146,35 +151,49 @@
   // the pipe path can race with child's connection to the pipe.
   PlatformChannelEndpoint local_channel_endpoint;
   PlatformChannelServerEndpoint server_endpoint;
-  if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) {
-    local_channel_endpoint = channel.TakeLocalEndpoint();
-  } else if (launch_type == LaunchType::NAMED_CHILD ||
-             launch_type == LaunchType::NAMED_PEER) {
-    NamedPlatformChannel::Options options;
-    options.server_name = server_name;
-    NamedPlatformChannel named_channel(options);
-    server_endpoint = named_channel.TakeServerEndpoint();
-  }
+  switch (launch_type) {
+    case LaunchType::CHILD:
+    case LaunchType::PEER:
+      local_channel_endpoint = channel.TakeLocalEndpoint();
+      break;
+#if !defined(OS_FUCHSIA)
+    case LaunchType::NAMED_CHILD:
+    case LaunchType::NAMED_PEER: {
+      NamedPlatformChannel::Options options;
+      options.server_name = server_name;
+      NamedPlatformChannel named_channel(options);
+      server_endpoint = named_channel.TakeServerEndpoint();
+      break;
+    }
+#endif  // !defined(OS_FUCHSIA)
+  };
 
   OutgoingInvitation child_invitation;
   ScopedMessagePipeHandle pipe;
-  if (launch_type == LaunchType::CHILD ||
-      launch_type == LaunchType::NAMED_CHILD) {
-    pipe = child_invitation.AttachMessagePipe(kTestChildMessagePipeName);
-    command_line.AppendSwitch(kRunAsBrokerClient);
-  } else if (launch_type == LaunchType::PEER ||
-             launch_type == LaunchType::NAMED_PEER) {
-    isolated_connection_ = std::make_unique<IsolatedConnection>();
-    if (local_channel_endpoint.is_valid()) {
-      pipe = isolated_connection_->Connect(std::move(local_channel_endpoint));
-    } else {
-#if defined(OS_POSIX) || defined(OS_WIN)
-      DCHECK(server_endpoint.is_valid());
-      pipe = isolated_connection_->Connect(std::move(server_endpoint));
-#else
-      NOTREACHED();
+  switch (launch_type) {
+    case LaunchType::CHILD:
+#if !defined(OS_FUCHSIA)
+    case LaunchType::NAMED_CHILD:
 #endif
-    }
+      pipe = child_invitation.AttachMessagePipe(kTestChildMessagePipeName);
+      command_line.AppendSwitch(kRunAsBrokerClient);
+      break;
+    case LaunchType::PEER:
+#if !defined(OS_FUCHSIA)
+    case LaunchType::NAMED_PEER:
+#endif
+      isolated_connection_ = std::make_unique<IsolatedConnection>();
+      if (local_channel_endpoint.is_valid()) {
+        pipe = isolated_connection_->Connect(std::move(local_channel_endpoint));
+      } else {
+#if defined(OS_POSIX) || defined(OS_WIN)
+        DCHECK(server_endpoint.is_valid());
+        pipe = isolated_connection_->Connect(std::move(server_endpoint));
+#else
+        NOTREACHED();
+#endif
+      }
+      break;
   }
 
   test_child_ =
@@ -187,12 +206,15 @@
     OutgoingInvitation::Send(std::move(child_invitation), test_child_.Handle(),
                              std::move(local_channel_endpoint),
                              mojo::ProcessErrorCallback());
-  } else if (launch_type == LaunchType::NAMED_CHILD) {
+  }
+#if !defined(OS_FUCHSIA)
+  else if (launch_type == LaunchType::NAMED_CHILD) {
     DCHECK(server_endpoint.is_valid());
     OutgoingInvitation::Send(std::move(child_invitation), test_child_.Handle(),
                              std::move(server_endpoint),
                              mojo::ProcessErrorCallback());
   }
+#endif  //  !defined(OS_FUCHSIA)
 
   CHECK(test_child_.IsValid());
   return pipe;
diff --git a/mojo/core/test/multiprocess_test_helper.h b/mojo/core/test/multiprocess_test_helper.h
index e7be8355..becc74c6 100644
--- a/mojo/core/test/multiprocess_test_helper.h
+++ b/mojo/core/test/multiprocess_test_helper.h
@@ -12,6 +12,7 @@
 #include "base/process/process.h"
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_timeouts.h"
+#include "build/build_config.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "testing/multiprocess_func_list.h"
 
@@ -33,6 +34,7 @@
     // Launch the child process as an unrelated peer process in the mojo system.
     PEER,
 
+#if !defined(OS_FUCHSIA)
     // Launch the child process as a child in the mojo system, using a named
     // pipe.
     NAMED_CHILD,
@@ -40,6 +42,7 @@
     // Launch the child process as an unrelated peer process in the mojo
     // system, using a named pipe.
     NAMED_PEER,
+#endif  //  !defined(OS_FUCHSIA)
   };
 
   MultiprocessTestHelper();
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 2715283..86d57ca 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -208,6 +208,7 @@
   # TODO(yzshen): crbug.com/617718 Consider moving this into blink.
   source_set("wtf_support") {
     sources = [
+      "array_traits_web_vector.h",
       "array_traits_wtf_vector.h",
       "lib/string_traits_wtf.cc",
       "lib/wtf_clone_equals_util.h",
@@ -219,6 +220,7 @@
 
     public_deps = [
       ":bindings",
+      "//third_party/blink/public:blink_headers",
       "//third_party/blink/renderer/platform:platform_export",
       "//third_party/blink/renderer/platform/wtf",
     ]
diff --git a/mojo/public/cpp/bindings/DEPS b/mojo/public/cpp/bindings/DEPS
index 4633930..4a2a9b7 100644
--- a/mojo/public/cpp/bindings/DEPS
+++ b/mojo/public/cpp/bindings/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+third_party/blink/public/platform/web_vector.h",
   "+third_party/blink/renderer/platform/wtf",
 ]
diff --git a/mojo/public/cpp/bindings/array_traits_web_vector.h b/mojo/public/cpp/bindings/array_traits_web_vector.h
new file mode 100644
index 0000000..9e17892
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits_web_vector.h
@@ -0,0 +1,56 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WEB_VECTOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WEB_VECTOR_H_
+
+#include "mojo/public/cpp/bindings/array_traits.h"
+#include "third_party/blink/public/platform/web_vector.h"
+
+namespace mojo {
+
+template <typename U>
+struct ArrayTraits<blink::WebVector<U>> {
+  using Element = U;
+
+  static bool IsNull(const blink::WebVector<U>& input) {
+    // blink::WebVector<> is always converted to non-null mojom array.
+    return false;
+  }
+
+  static void SetToNull(blink::WebVector<U>* output) {
+    // blink::WebVector<> doesn't support null state. Set it to empty instead.
+    output->Clear();
+  }
+
+  static size_t GetSize(const blink::WebVector<U>& input) {
+    return input.size();
+  }
+
+  static U* GetData(blink::WebVector<U>& input) { return input.Data(); }
+
+  static const U* GetData(const blink::WebVector<U>& input) {
+    return input.Data();
+  }
+
+  static U& GetAt(blink::WebVector<U>& input, size_t index) {
+    return input[index];
+  }
+
+  static const U& GetAt(const blink::WebVector<U>& input, size_t index) {
+    return input[index];
+  }
+
+  static bool Resize(blink::WebVector<U>& input, size_t size) {
+    // WebVector DCHECKs if the new size is larger than capacity().  Call
+    // reserve() first to be safe.
+    input.reserve(size);
+    input.resize(size);
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WEB_VECTOR_H_
diff --git a/net/BUILD.gn b/net/BUILD.gn
index d4d9e35..3cad52a 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1534,8 +1534,6 @@
       "third_party/quic/platform/api/quic_string.h",
       "third_party/quic/platform/api/quic_string_piece.h",
       "third_party/quic/platform/api/quic_text_utils.h",
-      "third_party/quic/platform/api/quic_url.cc",
-      "third_party/quic/platform/api/quic_url.h",
       "third_party/quic/platform/impl/quic_aligned_impl.h",
       "third_party/quic/platform/impl/quic_arraysize_impl.h",
       "third_party/quic/platform/impl/quic_bug_tracker_impl.h",
@@ -1579,8 +1577,6 @@
       "third_party/quic/platform/impl/quic_string_impl.h",
       "third_party/quic/platform/impl/quic_string_piece_impl.h",
       "third_party/quic/platform/impl/quic_text_utils_impl.h",
-      "third_party/quic/platform/impl/quic_url_impl.cc",
-      "third_party/quic/platform/impl/quic_url_impl.h",
       "third_party/quic/quartc/quartc_factory.cc",
       "third_party/quic/quartc/quartc_factory.h",
       "third_party/quic/quartc/quartc_packet_writer.cc",
@@ -3259,6 +3255,8 @@
     "third_party/quic/tools/quic_simple_server_stream.h",
     "third_party/quic/tools/quic_spdy_client_base.cc",
     "third_party/quic/tools/quic_spdy_client_base.h",
+    "third_party/quic/tools/quic_url.cc",
+    "third_party/quic/tools/quic_url.h",
     "tools/quic/quic_client_message_loop_network_helper.cc",
     "tools/quic/quic_client_message_loop_network_helper.h",
     "tools/quic/quic_http_proxy_backend.cc",
@@ -5146,7 +5144,6 @@
     "third_party/quic/platform/api/quic_singleton_test.cc",
     "third_party/quic/platform/api/quic_str_cat_test.cc",
     "third_party/quic/platform/api/quic_text_utils_test.cc",
-    "third_party/quic/platform/api/quic_url_test.cc",
     "third_party/quic/platform/impl/quic_chromium_clock_test.cc",
     "third_party/quic/platform/impl/quic_uint128_impl_unittest.cc",
     "third_party/quic/quartc/quartc_session_test.cc",
@@ -5329,6 +5326,7 @@
       "third_party/quic/tools/quic_server_test.cc",
       "third_party/quic/tools/quic_simple_server_session_test.cc",
       "third_party/quic/tools/quic_simple_server_stream_test.cc",
+      "third_party/quic/tools/quic_url_test.cc",
       "tools/quic/quic_http_proxy_backend_stream_test.cc",
       "tools/quic/quic_http_proxy_backend_test.cc",
       "tools/quic/quic_simple_server_session_helper_test.cc",
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index d8cf8e5..0097c9b 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -13,6 +13,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -34,6 +35,46 @@
     --(*end);
 }
 
+// Helper class that builds the list of languages for the Accept-Language
+// headers.
+// The output is a comma-separated list of languages as string.
+// Duplicates are removed.
+class AcceptLanguageBuilder {
+ public:
+  // Adds a language to the string.
+  // Duplicates are ignored.
+  void AddLanguageCode(const std::string& language) {
+    // No Q score supported, only supports ASCII.
+    DCHECK_EQ(std::string::npos, language.find_first_of("; "));
+    DCHECK(base::IsStringASCII(language));
+    if (seen_.find(language) == seen_.end()) {
+      if (str_.empty()) {
+        base::StringAppendF(&str_, "%s", language.c_str());
+      } else {
+        base::StringAppendF(&str_, ",%s", language.c_str());
+      }
+      seen_.insert(language);
+    }
+  }
+
+  // Returns the string constructed up to this point.
+  std::string GetString() const { return str_; }
+
+ private:
+  // The string that contains the list of languages, comma-separated.
+  std::string str_;
+  // Set the remove duplicates.
+  std::unordered_set<std::string> seen_;
+};
+
+// Extract the base language code from a language code.
+// If there is no '-' in the code, the original code is returned.
+std::string GetBaseLanguageCode(const std::string& language_code) {
+  const std::vector<std::string> tokens = base::SplitString(
+      language_code, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  return tokens.empty() ? "" : tokens[0];
+}
+
 }  // namespace
 
 // HttpUtil -------------------------------------------------------------------
@@ -773,6 +814,34 @@
   return disassembled_headers;
 }
 
+std::string HttpUtil::ExpandLanguageList(const std::string& language_prefs) {
+  const std::vector<std::string> languages = base::SplitString(
+      language_prefs, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+  if (languages.empty())
+    return "";
+
+  AcceptLanguageBuilder builder;
+
+  const int size = languages.size();
+  for (int i = 0; i < size; ++i) {
+    const std::string& language = languages[i];
+    builder.AddLanguageCode(language);
+
+    // Extract the base language
+    const std::string& base_language = GetBaseLanguageCode(language);
+
+    // Look ahead and add the base language if the next language is not part
+    // of the same family.
+    const int j = i + 1;
+    if (j >= size || GetBaseLanguageCode(languages[j]) != base_language) {
+      builder.AddLanguageCode(base_language);
+    }
+  }
+
+  return builder.GetString();
+}
+
 // TODO(jungshik): This function assumes that the input is a comma separated
 // list without any whitespace. As long as it comes from the preference and
 // a user does not manually edit the preference file, it's the case. Still,
diff --git a/net/http/http_util.h b/net/http/http_util.h
index 0bcb45fa..b4dd8ef0 100644
--- a/net/http/http_util.h
+++ b/net/http/http_util.h
@@ -204,6 +204,14 @@
   // consists of status line and then one line for each header.
   static std::string ConvertHeadersBackToHTTPResponse(const std::string& str);
 
+  // Given a comma separated ordered list of language codes, return an expanded
+  // list by adding the base language from language-region pair if it doesn't
+  // already exist. This increases the chances of language matching in many
+  // cases as explained at this w3c doc:
+  // https://www.w3.org/International/questions/qa-lang-priorities#langtagdetail
+  // Note that we do not support Q values (e.g. ;q=0.9) in |language_prefs|.
+  static std::string ExpandLanguageList(const std::string& language_prefs);
+
   // Given a comma separated ordered list of language codes, return
   // the list with a qvalue appended to each language.
   // The way qvalues are assigned is rather simple. The qvalue
diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc
index cce5d34..d0b59ca 100644
--- a/net/http/http_util_unittest.cc
+++ b/net/http/http_util_unittest.cc
@@ -1570,4 +1570,28 @@
   }
 }
 
+// Test the expansion of the Language List.
+TEST(HttpUtilTest, ExpandLanguageList) {
+  EXPECT_EQ("", HttpUtil::ExpandLanguageList(""));
+  EXPECT_EQ("en-US,en", HttpUtil::ExpandLanguageList("en-US"));
+  EXPECT_EQ("fr", HttpUtil::ExpandLanguageList("fr"));
+
+  // The base language is added after all regional codes...
+  EXPECT_EQ("en-US,en-CA,en", HttpUtil::ExpandLanguageList("en-US,en-CA"));
+
+  // ... but before other language families.
+  EXPECT_EQ("en-US,en-CA,en,fr",
+            HttpUtil::ExpandLanguageList("en-US,en-CA,fr"));
+  EXPECT_EQ("en-US,en-CA,en,fr,en-AU",
+            HttpUtil::ExpandLanguageList("en-US,en-CA,fr,en-AU"));
+  EXPECT_EQ("en-US,en-CA,en,fr-CA,fr",
+            HttpUtil::ExpandLanguageList("en-US,en-CA,fr-CA"));
+
+  // Add a base language even if it's already in the list.
+  EXPECT_EQ("en-US,en,fr-CA,fr,it,es-AR,es,it-IT",
+            HttpUtil::ExpandLanguageList("en-US,fr-CA,it,fr,es-AR,it-IT"));
+  // Trims a whitespace.
+  EXPECT_EQ("en-US,en,fr", HttpUtil::ExpandLanguageList("en-US, fr"));
+}
+
 }  // namespace net
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 16d1bef..e32c6f4d 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -239,7 +239,6 @@
       transport_rtt_observation_count_last_ect_computation_(0),
       new_rtt_observations_since_last_ect_computation_(0),
       new_throughput_observations_since_last_ect_computation_(0),
-      increase_in_transport_rtt_updater_posted_(false),
       effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
       cached_estimate_applied_(false),
       net_log_(NetLogWithSource::Make(
@@ -929,120 +928,6 @@
                           bandwidth_delay_product_kbits_.value());
 }
 
-void NetworkQualityEstimator::IncreaseInTransportRTTUpdater() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  increase_in_transport_rtt_ = ComputeIncreaseInTransportRTT();
-
-  // Stop the timer if there was no recent data and |increase_in_transport_rtt_|
-  // could not be computed. This is fine because |increase_in_transport_rtt| can
-  // only be computed if there is recent transport RTT data, and the timer is
-  // restarted when there is a new observation.
-  if (!increase_in_transport_rtt_) {
-    increase_in_transport_rtt_updater_posted_ = false;
-    return;
-  }
-
-  increase_in_transport_rtt_updater_posted_ = true;
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&NetworkQualityEstimator::IncreaseInTransportRTTUpdater,
-                 weak_ptr_factory_.GetWeakPtr()),
-      params_->increase_in_transport_rtt_logging_interval());
-}
-
-base::Optional<int32_t> NetworkQualityEstimator::ComputeIncreaseInTransportRTT()
-    const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  base::TimeTicks now = tick_clock_->NowTicks();
-
-  // The time after which the observations are considered to be recent enough to
-  // be a good proxy for the current level of congestion.
-  base::TimeTicks recent_start_time = now - params_->recent_time_threshold();
-
-  // Get the median transport RTT observed over the last 5 seconds for each
-  // remote host. This is an estimate of the current RTT which will be compared
-  // to the baseline obtained from historical data to detect an increase in RTT.
-  std::map<nqe::internal::IPHash, int32_t> recent_median_rtts;
-  std::map<nqe::internal::IPHash, size_t> recent_observation_counts;
-  rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
-      .GetPercentileForEachHostWithCounts(recent_start_time, 50, base::nullopt,
-                                          &recent_median_rtts,
-                                          &recent_observation_counts);
-
-  if (recent_median_rtts.empty())
-    return base::nullopt;
-
-  // The time after which the observations are used to calculate the baseline.
-  // This is needed because the general network characteristics could have
-  // changed over time.
-  base::TimeTicks history_start_time =
-      now - params_->historical_time_threshold();
-
-  // Create a set of the remote hosts seen in the recent observations so that
-  // the data can be filtered while calculating the percentiles.
-  std::set<nqe::internal::IPHash> recent_hosts_set;
-  for (const auto& recent_median_rtts_for_host : recent_median_rtts)
-    recent_hosts_set.insert(recent_median_rtts_for_host.first);
-
-  // Get the minimum transport RTT observed over 1 minute for each remote host.
-  // This is an estimate of the true RTT which will be used as a baseline value
-  // to detect an increase in RTT. The minimum value is used here because the
-  // observed values cannot be lower than the true RTT. The median is used for
-  // the recent data to reduce noise in the calculation.
-  std::map<nqe::internal::IPHash, int32_t> historical_min_rtts;
-  std::map<nqe::internal::IPHash, size_t> historical_observation_counts;
-  rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
-      .GetPercentileForEachHostWithCounts(
-          history_start_time, 0, recent_hosts_set, &historical_min_rtts,
-          &historical_observation_counts);
-
-  // Calculate the total observation counts for the hosts common to the recent
-  // data and the historical data.
-  size_t total_historical_count = 0;
-  size_t total_recent_count = 0;
-  for (const auto& recent_median_rtts_for_host : recent_median_rtts) {
-    nqe::internal::IPHash host = recent_median_rtts_for_host.first;
-    total_historical_count += historical_observation_counts[host];
-    total_recent_count += recent_observation_counts[host];
-  }
-
-  // Compute the increases in transport RTT for each remote host. Also compute
-  // the weight for each remote host based on the number of observations.
-  double total_weight = 0.0;
-  std::vector<nqe::internal::WeightedObservation> weighted_rtts;
-  for (auto& host : recent_hosts_set) {
-    // The relative weight signifies the amount of confidence in the data. The
-    // weight is higher if there were more observations. A regularization term
-    // of |1 / recent_hosts_set.size()| is added so that if one particular
-    // remote host has a lot of observations, the results do not get skewed.
-    double weight =
-        1.0 / recent_hosts_set.size() +
-        std::min(static_cast<double>(recent_observation_counts[host]) /
-                     total_recent_count,
-                 static_cast<double>(historical_observation_counts[host]) /
-                     total_historical_count);
-    weighted_rtts.push_back(nqe::internal::WeightedObservation(
-        recent_median_rtts[host] - historical_min_rtts[host], weight));
-    total_weight += weight;
-  }
-
-  // Sort the increases in RTT for percentile computation.
-  std::sort(weighted_rtts.begin(), weighted_rtts.end());
-
-  // Calculate the weighted 50th percentile increase in transport RTT.
-  double desired_weight = 0.5 * total_weight;
-  for (nqe::internal::WeightedObservation wo : weighted_rtts) {
-    desired_weight -= wo.weight;
-    if (desired_weight <= 0)
-      return wo.value;
-  }
-
-  // Calculation will reach here when the 50th percentile is the last value.
-  return weighted_rtts.back().value;
-}
-
 void NetworkQualityEstimator::ComputeEffectiveConnectionType() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -1530,11 +1415,6 @@
                           current_network_id_.signal_strength,
                           ProtocolSourceToObservationSource(protocol), host);
   AddAndNotifyObserversOfRTT(observation);
-
-  // Post a task to compute and update the increase in RTT if not already
-  // posted.
-  if (!increase_in_transport_rtt_updater_posted_)
-    IncreaseInTransportRTTUpdater();
 }
 
 void NetworkQualityEstimator::AddAndNotifyObserversOfRTT(
diff --git a/net/nqe/network_quality_estimator.h b/net/nqe/network_quality_estimator.h
index 626f25c0..a3dbd14 100644
--- a/net/nqe/network_quality_estimator.h
+++ b/net/nqe/network_quality_estimator.h
@@ -372,10 +372,6 @@
   // |observed_http_rtt| with the expected HTTP and transport RTT.
   bool IsHangingRequest(base::TimeDelta observed_http_rtt) const;
 
-  base::Optional<int32_t> ComputeIncreaseInTransportRTTForTests() {
-    return ComputeIncreaseInTransportRTT();
-  }
-
   // Returns the current network signal strength by querying the platform APIs.
   // Set to INT32_MIN when the value is unavailable. Otherwise, must be between
   // 0 and 4 (both inclusive). This may take into account many different radio
@@ -422,11 +418,6 @@
                            ForceEffectiveConnectionTypeThroughFieldTrial);
   FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, TestBDPComputation);
   FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest,
-                           TestComputeIncreaseInTransportRTTFullHostsOverlap);
-  FRIEND_TEST_ALL_PREFIXES(
-      NetworkQualityEstimatorTest,
-      TestComputeIncreaseInTransportRTTPartialHostsOverlap);
-  FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest,
                            ObservationDiscardedIfCachedEstimateAvailable);
   FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest,
                            TestRttThroughputObservers);
@@ -528,17 +519,6 @@
   // |GetBandwidthDelayProductKbits|.
   void ComputeBandwidthDelayProduct();
 
-  // Computes the current increase in transport RTT in milliseconds over the
-  // baseline transport RTT due to congestion. This value can be interpreted as
-  // the additional delay caused due to an increase in queue length in the last
-  // mile. The baseline is computed using the transport RTT observations in the
-  // past 60 seconds. The current RTT is computed using the observations in the
-  // past 5 seconds. Returns an empty optional when there was no recent data.
-  base::Optional<int32_t> ComputeIncreaseInTransportRTT() const;
-
-  // Periodically updates |increase_in_transport_rtt_| by posting delayed tasks.
-  void IncreaseInTransportRTTUpdater();
-
   // Gathers metrics for the next connection type. Called when there is a change
   // in the connection type.
   void GatherEstimatesForNextConnectionType();
@@ -648,12 +628,6 @@
   // Current estimate of the bandwidth delay product (BDP) in kilobits.
   base::Optional<int32_t> bandwidth_delay_product_kbits_;
 
-  // Current estimate of the increase in the transport RTT due to congestion.
-  base::Optional<int32_t> increase_in_transport_rtt_;
-
-  // This is true if there is a task posted for |IncreaseInTransportRTTUpdater|.
-  bool increase_in_transport_rtt_updater_posted_;
-
   // Current effective connection type. It is updated on connection change
   // events. It is also updated every time there is network traffic (provided
   // the last computation was more than
diff --git a/net/nqe/network_quality_estimator_unittest.cc b/net/nqe/network_quality_estimator_unittest.cc
index 65ad922f..1bb16ee 100644
--- a/net/nqe/network_quality_estimator_unittest.cc
+++ b/net/nqe/network_quality_estimator_unittest.cc
@@ -2716,87 +2716,6 @@
             (int32_t)(std::pow(2, 2) * std::pow(3, 8) / 1000));
 }
 
-TEST_F(NetworkQualityEstimatorTest,
-       TestComputeIncreaseInTransportRTTFullHostsOverlap) {
-  base::SimpleTestTickClock tick_clock;
-  tick_clock.Advance(base::TimeDelta::FromMinutes(1));
-
-  std::map<std::string, std::string> variation_params;
-  variation_params["add_default_platform_observations"] = "false";
-  TestNetworkQualityEstimator estimator(variation_params);
-  estimator.SetTickClockForTesting(&tick_clock);
-
-  base::TimeTicks now = tick_clock.NowTicks();
-  base::TimeTicks recent = now - base::TimeDelta::FromMilliseconds(2500);
-  base::TimeTicks historical = now - base::TimeDelta::FromSeconds(20);
-
-  // Add historical observations. The 0 percentile for |host| is |10 * host|
-  // ms.
-  for (int host = 1; host <= 3; ++host) {
-    for (int rtt = 10 * host; rtt <= 10 * host + 20; ++rtt) {
-      estimator
-          .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
-          .AddObservation(NetworkQualityEstimator::Observation(
-              rtt, historical, INT32_MIN,
-              NETWORK_QUALITY_OBSERVATION_SOURCE_TCP,
-              static_cast<uint64_t>(host)));
-    }
-  }
-
-  // Add recent observations. The 50 percentile for |host| is |10 * host + 10|
-  // ms. The difference between them is expected to be 10 ms.
-  for (int host = 1; host <= 3; ++host) {
-    for (int rtt = 10 * host + 5; rtt <= 10 * host + 15; ++rtt) {
-      estimator
-          .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
-          .AddObservation(NetworkQualityEstimator::Observation(
-              rtt, recent, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP,
-              static_cast<uint64_t>(host)));
-    }
-  }
-
-  EXPECT_EQ(10, estimator.ComputeIncreaseInTransportRTTForTests().value_or(0));
-}
-
-TEST_F(NetworkQualityEstimatorTest,
-       TestComputeIncreaseInTransportRTTPartialHostsOverlap) {
-  base::SimpleTestTickClock tick_clock;
-  tick_clock.Advance(base::TimeDelta::FromMinutes(1));
-
-  std::map<std::string, std::string> variation_params;
-  variation_params["add_default_platform_observations"] = "false";
-  TestNetworkQualityEstimator estimator(variation_params);
-  estimator.SetTickClockForTesting(&tick_clock);
-
-  base::TimeTicks now = tick_clock.NowTicks();
-  base::TimeTicks recent = now - base::TimeDelta::FromMilliseconds(2500);
-  base::TimeTicks historical = now - base::TimeDelta::FromSeconds(20);
-
-  // Add historical observations for hosts 1 and 2 with minimum RTT as
-  // |10 * host|.
-  for (int host = 1; host <= 2; ++host) {
-    for (int rtt = 10 * host; rtt <= 10 * host + 20; ++rtt) {
-      estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation(
-          rtt, historical, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP,
-          static_cast<uint64_t>(host)));
-    }
-  }
-
-  // Add recent observations, with median RTT as |10 + host| over the
-  // historical minimum for hosts 2 and 3.
-  for (int host = 2; host <= 3; ++host) {
-    for (int rtt = 11 * host + 5; rtt <= 11 * host + 15; ++rtt) {
-      estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation(
-          rtt, recent, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP,
-          static_cast<uint64_t>(host)));
-    }
-  }
-
-  // Only host 2 should have contributed to the calculation. Hence, the median
-  // should be |10 + 2 = 12|.
-  EXPECT_EQ(12, estimator.ComputeIncreaseInTransportRTTForTests().value_or(0));
-}
-
 // Verifies that when the cached network qualities from the prefs are available,
 // then estimates from the platform or the external estimate provider are not
 // used.
diff --git a/net/nqe/observation_buffer.cc b/net/nqe/observation_buffer.cc
index 4fa1687d..8332ba3 100644
--- a/net/nqe/observation_buffer.cc
+++ b/net/nqe/observation_buffer.cc
@@ -112,62 +112,6 @@
   return weighted_observations.at(weighted_observations.size() - 1).value;
 }
 
-void ObservationBuffer::GetPercentileForEachHostWithCounts(
-    base::TimeTicks begin_timestamp,
-    int percentile,
-    const base::Optional<std::set<IPHash>>& host_filter,
-    std::map<IPHash, int32_t>* host_keyed_percentiles,
-    std::map<IPHash, size_t>* host_keyed_counts) const {
-  DCHECK_GE(Capacity(), Size());
-  DCHECK_LE(0, percentile);
-  DCHECK_GE(100, percentile);
-
-  host_keyed_percentiles->clear();
-  host_keyed_counts->clear();
-
-  // Filter the observations based on timestamp, and the
-  // presence of a valid host tag. Split the observations into a map keyed by
-  // the remote host to make it easy to calculate percentiles for each host.
-  std::map<IPHash, std::vector<int32_t>> host_keyed_observations;
-  for (const auto& observation : observations_) {
-    // Look at only those observations which have a |host|.
-    if (!observation.host())
-      continue;
-
-    IPHash host = observation.host().value();
-    if (host_filter && (host_filter->find(host) == host_filter->end()))
-      continue;
-
-    // Filter the observations recorded before |begin_timestamp|.
-    if (observation.timestamp() < begin_timestamp)
-      continue;
-
-    // Skip 0 values of RTT.
-    if (observation.value() < 1)
-      continue;
-
-    // Create the map entry if it did not already exist. Does nothing if
-    // |host| was seen before.
-    host_keyed_observations.emplace(host, std::vector<int32_t>());
-    host_keyed_observations[host].push_back(observation.value());
-  }
-
-  if (host_keyed_observations.empty())
-    return;
-
-  // Calculate the percentile values for each host.
-  for (auto& host_observations : host_keyed_observations) {
-    IPHash host = host_observations.first;
-    auto& observations = host_observations.second;
-    std::sort(observations.begin(), observations.end());
-    size_t count = observations.size();
-    DCHECK_GT(count, 0u);
-    (*host_keyed_counts)[host] = count;
-    int percentile_index = ((count - 1) * percentile) / 100;
-    (*host_keyed_percentiles)[host] = observations[percentile_index];
-  }
-}
-
 void ObservationBuffer::RemoveObservationsWithSource(
     bool deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_MAX]) {
   base::EraseIf(observations_,
diff --git a/net/nqe/observation_buffer.h b/net/nqe/observation_buffer.h
index 1527360..d3526581 100644
--- a/net/nqe/observation_buffer.h
+++ b/net/nqe/observation_buffer.h
@@ -83,19 +83,6 @@
     tick_clock_ = tick_clock;
   }
 
-  // Computes percentiles separately for each host. Observations without
-  // a host tag are skipped. Only data from the hosts present in |host_filter|
-  // are considered. Observations before |begin_timestamp| are skipped. The
-  // percentile value for each host is returned in |host_keyed_percentiles|. The
-  // number of valid observations for each host used for the computation is
-  // returned in |host_keyed_counts|.
-  void GetPercentileForEachHostWithCounts(
-      base::TimeTicks begin_timestamp,
-      int percentile,
-      const base::Optional<std::set<IPHash>>& host_filter,
-      std::map<IPHash, int32_t>* host_keyed_percentiles,
-      std::map<IPHash, size_t>* host_keyed_counts) const;
-
   // Removes all observations from the buffer whose corresponding entry in
   // |deleted_observation_sources| is set to true. For example, if index 1 and
   // 3 in |deleted_observation_sources| are set to true, then all observations
diff --git a/net/nqe/observation_buffer_unittest.cc b/net/nqe/observation_buffer_unittest.cc
index 8e5d4e1..f5d946f 100644
--- a/net/nqe/observation_buffer_unittest.cc
+++ b/net/nqe/observation_buffer_unittest.cc
@@ -370,174 +370,6 @@
   }
 }
 
-// Test that time filtering works and the remote hosts are split correctly.
-TEST(NetworkQualityObservationBufferTest,
-     RestGetPercentileForEachRemoteHostSinceTimeStamp) {
-  std::map<std::string, std::string> variation_params;
-  NetworkQualityEstimatorParams params(variation_params);
-  base::SimpleTestTickClock tick_clock;
-  tick_clock.Advance(base::TimeDelta::FromMinutes(1));
-  const uint64_t new_host = 0x101010UL;
-  const int32_t new_host_observation = 1000;
-  const size_t new_host_num_obs = 10;
-  const uint64_t old_host = 0x202020UL;
-  const int32_t old_host_observation = 2000;
-  const size_t old_host_num_obs = 20;
-  ObservationBuffer buffer(&params, &tick_clock, 0.5, 1.0);
-  base::TimeTicks now = tick_clock.NowTicks();
-  for (unsigned int i = 0; i < old_host_num_obs; ++i) {
-    buffer.AddObservation(Observation(
-        old_host_observation, now - base::TimeDelta::FromSeconds(100),
-        INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, old_host));
-  }
-
-  for (unsigned int i = 0; i < new_host_num_obs; ++i) {
-    buffer.AddObservation(Observation(new_host_observation, now, INT32_MIN,
-                                      NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP,
-                                      new_host));
-  }
-
-  std::map<uint64_t, int32_t> host_keyed_percentiles;
-  std::map<uint64_t, size_t> host_keyed_counts;
-  buffer.GetPercentileForEachHostWithCounts(
-      now - base::TimeDelta::FromSeconds(50), 50, base::nullopt,
-      &host_keyed_percentiles, &host_keyed_counts);
-  EXPECT_EQ(1u, host_keyed_percentiles.size());
-  EXPECT_EQ(1u, host_keyed_counts.size());
-  EXPECT_EQ(new_host_observation, host_keyed_percentiles[new_host]);
-  EXPECT_EQ(new_host_num_obs, host_keyed_counts[new_host]);
-
-  host_keyed_percentiles.clear();
-  host_keyed_counts.clear();
-
-  buffer.GetPercentileForEachHostWithCounts(
-      now - base::TimeDelta::FromSeconds(150), 50, base::nullopt,
-      &host_keyed_percentiles, &host_keyed_counts);
-  EXPECT_EQ(2u, host_keyed_percentiles.size());
-  EXPECT_EQ(2u, host_keyed_counts.size());
-  EXPECT_EQ(new_host_observation, host_keyed_percentiles[new_host]);
-  EXPECT_EQ(new_host_num_obs, host_keyed_counts[new_host]);
-  EXPECT_EQ(old_host_observation, host_keyed_percentiles[old_host]);
-  EXPECT_EQ(old_host_num_obs, host_keyed_counts[old_host]);
-}
-
-// Test that the result is split correctly for multiple remote hosts and that
-// the count for each host is correct.
-TEST(NetworkQualityObservationBufferTest,
-     RestGetPercentileForEachRemoteHostCounts) {
-  std::map<std::string, std::string> variation_params;
-  NetworkQualityEstimatorParams params(variation_params);
-  base::SimpleTestTickClock tick_clock;
-  tick_clock.Advance(base::TimeDelta::FromMinutes(1));
-  ObservationBuffer buffer(&params, &tick_clock, 0.5, 1.0);
-  base::TimeTicks now = tick_clock.NowTicks();
-  const size_t num_remote_hosts = 5;
-
-  // Add |2*i| observations having value |4*i| for host |i|.
-  for (unsigned int host_index = 1; host_index <= num_remote_hosts;
-       ++host_index) {
-    for (unsigned int count = 1; count <= 2 * host_index; ++count) {
-      buffer.AddObservation(Observation(4 * host_index, now, INT32_MIN,
-                                        NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP,
-                                        static_cast<uint64_t>(host_index)));
-    }
-  }
-
-  std::map<uint64_t, int32_t> host_keyed_percentiles;
-  std::map<uint64_t, size_t> host_keyed_counts;
-  buffer.GetPercentileForEachHostWithCounts(
-      base::TimeTicks(), 50, base::nullopt, &host_keyed_percentiles,
-      &host_keyed_counts);
-  EXPECT_EQ(num_remote_hosts, host_keyed_percentiles.size());
-  EXPECT_EQ(num_remote_hosts, host_keyed_counts.size());
-
-  for (unsigned int host_index = 1; host_index <= num_remote_hosts;
-       ++host_index) {
-    EXPECT_EQ(2u * host_index,
-              host_keyed_counts[static_cast<uint64_t>(host_index)]);
-    EXPECT_EQ(static_cast<int32_t>(4 * host_index),
-              host_keyed_percentiles[static_cast<uint64_t>(host_index)]);
-  }
-}
-
-// Test that the percentiles are computed correctly for different remote hosts.
-TEST(NetworkQualityObservationBufferTest,
-     RestGetPercentileForEachRemoteHostComputation) {
-  std::map<std::string, std::string> variation_params;
-  NetworkQualityEstimatorParams params(variation_params);
-  base::SimpleTestTickClock tick_clock;
-  tick_clock.Advance(base::TimeDelta::FromMinutes(1));
-  ObservationBuffer buffer(&params, &tick_clock, 0.5, 1.0);
-  base::TimeTicks now = tick_clock.NowTicks();
-  const size_t num_hosts = 3;
-
-  // For three different remote hosts, add observations such that the 50
-  // percentiles are different.
-  for (unsigned int host_index = 1; host_index <= num_hosts; host_index++) {
-    // Add |20 * host_index + 1| observations for host |host_index|.
-    for (unsigned int observation_value = 90 * host_index;
-         observation_value <= 110 * host_index; observation_value++) {
-      buffer.AddObservation(Observation(observation_value, now, INT32_MIN,
-                                        NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP,
-                                        static_cast<uint64_t>(host_index)));
-    }
-  }
-  std::map<uint64_t, int32_t> host_keyed_percentiles;
-  std::map<uint64_t, size_t> host_keyed_counts;
-
-  // Test the computation of the median.
-  buffer.GetPercentileForEachHostWithCounts(
-      base::TimeTicks(), 50, base::nullopt, &host_keyed_percentiles,
-      &host_keyed_counts);
-
-  EXPECT_EQ(num_hosts, host_keyed_percentiles.size());
-  EXPECT_EQ(num_hosts, host_keyed_counts.size());
-
-  // The median must be equal to |100 * i| and the count must be equal to
-  // |20 * i + 1| for host |i|.
-  for (unsigned int host_index = 1; host_index <= num_hosts; host_index++) {
-    EXPECT_EQ(100u * host_index,
-              static_cast<uint32_t>(
-                  host_keyed_percentiles[static_cast<uint64_t>(host_index)]));
-    EXPECT_EQ(static_cast<size_t>(20 * host_index + 1),
-              host_keyed_counts[static_cast<uint64_t>(host_index)]);
-  }
-
-  // Test the computation of 0th percentile.
-  buffer.GetPercentileForEachHostWithCounts(base::TimeTicks(), 0, base::nullopt,
-                                            &host_keyed_percentiles,
-                                            &host_keyed_counts);
-
-  EXPECT_EQ(num_hosts, host_keyed_percentiles.size());
-  EXPECT_EQ(num_hosts, host_keyed_counts.size());
-
-  // The 0 percentile must be equal to |90 * i| and the count must be equal to
-  // |20 * i| for host |i|.
-  for (unsigned int host_index = 1; host_index <= num_hosts; host_index++) {
-    EXPECT_EQ(90u * host_index,
-              static_cast<uint32_t>(
-                  host_keyed_percentiles[static_cast<uint64_t>(host_index)]));
-    EXPECT_EQ(static_cast<size_t>(20 * host_index + 1),
-              host_keyed_counts[static_cast<uint64_t>(host_index)]);
-  }
-
-  // Test the computation of 100th percentile.
-  buffer.GetPercentileForEachHostWithCounts(
-      base::TimeTicks(), 100, base::nullopt, &host_keyed_percentiles,
-      &host_keyed_counts);
-
-  EXPECT_EQ(num_hosts, host_keyed_percentiles.size());
-  EXPECT_EQ(num_hosts, host_keyed_counts.size());
-
-  // The 0 percentile must be equal to |90 * i| and the count must be equal to
-  // |20 * i| for host |i|.
-  for (int host_index = 1; host_index <= 3; host_index++) {
-    EXPECT_EQ(110 * host_index,
-              host_keyed_percentiles[static_cast<uint64_t>(host_index)]);
-    EXPECT_EQ(static_cast<size_t>(20 * host_index + 1),
-              host_keyed_counts[static_cast<uint64_t>(host_index)]);
-  }
-}
 
 }  // namespace
 
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 4ebd7d6d..292ac7f 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -253,13 +253,6 @@
           FLAGS_quic_reloadable_flag_quic_deprecate_post_process_after_data,
           true)
 
-// If true, QuicSpdyClientSessionBase::OnPromiseHeaderList() will close the
-// connection if the stream id referenced indicates a static stream.
-QUIC_FLAG(
-    bool,
-    FLAGS_quic_reloadable_flag_quic_check_stream_nonstatic_on_promised_headers,
-    false)
-
 // When the STMP connection option is sent by the client, timestamps in the QUIC
 // ACK frame are sent and processed.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_send_timestamps, false)
@@ -270,3 +263,8 @@
 // When true, don't arm the path degrading alarm on the server side and stop
 // using HasUnackedPackets to decide when to arm it.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_path_degrading_alarm, false)
+
+// When true, QUIC server push uses a unidirectional stream.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_unidirectional_server_push_stream,
+          false)
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index ca102cdd..656d0e1b 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -4945,9 +4945,25 @@
      3},
 };
 
+namespace {
+namespace test_default {
+#include "net/http/transport_security_state_static_unittest_default.h"
+}  // namespace test_default
+}  // namespace
+
 class TLS13DowngradeMetricsTest
     : public SSLClientSocketTest,
-      public ::testing::WithParamInterface<TLS13DowngradeMetricsParams> {};
+      public ::testing::WithParamInterface<TLS13DowngradeMetricsParams> {
+ public:
+  TLS13DowngradeMetricsTest() {
+    // Switch the static preload list, so the tests using mail.google.com below
+    // do not trip the usual pins.
+    SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource);
+  }
+  ~TLS13DowngradeMetricsTest() {
+    SetTransportSecurityStateSourceForTesting(nullptr);
+  }
+};
 
 INSTANTIATE_TEST_CASE_P(/* no prefix */,
                         TLS13DowngradeMetricsTest,
diff --git a/net/third_party/quic/core/http/quic_spdy_client_session_base.cc b/net/third_party/quic/core/http/quic_spdy_client_session_base.cc
index 3e11c67..dd0502b 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_session_base.cc
+++ b/net/third_party/quic/core/http/quic_spdy_client_session_base.cc
@@ -6,7 +6,6 @@
 
 #include "net/third_party/quic/core/http/quic_client_promised_info.h"
 #include "net/third_party/quic/core/http/spdy_utils.h"
-#include "net/third_party/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
@@ -61,15 +60,11 @@
     QuicStreamId promised_stream_id,
     size_t frame_len,
     const QuicHeaderList& header_list) {
-  if (GetQuicReloadableFlag(quic_check_stream_nonstatic_on_promised_headers)) {
-    QUIC_FLAG_COUNT(
-        quic_reloadable_flag_quic_check_stream_nonstatic_on_promised_headers);
-    if (QuicContainsKey(static_streams(), stream_id)) {
-      connection()->CloseConnection(
-          QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
-          ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-      return;
-    }
+  if (QuicContainsKey(static_streams(), stream_id)) {
+    connection()->CloseConnection(
+        QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return;
   }
   if (promised_stream_id != kInvalidStreamId &&
       promised_stream_id <= largest_promised_stream_id_) {
diff --git a/net/third_party/quic/core/http/quic_spdy_client_session_test.cc b/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
index dcb4567..6c27b55 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
@@ -326,8 +326,6 @@
 TEST_P(QuicSpdyClientSessionTest, OnPromiseHeaderListWithStaticStream) {
   // Test situation where OnPromiseHeaderList is called by stream with static
   // id.
-  FLAGS_quic_reloadable_flag_quic_check_stream_nonstatic_on_promised_headers =
-      true;
   CompleteCryptoHandshake();
 
   QuicHeaderList trailers;
diff --git a/net/third_party/quic/core/http/quic_spdy_session.h b/net/third_party/quic/core/http/quic_spdy_session.h
index 1bea32e9..83762be5 100644
--- a/net/third_party/quic/core/http/quic_spdy_session.h
+++ b/net/third_party/quic/core/http/quic_spdy_session.h
@@ -139,8 +139,8 @@
   // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to
   // make sure that all data streams are QuicSpdyStreams.
   QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override = 0;
-  QuicSpdyStream* CreateOutgoingBidirectionalStream() override = 0;
-  QuicSpdyStream* CreateOutgoingUnidirectionalStream() override = 0;
+  virtual QuicSpdyStream* CreateOutgoingBidirectionalStream() = 0;
+  virtual QuicSpdyStream* CreateOutgoingUnidirectionalStream() = 0;
 
   QuicSpdyStream* GetSpdyDataStream(const QuicStreamId stream_id);
 
@@ -148,6 +148,10 @@
   virtual bool ShouldCreateIncomingStream(QuicStreamId id) = 0;
 
   // If an outgoing stream can be created, return true.
+  // TODO(fayang): In IETF QUIC, there are different stream limits on
+  // unidirectional v.s. bidirectional outgoing streams. Need to split this
+  // method to ShouldCreateOutgoingUnidirectionalStream and
+  // ShouldCreateOutgoingBidirectionalStream.
   virtual bool ShouldCreateOutgoingStream() = 0;
 
   // This was formerly QuicHeadersStream::WriteHeaders.  Needs to be
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc
index b25f0923..3d39eb8 100644
--- a/net/third_party/quic/core/quic_connection.cc
+++ b/net/third_party/quic/core/quic_connection.cc
@@ -450,6 +450,7 @@
       config.HasClientSentConnectionOption(kSTMP, perspective_)) {
     QUIC_FLAG_COUNT(quic_reloadable_flag_quic_send_timestamps);
     framer_.set_process_timestamps(true);
+    received_packet_manager_.set_save_timestamps(true);
   }
 }
 
diff --git a/net/third_party/quic/core/quic_packet_generator_test.cc b/net/third_party/quic/core/quic_packet_generator_test.cc
index cd523e9f..7a6960de 100644
--- a/net/third_party/quic/core/quic_packet_generator_test.cc
+++ b/net/third_party/quic/core/quic_packet_generator_test.cc
@@ -27,7 +27,6 @@
 #include "net/third_party/quic/test_tools/simple_data_producer.h"
 #include "net/third_party/quic/test_tools/simple_quic_framer.h"
 
-using std::string;
 using testing::_;
 using testing::InSequence;
 using testing::Return;
@@ -1097,7 +1096,7 @@
   frame->error_code = QUIC_PACKET_WRITE_ERROR;
   char buf[2000] = {};
   QuicStringPiece error_details(buf, 2000);
-  frame->error_details = string(error_details);
+  frame->error_details = QuicString(error_details);
   generator_.AddControlFrame(QuicFrame(frame));
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
diff --git a/net/third_party/quic/core/quic_received_packet_manager.cc b/net/third_party/quic/core/quic_received_packet_manager.cc
index ed280968..53f1043 100644
--- a/net/third_party/quic/core/quic_received_packet_manager.cc
+++ b/net/third_party/quic/core/quic_received_packet_manager.cc
@@ -29,6 +29,7 @@
       ack_frame_updated_(false),
       max_ack_ranges_(0),
       time_largest_observed_(QuicTime::Zero()),
+      save_timestamps_(false),
       stats_(stats) {}
 
 QuicReceivedPacketManager::~QuicReceivedPacketManager() {}
@@ -60,8 +61,19 @@
   }
   ack_frame_.packets.Add(packet_number);
 
-  ack_frame_.received_packet_times.push_back(
-      std::make_pair(packet_number, receipt_time));
+  if (save_timestamps_) {
+    // The timestamp format only handles packets in time order.
+    if (!ack_frame_.received_packet_times.empty() &&
+        ack_frame_.received_packet_times.back().second > receipt_time) {
+      LOG(WARNING)
+          << "Receive time went backwards from: "
+          << ack_frame_.received_packet_times.back().second.ToDebuggingValue()
+          << " to " << receipt_time.ToDebuggingValue();
+    } else {
+      ack_frame_.received_packet_times.push_back(
+          std::make_pair(packet_number, receipt_time));
+    }
+  }
 }
 
 bool QuicReceivedPacketManager::IsMissing(QuicPacketNumber packet_number) {
diff --git a/net/third_party/quic/core/quic_received_packet_manager.h b/net/third_party/quic/core/quic_received_packet_manager.h
index 4438194..21fe690f 100644
--- a/net/third_party/quic/core/quic_received_packet_manager.h
+++ b/net/third_party/quic/core/quic_received_packet_manager.h
@@ -72,6 +72,10 @@
     max_ack_ranges_ = max_ack_ranges;
   }
 
+  void set_save_timestamps(bool save_timestamps) {
+    save_timestamps_ = save_timestamps;
+  }
+
  private:
   friend class test::QuicConnectionPeer;
 
@@ -94,6 +98,9 @@
   // Needed for calculating ack_delay_time.
   QuicTime time_largest_observed_;
 
+  // If true, save timestamps in the ack_frame_.
+  bool save_timestamps_;
+
   QuicConnectionStats* stats_;
 };
 
diff --git a/net/third_party/quic/core/quic_received_packet_manager_test.cc b/net/third_party/quic/core/quic_received_packet_manager_test.cc
index 3191cd3..cfa42ff 100644
--- a/net/third_party/quic/core/quic_received_packet_manager_test.cc
+++ b/net/third_party/quic/core/quic_received_packet_manager_test.cc
@@ -38,7 +38,9 @@
 
 class QuicReceivedPacketManagerTest : public QuicTestWithParam<TestParams> {
  protected:
-  QuicReceivedPacketManagerTest() : received_manager_(&stats_) {}
+  QuicReceivedPacketManagerTest() : received_manager_(&stats_) {
+    received_manager_.set_save_timestamps(true);
+  }
 
   void RecordPacketReceipt(QuicPacketNumber packet_number) {
     RecordPacketReceipt(packet_number, QuicTime::Zero());
@@ -141,6 +143,18 @@
   }
 }
 
+TEST_P(QuicReceivedPacketManagerTest, IgnoreOutOfOrderTimestamps) {
+  EXPECT_FALSE(received_manager_.ack_frame_updated());
+  RecordPacketReceipt(1, QuicTime::Zero());
+  EXPECT_TRUE(received_manager_.ack_frame_updated());
+  EXPECT_EQ(1u, received_manager_.ack_frame().received_packet_times.size());
+  RecordPacketReceipt(2,
+                      QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1));
+  EXPECT_EQ(2u, received_manager_.ack_frame().received_packet_times.size());
+  RecordPacketReceipt(3, QuicTime::Zero());
+  EXPECT_EQ(2u, received_manager_.ack_frame().received_packet_times.size());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_session.h b/net/third_party/quic/core/quic_session.h
index da9bd76..1b1d7d5 100644
--- a/net/third_party/quic/core/quic_session.h
+++ b/net/third_party/quic/core/quic_session.h
@@ -358,16 +358,6 @@
   // Returns nullptr and does error handling if the stream can not be created.
   virtual QuicStream* CreateIncomingStream(QuicStreamId id) = 0;
 
-  // Create a new stream to handle a locally-initiated bidirectional stream.
-  // Caller does not own the returned stream.
-  // Returns nullptr if max streams have already been opened.
-  virtual QuicStream* CreateOutgoingBidirectionalStream() = 0;
-
-  // Create a new stream to handle a locally-initiated write unidirectional
-  // stream. Caller does not own the returned stream. Returns nullptr if max
-  // streams have already been opened.
-  virtual QuicStream* CreateOutgoingUnidirectionalStream() = 0;
-
   // Return the reserved crypto stream.
   virtual QuicCryptoStream* GetMutableCryptoStream() = 0;
 
diff --git a/net/third_party/quic/core/quic_session_test.cc b/net/third_party/quic/core/quic_session_test.cc
index d0d61f7..f983472 100644
--- a/net/third_party/quic/core/quic_session_test.cc
+++ b/net/third_party/quic/core/quic_session_test.cc
@@ -144,14 +144,14 @@
     return &crypto_stream_;
   }
 
-  TestStream* CreateOutgoingBidirectionalStream() override {
+  TestStream* CreateOutgoingBidirectionalStream() {
     TestStream* stream =
         new TestStream(GetNextOutgoingStreamId(), this, BIDIRECTIONAL);
     ActivateStream(QuicWrapUnique(stream));
     return stream;
   }
 
-  TestStream* CreateOutgoingUnidirectionalStream() override {
+  TestStream* CreateOutgoingUnidirectionalStream() {
     TestStream* stream =
         new TestStream(GetNextOutgoingStreamId(), this, WRITE_UNIDIRECTIONAL);
     ActivateStream(QuicWrapUnique(stream));
diff --git a/net/third_party/quic/core/quic_stream.h b/net/third_party/quic/core/quic_stream.h
index c132ac07..2ff6d68 100644
--- a/net/third_party/quic/core/quic_stream.h
+++ b/net/third_party/quic/core/quic_stream.h
@@ -274,6 +274,8 @@
                                 QuicByteCount data_length,
                                 bool fin) const;
 
+  StreamType type() const { return type_; }
+
  protected:
   // Sends as many bytes in the first |count| buffers of |iov| to the connection
   // as the connection will consume. If FIN is consumed, the write side is
diff --git a/net/third_party/quic/platform/api/quic_url.cc b/net/third_party/quic/platform/api/quic_url.cc
deleted file mode 100644
index 31953fd4..0000000
--- a/net/third_party/quic/platform/api/quic_url.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/third_party/quic/platform/api/quic_url.h"
-#include "net/third_party/quic/platform/api/quic_string.h"
-
-namespace quic {
-
-QuicUrl::QuicUrl(QuicStringPiece url) : impl_(url) {}
-
-QuicUrl::QuicUrl(QuicStringPiece url, QuicStringPiece default_scheme)
-    : impl_(url, default_scheme) {}
-
-QuicUrl::QuicUrl(const QuicUrl& url) : impl_(url.impl()) {}
-
-bool QuicUrl::IsValid() const {
-  return impl_.IsValid();
-}
-
-QuicString QuicUrl::ToString() const {
-  return impl_.ToStringIfValid();
-}
-
-QuicString QuicUrl::HostPort() const {
-  return impl_.HostPort();
-}
-
-QuicString QuicUrl::PathParamsQuery() const {
-  return impl_.PathParamsQuery();
-}
-
-QuicString QuicUrl::host() const {
-  return impl_.host();
-}
-
-QuicString QuicUrl::path() const {
-  return impl_.path();
-}
-
-QuicString QuicUrl::scheme() const {
-  return impl_.scheme();
-}
-
-uint16_t QuicUrl::port() const {
-  return impl_.port();
-}
-
-}  // namespace quic
diff --git a/net/third_party/quic/platform/api/quic_url.h b/net/third_party/quic/platform/api/quic_url.h
deleted file mode 100644
index 3725adb..0000000
--- a/net/third_party/quic/platform/api/quic_url.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_URL_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_URL_H_
-
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/platform/api/quic_string.h"
-#include "net/third_party/quic/platform/api/quic_string_piece.h"
-#include "net/third_party/quic/platform/impl/quic_url_impl.h"
-
-namespace quic {
-
-// QuicUrl stores a representation of a URL.
-class QUIC_EXPORT_PRIVATE QuicUrl {
- public:
-  // Constructs an empty QuicUrl.
-  QuicUrl() = default;
-
-  // Constructs a QuicUrl from the url string |url|.
-  // NOTE: If |url| doesn't have a scheme, it will have an empty scheme
-  // field. If that's not what you want, use the QuicUrlImpl(url,
-  // default_scheme) form below.
-  explicit QuicUrl(QuicStringPiece url);
-
-  // Constructs a QuicUrl from |url|, assuming that the scheme for the QuicUrl
-  // is |default_scheme| if there is no scheme specified in |url|.
-  QuicUrl(QuicStringPiece url, QuicStringPiece default_scheme);
-
-  QuicUrl(const QuicUrl& url);
-
-  // Returns false if any of these conditions occur:
-  // No scheme specified
-  // Host name too long (the maximum hostname length is platform-dependent)
-  // Invalid characters in host name, path or params
-  // Invalid port number (e.g. greater than 65535)
-  bool IsValid() const;
-
-  // PLEASE NOTE: ToString(), HostPort(), PathParamsQuery(), scheme(), host(),
-  // path() and port() functions should be only called on a valid QuicUrl.
-  // Return values are platform-dependent if called on a invalid QuicUrl.
-
-  // Returns full text of the QuicUrl.
-  QuicString ToString() const;
-
-  // Returns host:port.
-  // If the host is empty, it will return an empty string.
-  // If the host is an IPv6 address, it will be bracketed.
-  // If port is not present or is equal to default_port of scheme (e.g., port
-  // 80 for HTTP), it won't be returned.
-  QuicString HostPort() const;
-
-  // Returns a string assembles path, parameters and query.
-  QuicString PathParamsQuery() const;
-
-  QuicString scheme() const;
-  QuicString host() const;
-  QuicString path() const;
-  uint16_t port() const;
-
-  const QuicUrlImpl& impl() const { return impl_; }
-
- private:
-  QuicUrlImpl impl_;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_URL_H_
diff --git a/net/third_party/quic/platform/impl/quic_url_impl.cc b/net/third_party/quic/platform/impl/quic_url_impl.cc
deleted file mode 100644
index d9c1fd8d..0000000
--- a/net/third_party/quic/platform/impl/quic_url_impl.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/third_party/quic/platform/impl/quic_url_impl.h"
-
-#include "net/third_party/quic/platform/api/quic_text_utils.h"
-
-using std::string;
-
-namespace quic {
-
-QuicUrlImpl::QuicUrlImpl(QuicStringPiece url) : url_(url) {}
-
-QuicUrlImpl::QuicUrlImpl(QuicStringPiece url, QuicStringPiece default_scheme)
-    : url_(url) {
-  if (url_.has_scheme()) {
-    return;
-  }
-  string buffer = default_scheme.as_string() + "://" + url.as_string();
-  url_ = GURL(buffer);
-}
-
-QuicUrlImpl::QuicUrlImpl(const QuicUrlImpl& url) : url_(url.url()) {}
-
-string QuicUrlImpl::ToStringIfValid() const {
-  if (IsValid()) {
-    return url_.spec();
-  }
-  return "";
-}
-
-bool QuicUrlImpl::IsValid() const {
-  if (!url_.is_valid() || !url_.has_scheme()) {
-    return false;
-  }
-
-  if (url_.has_host() && url_.host().length() > kMaxHostNameLength) {
-    return false;
-  }
-
-  return true;
-}
-
-string QuicUrlImpl::HostPort() const {
-  if (!IsValid() || !url_.has_host()) {
-    return "";
-  }
-
-  string buffer = url_.host();
-  int port = url_.IntPort();
-  string scheme = url_.scheme();
-  if (port == url::PORT_UNSPECIFIED ||
-      (url_.IsStandard() &&
-       port == url::DefaultPortForScheme(scheme.c_str(), scheme.length()))) {
-    return buffer;
-  }
-  buffer = buffer + ":" + std::to_string(port);
-  return buffer;
-}
-
-string QuicUrlImpl::PathParamsQuery() const {
-  if (!IsValid() || !url_.has_path()) {
-    return "/";
-  }
-
-  return url_.PathForRequest();
-}
-
-string QuicUrlImpl::scheme() const {
-  if (!IsValid()) {
-    return "";
-  }
-
-  return url_.scheme();
-}
-
-string QuicUrlImpl::host() const {
-  if (!IsValid()) {
-    return "";
-  }
-
-  return url_.HostNoBrackets();
-}
-
-string QuicUrlImpl::path() const {
-  if (!IsValid()) {
-    return "";
-  }
-
-  return url_.path();
-}
-
-uint16_t QuicUrlImpl::port() const {
-  if (!IsValid()) {
-    return 0;
-  }
-
-  int port = url_.EffectiveIntPort();
-  if (port == url::PORT_UNSPECIFIED) {
-    return 0;
-  }
-  return port;
-}
-
-}  // namespace quic
diff --git a/net/third_party/quic/platform/impl/quic_url_impl.h b/net/third_party/quic/platform/impl/quic_url_impl.h
deleted file mode 100644
index d435dd56..0000000
--- a/net/third_party/quic/platform/impl/quic_url_impl.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_URL_IMPL_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_URL_IMPL_H_
-
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/platform/api/quic_string_piece.h"
-#include "url/gurl.h"
-
-namespace quic {
-
-class QUIC_EXPORT_PRIVATE QuicUrlImpl {
- public:
-  static const size_t kMaxHostNameLength = 256;
-
-  // Constructs an empty QuicUrl.
-  QuicUrlImpl() = default;
-
-  // Constructs a QuicUrlImpl from the url string |url|.
-  // NOTE: If |url| doesn't have a scheme, it will have an empty scheme
-  // field. If that's not what you want, use the QuicUrlImpl(url,
-  // default_scheme) form below.
-  explicit QuicUrlImpl(QuicStringPiece url);
-
-  // Constructs a QuicUrlImpl from |url|, assuming that the scheme for the URL
-  // is |default_scheme| if there is no scheme specified in |url|.
-  QuicUrlImpl(QuicStringPiece url, QuicStringPiece default_scheme);
-
-  QuicUrlImpl(const QuicUrlImpl& url);
-
-  // Returns false if any of these conditions occur:
-  // No scheme specified
-  // Host name too long (> 256 bytes)
-  // Invalid characters in host name, path or params
-  // Invalid port number (e.g. greater than 65535)
-  bool IsValid() const;
-
-  // Returns full text of the QuicUrlImpl if it is valid. Return empty string
-  // otherwise.
-  std::string ToStringIfValid() const;
-
-  // Returns host:port.
-  // If the host is empty, it will return an empty std::string.
-  // If the host is an IPv6 address, it will be bracketed.
-  // If port is not present or is equal to default_port of scheme (e.g., port
-  // 80 for HTTP), it won't be returned.
-  std::string HostPort() const;
-
-  // Returns a string assembles path, parameters and query.
-  std::string PathParamsQuery() const;
-
-  std::string scheme() const;
-  std::string host() const;
-  std::string path() const;
-  uint16_t port() const;
-
-  const GURL& url() const { return url_; }
-
- private:
-  GURL url_;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_URL_IMPL_H_
diff --git a/net/third_party/quic/quartc/quartc_session.cc b/net/third_party/quic/quartc/quartc_session.cc
index c08e0c9..5e0ff17 100644
--- a/net/third_party/quic/quartc/quartc_session.cc
+++ b/net/third_party/quic/quartc/quartc_session.cc
@@ -177,11 +177,6 @@
                                              QuicStream::kDefaultPriority));
 }
 
-QuartcStream* QuartcSession::CreateOutgoingUnidirectionalStream() {
-  DCHECK(false);
-  return nullptr;
-}
-
 void QuartcSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
   QuicSession::OnCryptoHandshakeEvent(event);
   if (event == HANDSHAKE_CONFIRMED) {
diff --git a/net/third_party/quic/quartc/quartc_session.h b/net/third_party/quic/quartc/quartc_session.h
index 742d9da..204ad07d 100644
--- a/net/third_party/quic/quartc/quartc_session.h
+++ b/net/third_party/quic/quartc/quartc_session.h
@@ -51,9 +51,7 @@
 
   const QuicCryptoStream* GetCryptoStream() const override;
 
-  QuartcStream* CreateOutgoingBidirectionalStream() override;
-
-  QuartcStream* CreateOutgoingUnidirectionalStream() override;
+  QuartcStream* CreateOutgoingBidirectionalStream();
 
   void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
 
diff --git a/net/third_party/quic/quartc/quartc_stream_test.cc b/net/third_party/quic/quartc/quartc_stream_test.cc
index f2ff1c4..45258606 100644
--- a/net/third_party/quic/quartc/quartc_stream_test.cc
+++ b/net/third_party/quic/quartc/quartc_stream_test.cc
@@ -62,12 +62,6 @@
     return nullptr;
   }
 
-  QuartcStream* CreateOutgoingBidirectionalStream() override { return nullptr; }
-
-  QuartcStream* CreateOutgoingUnidirectionalStream() override {
-    return nullptr;
-  }
-
   const QuicCryptoStream* GetCryptoStream() const override { return nullptr; }
   QuicCryptoStream* GetMutableCryptoStream() override { return nullptr; }
 
diff --git a/net/third_party/quic/test_tools/quic_test_client.cc b/net/third_party/quic/test_tools/quic_test_client.cc
index 10bb8ea2..c6c4e03f 100644
--- a/net/third_party/quic/test_tools/quic_test_client.cc
+++ b/net/third_party/quic/test_tools/quic_test_client.cc
@@ -20,14 +20,13 @@
 #include "net/third_party/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quic/platform/api/quic_stack_trace.h"
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
-#include "net/third_party/quic/platform/api/quic_url.h"
 #include "net/third_party/quic/test_tools/crypto_test_utils.h"
 #include "net/third_party/quic/test_tools/quic_client_peer.h"
 #include "net/third_party/quic/test_tools/quic_connection_peer.h"
 #include "net/third_party/quic/test_tools/quic_spdy_session_peer.h"
 #include "net/third_party/quic/test_tools/quic_stream_peer.h"
 #include "net/third_party/quic/test_tools/quic_test_utils.h"
-
+#include "net/third_party/quic/tools/quic_url.h"
 
 namespace quic {
 namespace test {
diff --git a/net/third_party/quic/test_tools/quic_test_utils.h b/net/third_party/quic/test_tools/quic_test_utils.h
index 30a2d6c6..356f919 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.h
+++ b/net/third_party/quic/test_tools/quic_test_utils.h
@@ -558,8 +558,6 @@
                     const QuicString& error_details,
                     ConnectionCloseSource source));
   MOCK_METHOD1(CreateIncomingStream, QuicStream*(QuicStreamId id));
-  MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicStream*());
-  MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicStream*());
   MOCK_METHOD1(ShouldCreateIncomingStream2, bool(QuicStreamId id));
   MOCK_METHOD0(ShouldCreateOutgoingStream2, bool());
   MOCK_METHOD5(WritevData,
diff --git a/net/third_party/quic/test_tools/simulator/actor.cc b/net/third_party/quic/test_tools/simulator/actor.cc
index cd6e536..98aaca6 100644
--- a/net/third_party/quic/test_tools/simulator/actor.cc
+++ b/net/third_party/quic/test_tools/simulator/actor.cc
@@ -5,12 +5,10 @@
 #include "net/third_party/quic/test_tools/simulator/actor.h"
 #include "net/third_party/quic/test_tools/simulator/simulator.h"
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
-Actor::Actor(Simulator* simulator, string name)
+Actor::Actor(Simulator* simulator, QuicString name)
     : simulator_(simulator),
       clock_(simulator->GetClock()),
       name_(std::move(name)) {
diff --git a/net/third_party/quic/test_tools/simulator/actor.h b/net/third_party/quic/test_tools/simulator/actor.h
index 514e8f0..17b90d0 100644
--- a/net/third_party/quic/test_tools/simulator/actor.h
+++ b/net/third_party/quic/test_tools/simulator/actor.h
@@ -29,7 +29,7 @@
 //    will not be called again unless Schedule() is called.
 class Actor {
  public:
-  Actor(Simulator* simulator, std::string name);
+  Actor(Simulator* simulator, QuicString name);
   virtual ~Actor();
 
   // Trigger all the events the actor can potentially handle at this point.
@@ -37,7 +37,7 @@
   // to schedule the next call manually.
   virtual void Act() = 0;
 
-  inline std::string name() const { return name_; }
+  inline QuicString name() const { return name_; }
   inline Simulator* simulator() const { return simulator_; }
 
  protected:
@@ -49,7 +49,7 @@
 
   Simulator* simulator_;
   const QuicClock* clock_;
-  std::string name_;
+  QuicString name_;
 
  private:
   // Since the Actor object registers itself with a simulator using a pointer to
diff --git a/net/third_party/quic/test_tools/simulator/alarm_factory.cc b/net/third_party/quic/test_tools/simulator/alarm_factory.cc
index bb9921e1..04ea6fb0 100644
--- a/net/third_party/quic/test_tools/simulator/alarm_factory.cc
+++ b/net/third_party/quic/test_tools/simulator/alarm_factory.cc
@@ -6,8 +6,6 @@
 #include "net/third_party/quic/core/quic_alarm.h"
 #include "net/third_party/quic/platform/api/quic_str_cat.h"
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
@@ -16,7 +14,7 @@
 class Alarm : public QuicAlarm {
  public:
   Alarm(Simulator* simulator,
-        string name,
+        QuicString name,
         QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
       : QuicAlarm(std::move(delegate)), adapter_(simulator, name, this) {}
   ~Alarm() override {}
@@ -34,7 +32,7 @@
   // interfaces.
   class Adapter : public Actor {
    public:
-    Adapter(Simulator* simulator, string name, Alarm* parent)
+    Adapter(Simulator* simulator, QuicString name, Alarm* parent)
         : Actor(simulator, name), parent_(parent) {}
     ~Adapter() override {}
 
@@ -52,12 +50,12 @@
   Adapter adapter_;
 };
 
-AlarmFactory::AlarmFactory(Simulator* simulator, string name)
+AlarmFactory::AlarmFactory(Simulator* simulator, QuicString name)
     : simulator_(simulator), name_(std::move(name)), counter_(0) {}
 
 AlarmFactory::~AlarmFactory() {}
 
-string AlarmFactory::GetNewAlarmName() {
+QuicString AlarmFactory::GetNewAlarmName() {
   ++counter_;
   return QuicStringPrintf("%s (alarm %i)", name_.c_str(), counter_);
 }
diff --git a/net/third_party/quic/test_tools/simulator/alarm_factory.h b/net/third_party/quic/test_tools/simulator/alarm_factory.h
index 33d7e099..8ec6d1e 100644
--- a/net/third_party/quic/test_tools/simulator/alarm_factory.h
+++ b/net/third_party/quic/test_tools/simulator/alarm_factory.h
@@ -14,7 +14,7 @@
 // AlarmFactory allows to schedule QuicAlarms using the simulation event queue.
 class AlarmFactory : public QuicAlarmFactory {
  public:
-  AlarmFactory(Simulator* simulator, std::string name);
+  AlarmFactory(Simulator* simulator, QuicString name);
   AlarmFactory(const AlarmFactory&) = delete;
   AlarmFactory& operator=(const AlarmFactory&) = delete;
   ~AlarmFactory() override;
@@ -26,10 +26,10 @@
 
  private:
   // Automatically generate a name for a new alarm.
-  std::string GetNewAlarmName();
+  QuicString GetNewAlarmName();
 
   Simulator* simulator_;
-  std::string name_;
+  QuicString name_;
   int counter_;
 };
 
diff --git a/net/third_party/quic/test_tools/simulator/link.cc b/net/third_party/quic/test_tools/simulator/link.cc
index 433583b..d33fd028 100644
--- a/net/third_party/quic/test_tools/simulator/link.cc
+++ b/net/third_party/quic/test_tools/simulator/link.cc
@@ -7,8 +7,6 @@
 #include "net/third_party/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quic/test_tools/simulator/simulator.h"
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
@@ -16,7 +14,7 @@
 const uint64_t kMaxRandomDelayUs = 10;
 
 OneWayLink::OneWayLink(Simulator* simulator,
-                       string name,
+                       QuicString name,
                        UnconstrainedPortInterface* sink,
                        QuicBandwidth bandwidth,
                        QuicTime::Delta propagation_delay)
@@ -87,7 +85,7 @@
 }
 
 SymmetricLink::SymmetricLink(Simulator* simulator,
-                             string name,
+                             QuicString name,
                              UnconstrainedPortInterface* sink_a,
                              UnconstrainedPortInterface* sink_b,
                              QuicBandwidth bandwidth,
diff --git a/net/third_party/quic/test_tools/simulator/link.h b/net/third_party/quic/test_tools/simulator/link.h
index 64dfabdc..3f097bd 100644
--- a/net/third_party/quic/test_tools/simulator/link.h
+++ b/net/third_party/quic/test_tools/simulator/link.h
@@ -20,7 +20,7 @@
 class OneWayLink : public Actor, public ConstrainedPortInterface {
  public:
   OneWayLink(Simulator* simulator,
-             std::string name,
+             QuicString name,
              UnconstrainedPortInterface* sink,
              QuicBandwidth bandwidth,
              QuicTime::Delta propagation_delay);
@@ -66,7 +66,7 @@
 class SymmetricLink {
  public:
   SymmetricLink(Simulator* simulator,
-                std::string name,
+                QuicString name,
                 UnconstrainedPortInterface* sink_a,
                 UnconstrainedPortInterface* sink_b,
                 QuicBandwidth bandwidth,
diff --git a/net/third_party/quic/test_tools/simulator/packet_filter.cc b/net/third_party/quic/test_tools/simulator/packet_filter.cc
index 1e2e18b..069d5c7 100644
--- a/net/third_party/quic/test_tools/simulator/packet_filter.cc
+++ b/net/third_party/quic/test_tools/simulator/packet_filter.cc
@@ -4,12 +4,12 @@
 
 #include "net/third_party/quic/test_tools/simulator/packet_filter.h"
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
-PacketFilter::PacketFilter(Simulator* simulator, string name, Endpoint* input)
+PacketFilter::PacketFilter(Simulator* simulator,
+                           QuicString name,
+                           Endpoint* input)
     : Endpoint(simulator, name), input_(input) {
   input_->SetTxPort(this);
 }
diff --git a/net/third_party/quic/test_tools/simulator/packet_filter.h b/net/third_party/quic/test_tools/simulator/packet_filter.h
index e769db1..dc31ce8 100644
--- a/net/third_party/quic/test_tools/simulator/packet_filter.h
+++ b/net/third_party/quic/test_tools/simulator/packet_filter.h
@@ -39,7 +39,7 @@
  public:
   // Initialize the filter by wrapping around |input|.  Does not take the
   // ownership of |input|.
-  PacketFilter(Simulator* simulator, std::string name, Endpoint* input);
+  PacketFilter(Simulator* simulator, QuicString name, Endpoint* input);
   PacketFilter(const PacketFilter&) = delete;
   PacketFilter& operator=(const PacketFilter&) = delete;
   ~PacketFilter() override;
diff --git a/net/third_party/quic/test_tools/simulator/port.cc b/net/third_party/quic/test_tools/simulator/port.cc
index 5d5de94..b90f80b 100644
--- a/net/third_party/quic/test_tools/simulator/port.cc
+++ b/net/third_party/quic/test_tools/simulator/port.cc
@@ -4,8 +4,6 @@
 
 #include "net/third_party/quic/test_tools/simulator/port.h"
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
@@ -16,7 +14,7 @@
 
 Packet::Packet(const Packet& packet) = default;
 
-Endpoint::Endpoint(Simulator* simulator, string name)
+Endpoint::Endpoint(Simulator* simulator, QuicString name)
     : Actor(simulator, name) {}
 
 }  // namespace simulator
diff --git a/net/third_party/quic/test_tools/simulator/port.h b/net/third_party/quic/test_tools/simulator/port.h
index c98cb6b8..80bbc34 100644
--- a/net/third_party/quic/test_tools/simulator/port.h
+++ b/net/third_party/quic/test_tools/simulator/port.h
@@ -19,11 +19,11 @@
   ~Packet();
   Packet(const Packet& packet);
 
-  std::string source;
-  std::string destination;
+  QuicString source;
+  QuicString destination;
   QuicTime tx_timestamp;
 
-  std::string contents;
+  QuicString contents;
   QuicByteCount size;
 };
 
@@ -57,7 +57,7 @@
   virtual void SetTxPort(ConstrainedPortInterface* port) = 0;
 
  protected:
-  Endpoint(Simulator* simulator, std::string name);
+  Endpoint(Simulator* simulator, QuicString name);
 };
 
 }  // namespace simulator
diff --git a/net/third_party/quic/test_tools/simulator/queue.cc b/net/third_party/quic/test_tools/simulator/queue.cc
index 967c3b0..c7a141d 100644
--- a/net/third_party/quic/test_tools/simulator/queue.cc
+++ b/net/third_party/quic/test_tools/simulator/queue.cc
@@ -7,14 +7,12 @@
 #include "net/third_party/quic/platform/api/quic_logging.h"
 #include "net/third_party/quic/test_tools/simulator/simulator.h"
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
 Queue::ListenerInterface::~ListenerInterface() {}
 
-Queue::Queue(Simulator* simulator, string name, QuicByteCount capacity)
+Queue::Queue(Simulator* simulator, QuicString name, QuicByteCount capacity)
     : Actor(simulator, name),
       capacity_(capacity),
       bytes_queued_(0),
diff --git a/net/third_party/quic/test_tools/simulator/queue.h b/net/third_party/quic/test_tools/simulator/queue.h
index e9641d30..5de086e 100644
--- a/net/third_party/quic/test_tools/simulator/queue.h
+++ b/net/third_party/quic/test_tools/simulator/queue.h
@@ -23,7 +23,7 @@
     virtual void OnPacketDequeued() = 0;
   };
 
-  Queue(Simulator* simulator, std::string name, QuicByteCount capacity);
+  Queue(Simulator* simulator, QuicString name, QuicByteCount capacity);
   Queue(const Queue&) = delete;
   Queue& operator=(const Queue&) = delete;
   ~Queue() override;
diff --git a/net/third_party/quic/test_tools/simulator/quic_endpoint.cc b/net/third_party/quic/test_tools/simulator/quic_endpoint.cc
index af0d65fb..0edddf2 100644
--- a/net/third_party/quic/test_tools/simulator/quic_endpoint.cc
+++ b/net/third_party/quic/test_tools/simulator/quic_endpoint.cc
@@ -16,8 +16,6 @@
 #include "net/third_party/quic/test_tools/quic_test_utils.h"
 #include "net/third_party/quic/test_tools/simulator/simulator.h"
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
@@ -26,8 +24,8 @@
 const char kStreamDataContents = 'Q';
 
 // Takes a SHA-1 hash of the name and converts it into five 32-bit integers.
-static std::vector<uint32_t> HashNameIntoFive32BitIntegers(string name) {
-  const string hash = test::Sha1Hash(name);
+static std::vector<uint32_t> HashNameIntoFive32BitIntegers(QuicString name) {
+  const QuicString hash = test::Sha1Hash(name);
 
   std::vector<uint32_t> output;
   uint32_t current_number = 0;
@@ -42,14 +40,14 @@
   return output;
 }
 
-QuicSocketAddress GetAddressFromName(string name) {
+QuicSocketAddress GetAddressFromName(QuicString name) {
   const std::vector<uint32_t> hash = HashNameIntoFive32BitIntegers(name);
 
   // Generate a random port between 1025 and 65535.
   const uint16_t port = 1025 + hash[0] % (65535 - 1025 + 1);
 
   // Generate a random 10.x.x.x address, where x is between 1 and 254.
-  string ip_address{"\xa\0\0\0", 4};
+  QuicString ip_address{"\xa\0\0\0", 4};
   for (size_t i = 1; i < 4; i++) {
     ip_address[i] = 1 + hash[i] % 254;
   }
@@ -59,8 +57,8 @@
 }
 
 QuicEndpoint::QuicEndpoint(Simulator* simulator,
-                           string name,
-                           string peer_name,
+                           QuicString name,
+                           QuicString peer_name,
                            Perspective perspective,
                            QuicConnectionId connection_id)
     : Endpoint(simulator, name),
@@ -106,7 +104,7 @@
   // primarily because
   //  - this enables pacing, and
   //  - this sets the non-handshake timeouts.
-  std::string error;
+  QuicString error;
   CryptoHandshakeMessage peer_hello;
   peer_hello.SetValue(kICSL,
                       static_cast<uint32_t>(kMaximumIdleTimeoutSecs - 1));
@@ -125,7 +123,7 @@
     const char* perspective_prefix =
         connection_.perspective() == Perspective::IS_CLIENT ? "C" : "S";
 
-    string identifier =
+    QuicString identifier =
         QuicStrCat(perspective_prefix, connection_.connection_id());
     QuicRecordTestOutput(identifier,
                          trace_visitor_->trace()->SerializeAsString());
@@ -305,7 +303,7 @@
   packet->destination = endpoint_->peer_name_;
   packet->tx_timestamp = endpoint_->clock_->Now();
 
-  packet->contents = string(buffer, buf_len);
+  packet->contents = QuicString(buffer, buf_len);
   packet->size = buf_len;
 
   endpoint_->nic_tx_queue_.AcceptPacket(std::move(packet));
@@ -380,7 +378,7 @@
 }
 
 QuicEndpointMultiplexer::QuicEndpointMultiplexer(
-    string name,
+    QuicString name,
     std::initializer_list<QuicEndpoint*> endpoints)
     : Endpoint((*endpoints.begin())->simulator(), name) {
   for (QuicEndpoint* endpoint : endpoints) {
diff --git a/net/third_party/quic/test_tools/simulator/quic_endpoint.h b/net/third_party/quic/test_tools/simulator/quic_endpoint.h
index 5daacf80..88f4c45 100644
--- a/net/third_party/quic/test_tools/simulator/quic_endpoint.h
+++ b/net/third_party/quic/test_tools/simulator/quic_endpoint.h
@@ -26,7 +26,7 @@
 
 // Generate a random local network host-port tuple based on the name of the
 // endpoint.
-QuicSocketAddress GetAddressFromName(std::string name);
+QuicSocketAddress GetAddressFromName(QuicString name);
 
 // A QUIC connection endpoint.  Wraps around QuicConnection.  In order to
 // initiate a transfer, the caller has to call AddBytesToTransfer().  The data
@@ -40,8 +40,8 @@
                      public SessionNotifierInterface {
  public:
   QuicEndpoint(Simulator* simulator,
-               std::string name,
-               std::string peer_name,
+               QuicString name,
+               QuicString peer_name,
                Perspective perspective,
                QuicConnectionId connection_id);
   ~QuicEndpoint() override;
@@ -91,7 +91,7 @@
   void OnGoAway(const QuicGoAwayFrame& frame) override {}
   void OnMessageReceived(QuicStringPiece message) override {}
   void OnConnectionClosed(QuicErrorCode error,
-                          const std::string& error_details,
+                          const QuicString& error_details,
                           ConnectionCloseSource source) override {}
   void OnWriteBlocked() override {}
   void OnSuccessfulVersionNegotiation(
@@ -163,7 +163,7 @@
   // write-blocked.
   void WriteStreamData();
 
-  std::string peer_name_;
+  QuicString peer_name_;
 
   Writer writer_;
   DataProducer producer_;
@@ -197,7 +197,7 @@
 class QuicEndpointMultiplexer : public Endpoint,
                                 public UnconstrainedPortInterface {
  public:
-  QuicEndpointMultiplexer(std::string name,
+  QuicEndpointMultiplexer(QuicString name,
                           std::initializer_list<QuicEndpoint*> endpoints);
   ~QuicEndpointMultiplexer() override;
 
@@ -212,7 +212,7 @@
   void Act() override {}
 
  private:
-  QuicUnorderedMap<std::string, QuicEndpoint*> mapping_;
+  QuicUnorderedMap<QuicString, QuicEndpoint*> mapping_;
 };
 
 }  // namespace simulator
diff --git a/net/third_party/quic/test_tools/simulator/simulator.h b/net/third_party/quic/test_tools/simulator/simulator.h
index a086eb2..74ed03a 100644
--- a/net/third_party/quic/test_tools/simulator/simulator.h
+++ b/net/third_party/quic/test_tools/simulator/simulator.h
@@ -126,7 +126,7 @@
   // For each actor, maintain the time it is scheduled at.  The value for
   // unscheduled actors is QuicTime::Infinite().
   QuicUnorderedMap<Actor*, QuicTime> scheduled_times_;
-  QuicUnorderedSet<std::string> actor_names_;
+  QuicUnorderedSet<QuicString> actor_names_;
 };
 
 template <class TerminationPredicate>
diff --git a/net/third_party/quic/test_tools/simulator/simulator_test.cc b/net/third_party/quic/test_tools/simulator/simulator_test.cc
index 19c80a7..fdda91c 100644
--- a/net/third_party/quic/test_tools/simulator/simulator_test.cc
+++ b/net/third_party/quic/test_tools/simulator/simulator_test.cc
@@ -16,7 +16,6 @@
 #include "net/third_party/quic/test_tools/simulator/switch.h"
 #include "net/third_party/quic/test_tools/simulator/traffic_policer.h"
 
-using std::string;
 using testing::_;
 using testing::Return;
 using testing::StrictMock;
@@ -27,7 +26,7 @@
 // A simple counter that increments its value by 1 every specified period.
 class Counter : public Actor {
  public:
-  Counter(Simulator* simulator, string name, QuicTime::Delta period)
+  Counter(Simulator* simulator, QuicString name, QuicTime::Delta period)
       : Actor(simulator, name), value_(-1), period_(period) {
     Schedule(clock_->Now());
   }
@@ -87,7 +86,7 @@
     per_destination_packet_counter_.clear();
   }
 
-  QuicPacketCount CountPacketsForDestination(string destination) const {
+  QuicPacketCount CountPacketsForDestination(QuicString destination) const {
     auto result_it = per_destination_packet_counter_.find(destination);
     if (result_it == per_destination_packet_counter_.cend()) {
       return 0;
@@ -99,7 +98,7 @@
   QuicByteCount bytes_;
   QuicPacketCount packets_;
 
-  QuicUnorderedMap<string, QuicPacketCount> per_destination_packet_counter_;
+  QuicUnorderedMap<QuicString, QuicPacketCount> per_destination_packet_counter_;
 };
 
 // Sends the packet to the specified destination at the uplink rate.  Provides a
@@ -107,9 +106,9 @@
 class LinkSaturator : public Endpoint {
  public:
   LinkSaturator(Simulator* simulator,
-                string name,
+                QuicString name,
                 QuicByteCount packet_size,
-                string destination)
+                QuicString destination)
       : Endpoint(simulator, name),
         packet_size_(packet_size),
         destination_(std::move(destination)),
@@ -153,7 +152,7 @@
 
  private:
   QuicByteCount packet_size_;
-  string destination_;
+  QuicString destination_;
 
   ConstrainedPortInterface* tx_port_;
   CounterPort rx_port_;
@@ -425,7 +424,7 @@
 class AlarmToggler : public Actor {
  public:
   AlarmToggler(Simulator* simulator,
-               string name,
+               QuicString name,
                QuicAlarm* alarm,
                QuicTime::Delta interval)
       : Actor(simulator, name),
@@ -589,7 +588,7 @@
 
 class MockPacketFilter : public PacketFilter {
  public:
-  MockPacketFilter(Simulator* simulator, string name, Endpoint* endpoint)
+  MockPacketFilter(Simulator* simulator, QuicString name, Endpoint* endpoint)
       : PacketFilter(simulator, name, endpoint) {}
   MOCK_METHOD1(FilterPacket, bool(const Packet&));
 };
diff --git a/net/third_party/quic/test_tools/simulator/switch.cc b/net/third_party/quic/test_tools/simulator/switch.cc
index b8e5c0bc..a4464043 100644
--- a/net/third_party/quic/test_tools/simulator/switch.cc
+++ b/net/third_party/quic/test_tools/simulator/switch.cc
@@ -9,13 +9,11 @@
 #include "net/third_party/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quic/test_tools/simulator/switch.h"
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
 Switch::Switch(Simulator* simulator,
-               string name,
+               QuicString name,
                SwitchPortNumber port_count,
                QuicByteCount queue_capacity) {
   for (size_t port_number = 1; port_number <= port_count; port_number++) {
@@ -29,7 +27,7 @@
 Switch::~Switch() {}
 
 Switch::Port::Port(Simulator* simulator,
-                   string name,
+                   QuicString name,
                    Switch* parent,
                    SwitchPortNumber port_number,
                    QuicByteCount queue_capacity)
diff --git a/net/third_party/quic/test_tools/simulator/switch.h b/net/third_party/quic/test_tools/simulator/switch.h
index 0b272f3..b0164cae 100644
--- a/net/third_party/quic/test_tools/simulator/switch.h
+++ b/net/third_party/quic/test_tools/simulator/switch.h
@@ -20,7 +20,7 @@
 class Switch {
  public:
   Switch(Simulator* simulator,
-         std::string name,
+         QuicString name,
          SwitchPortNumber port_count,
          QuicByteCount queue_capacity);
   Switch(const Switch&) = delete;
@@ -42,7 +42,7 @@
   class Port : public Endpoint, public UnconstrainedPortInterface {
    public:
     Port(Simulator* simulator,
-         std::string name,
+         QuicString name,
          Switch* parent,
          SwitchPortNumber port_number,
          QuicByteCount queue_capacity);
@@ -80,7 +80,7 @@
   // This can not be a QuicDeque since pointers into this are
   // assumed to be stable.
   std::deque<Port> ports_;
-  QuicUnorderedMap<std::string, Port*> switching_table_;
+  QuicUnorderedMap<QuicString, Port*> switching_table_;
 };
 
 }  // namespace simulator
diff --git a/net/third_party/quic/test_tools/simulator/traffic_policer.cc b/net/third_party/quic/test_tools/simulator/traffic_policer.cc
index 3e62791..fb93329 100644
--- a/net/third_party/quic/test_tools/simulator/traffic_policer.cc
+++ b/net/third_party/quic/test_tools/simulator/traffic_policer.cc
@@ -6,13 +6,11 @@
 
 #include <algorithm>
 
-using std::string;
-
 namespace quic {
 namespace simulator {
 
 TrafficPolicer::TrafficPolicer(Simulator* simulator,
-                               string name,
+                               QuicString name,
                                QuicByteCount initial_bucket_size,
                                QuicByteCount max_bucket_size,
                                QuicBandwidth target_bandwidth,
diff --git a/net/third_party/quic/test_tools/simulator/traffic_policer.h b/net/third_party/quic/test_tools/simulator/traffic_policer.h
index fd38fcf..2ebbf4c 100644
--- a/net/third_party/quic/test_tools/simulator/traffic_policer.h
+++ b/net/third_party/quic/test_tools/simulator/traffic_policer.h
@@ -20,7 +20,7 @@
 class TrafficPolicer : public PacketFilter {
  public:
   TrafficPolicer(Simulator* simulator,
-                 std::string name,
+                 QuicString name,
                  QuicByteCount initial_bucket_size,
                  QuicByteCount max_bucket_size,
                  QuicBandwidth target_bandwidth,
@@ -45,7 +45,7 @@
   QuicTime last_refill_time_;
 
   // Maps each destination to the number of tokens it has left.
-  QuicUnorderedMap<std::string, QuicByteCount> token_buckets_;
+  QuicUnorderedMap<QuicString, QuicByteCount> token_buckets_;
 };
 
 }  // namespace simulator
diff --git a/net/third_party/quic/tools/quic_backend_response.h b/net/third_party/quic/tools/quic_backend_response.h
index 81698d0..743f436 100644
--- a/net/third_party/quic/tools/quic_backend_response.h
+++ b/net/third_party/quic/tools/quic_backend_response.h
@@ -6,7 +6,7 @@
 #define NET_THIRD_PARTY_QUIC_TOOLS_QUIC_BACKEND_RESPONSE_H_
 
 #include "net/third_party/quic/core/http/spdy_utils.h"
-#include "net/third_party/quic/platform/api/quic_url.h"
+#include "net/third_party/quic/tools/quic_url.h"
 
 namespace quic {
 
diff --git a/net/third_party/quic/tools/quic_client_base.h b/net/third_party/quic/tools/quic_client_base.h
index e722180..60dd7fad3 100644
--- a/net/third_party/quic/tools/quic_client_base.h
+++ b/net/third_party/quic/tools/quic_client_base.h
@@ -124,7 +124,7 @@
   // This should only be set before the initial Connect()
   void set_server_id(const QuicServerId& server_id) { server_id_ = server_id; }
 
-  void SetUserAgentID(const std::string& user_agent_id) {
+  void SetUserAgentID(const QuicString& user_agent_id) {
     crypto_config_.set_user_agent_id(user_agent_id);
   }
 
diff --git a/net/third_party/quic/tools/quic_client_bin.cc b/net/third_party/quic/tools/quic_client_bin.cc
index 7e17273..297a1e9 100644
--- a/net/third_party/quic/tools/quic_client_bin.cc
+++ b/net/third_party/quic/tools/quic_client_bin.cc
@@ -60,8 +60,8 @@
 #include "net/third_party/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
-#include "net/third_party/quic/platform/api/quic_url.h"
 #include "net/third_party/quic/tools/quic_client.h"
+#include "net/third_party/quic/tools/quic_url.h"
 #include "net/third_party/spdy/core/spdy_header_block.h"
 #include "net/tools/epoll_server/epoll_server.h"
 #include "net/tools/quic/synchronous_host_resolver.h"
diff --git a/net/third_party/quic/tools/quic_client_epoll_network_helper.cc b/net/third_party/quic/tools/quic_client_epoll_network_helper.cc
index 784282f..dfdf2fc7 100644
--- a/net/third_party/quic/tools/quic_client_epoll_network_helper.cc
+++ b/net/third_party/quic/tools/quic_client_epoll_network_helper.cc
@@ -31,7 +31,6 @@
 
 // TODO(rtenneti): Add support for MMSG_MORE.
 #define MMSG_MORE 0
-using std::string;
 
 namespace quic {
 
@@ -59,7 +58,7 @@
   CleanUpAllUDPSockets();
 }
 
-string QuicClientEpollNetworkHelper::Name() const {
+QuicString QuicClientEpollNetworkHelper::Name() const {
   return "QuicClientEpollNetworkHelper";
 }
 
diff --git a/net/third_party/quic/tools/quic_client_epoll_network_helper.h b/net/third_party/quic/tools/quic_client_epoll_network_helper.h
index 2a6d22e3..b5a7d217 100644
--- a/net/third_party/quic/tools/quic_client_epoll_network_helper.h
+++ b/net/third_party/quic/tools/quic_client_epoll_network_helper.h
@@ -48,7 +48,7 @@
   ~QuicClientEpollNetworkHelper() override;
 
   // Return a name describing the class for use in debug/error reporting.
-  std::string Name() const override;
+  QuicString Name() const override;
 
   // From net::EpollCallbackInterface
   void OnRegistration(net::EpollServer* eps, int fd, int event_mask) override;
diff --git a/net/third_party/quic/tools/quic_memory_cache_backend.h b/net/third_party/quic/tools/quic_memory_cache_backend.h
index ee605a4..454328d 100644
--- a/net/third_party/quic/tools/quic_memory_cache_backend.h
+++ b/net/third_party/quic/tools/quic_memory_cache_backend.h
@@ -14,9 +14,9 @@
 #include "net/third_party/quic/platform/api/quic_containers.h"
 #include "net/third_party/quic/platform/api/quic_mutex.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
-#include "net/third_party/quic/platform/api/quic_url.h"
 #include "net/third_party/quic/tools/quic_backend_response.h"
 #include "net/third_party/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quic/tools/quic_url.h"
 #include "net/third_party/spdy/core/spdy_framer.h"
 
 namespace quic {
diff --git a/net/third_party/quic/tools/quic_packet_printer_bin.cc b/net/third_party/quic/tools/quic_packet_printer_bin.cc
index b9c3114..4b841c5 100644
--- a/net/third_party/quic/tools/quic_packet_printer_bin.cc
+++ b/net/third_party/quic/tools/quic_packet_printer_bin.cc
@@ -41,14 +41,12 @@
 #include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
 
-using std::string;
-
 // If set, specify the QUIC version to use.
-string FLAGS_quic_version = "";
+quic::QuicString FLAGS_quic_version = "";
 
 namespace {
 
-string ArgToString(base::CommandLine::StringType arg) {
+quic::QuicString ArgToString(base::CommandLine::StringType arg) {
 #if defined(OS_WIN)
   return base::UTF16ToASCII(arg);
 #else
@@ -231,7 +229,7 @@
     FLAGS_quic_version = line->GetSwitchValueASCII("quic_version");
   }
 
-  string perspective_string = ArgToString(args[0]);
+  quic::QuicString perspective_string = ArgToString(args[0]);
   quic::Perspective perspective;
   if (perspective_string == "client") {
     perspective = quic::Perspective::IS_CLIENT;
@@ -242,7 +240,7 @@
               << " Usage: " << args[0] << " client|server <hex>\n";
     return 1;
   }
-  string hex = quic::QuicTextUtils::HexDecode(argv[2]);
+  quic::QuicString hex = quic::QuicTextUtils::HexDecode(argv[2]);
   quic::ParsedQuicVersionVector versions = quic::AllSupportedVersions();
   // Fake a time since we're not actually generating acks.
   quic::QuicTime start(quic::QuicTime::Zero());
diff --git a/net/third_party/quic/tools/quic_server.h b/net/third_party/quic/tools/quic_server.h
index c181a49..77e31f4a 100644
--- a/net/third_party/quic/tools/quic_server.h
+++ b/net/third_party/quic/tools/quic_server.h
@@ -47,7 +47,7 @@
 
   ~QuicServer() override;
 
-  std::string Name() const override { return "QuicServer"; }
+  QuicString Name() const override { return "QuicServer"; }
 
   // Start listening on the specified address.
   bool CreateUDPSocketAndListen(const QuicSocketAddress& address);
diff --git a/net/third_party/quic/tools/quic_spdy_client_base.cc b/net/third_party/quic/tools/quic_spdy_client_base.cc
index 01ff7cb..f13ee93 100644
--- a/net/third_party/quic/tools/quic_spdy_client_base.cc
+++ b/net/third_party/quic/tools/quic_spdy_client_base.cc
@@ -13,7 +13,6 @@
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
 
 using base::StringToInt;
-using std::string;
 
 namespace quic {
 
@@ -133,7 +132,7 @@
 }
 
 void QuicSpdyClientBase::SendRequestsAndWaitForResponse(
-    const std::vector<string>& url_list) {
+    const std::vector<QuicString>& url_list) {
   for (size_t i = 0; i < url_list.size(); ++i) {
     spdy::SpdyHeaderBlock headers;
     if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) {
@@ -244,12 +243,12 @@
   return latest_response_code_;
 }
 
-const string& QuicSpdyClientBase::latest_response_headers() const {
+const QuicString& QuicSpdyClientBase::latest_response_headers() const {
   QUIC_BUG_IF(!store_response_) << "Response not stored!";
   return latest_response_headers_;
 }
 
-const string& QuicSpdyClientBase::preliminary_response_headers() const {
+const QuicString& QuicSpdyClientBase::preliminary_response_headers() const {
   QUIC_BUG_IF(!store_response_) << "Response not stored!";
   return preliminary_response_headers_;
 }
@@ -260,12 +259,12 @@
   return latest_response_header_block_;
 }
 
-const string& QuicSpdyClientBase::latest_response_body() const {
+const QuicString& QuicSpdyClientBase::latest_response_body() const {
   QUIC_BUG_IF(!store_response_) << "Response not stored!";
   return latest_response_body_;
 }
 
-const string& QuicSpdyClientBase::latest_response_trailers() const {
+const QuicString& QuicSpdyClientBase::latest_response_trailers() const {
   QUIC_BUG_IF(!store_response_) << "Response not stored!";
   return latest_response_trailers_;
 }
diff --git a/net/third_party/quic/tools/quic_spdy_client_base.h b/net/third_party/quic/tools/quic_spdy_client_base.h
index edb2003..fb5f149 100644
--- a/net/third_party/quic/tools/quic_spdy_client_base.h
+++ b/net/third_party/quic/tools/quic_spdy_client_base.h
@@ -37,7 +37,7 @@
     virtual void OnCompleteResponse(
         QuicStreamId id,
         const spdy::SpdyHeaderBlock& response_headers,
-        const std::string& response_body) = 0;
+        const QuicString& response_body) = 0;
   };
 
   // The client uses these objects to keep track of any data to resend upon
@@ -99,7 +99,7 @@
 
   // Sends a request simple GET for each URL in |url_list|, and then waits for
   // each to complete.
-  void SendRequestsAndWaitForResponse(const std::vector<std::string>& url_list);
+  void SendRequestsAndWaitForResponse(const std::vector<QuicString>& url_list);
 
   // Returns a newly created QuicSpdyClientStream.
   QuicSpdyClientStream* CreateClientStream();
@@ -126,11 +126,11 @@
   void set_store_response(bool val) { store_response_ = val; }
 
   size_t latest_response_code() const;
-  const std::string& latest_response_headers() const;
-  const std::string& preliminary_response_headers() const;
+  const QuicString& latest_response_headers() const;
+  const QuicString& preliminary_response_headers() const;
   const spdy::SpdyHeaderBlock& latest_response_header_block() const;
-  const std::string& latest_response_body() const;
-  const std::string& latest_response_trailers() const;
+  const QuicString& latest_response_body() const;
+  const QuicString& latest_response_trailers() const;
 
   void set_response_listener(std::unique_ptr<ResponseListener> listener) {
     response_listener_ = std::move(listener);
@@ -190,15 +190,15 @@
   // HTTP response code from most recent response.
   int latest_response_code_;
   // HTTP/2 headers from most recent response.
-  std::string latest_response_headers_;
+  QuicString latest_response_headers_;
   // preliminary 100 Continue HTTP/2 headers from most recent response, if any.
-  std::string preliminary_response_headers_;
+  QuicString preliminary_response_headers_;
   // HTTP/2 headers from most recent response.
   spdy::SpdyHeaderBlock latest_response_header_block_;
   // Body of most recent response.
-  std::string latest_response_body_;
+  QuicString latest_response_body_;
   // HTTP/2 trailers from most recent response.
-  std::string latest_response_trailers_;
+  QuicString latest_response_trailers_;
 
   // Listens for full responses.
   std::unique_ptr<ResponseListener> response_listener_;
diff --git a/net/third_party/quic/tools/quic_url.cc b/net/third_party/quic/tools/quic_url.cc
new file mode 100644
index 0000000..f7f6b2a
--- /dev/null
+++ b/net/third_party/quic/tools/quic_url.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quic/tools/quic_url.h"
+
+#include "net/third_party/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+static constexpr size_t kMaxHostNameLength = 256;
+
+QuicUrl::QuicUrl(QuicStringPiece url) : url_(static_cast<QuicString>(url)) {}
+
+QuicUrl::QuicUrl(QuicStringPiece url, QuicStringPiece default_scheme)
+    : QuicUrl(url) {
+  if (url_.has_scheme()) {
+    return;
+  }
+
+  url_ = GURL(QuicStrCat(default_scheme, "://", url));
+}
+
+QuicString QuicUrl::ToString() const {
+  if (IsValid()) {
+    return url_.spec();
+  }
+  return "";
+}
+
+bool QuicUrl::IsValid() const {
+  if (!url_.is_valid() || !url_.has_scheme()) {
+    return false;
+  }
+
+  if (url_.has_host() && url_.host().length() > kMaxHostNameLength) {
+    return false;
+  }
+
+  return true;
+}
+
+QuicString QuicUrl::HostPort() const {
+  if (!IsValid() || !url_.has_host()) {
+    return "";
+  }
+
+  QuicString host = url_.host();
+  int port = url_.IntPort();
+  if (port == url::PORT_UNSPECIFIED) {
+    return host;
+  }
+  return QuicStrCat(host, ":", port);
+}
+
+QuicString QuicUrl::PathParamsQuery() const {
+  if (!IsValid() || !url_.has_path()) {
+    return "/";
+  }
+
+  return url_.PathForRequest();
+}
+
+QuicString QuicUrl::scheme() const {
+  if (!IsValid()) {
+    return "";
+  }
+
+  return url_.scheme();
+}
+
+QuicString QuicUrl::host() const {
+  if (!IsValid()) {
+    return "";
+  }
+
+  return url_.HostNoBrackets();
+}
+
+QuicString QuicUrl::path() const {
+  if (!IsValid()) {
+    return "";
+  }
+
+  return url_.path();
+}
+
+uint16_t QuicUrl::port() const {
+  if (!IsValid()) {
+    return 0;
+  }
+
+  int port = url_.EffectiveIntPort();
+  if (port == url::PORT_UNSPECIFIED) {
+    return 0;
+  }
+  return port;
+}
+
+}  // namespace quic
diff --git a/net/third_party/quic/tools/quic_url.h b/net/third_party/quic/tools/quic_url.h
new file mode 100644
index 0000000..ccda449
--- /dev/null
+++ b/net/third_party/quic/tools/quic_url.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_THIRD_PARTY_QUIC_TOOLS_QUIC_URL_H_
+#define NET_THIRD_PARTY_QUIC_TOOLS_QUIC_URL_H_
+
+#include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
+#include "net/third_party/quic/platform/api/quic_string_piece.h"
+#include "url/gurl.h"
+
+namespace quic {
+
+// A utility class that wraps GURL.
+class QuicUrl {
+ public:
+  // Constructs an empty QuicUrl.
+  QuicUrl() = default;
+
+  // Constructs a QuicUrl from the url string |url|.
+  //
+  // NOTE: If |url| doesn't have a scheme, it will have an empty scheme
+  // field. If that's not what you want, use the QuicUrlImpl(url,
+  // default_scheme) form below.
+  explicit QuicUrl(QuicStringPiece url);
+
+  // Constructs a QuicUrlImpl from |url|, assuming that the scheme for the URL
+  // is |default_scheme| if there is no scheme specified in |url|.
+  QuicUrl(QuicStringPiece url, QuicStringPiece default_scheme);
+
+  // Returns false if the URL is not valid.
+  bool IsValid() const;
+
+  // Returns full text of the QuicUrl if it is valid. Return empty string
+  // otherwise.
+  QuicString ToString() const;
+
+  // Returns host:port.
+  // If the host is empty, it will return an empty string.
+  // If the host is an IPv6 address, it will be bracketed.
+  // If port is not present or is equal to default_port of scheme (e.g., port
+  // 80 for HTTP), it won't be returned.
+  QuicString HostPort() const;
+
+  // Returns a string assembles path, parameters and query.
+  QuicString PathParamsQuery() const;
+
+  QuicString scheme() const;
+  QuicString host() const;
+  QuicString path() const;
+  uint16_t port() const;
+
+ private:
+  GURL url_;
+};
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_TOOLS_QUIC_URL_H_
diff --git a/net/third_party/quic/platform/api/quic_url_test.cc b/net/third_party/quic/tools/quic_url_test.cc
similarity index 96%
rename from net/third_party/quic/platform/api/quic_url_test.cc
rename to net/third_party/quic/tools/quic_url_test.cc
index fdeb8e62..db76e5f 100644
--- a/net/third_party/quic/platform/api/quic_url_test.cc
+++ b/net/third_party/quic/tools/quic_url_test.cc
@@ -1,9 +1,10 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "net/third_party/quic/platform/api/quic_url.h"
+#include "net/third_party/quic/tools/quic_url.h"
 
+#include "net/third_party/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 
diff --git a/net/websockets/websocket_basic_handshake_stream.cc b/net/websockets/websocket_basic_handshake_stream.cc
index 266f6aa..1d4bf433 100644
--- a/net/websockets/websocket_basic_handshake_stream.cc
+++ b/net/websockets/websocket_basic_handshake_stream.cc
@@ -161,7 +161,7 @@
 
 const base::Feature
     WebSocketBasicHandshakeStream::kWebSocketHandshakeReuseConnection{
-        "WebSocketHandshakeReuseConnection", base::FEATURE_DISABLED_BY_DEFAULT};
+        "WebSocketHandshakeReuseConnection", base::FEATURE_ENABLED_BY_DEFAULT};
 
 WebSocketBasicHandshakeStream::WebSocketBasicHandshakeStream(
     std::unique_ptr<ClientSocketHandle> connection,
diff --git a/net/websockets/websocket_stream_test.cc b/net/websockets/websocket_stream_test.cc
index b278e00..501f967 100644
--- a/net/websockets/websocket_stream_test.cc
+++ b/net/websockets/websocket_stream_test.cc
@@ -426,28 +426,24 @@
 // send the authenticated request on.
 class CommonAuthTestHelper {
  public:
-  CommonAuthTestHelper() : reads1_(), writes1_(), reads2_(), writes2_() {}
+  CommonAuthTestHelper() : reads_(), writes_() {}
 
-  std::unique_ptr<SequencedSocketData> BuildSocketData1(
-      const std::string& response) {
+  std::unique_ptr<SequencedSocketData> BuildAuthSocketData(
+      std::string response1,
+      std::string request2,
+      std::string response2) {
     request1_ =
         WebSocketStandardRequest("/", "www.example.org", Origin(), "", "");
-    writes1_[0] = MockWrite(SYNCHRONOUS, 0, request1_.c_str());
-    response1_ = response;
-    reads1_[0] = MockRead(SYNCHRONOUS, 1, response1_.c_str());
-    reads1_[1] = MockRead(SYNCHRONOUS, OK, 2);  // Close connection
+    response1_ = std::move(response1);
+    request2_ = std::move(request2);
+    response2_ = std::move(response2);
+    writes_[0] = MockWrite(SYNCHRONOUS, 0, request1_.c_str());
+    reads_[0] = MockRead(SYNCHRONOUS, 1, response1_.c_str());
+    writes_[1] = MockWrite(SYNCHRONOUS, 2, request2_.c_str());
+    reads_[1] = MockRead(SYNCHRONOUS, 3, response2_.c_str());
+    reads_[2] = MockRead(SYNCHRONOUS, OK, 4);  // Close connection
 
-    return BuildSocketData(reads1_, writes1_);
-  }
-
-  std::unique_ptr<SequencedSocketData> BuildSocketData2(
-      const std::string& request,
-      const std::string& response) {
-    request2_ = request;
-    response2_ = response;
-    writes2_[0] = MockWrite(SYNCHRONOUS, 0, request2_.c_str());
-    reads2_[0] = MockRead(SYNCHRONOUS, 1, response2_.c_str());
-    return BuildSocketData(reads2_, writes2_);
+    return BuildSocketData(reads_, writes_);
   }
 
  private:
@@ -457,10 +453,8 @@
   std::string request2_;
   std::string response1_;
   std::string response2_;
-  MockRead reads1_[2];
-  MockWrite writes1_[1];
-  MockRead reads2_[1];
-  MockWrite writes2_[1];
+  MockRead reads_[3];
+  MockWrite writes_[2];
 
   DISALLOW_COPY_AND_ASSIGN(CommonAuthTestHelper);
 };
@@ -471,12 +465,11 @@
   void CreateAndConnectAuthHandshake(base::StringPiece url,
                                      base::StringPiece base64_user_pass,
                                      base::StringPiece response2) {
-    AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
-
     CreateAndConnectRawExpectations(
         url, NoSubProtocols(), HttpRequestHeaders(),
-        helper_.BuildSocketData2(RequestExpectation(base64_user_pass),
-                                 response2.as_string()));
+        helper_.BuildAuthSocketData(kUnauthorizedResponse,
+                                    RequestExpectation(base64_user_pass),
+                                    response2.as_string()));
   }
 
   static std::string RequestExpectation(base::StringPiece base64_user_pass) {
@@ -1479,10 +1472,6 @@
 }
 
 TEST_P(WebSocketStreamCreateBasicAuthTest, SuccessfulConnectionReuse) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      WebSocketBasicHandshakeStream ::kWebSocketHandshakeReuseConnection);
-
   std::string request1 =
       WebSocketStandardRequest("/", "www.example.org", Origin(), "", "");
   std::string response1 = kUnauthorizedResponse;
@@ -1528,8 +1517,11 @@
 }
 
 TEST_P(WebSocketStreamCreateBasicAuthTest, OnAuthRequiredSetAuth) {
-  CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
-                                 {}, kUnauthorizedResponse);
+  CreateAndConnectRawExpectations(
+      "ws://www.example.org/", NoSubProtocols(), HttpRequestHeaders(),
+      helper_.BuildAuthSocketData(kUnauthorizedResponse,
+                                  RequestExpectation("Zm9vOmJheg=="),
+                                  WebSocketStandardResponse(std::string())));
 
   EXPECT_FALSE(request_info_);
   EXPECT_FALSE(response_info_);
@@ -1543,12 +1535,6 @@
                               base::ASCIIToUTF16("baz"));
   std::move(on_auth_required_callback_).Run(&credentials);
 
-  // As we are re-establishing the connection with additional credentials,
-  // add new expectations.
-  AddRawExpectations(
-      helper_.BuildSocketData2(RequestExpectation("Zm9vOmJheg=="),
-                               WebSocketStandardResponse(std::string())));
-
   WaitUntilConnectDone();
   EXPECT_TRUE(stream_);
   EXPECT_FALSE(has_failed());
@@ -1558,13 +1544,11 @@
 // generally assume that whatever works for Basic auth will also work for
 // Digest. There's just one test here, to confirm that it works at all.
 TEST_P(WebSocketStreamCreateDigestAuthTest, DigestPasswordInUrl) {
-  AddRawExpectations(helper_.BuildSocketData1(kUnauthorizedResponse));
-
   CreateAndConnectRawExpectations(
       "ws://FooBar:pass@www.example.org/", NoSubProtocols(),
       HttpRequestHeaders(),
-      helper_.BuildSocketData2(kAuthorizedRequest,
-                               WebSocketStandardResponse(std::string())));
+      helper_.BuildAuthSocketData(kUnauthorizedResponse, kAuthorizedRequest,
+                                  WebSocketStandardResponse(std::string())));
   WaitUntilConnectDone();
   EXPECT_FALSE(has_failed());
   EXPECT_TRUE(stream_);
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.cc b/ppapi/proxy/ppapi_command_buffer_proxy.cc
index ab29b16..90f4147 100644
--- a/ppapi/proxy/ppapi_command_buffer_proxy.cc
+++ b/ppapi/proxy/ppapi_command_buffer_proxy.cc
@@ -171,7 +171,7 @@
 }
 
 void PpapiCommandBufferProxy::SetLock(base::Lock*) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void PpapiCommandBufferProxy::EnsureWorkVisible() {
@@ -206,23 +206,22 @@
 }
 
 bool PpapiCommandBufferProxy::IsFenceSyncReleased(uint64_t release) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
   return false;
 }
 
 void PpapiCommandBufferProxy::SignalSyncToken(const gpu::SyncToken& sync_token,
                                               base::OnceClosure callback) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
-// Pepper plugin does not expose or call WaitSyncTokenCHROMIUM.
-void PpapiCommandBufferProxy::WaitSyncToken(const gpu::SyncToken& sync_token) {
-  NOTREACHED();
+void PpapiCommandBufferProxy::WaitSyncTokenHint(
+    const gpu::SyncToken& sync_token) {
+  // TODO(sunnyps): Forward sync token dependency hints to the renderer.
 }
 
 bool PpapiCommandBufferProxy::CanWaitUnverifiedSyncToken(
     const gpu::SyncToken& sync_token) {
-  NOTREACHED();
   return false;
 }
 
@@ -233,13 +232,13 @@
 
 void PpapiCommandBufferProxy::CreateGpuFence(uint32_t gpu_fence_id,
                                              ClientGpuFence source) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void PpapiCommandBufferProxy::GetGpuFence(
     uint32_t gpu_fence_id,
     base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void PpapiCommandBufferProxy::SetGpuControlClient(gpu::GpuControlClient*) {
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.h b/ppapi/proxy/ppapi_command_buffer_proxy.h
index 8d4d23c..28588f2 100644
--- a/ppapi/proxy/ppapi_command_buffer_proxy.h
+++ b/ppapi/proxy/ppapi_command_buffer_proxy.h
@@ -76,7 +76,7 @@
   bool IsFenceSyncReleased(uint64_t release) override;
   void SignalSyncToken(const gpu::SyncToken& sync_token,
                        base::OnceClosure callback) override;
-  void WaitSyncToken(const gpu::SyncToken& sync_token) override;
+  void WaitSyncTokenHint(const gpu::SyncToken& sync_token) override;
   bool CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) override;
 
  private:
diff --git a/ppapi/tests/test_url_loader.cc b/ppapi/tests/test_url_loader.cc
index d852353f..9f68aec 100644
--- a/ppapi/tests/test_url_loader.cc
+++ b/ppapi/tests/test_url_loader.cc
@@ -874,7 +874,7 @@
     loader.GetDownloadProgress(&bytes_received, &total_bytes_to_be_received);
     if (total_bytes_to_be_received <= 0)
       return ReportError("URLLoader::GetDownloadProgress total size",
-          total_bytes_to_be_received);
+                         static_cast<int32_t>(total_bytes_to_be_received));
     if (bytes_received == total_bytes_to_be_received)
       break;
     // Yield if we're on the main thread, so that URLLoader can receive more
diff --git a/remoting/test/app_remoting_test_driver.cc b/remoting/test/app_remoting_test_driver.cc
index 0fbe1f1..310ee4a 100644
--- a/remoting/test/app_remoting_test_driver.cc
+++ b/remoting/test/app_remoting_test_driver.cc
@@ -164,6 +164,8 @@
   // We do not want to retry failures as a failed test should signify an error
   // to be investigated.
   command_line->AppendSwitchASCII(switches::kTestLauncherRetryLimit, "0");
+  command_line->AppendSwitchASCII(
+      switches::kIsolatedScriptTestLauncherRetryLimit, "0");
 
   // We do not want to run the tests in parallel and we do not want to retry
   // failures.  The reason for running in a single process is that some tests
diff --git a/remoting/test/chromoting_test_driver.cc b/remoting/test/chromoting_test_driver.cc
index aa7da51..279368f 100644
--- a/remoting/test/chromoting_test_driver.cc
+++ b/remoting/test/chromoting_test_driver.cc
@@ -165,6 +165,8 @@
 
   // Do not retry if tests fails.
   command_line->AppendSwitchASCII(switches::kTestLauncherRetryLimit, "0");
+  command_line->AppendSwitchASCII(
+      switches::kIsolatedScriptTestLauncherRetryLimit, "0");
 
   // Different tests may require access to the same host if run in parallel.
   // To avoid shared resource contention, tests will be run one at a time.
diff --git a/sandbox/win/src/registry_interception.cc b/sandbox/win/src/registry_interception.cc
index 11a7781..4d381a9 100644
--- a/sandbox/win/src/registry_interception.cc
+++ b/sandbox/win/src/registry_interception.cc
@@ -67,24 +67,26 @@
     CountedParameterSet<OpenKey> params;
     params[OpenKey::ACCESS] = ParamPickerMake(desired_access_uint32);
 
-    wchar_t* full_name = nullptr;
-    const wchar_t* name_ptr = name.get();
+    bool query_broker = false;
+    {
+      std::unique_ptr<wchar_t, NtAllocDeleter> full_name;
+      const wchar_t* name_ptr = name.get();
+      const wchar_t* full_name_ptr = nullptr;
 
-    if (root_directory) {
-      ret =
-          sandbox::AllocAndGetFullPath(root_directory, name.get(), &full_name);
-      if (!NT_SUCCESS(ret) || !full_name)
-        break;
-      params[OpenKey::NAME] = ParamPickerMake(full_name);
-    } else {
-      params[OpenKey::NAME] = ParamPickerMake(name_ptr);
+      if (root_directory) {
+        ret = sandbox::AllocAndGetFullPath(root_directory, name.get(),
+                                           &full_name);
+        if (!NT_SUCCESS(ret) || !full_name)
+          break;
+        full_name_ptr = full_name.get();
+        params[OpenKey::NAME] = ParamPickerMake(full_name_ptr);
+      } else {
+        params[OpenKey::NAME] = ParamPickerMake(name_ptr);
+      }
+
+      query_broker = QueryBroker(IPC_NTCREATEKEY_TAG, params.GetBase());
     }
 
-    bool query_broker = QueryBroker(IPC_NTCREATEKEY_TAG, params.GetBase());
-
-    if (full_name)
-      operator delete(full_name, NT_ALLOC);
-
     if (!query_broker)
       break;
 
@@ -150,24 +152,26 @@
     CountedParameterSet<OpenKey> params;
     params[OpenKey::ACCESS] = ParamPickerMake(desired_access_uint32);
 
-    wchar_t* full_name = nullptr;
-    const wchar_t* name_ptr = name.get();
+    bool query_broker = false;
+    {
+      std::unique_ptr<wchar_t, NtAllocDeleter> full_name;
+      const wchar_t* name_ptr = name.get();
+      const wchar_t* full_name_ptr = nullptr;
 
-    if (root_directory) {
-      ret =
-          sandbox::AllocAndGetFullPath(root_directory, name.get(), &full_name);
-      if (!NT_SUCCESS(ret) || !full_name)
-        break;
-      params[OpenKey::NAME] = ParamPickerMake(full_name);
-    } else {
-      params[OpenKey::NAME] = ParamPickerMake(name_ptr);
+      if (root_directory) {
+        ret = sandbox::AllocAndGetFullPath(root_directory, name.get(),
+                                           &full_name);
+        if (!NT_SUCCESS(ret) || !full_name)
+          break;
+        full_name_ptr = full_name.get();
+        params[OpenKey::NAME] = ParamPickerMake(full_name_ptr);
+      } else {
+        params[OpenKey::NAME] = ParamPickerMake(name_ptr);
+      }
+
+      query_broker = QueryBroker(IPC_NTOPENKEY_TAG, params.GetBase());
     }
 
-    bool query_broker = QueryBroker(IPC_NTOPENKEY_TAG, params.GetBase());
-
-    if (full_name)
-      operator delete(full_name, NT_ALLOC);
-
     if (!query_broker)
       break;
 
diff --git a/sandbox/win/src/sandbox_nt_util.cc b/sandbox/win/src/sandbox_nt_util.cc
index b4f6ab9..f71177fd 100644
--- a/sandbox/win/src/sandbox_nt_util.cc
+++ b/sandbox/win/src/sandbox_nt_util.cc
@@ -228,14 +228,15 @@
   return ret;
 }
 
-NTSTATUS AllocAndGetFullPath(HANDLE root, wchar_t* path, wchar_t** full_path) {
+NTSTATUS AllocAndGetFullPath(
+    HANDLE root,
+    const wchar_t* path,
+    std::unique_ptr<wchar_t, NtAllocDeleter>* full_path) {
   if (!InitHeap())
     return STATUS_NO_MEMORY;
 
   DCHECK_NT(full_path);
   DCHECK_NT(path);
-  *full_path = nullptr;
-  OBJECT_NAME_INFORMATION* handle_name = nullptr;
   NTSTATUS ret = STATUS_UNSUCCESSFUL;
   __try {
     do {
@@ -247,14 +248,15 @@
       // Query the name information a first time to get the size of the name.
       ret = NtQueryObject(root, ObjectNameInformation, nullptr, 0, &size);
 
+      std::unique_ptr<OBJECT_NAME_INFORMATION, NtAllocDeleter> handle_name;
       if (size) {
-        handle_name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(
-            new (NT_ALLOC) BYTE[size]);
+        handle_name.reset(reinterpret_cast<OBJECT_NAME_INFORMATION*>(
+            new (NT_ALLOC) BYTE[size]));
 
         // Query the name information a second time to get the name of the
         // object referenced by the handle.
-        ret = NtQueryObject(root, ObjectNameInformation, handle_name, size,
-                            &size);
+        ret = NtQueryObject(root, ObjectNameInformation, handle_name.get(),
+                            size, &size);
       }
 
       if (STATUS_SUCCESS != ret)
@@ -263,10 +265,10 @@
       // Space for path + '\' + name + '\0'.
       size_t name_length =
           handle_name->ObjectName.Length + (wcslen(path) + 2) * sizeof(wchar_t);
-      *full_path = new (NT_ALLOC) wchar_t[name_length / sizeof(wchar_t)];
+      full_path->reset(new (NT_ALLOC) wchar_t[name_length / sizeof(wchar_t)]);
       if (!*full_path)
         break;
-      wchar_t* off = *full_path;
+      wchar_t* off = full_path->get();
       ret = CopyData(off, handle_name->ObjectName.Buffer,
                      handle_name->ObjectName.Length);
       if (!NT_SUCCESS(ret))
@@ -284,16 +286,8 @@
     ret = GetExceptionCode();
   }
 
-  if (!NT_SUCCESS(ret)) {
-    if (*full_path) {
-      operator delete(*full_path, NT_ALLOC);
-      *full_path = nullptr;
-    }
-    if (handle_name) {
-      operator delete(handle_name, NT_ALLOC);
-      handle_name = nullptr;
-    }
-  }
+  if (!NT_SUCCESS(ret) && *full_path)
+    full_path->reset(nullptr);
 
   return ret;
 }
diff --git a/sandbox/win/src/sandbox_nt_util.h b/sandbox/win/src/sandbox_nt_util.h
index 1e777c7..85743e7 100644
--- a/sandbox/win/src/sandbox_nt_util.h
+++ b/sandbox/win/src/sandbox_nt_util.h
@@ -119,7 +119,10 @@
                           HANDLE* root);
 
 // Determine full path name from object root and path.
-NTSTATUS AllocAndGetFullPath(HANDLE root, wchar_t* path, wchar_t** full_path);
+NTSTATUS AllocAndGetFullPath(
+    HANDLE root,
+    const wchar_t* path,
+    std::unique_ptr<wchar_t, NtAllocDeleter>* full_path);
 
 // Initializes our ntdll level heap
 bool InitHeap();
diff --git a/services/device/bluetooth/OWNERS b/services/device/bluetooth/OWNERS
new file mode 100644
index 0000000..b6bdb83b
--- /dev/null
+++ b/services/device/bluetooth/OWNERS
@@ -0,0 +1,2 @@
+ortuno@chromium.org
+reillyg@chromium.org
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index f7bec193..4ddb8b6 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -7,6 +7,7 @@
 #include <map>
 #include <utility>
 
+#include "base/hash.h"
 #include "base/json/json_writer.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/no_destructor.h"
@@ -127,28 +128,35 @@
     trace_packet_handle_ = trace_writer_->NewTracePacket();
     event_bundle_ =
         ChromeEventBundleHandle(trace_packet_handle_->set_chrome_events());
-    string_table_.clear();
-    next_string_table_index_ = 0;
+    interned_strings_.clear();
+#if DCHECK_IS_ON()
+    hash_verifier_.clear();
+#endif
   }
 
-  int GetStringTableIndexForString(const char* str_value) {
+  uint32_t GetStringTableIndexForString(const char* str_value) {
     EnsureValidHandles();
 
-    auto it = string_table_.find(reinterpret_cast<intptr_t>(str_value));
-    if (it != string_table_.end()) {
-      CHECK_EQ(std::string(reinterpret_cast<const char*>(it->first)),
-               std::string(str_value));
+    uint32_t string_table_index = base::Hash(str_value, strlen(str_value));
 
-      return it->second;
+    if (interned_strings_.find(string_table_index) != interned_strings_.end()) {
+#if DCHECK_IS_ON()
+      DCHECK_EQ(hash_verifier_[string_table_index], str_value);
+#endif
+
+      return string_table_index;
     }
 
-    int string_table_index = ++next_string_table_index_;
-    string_table_[reinterpret_cast<intptr_t>(str_value)] = string_table_index;
+    interned_strings_.insert(string_table_index);
 
     auto* new_string_table_entry = event_bundle_->add_string_table();
     new_string_table_entry->set_value(str_value);
     new_string_table_entry->set_index(string_table_index);
 
+#if DCHECK_IS_ON()
+    hash_verifier_[string_table_index] = str_value;
+#endif
+
     return string_table_index;
   }
 
@@ -176,9 +184,11 @@
 
     EnsureValidHandles();
 
-    int name_index = 0;
-    int category_name_index = 0;
-    int arg_name_indices[base::trace_event::kTraceMaxNumArgs] = {0};
+    uint32_t name_index = 0;
+    uint32_t category_name_index = 0;
+    uint32_t arg_name_indices[base::trace_event::kTraceMaxNumArgs] = {0};
+
+    char phase = trace_event.phase();
 
     // Populate any new string table parts first; has to be done before
     // the add_trace_events() call (as the string table is part of the outer
@@ -188,9 +198,18 @@
     // the string every time.
     bool string_table_enabled = !(trace_event.flags() & TRACE_EVENT_FLAG_COPY);
     if (string_table_enabled) {
-      name_index = GetStringTableIndexForString(trace_event.name());
-      category_name_index = GetStringTableIndexForString(
-          TraceLog::GetCategoryGroupName(trace_event.category_group_enabled()));
+      // Optimization: If it's an _END event, we know that the string table
+      // entries for the name and the category have already been emitted.
+      if (phase == TRACE_EVENT_PHASE_END) {
+        name_index = base::Hash(trace_event.name());
+        category_name_index = base::Hash(TraceLog::GetCategoryGroupName(
+            trace_event.category_group_enabled()));
+      } else {
+        name_index = GetStringTableIndexForString(trace_event.name());
+        category_name_index =
+            GetStringTableIndexForString(TraceLog::GetCategoryGroupName(
+                trace_event.category_group_enabled()));
+      }
 
       for (int i = 0;
            i < base::trace_event::kTraceMaxNumArgs && trace_event.arg_name(i);
@@ -235,7 +254,6 @@
     new_trace_event->set_process_id(process_id);
     new_trace_event->set_thread_id(thread_id);
 
-    char phase = trace_event.phase();
     new_trace_event->set_phase(phase);
 
     for (int i = 0;
@@ -288,11 +306,6 @@
       int64_t duration = trace_event.duration().InMicroseconds();
       if (duration != -1) {
         new_trace_event->set_duration(duration);
-      } else {
-        // TODO(oysteine): Workaround until TRACE_EVENT_PHASE_COMPLETE can be
-        // split into begin/end pairs. If the duration is -1 and the
-        // trace-viewer will spend forever generating a warning for each event.
-        new_trace_event->set_duration(0);
       }
 
       if (!trace_event.thread_timestamp().is_null()) {
@@ -329,16 +342,16 @@
     event_bundle_ = ChromeEventBundleHandle();
     trace_packet_handle_ = perfetto::TraceWriter::TracePacketHandle();
     trace_writer_->Flush();
-    token_ = "";
   }
 
  private:
   std::unique_ptr<perfetto::TraceWriter> trace_writer_;
   ChromeEventBundleHandle event_bundle_;
   perfetto::TraceWriter::TracePacketHandle trace_packet_handle_;
-  std::map<intptr_t, int> string_table_;
-  int next_string_table_index_ = 0;
-  std::string token_;
+  std::set<uint32_t> interned_strings_;
+#if DCHECK_IS_ON()
+  std::map<uint32_t, std::string> hash_verifier_;
+#endif
 };
 
 namespace {
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
index 7f7b704..170f268 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
@@ -72,10 +72,6 @@
 # CalledOnValidSequence() from SchedulerWorkerDelegate::OnMainExit
 -LoginScreenDefaultPolicyLoginScreenBrowsertest.*
 
-# ash::Shell access in test for display configuration.
-# http://crbug.com/831826
--ShelfAppBrowserTest.LaunchAppFromDisplayWithoutFocus*
-
 # Timeout because first non-empty paint isn't triggered.
 # https://crbug.com/885318
 -NoBackgroundTasksTest.FirstNonEmptyPaintWithoutBackgroundTasks
@@ -109,7 +105,7 @@
 -PictureInPictureLazyBackgroundPageApiTest.PictureInPictureInBackgroundPage
 
 # These started failing with the switch to ws2.
-# https:://crbug.com/855767
+# https://crbug.com/855767
 -AppWindowApiTest.OnRestoredEvent
 -BrowserActionApiTest.BrowserActionPopupWithIframe
 -FirstRunUIBrowserTest.ModalWindowDoesNotBlock
@@ -224,9 +220,6 @@
 -WindowOpenApiTest.UpdateWindowToLockedFullscreen
 -WindowOpenApiTest.VerifyCommandsInLockedFullscreen
 
-# Value of: immersive_controller->IsRevealed()
--ZoomBubbleBrowserTest.ImmersiveFullscreen
-
 # Flaky tests. crbug.com/833144
 -GetAuthTokenFunctionPublicSessionTest.NonWhitelisted
 
diff --git a/testing/buildbot/filters/fuchsia.mojo_unittests.filter b/testing/buildbot/filters/fuchsia.mojo_unittests.filter
index 7a1c767..8f529675 100644
--- a/testing/buildbot/filters/fuchsia.mojo_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.mojo_unittests.filter
@@ -4,10 +4,6 @@
 -MessagePipeTest.ClosePipesStressTest
 -MessageTest.ExtendMessagePayloadLarge
 
-# These tests require support for named channels. See crbug.com/754038.
--MultiprocessMessagePipeTestWithPeerSupport*/2
--MultiprocessMessagePipeTestWithPeerSupport*/3
-
 # crbug.com/780317 - These timeout under QEMU s/w emulation of ARM64.
 -MultiprocessMessagePipeTestWithPeerSupport.PingPongPipe*
 -MessagePipeTest.SharedBufferHandlePingPong
diff --git a/testing/buildbot/filters/webui_polymer2_browser_tests.filter b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
index 20c2820..f44478c 100644
--- a/testing/buildbot/filters/webui_polymer2_browser_tests.filter
+++ b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
@@ -189,6 +189,7 @@
 FileManagerPathUtilConvertUrlTest.*
 FileManagerUITest.*
 FileTasksBrowserTest.*
+*/FilesAppBrowserTest.*
 GalleryBrowserTest.*
 GalleryBrowserTestInGuestMode.*
 GalleryJsTest.*
@@ -279,7 +280,6 @@
 SigninSyncConfirmationTest.*
 SiteEngagementBrowserTest.*
 SysInternalsBrowserTest.*
-TabIndex/FilesAppBrowserTest.*
 TextDefaultsTest.*
 TtsAccessibilityTest.*
 UserManagerBrowserTest.*
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 94f77fcd..0b20114 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -694,13 +694,6 @@
       "--enable-features=AutofillManualFallback",
     ],
   },
-  "ios_chrome_payments_egtests": {
-    "label": "//ios/chrome/test/earl_grey:ios_chrome_payments_egtests",
-    "type": "raw",
-    "args": [
-      "--enable-features=WebPayments",
-    ],
-  },
   "ios_chrome_reading_list_egtests": {
     "label": "//ios/chrome/test/earl_grey:ios_chrome_reading_list_egtests",
     "type": "raw",
diff --git a/testing/buildbot/manage.py b/testing/buildbot/manage.py
index 2767a32..09133545 100755
--- a/testing/buildbot/manage.py
+++ b/testing/buildbot/manage.py
@@ -96,7 +96,6 @@
   'ios_chrome_bookmarks_egtests',
   'ios_chrome_integration_egtests',
   'ios_chrome_manual_fill_egtests',
-  'ios_chrome_payments_egtests',
   'ios_chrome_reading_list_egtests',
   'ios_chrome_settings_egtests',
   'ios_chrome_smoke_egtests',
diff --git a/testing/buildbot/tryserver.webrtc.json b/testing/buildbot/tryserver.webrtc.json
index 375922ca..b57d1b6 100644
--- a/testing/buildbot/tryserver.webrtc.json
+++ b/testing/buildbot/tryserver.webrtc.json
@@ -8,7 +8,8 @@
       "content_browsertests",
       "content_unittests",
       "jingle_unittests",
-      "remoting_unittests"
+      "remoting_unittests",
+      "webkit_unit_tests"
     ]
   },
   "linux_chromium_compile": {
@@ -19,7 +20,8 @@
       "content_unittests",
       "jingle_unittests",
       "remoting_unittests",
-      "remoting/webapp:webapp"
+      "remoting/webapp:webapp",
+      "webkit_unit_tests"
     ]
   },
   "mac_chromium_compile": {
@@ -30,7 +32,8 @@
       "content_unittests",
       "jingle_unittests",
       "remoting_unittests",
-      "remoting/webapp:webapp"
+      "remoting/webapp:webapp",
+      "webkit_unit_tests"
     ]
   },
   "win_chromium_compile": {
@@ -41,7 +44,8 @@
       "content_unittests",
       "jingle_unittests",
       "remoting_unittests",
-      "remoting/webapp:webapp"
+      "remoting/webapp:webapp",
+      "webkit_unit_tests"
     ]
   }
 }
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 6da83ce..f58a0c4 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3919,6 +3919,7 @@
           'content_unittests',
           'jingle_unittests',
           'remoting_unittests',
+          'webkit_unit_tests',
         ],
       },
       'linux_chromium_compile': {
@@ -3930,6 +3931,7 @@
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
+          'webkit_unit_tests',
         ],
       },
       'mac_chromium_compile': {
@@ -3941,6 +3943,7 @@
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
+          'webkit_unit_tests',
         ],
       },
       'win_chromium_compile': {
@@ -3952,6 +3955,7 @@
           'jingle_unittests',
           'remoting_unittests',
           'remoting/webapp:webapp',
+          'webkit_unit_tests',
         ],
       },
     },
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index b44c4f5..f88bb92 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1511,6 +1511,7 @@
 crbug.com/249112 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-007.xht [ Skip ]
 crbug.com/467127 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-008.xht [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-009.html [ Skip ]
+crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-010.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-002.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-003.xht [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-on-different-targets-via-main-thread-expected.txt b/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-on-different-targets-via-main-thread-expected.txt
new file mode 100644
index 0000000..07ff5d8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-on-different-targets-via-main-thread-expected.txt
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: line 63: background-color for the first target is: rgb(0, 128, 128)
+CONSOLE MESSAGE: line 65: box-shadow for the second target is: rgb(0, 128, 128) 4px 4px 25px 0px
+
diff --git a/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-via-main-thread.html b/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-on-different-targets-via-main-thread.html
similarity index 69%
rename from third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-via-main-thread.html
rename to third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-on-different-targets-via-main-thread.html
index 7ac66cf..58418d0 100644
--- a/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-via-main-thread.html
+++ b/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-on-different-targets-via-main-thread.html
@@ -19,7 +19,9 @@
 <script id="simple_animate" type="text/worklet">
 registerAnimator("test_animator", class {
   animate(currentTime, effect) {
-    effect.localTime = 1000;
+    let effects = effect.getChildren();
+    effects[0].localTime = 1000;
+    effects[1].localTime = 1000;
   }
 });
 </script>
@@ -52,38 +54,16 @@
       { duration: 2000 }
   );
 
-  const effect3 = new KeyframeEffect(
-      document.getElementById("target"),
-      [
-        { width: '100px' },
-        { width: '200px' }
-      ],
-      { duration: 2000 }
-  );
-
-  const effect4 = new KeyframeEffect(
-      document.getElementById("target2"),
-      [
-        { opacity: 1 },
-        { opacity: 0 }
-      ],
-      { duration: 2000 }
-  );
-
   const animation = new WorkletAnimation('test_animator',
-      [ effect, effect2, effect3, effect4 ]);
+      [ effect, effect2 ]);
   animation.play();
 
   if (window.testRunner) {
     waitTwoAnimationFrames( _ => {
       console.log('background-color for the first target is: ' +
           getComputedStyle(document.getElementById('target')).backgroundColor);
-      console.log('width for the first target is: ' +
-          getComputedStyle(document.getElementById('target')).width);
       console.log('box-shadow for the second target is: ' +
           getComputedStyle(document.getElementById('target2')).boxShadow);
-      console.log('opacity for the second target is: ' +
-          getComputedStyle(document.getElementById('target2')).opacity);
       testRunner.notifyDone();
     });
   }
diff --git a/third_party/WebKit/LayoutTests/animations/animationworklet/multiple-effects-on-same-target-driven-by-individual-local-time-expected.txt b/third_party/WebKit/LayoutTests/animations/animationworklet/multiple-effects-on-same-target-driven-by-individual-local-time-expected.txt
new file mode 100644
index 0000000..1bc3597e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/animationworklet/multiple-effects-on-same-target-driven-by-individual-local-time-expected.txt
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: line 62: background-color for the first target is: rgb(0, 255, 0)
+CONSOLE MESSAGE: line 64: width for the first target is: 150px
+
diff --git a/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-via-main-thread.html b/third_party/WebKit/LayoutTests/animations/animationworklet/multiple-effects-on-same-target-driven-by-individual-local-time.html
similarity index 65%
copy from third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-via-main-thread.html
copy to third_party/WebKit/LayoutTests/animations/animationworklet/multiple-effects-on-same-target-driven-by-individual-local-time.html
index 7ac66cf..e927d98 100644
--- a/third_party/WebKit/LayoutTests/animations/animationworklet/animate-multiple-effects-via-main-thread.html
+++ b/third_party/WebKit/LayoutTests/animations/animationworklet/multiple-effects-on-same-target-driven-by-individual-local-time.html
@@ -14,12 +14,13 @@
 </style>
 
 <div id="target"></div>
-<div id="target2"></div>
 
 <script id="simple_animate" type="text/worklet">
 registerAnimator("test_animator", class {
   animate(currentTime, effect) {
-    effect.localTime = 1000;
+    let effects = effect.getChildren();
+    effects[0].localTime = 0;
+    effects[1].localTime = 1000;
   }
 });
 </script>
@@ -44,15 +45,6 @@
   );
 
   const effect2 = new KeyframeEffect(
-      document.getElementById("target2"),
-      [
-        { boxShadow: '4px 4px 25px #00f' },
-        { boxShadow: '4px 4px 25px #0f0' }
-      ],
-      { duration: 2000 }
-  );
-
-  const effect3 = new KeyframeEffect(
       document.getElementById("target"),
       [
         { width: '100px' },
@@ -61,17 +53,8 @@
       { duration: 2000 }
   );
 
-  const effect4 = new KeyframeEffect(
-      document.getElementById("target2"),
-      [
-        { opacity: 1 },
-        { opacity: 0 }
-      ],
-      { duration: 2000 }
-  );
-
   const animation = new WorkletAnimation('test_animator',
-      [ effect, effect2, effect3, effect4 ]);
+      [ effect, effect2 ]);
   animation.play();
 
   if (window.testRunner) {
@@ -80,10 +63,6 @@
           getComputedStyle(document.getElementById('target')).backgroundColor);
       console.log('width for the first target is: ' +
           getComputedStyle(document.getElementById('target')).width);
-      console.log('box-shadow for the second target is: ' +
-          getComputedStyle(document.getElementById('target2')).boxShadow);
-      console.log('opacity for the second target is: ' +
-          getComputedStyle(document.getElementById('target2')).opacity);
       testRunner.notifyDone();
     });
   }
diff --git a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-different-image-formats-expected.png b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-different-image-formats-expected.png
index 94e02a6b..46520bae 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-different-image-formats-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-different-image-formats-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-image-image-expected.png b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-image-image-expected.png
index 260b6a7a..2123608 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-image-image-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-image-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-010.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-010.html
new file mode 100644
index 0000000..482c062d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-010.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<title>Tests correct handling of min-height: min-content with dynamic changes</title>
+<link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths" title="4.5. Implied Minimum Size of Flex Items" />
+<link rel="author" title="Google Inc." href="http://www.google.com/">
+<link href="support/flexbox.css" rel="stylesheet">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<style>
+.container {
+  height: 300px;
+  outline: 2px solid black;
+}
+
+.inner
+{
+  width: 400px;
+  flex: 1;
+  background-color: green;
+}
+#container2 .flexbox > * { flex-basis: 0; }
+#container2 .column > * { flex-basis: auto; }
+.container .flexbox { min-height: min-content; }
+.container > .flexbox { min-height: 0; }
+</style>
+<script>
+function change() {
+  var container = document.getElementById('container');
+  container.offsetHeight;
+  container.style.height = '80px';
+  container = document.getElementById('container2');
+  container.offsetHeight;
+  container.style.height = '80px';
+  checkLayout('.container');
+}
+</script>
+<body onload="change()">
+<p>Green rectangle should be entirely within the black rectangle</p>
+<div id="log"></div>
+<div id="container" class="container">
+  <div class="flexbox column" style="height: 100%;">
+    <div class="flexbox flex-one">
+        <div class="flexbox column">
+          <div class="flexbox column flex-one">
+            <div class="inner" data-expected-height="80">
+            </div>
+          </div>
+        </div>
+    </div>
+  </div>
+</div>
+
+<div id="container2" class="container">
+  <div class="flexbox column" style="height: 100%;">
+    <div class="flexbox flex-one">
+        <div class="flexbox column">
+          <div class="flexbox column flex-one">
+            <div class="inner" data-expected-height="80">
+            </div>
+          </div>
+        </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize18-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize18-expected.png
index 307a323..b16289d8 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize18-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize18-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize19-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize19-expected.png
index 436dbf16..caf1f2b 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize19-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize19-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize21-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize21-expected.png
index 307a323..b16289d8 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize21-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize21-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize22-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize22-expected.png
index 2ccbf0cc..d6a8962 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize22-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize22-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/contain-and-cover-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/contain-and-cover-expected.png
index 6eee9d4..d8a7026d 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/contain-and-cover-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/contain-and-cover-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/contain-and-cover-zoomed-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
index 55f3261..a9c7b12 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/hidpi/static/pointerevents/pointerevent_touch-adjustment_click_target.html b/third_party/WebKit/LayoutTests/fast/hidpi/static/pointerevents/pointerevent_touch-adjustment_click_target.html
new file mode 100644
index 0000000..a2b8bc1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/hidpi/static/pointerevents/pointerevent_touch-adjustment_click_target.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Touch-generated events should have the same target</title>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<body onload="inject_input()">
+<p>Touch letter 'O' below to run the test. If a "PASS" result appears the test passes, otherwise it fails</p>
+<p><a href="#" id="link" style="margin:3px">Link</a> <span id="target">O</span></p>
+<div id="log"></div>
+</body>
+<script>
+const target = document.getElementById('target');
+const xPosition = target.offsetLeft + 2;
+const yPosition = target.offsetTop + 2;
+
+async_test(t => {
+    const link = document.getElementById('link');
+    const expectedEventLog = ['pointerdown-link', 'touchstart-link', 'pointerup-link', 'touchend-link', 'click-link'];
+    const eventLogRecorder = [];
+
+    const eventNames = ['touchstart', 'touchmove', 'touchend', 'pointerdown', 'pointermove', 'pointerup', 'click'];
+    for (eventName of eventNames) {
+        document.addEventListener(eventName, t.step_func(event => {
+            // TouchEvent and PointerEvent should have the same un-adjusted coordinates.
+            // click event should have coordinates adjusted to link element.
+            const eventClientX = event.clientX || (event.touches.length > 0 ? event.touches[0].clientX : 0);
+            const eventClientY = event.clientY || (event.touches.length > 0 ? event.touches[0].clientY : 0);
+
+            if (event.type === 'click') {
+                assert_equals(document.elementFromPoint(eventClientX, eventClientY), link,
+                    'click should have clientX/Y adjusted to link.');
+            } else if (event.type != 'touchend') {
+                assert_equals(eventClientX, xPosition,
+                    `${event.type} should have un-adjusted x coordinates.`);
+                assert_equals(eventClientY, yPosition,
+                    `${event.type} should have un-adjusted y coordinates.`);
+            }
+
+            // All events should have target adjusted to link.
+            const targetName = event.target.id || event.target.nodeName || '[null]';
+            eventLogRecorder.push(`${event.type}-${targetName}`);
+            if (event.type === 'click') {
+                assert_array_equals(eventLogRecorder, expectedEventLog);
+                t.done();
+            }
+        }));
+    }
+});
+</script>
+<script>
+    function inject_input() {
+      return new Promise(function(resolve, reject) {
+        if (window.chrome && chrome.gpuBenchmarking) {
+          chrome.gpuBenchmarking.pointerActionSequence( [
+            {source: 'touch',
+             actions: [
+                { name: 'pointerDown', x: xPosition, y: yPosition },
+                { name: 'pointerUp' }
+            ]}], resolve);
+        } else {
+          reject();
+        }
+      });
+    }
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/network/failed-request-preview-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/network/failed-request-preview-expected.txt
new file mode 100644
index 0000000..0838edb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/network/failed-request-preview-expected.txt
@@ -0,0 +1,5 @@
+Verifies that network request previews don't have src set when the request fails
+request.url(): http://localhost:8000/
+request.failed: true
+previewImage.src: 
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/network/failed-request-preview.js b/third_party/WebKit/LayoutTests/http/tests/devtools/network/failed-request-preview.js
new file mode 100644
index 0000000..0de3e484
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/network/failed-request-preview.js
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`Verifies that network request previews don't have src set when the request fails`);
+  await TestRunner.loadModule('application_test_runner');
+  await TestRunner.loadModule('network_test_runner');
+  await TestRunner.showPanel('network');
+
+  SDK.multitargetNetworkManager.setBlockingEnabled(true);
+  TestRunner.networkManager.addEventListener(
+    SDK.NetworkManager.Events.RequestFinished, (event) => {
+      const request = event.data;
+      TestRunner.addResult('request.url(): ' + request.url());
+      TestRunner.addResult('request.failed: ' + request.failed);
+
+      const previewImage = createElementWithClass('img', 'image-network-icon-preview');
+      request.populateImageSource(previewImage).then(() => {
+        TestRunner.addResult('previewImage.src: ' + previewImage.src);
+        TestRunner.completeTest();
+      });
+    });
+
+  SDK.multitargetNetworkManager.setBlockedPatterns([
+    {url: '*', enabled: true}
+  ]);
+
+  NetworkTestRunner.makeXHR('GET', 'http://localhost:8000');
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt b/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
index c2476942..33995cd7 100644
--- a/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
@@ -25,6 +25,9 @@
 CONSOLE MESSAGE: line 153:     method constructor
 CONSOLE MESSAGE: line 153: interface WorkletGlobalScope
 CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WorkletGroupEffectProxy
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getChildren
 CONSOLE MESSAGE: line 153: interface WritableStream
 CONSOLE MESSAGE: line 153:     getter locked
 CONSOLE MESSAGE: line 153:     method abort
diff --git a/third_party/WebKit/LayoutTests/images/jpeg-yuv-progressive-canvas-expected.png b/third_party/WebKit/LayoutTests/images/jpeg-yuv-progressive-canvas-expected.png
index 3bbf755..23438b0 100644
--- a/third_party/WebKit/LayoutTests/images/jpeg-yuv-progressive-canvas-expected.png
+++ b/third_party/WebKit/LayoutTests/images/jpeg-yuv-progressive-canvas-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/images/jpeg-yuv-progressive-image-expected.png
index 136bf11..fc85a45 100644
--- a/third_party/WebKit/LayoutTests/images/jpeg-yuv-progressive-image-expected.png
+++ b/third_party/WebKit/LayoutTests/images/jpeg-yuv-progressive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-420-height-not-whole-mcu.jpg b/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-420-height-not-whole-mcu.jpg
new file mode 100644
index 0000000..c85a236
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-420-height-not-whole-mcu.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-420-width-not-whole-mcu.jpg b/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-420-width-not-whole-mcu.jpg
new file mode 100644
index 0000000..fc5bc164
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-420-width-not-whole-mcu.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-422-whole-mcus.jpg b/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-422-whole-mcus.jpg
new file mode 100644
index 0000000..5cdeb3c0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/icc-v2-gbr-422-whole-mcus.jpg
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/android/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt b/third_party/WebKit/LayoutTests/platform/android/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
index d07266d..6cde39f 100644
--- a/third_party/WebKit/LayoutTests/platform/android/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/http/tests/origin_trials/webexposed/animationworklet-origin-trial-interfaces-worklet-scope-expected.txt
@@ -25,6 +25,9 @@
 CONSOLE MESSAGE: line 151:     method constructor
 CONSOLE MESSAGE: line 151: interface WorkletGlobalScope
 CONSOLE MESSAGE: line 151:     method constructor
+CONSOLE MESSAGE: line 153: interface WorkletGroupEffectProxy
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getChildren
 CONSOLE MESSAGE: line 151: interface WritableStream
 CONSOLE MESSAGE: line 151:     getter locked
 CONSOLE MESSAGE: line 151:     method abort
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/size/contain-and-cover-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/size/contain-and-cover-expected.png
index b75d1616..a8784384 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/size/contain-and-cover-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/size/contain-and-cover-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
index ea3d4171..a0a9ef4 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
index 6f76cd9..dc78de88 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/carto.net/selectionlist-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/carto.net/selectionlist-expected.png
index 4599d87..c8c2c8e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/carto.net/selectionlist-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/carto.net/selectionlist-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/image-rescale-clip-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/image-rescale-clip-expected.png
index b4e57b2d..71983dd 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/image-rescale-clip-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/image-rescale-clip-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/image-rescale-scroll-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/image-rescale-scroll-expected.png
index 28b9bc8..ba7f258e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/image-rescale-scroll-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/image-rescale-scroll-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.png
index 5127861..aa25797 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug11026-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug11026-expected.png
index 3be7a63..d17ff81a 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug11026-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug11026-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4284-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4284-expected.png
index 8e11e44b..bc83055 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4284-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4284-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4427-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4427-expected.png
index 170ee2c..4a0b8ed 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4427-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4427-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug625-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug625-expected.png
index e2c14cdd..3912c50 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug625-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug625-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/bloomberg-expected.png
index 6edf690..8f10d11e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/bloomberg-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/bloomberg-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
deleted file mode 100644
index f550526..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
deleted file mode 100644
index f550526..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/tables/mozilla/core/bloomberg-expected.png
index 9626ece3..7c0a096142 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/tables/mozilla/core/bloomberg-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/tables/mozilla/core/bloomberg-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
deleted file mode 100644
index f550526..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
deleted file mode 100644
index f550526..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
deleted file mode 100644
index f550526..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
index c6704e4..ddae108 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
index 2dec00e..e174cf6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/carto.net/selectionlist-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/carto.net/selectionlist-expected.png
index 14efad6..404e509 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/carto.net/selectionlist-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/carto.net/selectionlist-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/image-rescale-clip-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/image-rescale-clip-expected.png
index de9687d..25f75b0 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/image-rescale-clip-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/image-rescale-clip-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/image-rescale-scroll-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/image-rescale-scroll-expected.png
index f8a10e88..2009544 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/image-rescale-scroll-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/image-rescale-scroll-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug101674-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug101674-expected.png
index 1945054..a4c7cdd 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug101674-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug101674-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug11026-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug11026-expected.png
index e98efd4..76b09fc 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug11026-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug11026-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4284-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4284-expected.png
index 64a5b14..8a86a2b5 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4284-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4284-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4427-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4427-expected.png
index 01d6cc8..97cdd585 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4427-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4427-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug625-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug625-expected.png
index cc918b77..1721820 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug625-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug625-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/bloomberg-expected.png
index 10a657e..b465b5187 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/bloomberg-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/bloomberg-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
deleted file mode 100644
index f550526..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
index 30547917..d78d28c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
index a2cf71ec..3d78dc78 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/coords-viewattr-02-b-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/carto.net/selectionlist-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/carto.net/selectionlist-expected.png
index 7e6a3de..90b6f6b6 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/carto.net/selectionlist-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/carto.net/selectionlist-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/image-rescale-clip-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/image-rescale-clip-expected.png
index 39c667e..a69cb76 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/image-rescale-clip-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/image-rescale-clip-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/image-rescale-scroll-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/image-rescale-scroll-expected.png
index 51633f1e..e7d91f46 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/image-rescale-scroll-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/image-rescale-scroll-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug101674-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug101674-expected.png
index eb316b4..f4e1f57 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug101674-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug101674-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug11026-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug11026-expected.png
index 9a17ac31..0efcc81 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug11026-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug11026-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4284-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4284-expected.png
index e62d9ea..aef9ca7b 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4284-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4284-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4427-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4427-expected.png
index 1456ac4..bafd960f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4427-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4427-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug625-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug625-expected.png
index 49c6740..d087485 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug625-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug625-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/bloomberg-expected.png
index 46cd39a..da1b5fd0 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/bloomberg-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/bloomberg-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
deleted file mode 100644
index f550526..0000000
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
deleted file mode 100644
index f550526..0000000
--- a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/jpeg-yuv-progressive-canvas-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/jpeg-yuv-progressive-canvas-expected.png
index 3cb69e14..3be3edb 100644
--- a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/jpeg-yuv-progressive-canvas-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/jpeg-yuv-progressive-canvas-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/jpeg-yuv-progressive-image-expected.png
index 8b345df30..9d38684 100644
--- a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/jpeg-yuv-progressive-image-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/jpeg-yuv-progressive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/exif-orientation-height-image-document-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/exif-orientation-height-image-document-expected.png
index 55ef4be0..4ebe79a 100644
--- a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/exif-orientation-height-image-document-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/exif-orientation-height-image-document-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
index 42913af6..2143db1 100644
--- a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/jpeg-yuv-progressive-image-expected.png
Binary files differ
diff --git a/third_party/blink/public/platform/web_coalesced_input_event.h b/third_party/blink/public/platform/web_coalesced_input_event.h
index 19653c8..6488167 100644
--- a/third_party/blink/public/platform/web_coalesced_input_event.h
+++ b/third_party/blink/public/platform/web_coalesced_input_event.h
@@ -33,6 +33,11 @@
   const WebInputEvent& CoalescedEvent(size_t index) const;
   std::vector<const WebInputEvent*> GetCoalescedEventsPointers() const;
 
+  void AddPredictedEvent(const blink::WebInputEvent&);
+  size_t PredictedEventSize() const;
+  const WebInputEvent& PredictedEvent(size_t index) const;
+  std::vector<const WebInputEvent*> GetPredictedEventsPointers() const;
+
  private:
   // TODO(hans): Remove this once clang-cl knows to not inline dtors that
   // call operator(), https://crbug.com/691714
@@ -47,6 +52,7 @@
 
   WebScopedInputEvent event_;
   std::vector<WebScopedInputEvent> coalesced_events_;
+  std::vector<WebScopedInputEvent> predicted_events_;
 };
 
 using WebScopedCoalescedInputEvent = std::unique_ptr<WebCoalescedInputEvent>;
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index ece3c3fc..5e96a0e 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -63,6 +63,11 @@
 
 class WebWidget {
  public:
+  // Called during set up of the WebWidget to declare the WebLayerTreeView for
+  // the widget to use. This does not pass ownership, but the caller must keep
+  // the pointer valid until Close() is called.
+  virtual void SetLayerTreeView(WebLayerTreeView*) = 0;
+
   // This method closes and deletes the WebWidget.
   virtual void Close() {}
 
diff --git a/third_party/blink/public/web/web_widget_client.h b/third_party/blink/public/web/web_widget_client.h
index 9e8c537..fb95d331 100644
--- a/third_party/blink/public/web/web_widget_client.h
+++ b/third_party/blink/public/web/web_widget_client.h
@@ -64,12 +64,6 @@
   // Called when a region of the WebWidget needs to be re-painted.
   virtual void DidInvalidateRect(const WebRect&) {}
 
-  // Initializes the layer compositor for the widget, returning a pointer
-  // to the newly constructed LayerTreeView that provides access to the
-  // compositor, which is owned by the WebWidgetClient. Will return null if
-  // AllowsBrokenNullLayerTreeView() is true.
-  virtual WebLayerTreeView* InitializeLayerTreeView() = 0;
-
   // FIXME: Remove all overrides of this.
   virtual bool AllowsBrokenNullLayerTreeView() const { return false; }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/dom_wrapper_world_test.cc b/third_party/blink/renderer/bindings/core/v8/dom_wrapper_world_test.cc
index ef15fb17..64036ac 100644
--- a/third_party/blink/renderer/bindings/core/v8/dom_wrapper_world_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/dom_wrapper_world_test.cc
@@ -23,9 +23,9 @@
     v8::Isolate* isolate) {
   Vector<scoped_refptr<DOMWrapperWorld>> worlds;
   worlds.push_back(DOMWrapperWorld::EnsureIsolatedWorld(
-      isolate, DOMWrapperWorld::WorldId::kMainWorldId + 1));
+      isolate, DOMWrapperWorld::kMainWorldId + 1));
   worlds.push_back(DOMWrapperWorld::EnsureIsolatedWorld(
-      isolate, DOMWrapperWorld::WorldId::kIsolatedWorldIdLimit - 1));
+      isolate, DOMWrapperWorld::kDOMWrapperWorldEmbedderWorldIdLimit - 1));
   EXPECT_TRUE(worlds[0]->IsIsolatedWorld());
   EXPECT_TRUE(worlds[1]->IsIsolatedWorld());
   return worlds;
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.cc b/third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.cc
index ac84407..10efc8b6 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.cc
@@ -4,22 +4,19 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
+
 namespace blink {
 
-v8::Local<v8::Object> V8IteratorResultValue(v8::Isolate* isolate,
+v8::Local<v8::Object> V8IteratorResultValue(ScriptState* script_state,
                                             bool done,
                                             v8::Local<v8::Value> value) {
-  v8::Local<v8::Object> result = v8::Object::New(isolate);
   if (value.IsEmpty())
-    value = v8::Undefined(isolate);
-  if (!V8CallBoolean(result->CreateDataProperty(
-          isolate->GetCurrentContext(), V8AtomicString(isolate, "done"),
-          v8::Boolean::New(isolate, done))) ||
-      !V8CallBoolean(
-          result->CreateDataProperty(isolate->GetCurrentContext(),
-                                     V8AtomicString(isolate, "value"), value)))
-    return v8::Local<v8::Object>();
-  return result;
+    value = v8::Undefined(script_state->GetIsolate());
+  return V8ObjectBuilder(script_state)
+      .Add("done", done)
+      .Add("value", value)
+      .V8Value();
 }
 
 v8::MaybeLocal<v8::Value> V8UnpackIteratorResult(ScriptState* script_state,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h b/third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h
index 0c5c246..6d376489 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h
@@ -15,7 +15,7 @@
 // "Iterator result" in this file is an object returned from iterator.next()
 // having two members "done" and "value".
 
-CORE_EXPORT v8::Local<v8::Object> V8IteratorResultValue(v8::Isolate*,
+CORE_EXPORT v8::Local<v8::Object> V8IteratorResultValue(ScriptState*,
                                                         bool done,
                                                         v8::Local<v8::Value>);
 
@@ -28,7 +28,7 @@
 inline ScriptValue V8IteratorResult(ScriptState* script_state, const T& value) {
   return ScriptValue(
       script_state,
-      V8IteratorResultValue(script_state->GetIsolate(), false,
+      V8IteratorResultValue(script_state, false,
                             ToV8(value, script_state->GetContext()->Global(),
                                  script_state->GetIsolate())));
 }
@@ -36,7 +36,7 @@
 inline ScriptValue V8IteratorResultDone(ScriptState* script_state) {
   return ScriptValue(
       script_state,
-      V8IteratorResultValue(script_state->GetIsolate(), true,
+      V8IteratorResultValue(script_state, true,
                             v8::Undefined(script_state->GetIsolate())));
 }
 
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index a164dc1..8bd506f 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -313,7 +313,8 @@
   DCHECK_EQ(popup_client_->OwnerElement().GetDocument().ExistingAXObjectCache(),
             frame->GetDocument()->ExistingAXObjectCache());
 
-  InitializeLayerTreeView();
+  layer_tree_view_->SetVisible(true);
+  page_->LayerTreeViewInitialized(*layer_tree_view_, nullptr);
 
   scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
   popup_client_->WriteDocument(data.get());
@@ -324,6 +325,15 @@
   SetFocus(true);
 }
 
+void WebPagePopupImpl::SetLayerTreeView(WebLayerTreeView* layer_tree_view) {
+  // The WebWidgetClient is given |this| as its WebWidget but it is set up
+  // before Initialize() is called on |this|. So we store the |layer_tree_view|
+  // here, but finish setting it up in Initialize().
+  layer_tree_view_ = layer_tree_view;
+  animation_host_ = std::make_unique<CompositorAnimationHost>(
+      layer_tree_view_->CompositorAnimationHost());
+}
+
 void WebPagePopupImpl::PostMessageToPopup(const String& message) {
   if (!page_)
     return;
@@ -369,15 +379,6 @@
   }
 }
 
-void WebPagePopupImpl::InitializeLayerTreeView() {
-  TRACE_EVENT0("blink", "WebPagePopupImpl::InitializeLayerTreeView");
-  layer_tree_view_ = widget_client_->InitializeLayerTreeView();
-  layer_tree_view_->SetVisible(true);
-  animation_host_ = std::make_unique<CompositorAnimationHost>(
-      layer_tree_view_->CompositorAnimationHost());
-  page_->LayerTreeViewInitialized(*layer_tree_view_, nullptr);
-}
-
 void WebPagePopupImpl::SetSuppressFrameRequestsWorkaroundFor704763Only(
     bool suppress_frame_requests) {
   if (!page_)
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.h b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
index da5171d..d954c8654 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.h
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
@@ -82,6 +82,7 @@
 
  private:
   // WebWidget functions
+  void SetLayerTreeView(WebLayerTreeView*) override;
   void SetSuppressFrameRequestsWorkaroundFor704763Only(bool) final;
   void BeginFrame(base::TimeTicks last_frame_time) override;
   void UpdateLifecycle(LifecycleUpdate requested_update) override;
@@ -116,7 +117,6 @@
 
   explicit WebPagePopupImpl(WebWidgetClient*);
   void DestroyPage();
-  void InitializeLayerTreeView();
   void SetRootLayer(cc::Layer*);
 
   WebRect WindowRectInScreen() const;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index b428d92..ce20989d 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -349,15 +349,10 @@
   // they avoid the compositor by using a null client, and sometimes by having
   // the client return a null compositor. We should make things more consistent
   // and clear.
-  if (WidgetClient()) {
-    if (WidgetClient()->AllowsBrokenNullLayerTreeView()) {
-      // For some reason this was not set when WidgetClient() is not provided,
-      // even though there will be no LayerTreeView in that case either.
-      page_->GetSettings().SetAcceleratedCompositingEnabled(false);
-    } else {
-      InitializeLayerTreeView();
-    }
-  }
+  // For some reason this was not set when WidgetClient() is not provided,
+  // even though there will be no LayerTreeView in that case either.
+  if (WidgetClient() && WidgetClient()->AllowsBrokenNullLayerTreeView())
+    page_->GetSettings().SetAcceleratedCompositingEnabled(false);
 
   dev_tools_emulator_ = DevToolsEmulator::Create(this);
 
@@ -3269,8 +3264,8 @@
     client_->WidgetClient()->ScheduleAnimation();
 }
 
-void WebViewImpl::InitializeLayerTreeView() {
-  layer_tree_view_ = WidgetClient()->InitializeLayerTreeView();
+void WebViewImpl::SetLayerTreeView(WebLayerTreeView* layer_tree_view) {
+  layer_tree_view_ = layer_tree_view;
   if (Platform::Current()->IsThreadedAnimationEnabled()) {
     animation_host_ = std::make_unique<CompositorAnimationHost>(
         layer_tree_view_->CompositorAnimationHost());
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index c4837e1..8b0bde13 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -108,6 +108,7 @@
   static bool UseExternalPopupMenus();
 
   // WebWidget methods:
+  void SetLayerTreeView(WebLayerTreeView*) override;
   void Close() override;
   WebSize Size() override;
   void Resize(const WebSize&) override;
@@ -491,8 +492,6 @@
 
   void ConfigureAutoResizeMode();
 
-  void InitializeLayerTreeView();
-
   void SetIsAcceleratedCompositingActive(bool);
   void DoComposite();
   void ReallocateRenderer();
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 31893ff..9a892c7 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -3717,13 +3717,9 @@
 // correctly is the job of LayoutTests/fast/events/event-handler-count.html.
 TEST_F(WebViewTest, HasTouchEventHandlers) {
   TouchEventHandlerWebWidgetClient client;
-  // We need to create a LayerTreeView for the client before loading the page,
-  // otherwise ChromeClient will default to assuming there are touch handlers.
-  WebLayerTreeView* layer_tree_view = client.InitializeLayerTreeView();
   std::string url = RegisterMockedHttpURLLoad("has_touch_event_handlers.html");
   WebViewImpl* web_view_impl =
       web_view_helper_.InitializeAndLoad(url, nullptr, nullptr, &client);
-  ASSERT_TRUE(layer_tree_view);
   const EventHandlerRegistry::EventHandlerClass kTouchEvent =
       EventHandlerRegistry::kTouchStartOrMoveEventBlocking;
 
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index 92d1bd3d..db8201b2 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -91,14 +91,14 @@
 }
 
 // Helper to create a default test client if the supplied client pointer is
-// null.
+// null. The |owned_client| is used to store the client if it must be created.
+// In both cases the client to be used is returned.
 template <typename T>
-std::unique_ptr<T> CreateDefaultClientIfNeeded(T*& client) {
+T* CreateDefaultClientIfNeeded(T* client, std::unique_ptr<T>& owned_client) {
   if (client)
-    return nullptr;
-  auto owned_client = std::make_unique<T>();
-  client = owned_client.get();
-  return owned_client;
+    return client;
+  owned_client = std::make_unique<T>();
+  return owned_client.get();
 }
 
 std::unique_ptr<WebNavigationParams> BuildDummyNavigationParams() {
@@ -179,8 +179,8 @@
 WebLocalFrameImpl* CreateLocalChild(WebLocalFrame& parent,
                                     WebTreeScopeType scope,
                                     TestWebFrameClient* client) {
-  std::unique_ptr<TestWebFrameClient> owned_client =
-      CreateDefaultClientIfNeeded(client);
+  std::unique_ptr<TestWebFrameClient> owned_client;
+  client = CreateDefaultClientIfNeeded(client, owned_client);
   WebLocalFrameImpl* frame =
       ToWebLocalFrameImpl(parent.CreateLocalChild(scope, client, nullptr));
   client->Bind(frame, std::move(owned_client));
@@ -201,8 +201,8 @@
 
 WebLocalFrameImpl* CreateProvisional(WebRemoteFrame& old_frame,
                                      TestWebFrameClient* client) {
-  std::unique_ptr<TestWebFrameClient> owned_client =
-      CreateDefaultClientIfNeeded(client);
+  std::unique_ptr<TestWebFrameClient> owned_client;
+  client = CreateDefaultClientIfNeeded(client, owned_client);
   WebLocalFrameImpl* frame =
       ToWebLocalFrameImpl(WebLocalFrame::CreateProvisional(
           client, nullptr, &old_frame, WebSandboxFlags::kNone,
@@ -220,14 +220,17 @@
     WebFrameWidget* frame_widget =
         WebFrameWidget::CreateForChildLocalRoot(widget_client.get(), frame);
     frame_widget->Resize(WebSize());
+    // The WebWidget requires a LayerTreeView to be set, either by the
+    // WebWidgetClient itself or by someone else. We do that here.
+    frame_widget->SetLayerTreeView(widget_client->layer_tree_view());
     client->BindWidgetClient(std::move(widget_client));
   }
   return frame;
 }
 
 WebRemoteFrameImpl* CreateRemote(TestWebRemoteFrameClient* client) {
-  std::unique_ptr<TestWebRemoteFrameClient> owned_client =
-      CreateDefaultClientIfNeeded(client);
+  std::unique_ptr<TestWebRemoteFrameClient> owned_client;
+  client = CreateDefaultClientIfNeeded(client, owned_client);
   auto* frame = WebRemoteFrameImpl::Create(WebTreeScopeType::kDocument, client);
   client->Bind(frame, std::move(owned_client));
   return frame;
@@ -239,19 +242,24 @@
                                     WebFrame* previous_sibling,
                                     TestWebFrameClient* client,
                                     TestWebWidgetClient* widget_client) {
-  std::unique_ptr<TestWebFrameClient> owned_client =
-      CreateDefaultClientIfNeeded(client);
+  std::unique_ptr<TestWebFrameClient> owned_client;
+  client = CreateDefaultClientIfNeeded(client, owned_client);
   WebLocalFrameImpl* frame = ToWebLocalFrameImpl(parent.CreateLocalChild(
       WebTreeScopeType::kDocument, name, WebSandboxFlags::kNone, client,
       nullptr, previous_sibling, ParsedFeaturePolicy(), properties, nullptr));
   client->Bind(frame, std::move(owned_client));
 
-  std::unique_ptr<TestWebWidgetClient> owned_widget_client =
-      CreateDefaultClientIfNeeded(widget_client);
-  WebFrameWidget::CreateForChildLocalRoot(widget_client, frame);
+  std::unique_ptr<TestWebWidgetClient> owned_widget_client;
+  widget_client =
+      CreateDefaultClientIfNeeded(widget_client, owned_widget_client);
+  WebFrameWidget* frame_widget =
+      WebFrameWidget::CreateForChildLocalRoot(widget_client, frame);
   // Set an initial size for subframes.
   if (frame->Parent())
-    frame->FrameWidget()->Resize(WebSize());
+    frame_widget->Resize(WebSize());
+  // The WebWidget requires a LayerTreeView to be set, either by the
+  // WebWidgetClient itself or by someone else. We do that here.
+  frame_widget->SetLayerTreeView(widget_client->layer_tree_view());
   client->BindWidgetClient(std::move(owned_widget_client));
   return frame;
 }
@@ -261,8 +269,8 @@
     const WebString& name,
     scoped_refptr<SecurityOrigin> security_origin,
     TestWebRemoteFrameClient* client) {
-  std::unique_ptr<TestWebRemoteFrameClient> owned_client =
-      CreateDefaultClientIfNeeded(client);
+  std::unique_ptr<TestWebRemoteFrameClient> owned_client;
+  client = CreateDefaultClientIfNeeded(client, owned_client);
   auto* frame = ToWebRemoteFrameImpl(parent.CreateRemoteChild(
       WebTreeScopeType::kDocument, name, WebSandboxFlags::kNone,
       ParsedFeaturePolicy(), client, nullptr));
@@ -292,8 +300,9 @@
   if (update_settings_func)
     update_settings_func(web_view_->GetSettings());
 
-  std::unique_ptr<TestWebFrameClient> owned_web_frame_client =
-      CreateDefaultClientIfNeeded(web_frame_client);
+  std::unique_ptr<TestWebFrameClient> owned_web_frame_client;
+  web_frame_client =
+      CreateDefaultClientIfNeeded(web_frame_client, owned_web_frame_client);
   WebLocalFrame* frame = WebLocalFrame::CreateMainFrame(
       web_view_, web_frame_client, nullptr, opener);
   web_frame_client->Bind(frame, std::move(owned_web_frame_client));
@@ -342,8 +351,9 @@
 
   InitializeWebView(web_view_client, nullptr);
 
-  std::unique_ptr<TestWebRemoteFrameClient> owned_web_remote_frame_client =
-      CreateDefaultClientIfNeeded(web_remote_frame_client);
+  std::unique_ptr<TestWebRemoteFrameClient> owned_web_remote_frame_client;
+  web_remote_frame_client = CreateDefaultClientIfNeeded(
+      web_remote_frame_client, owned_web_remote_frame_client);
   WebRemoteFrameImpl* frame = WebRemoteFrameImpl::CreateMainFrame(
       web_view_, web_remote_frame_client, nullptr);
   web_remote_frame_client->Bind(frame,
@@ -387,7 +397,8 @@
 
 void WebViewHelper::InitializeWebView(TestWebViewClient* web_view_client,
                                       class WebView* opener) {
-  owned_test_web_view_client_ = CreateDefaultClientIfNeeded(web_view_client);
+  web_view_client =
+      CreateDefaultClientIfNeeded(web_view_client, owned_test_web_view_client_);
   web_view_ = static_cast<WebViewImpl*>(
       WebView::Create(web_view_client, web_view_client,
                       mojom::PageVisibilityState::kVisible, opener));
@@ -399,6 +410,8 @@
   //
   // Consequently, all external image resources must be mocked.
   web_view_->GetSettings()->SetLoadsImagesAutomatically(true);
+
+  web_view_->SetLayerTreeView(web_view_client->layer_tree_view());
   web_view_->SetDeviceScaleFactor(
       web_view_client->GetScreenInfo().device_scale_factor);
   web_view_->SetDefaultPageScaleLimits(1, 4);
@@ -507,13 +520,12 @@
   return layer_tree_view_.get();
 }
 
-WebLayerTreeView* TestWebWidgetClient::InitializeLayerTreeView() {
-  return layer_tree_view_factory_.Initialize();
+TestWebWidgetClient::TestWebWidgetClient() {
+  layer_tree_view_ = layer_tree_view_factory_.Initialize();
 }
 
-WebLayerTreeView* TestWebViewClient::InitializeLayerTreeView() {
-  layer_tree_view_ = layer_tree_view_factory_.Initialize();
-  return layer_tree_view_;
+TestWebViewClient::TestWebViewClient(content::LayerTreeViewDelegate* delegate) {
+  layer_tree_view_ = layer_tree_view_factory_.Initialize(delegate);
 }
 
 }  // namespace FrameTestHelpers
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.h b/third_party/blink/renderer/core/frame/frame_test_helpers.h
index d0ad38a..fe7b16c 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.h
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.h
@@ -180,23 +180,25 @@
 
 class TestWebWidgetClient : public WebWidgetClient {
  public:
+  TestWebWidgetClient();
   ~TestWebWidgetClient() override = default;
 
-  // WebWidgetClient:
-  WebLayerTreeView* InitializeLayerTreeView() override;
+  content::LayerTreeView* layer_tree_view() { return layer_tree_view_; }
 
  private:
+  content::LayerTreeView* layer_tree_view_ = nullptr;
   LayerTreeViewFactory layer_tree_view_factory_;
 };
 
 class TestWebViewClient : public WebViewClient, public WebWidgetClient {
  public:
+  // If no delegate is given, a stub is used.
+  explicit TestWebViewClient(content::LayerTreeViewDelegate* = nullptr);
   ~TestWebViewClient() override = default;
 
   content::LayerTreeView* layer_tree_view() { return layer_tree_view_; }
 
   // WebWidgetClient:
-  WebLayerTreeView* InitializeLayerTreeView() override;
   void ScheduleAnimation() override { animation_scheduled_ = true; }
 
   // WebViewClient:
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index c37c6a6b..6606792 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -560,8 +560,6 @@
 }
 
 void WebFrameWidgetImpl::Initialize() {
-  InitializeLayerTreeView();
-
   if (LocalRoot()->Parent())
     SetBackgroundColorOverride(Color::kTransparent);
 }
@@ -1043,11 +1041,10 @@
   return document->FocusedElement();
 }
 
-void WebFrameWidgetImpl::InitializeLayerTreeView() {
+void WebFrameWidgetImpl::SetLayerTreeView(WebLayerTreeView* layer_tree_view) {
   DCHECK(Client());
   DCHECK(!mutator_dispatcher_);
-  layer_tree_view_ = Client()->InitializeLayerTreeView();
-  DCHECK(layer_tree_view_);
+  layer_tree_view_ = layer_tree_view;
   if (Platform::Current()->IsThreadedAnimationEnabled()) {
     animation_host_ = std::make_unique<CompositorAnimationHost>(
         layer_tree_view_->CompositorAnimationHost());
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index e1979d2..5d215256 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -134,6 +134,7 @@
 
   // WebFrameWidgetBase overrides:
   void Initialize() override;
+  void SetLayerTreeView(WebLayerTreeView*) override;
   bool ForSubframe() const override { return true; }
   void ScheduleAnimation() override;
   void IntrinsicSizingInfoChanged(const IntrinsicSizingInfo&) override;
@@ -170,8 +171,6 @@
   HitTestResult HitTestResultForRootFramePos(
       const LayoutPoint& pos_in_root_frame);
 
-  void InitializeLayerTreeView();
-
   void SetIsAcceleratedCompositingActive(bool);
   void UpdateLayerTreeViewport();
   void UpdateLayerTreeBackgroundColor();
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 68c60ee..2ab2d4ae 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -689,7 +689,7 @@
     const WebScriptSource& source_in) {
   DCHECK(GetFrame());
   CHECK_GT(world_id, 0);
-  CHECK_LT(world_id, DOMWrapperWorld::kEmbedderWorldIdLimit);
+  CHECK_LT(world_id, DOMWrapperWorld::kDOMWrapperWorldEmbedderWorldIdLimit);
 
   // Note: An error event in an isolated world will never be dispatched to
   // a foreign world.
@@ -704,7 +704,7 @@
     const WebScriptSource& source_in) {
   DCHECK(GetFrame());
   CHECK_GT(world_id, 0);
-  CHECK_LT(world_id, DOMWrapperWorld::kEmbedderWorldIdLimit);
+  CHECK_LT(world_id, DOMWrapperWorld::kDOMWrapperWorldEmbedderWorldIdLimit);
 
   // Note: An error event in an isolated world will never be dispatched to
   // a foreign world.
@@ -850,7 +850,7 @@
     WebScriptExecutionCallback* callback) {
   DCHECK(GetFrame());
   CHECK_GT(world_id, 0);
-  CHECK_LT(world_id, DOMWrapperWorld::kEmbedderWorldIdLimit);
+  CHECK_LT(world_id, DOMWrapperWorld::kDOMWrapperWorldEmbedderWorldIdLimit);
 
   scoped_refptr<DOMWrapperWorld> isolated_world =
       DOMWrapperWorld::EnsureIsolatedWorld(ToIsolate(GetFrame()), world_id);
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index 17729d3c..41899703 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -185,6 +185,12 @@
   web_view_->SetCompositorVisibility(true);
 }
 
+void WebViewFrameWidget::SetLayerTreeView(WebLayerTreeView*) {
+  // The WebViewImpl already has its LayerTreeView, the WebWidgetClient
+  // thus does not initialize and set another one here.
+  NOTREACHED();
+}
+
 void WebViewFrameWidget::ScheduleAnimation() {
   web_view_->ScheduleAnimationForWidget();
 }
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.h b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
index 0bb0d8b7..af2734d 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.h
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
@@ -85,6 +85,7 @@
 
   // WebFrameWidgetBase overrides:
   void Initialize() override;
+  void SetLayerTreeView(WebLayerTreeView*) override;
   bool ForSubframe() const override { return false; }
   void ScheduleAnimation() override;
   base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index 833ea0c..30424f3 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -258,9 +258,9 @@
   SetSuggestedOption(nullptr);
   if (is_autofilled_by_preview_)
     SetAutofillState(WebAutofillState::kNotFilled);
-  SelectOptionFlags flags = kDeselectOtherOptions | kMakeOptionDirty;
+  SelectOptionFlags flags = kDeselectOtherOptionsFlag | kMakeOptionDirtyFlag;
   if (send_events)
-    flags |= kDispatchInputAndChangeEvent;
+    flags |= kDispatchInputAndChangeEventFlag;
   SelectOption(option, flags);
 
   if (send_events && previous_selected_option != option && !UsesMenuList())
@@ -826,7 +826,7 @@
     SelectOption(first_enabled_option,
                  reason == kResetReasonSelectedOptionRemoved
                      ? 0
-                     : kDeselectOtherOptions);
+                     : kDeselectOtherOptionsFlag);
     last_selected_option = first_enabled_option;
     did_change = true;
   }
@@ -857,7 +857,7 @@
 }
 
 void HTMLSelectElement::setSelectedIndex(int index) {
-  SelectOption(item(index), kDeselectOtherOptions | kMakeOptionDirty);
+  SelectOption(item(index), kDeselectOtherOptionsFlag | kMakeOptionDirtyFlag);
 }
 
 int HTMLSelectElement::SelectedListIndex() const {
@@ -919,9 +919,9 @@
                                                     bool option_is_selected) {
   DCHECK_EQ(option->OwnerSelectElement(), this);
   if (option_is_selected)
-    SelectOption(option, IsMultiple() ? 0 : kDeselectOtherOptions);
+    SelectOption(option, IsMultiple() ? 0 : kDeselectOtherOptionsFlag);
   else if (!UsesMenuList() || IsMultiple())
-    SelectOption(nullptr, IsMultiple() ? 0 : kDeselectOtherOptions);
+    SelectOption(nullptr, IsMultiple() ? 0 : kDeselectOtherOptionsFlag);
   else
     ResetToDefaultSelection();
 }
@@ -931,7 +931,7 @@
   DCHECK_EQ(option.OwnerSelectElement(), this);
   SetRecalcListItems();
   if (option_is_selected) {
-    SelectOption(&option, IsMultiple() ? 0 : kDeselectOtherOptions);
+    SelectOption(&option, IsMultiple() ? 0 : kDeselectOtherOptionsFlag);
   } else {
     // No need to reset if we already have a selected option.
     if (!last_on_change_option_)
@@ -1009,12 +1009,12 @@
     if (!element->Selected())
       should_update_popup = true;
     element->SetSelectedState(true);
-    if (flags & kMakeOptionDirty)
+    if (flags & kMakeOptionDirtyFlag)
       element->SetDirty(true);
   }
 
   // DeselectItemsWithoutValidation() is O(N).
-  if (flags & kDeselectOtherOptions)
+  if (flags & kDeselectOtherOptionsFlag)
     should_update_popup |= DeselectItemsWithoutValidation(element);
 
   // We should update active selection after finishing OPTION state change
@@ -1022,10 +1022,10 @@
   if (element) {
     // setActiveSelectionAnchor is O(N).
     if (!active_selection_anchor_ || !IsMultiple() ||
-        flags & kDeselectOtherOptions)
+        flags & kDeselectOtherOptionsFlag)
       SetActiveSelectionAnchor(element);
     if (!active_selection_end_ || !IsMultiple() ||
-        flags & kDeselectOtherOptions)
+        flags & kDeselectOtherOptionsFlag)
       SetActiveSelectionEnd(element);
   }
 
@@ -1033,7 +1033,7 @@
   // LayoutMenuList::UpdateFromElement.
   bool should_dispatch_events = false;
   if (UsesMenuList()) {
-    should_dispatch_events = (flags & kDispatchInputAndChangeEvent) &&
+    should_dispatch_events = (flags & kDispatchInputAndChangeEventFlag) &&
                              last_on_change_option_ != element;
     last_on_change_option_ = element;
   }
@@ -1163,7 +1163,7 @@
   if (items_size == 0)
     return;
 
-  SelectOption(nullptr, kDeselectOtherOptions);
+  SelectOption(nullptr, kDeselectOtherOptionsFlag);
 
   // The saved state should have at least one value and an index.
   DCHECK_GE(state.ValueSize(), 2u);
@@ -1222,7 +1222,7 @@
     // WebKit. However Edge seems to "ask for a reset" simply.  As of 2016
     // March, the HTML specification says nothing about this.
     if (old_selected_option)
-      SelectOption(old_selected_option, kDeselectOtherOptions);
+      SelectOption(old_selected_option, kDeselectOtherOptionsFlag);
     else
       ResetToDefaultSelection();
   }
@@ -1345,8 +1345,8 @@
       handled = false;
 
     if (handled && option) {
-      SelectOption(option, kDeselectOtherOptions | kMakeOptionDirty |
-                               kDispatchInputAndChangeEvent);
+      SelectOption(option, kDeselectOtherOptionsFlag | kMakeOptionDirtyFlag |
+                               kDispatchInputAndChangeEventFlag);
     }
 
     if (handled)
@@ -1770,9 +1770,9 @@
       event, TypeAhead::kMatchPrefix | TypeAhead::kCycleFirstChar);
   if (index < 0)
     return;
-  SelectOption(OptionAtListIndex(index), kDeselectOtherOptions |
-                                             kMakeOptionDirty |
-                                             kDispatchInputAndChangeEvent);
+  SelectOption(OptionAtListIndex(index), kDeselectOtherOptionsFlag |
+                                             kMakeOptionDirtyFlag |
+                                             kDispatchInputAndChangeEventFlag);
   if (!UsesMenuList())
     ListBoxOnChange();
 }
@@ -1787,8 +1787,8 @@
   EventQueueScope scope;
   // If this index is already selected, unselect. otherwise update the
   // selected index.
-  SelectOptionFlags flags =
-      kDispatchInputAndChangeEvent | (IsMultiple() ? 0 : kDeselectOtherOptions);
+  SelectOptionFlags flags = kDispatchInputAndChangeEventFlag |
+                            (IsMultiple() ? 0 : kDeselectOtherOptionsFlag);
   if (option->Selected()) {
     if (UsesMenuList())
       SelectOption(nullptr, flags);
@@ -1951,8 +1951,8 @@
   // the selected option is not change.
   if (option == SelectedOption())
     return;
-  SelectOption(option, kDeselectOtherOptions | kMakeOptionDirty |
-                           kDispatchInputAndChangeEvent);
+  SelectOption(option, kDeselectOtherOptionsFlag | kMakeOptionDirtyFlag |
+                           kDispatchInputAndChangeEventFlag);
 }
 
 void HTMLSelectElement::PopupDidCancel() {
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.h b/third_party/blink/renderer/core/html/forms/html_select_element.h
index eab2da2..9199c12 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.h
@@ -233,9 +233,9 @@
   bool HasPlaceholderLabelOption() const;
 
   enum SelectOptionFlag {
-    kDeselectOtherOptions = 1 << 0,
-    kDispatchInputAndChangeEvent = 1 << 1,
-    kMakeOptionDirty = 1 << 2,
+    kDeselectOtherOptionsFlag = 1 << 0,
+    kDispatchInputAndChangeEventFlag = 1 << 1,
+    kMakeOptionDirtyFlag = 1 << 2,
   };
   typedef unsigned SelectOptionFlags;
   void SelectOption(HTMLOptionElement*, SelectOptionFlags);
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index f939223..160d74b 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -1752,7 +1752,8 @@
           touch_adjustment_result_.adjusted_point);
     } else {
       hit_rect_size = GetHitTestRectForAdjustment(
-          LayoutSize(adjusted_event.TapAreaInRootFrame()));
+          LayoutSize(adjusted_event.TapAreaInRootFrame()),
+          frame_->PageZoomFactor());
       if (!hit_rect_size.IsEmpty())
         hit_type |= HitTestRequest::kListBased;
     }
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index 2faa7369..6da95a6 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -326,7 +326,8 @@
          WebPointerProperties::PointerType::kTouch);
 
   LayoutSize hit_rect_size = GetHitTestRectForAdjustment(
-      LayoutSize(pointer_event.width, pointer_event.height));
+      LayoutSize(pointer_event.width, pointer_event.height),
+      frame_->PageZoomFactor());
 
   if (hit_rect_size.IsEmpty())
     return;
diff --git a/third_party/blink/renderer/core/layout/layout_block.h b/third_party/blink/renderer/core/layout/layout_block.h
index fb9ba1b..211c556 100644
--- a/third_party/blink/renderer/core/layout/layout_block.h
+++ b/third_party/blink/renderer/core/layout/layout_block.h
@@ -449,11 +449,11 @@
 
  private:
   void AddVisualOverflowFromBlockChildren();
-  void AddVisualOverflowFromTheme();
   void AddLayoutOverflowFromPositionedObjects();
   void AddLayoutOverflowFromBlockChildren();
 
  protected:
+  void AddVisualOverflowFromTheme();
   virtual void ComputeVisualOverflow(
       const LayoutRect& previous_visual_overflow_rect,
       bool recompute_floats);
@@ -461,7 +461,7 @@
                                      bool recompute_floats);
 
   virtual void AddLayoutOverflowFromChildren();
-  virtual void AddVisualOverflowFromChildren();
+  void AddVisualOverflowFromChildren();
 
   void AddOutlineRects(Vector<LayoutRect>&,
                        const LayoutPoint& additional_offset,
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index 163847a8d..bf9be92a 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -2566,11 +2566,20 @@
 void LayoutBlockFlow::ComputeVisualOverflow(
     const LayoutRect& previous_visual_overflow_rect,
     bool recompute_floats) {
-  LayoutBlock::ComputeVisualOverflow(previous_visual_overflow_rect,
-                                     recompute_floats);
+  AddVisualOverflowFromChildren();
+
+  AddVisualEffectOverflow();
+  AddVisualOverflowFromTheme();
+
   if (recompute_floats || CreatesNewFormattingContext() ||
       HasSelfPaintingLayer())
     AddVisualOverflowFromFloats();
+
+  if (VisualOverflowRect() != previous_visual_overflow_rect) {
+    if (Layer())
+      Layer()->SetNeedsCompositingInputsUpdate();
+    GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired);
+  }
 }
 
 void LayoutBlockFlow::ComputeLayoutOverflow(LayoutUnit old_client_after_edge,
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index b1ea5be..6ac0179 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3181,7 +3181,12 @@
 
 DISABLE_CFI_PERF
 void LayoutBox::UpdateLogicalHeight() {
-  intrinsic_content_logical_height_ = ContentLogicalHeight();
+  if (!HasOverrideLogicalHeight()) {
+    // If we have an override height, our children will have sized themselves
+    // relative to our override height, which would make our intrinsic size
+    // incorrect (too big).
+    intrinsic_content_logical_height_ = ContentLogicalHeight();
+  }
 
   LogicalExtentComputedValues computed_values;
   ComputeLogicalHeight(computed_values);
diff --git a/third_party/blink/renderer/core/layout/layout_list_item.cc b/third_party/blink/renderer/core/layout/layout_list_item.cc
index f2a7ed1..a729d66 100644
--- a/third_party/blink/renderer/core/layout/layout_list_item.cc
+++ b/third_party/blink/renderer/core/layout/layout_list_item.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/layout/layout_list_marker.h"
 #include "third_party/blink/renderer/core/paint/list_item_painter.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/platform/wtf/saturated_arithmetic.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -289,6 +290,25 @@
   return false;
 }
 
+void LayoutListItem::ComputeVisualOverflow(
+    const LayoutRect& previous_visual_overflow_rect,
+    bool recompute_floats) {
+  AddVisualOverflowFromChildren();
+
+  AddVisualEffectOverflow();
+  AddVisualOverflowFromTheme();
+
+  if (recompute_floats || CreatesNewFormattingContext() ||
+      HasSelfPaintingLayer())
+    AddVisualOverflowFromFloats();
+
+  if (VisualOverflowRect() != previous_visual_overflow_rect) {
+    if (Layer())
+      Layer()->SetNeedsCompositingInputsUpdate();
+    GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired);
+  }
+}
+
 void LayoutListItem::AddVisualOverflowFromChildren() {
   LayoutBlockFlow::AddVisualOverflowFromChildren();
   UpdateOverflow(Visual);
diff --git a/third_party/blink/renderer/core/layout/layout_list_item.h b/third_party/blink/renderer/core/layout/layout_list_item.h
index e180df75..e0d7736 100644
--- a/third_party/blink/renderer/core/layout/layout_list_item.h
+++ b/third_party/blink/renderer/core/layout/layout_list_item.h
@@ -70,7 +70,9 @@
 
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
 
-  void AddVisualOverflowFromChildren() override;
+  void ComputeVisualOverflow(const LayoutRect&, bool recompute_floats) final;
+
+  void AddVisualOverflowFromChildren();
   void AddLayoutOverflowFromChildren() override;
 
   void AlignMarkerInBlockDirection();
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_set.cc b/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
index a7ae137..0adaeba0 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
 #include "third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h"
 #include "third_party/blink/renderer/core/paint/multi_column_set_painter.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 
 namespace blink {
 
@@ -520,6 +521,25 @@
   return result;
 }
 
+void LayoutMultiColumnSet::ComputeVisualOverflow(
+    const LayoutRect& previous_visual_overflow_rect,
+    bool recompute_floats) {
+  AddVisualOverflowFromChildren();
+
+  AddVisualEffectOverflow();
+  AddVisualOverflowFromTheme();
+
+  if (recompute_floats || CreatesNewFormattingContext() ||
+      HasSelfPaintingLayer())
+    AddVisualOverflowFromFloats();
+
+  if (VisualOverflowRect() != previous_visual_overflow_rect) {
+    if (Layer())
+      Layer()->SetNeedsCompositingInputsUpdate();
+    GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired);
+  }
+}
+
 void LayoutMultiColumnSet::AddVisualOverflowFromChildren() {
   // It's useless to calculate overflow if we haven't determined the page
   // logical height yet.
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_set.h b/third_party/blink/renderer/core/layout/layout_multi_column_set.h
index 8dbe3d27..8e66f98 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_set.h
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_set.h
@@ -255,7 +255,9 @@
   void PaintObject(const PaintInfo&,
                    const LayoutPoint& paint_offset) const override;
 
-  void AddVisualOverflowFromChildren() override;
+  void ComputeVisualOverflow(const LayoutRect&, bool recompute_floats) final;
+
+  void AddVisualOverflowFromChildren();
   void AddLayoutOverflowFromChildren() override;
 
   MultiColumnFragmentainerGroupList fragmentainer_groups_;
diff --git a/third_party/blink/renderer/core/layout/layout_table.cc b/third_party/blink/renderer/core/layout/layout_table.cc
index d6e1978c..410930b5 100644
--- a/third_party/blink/renderer/core/layout/layout_table.cc
+++ b/third_party/blink/renderer/core/layout/layout_table.cc
@@ -910,6 +910,21 @@
   }
 }
 
+void LayoutTable::ComputeVisualOverflow(
+    const LayoutRect& previous_visual_overflow_rect,
+    bool recompute_floats) {
+  AddVisualOverflowFromChildren();
+
+  AddVisualEffectOverflow();
+  AddVisualOverflowFromTheme();
+
+  if (VisualOverflowRect() != previous_visual_overflow_rect) {
+    if (Layer())
+      Layer()->SetNeedsCompositingInputsUpdate();
+    GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired);
+  }
+}
+
 void LayoutTable::AddVisualOverflowFromChildren() {
   // Add overflow from borders.
   // Technically it's odd that we are incorporating the borders into layout
diff --git a/third_party/blink/renderer/core/layout/layout_table.h b/third_party/blink/renderer/core/layout/layout_table.h
index 48af1bc..983880a8 100644
--- a/third_party/blink/renderer/core/layout/layout_table.h
+++ b/third_party/blink/renderer/core/layout/layout_table.h
@@ -474,7 +474,9 @@
       OverlayScrollbarClipBehavior =
           kIgnorePlatformOverlayScrollbarSize) const override;
 
-  void AddVisualOverflowFromChildren() override;
+  void ComputeVisualOverflow(const LayoutRect&, bool recompute_floats) final;
+
+  void AddVisualOverflowFromChildren();
   void AddLayoutOverflowFromChildren() override;
 
   void RecalcSections() const;
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc b/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc
index 79a43d1..086ca24f23 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/text_control_single_line_painter.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
 
@@ -317,4 +318,23 @@
   return ToHTMLInputElement(GetNode());
 }
 
+void LayoutTextControlSingleLine::ComputeVisualOverflow(
+    const LayoutRect& previous_visual_overflow_rect,
+    bool recompute_floats) {
+  AddVisualOverflowFromChildren();
+
+  AddVisualEffectOverflow();
+  AddVisualOverflowFromTheme();
+
+  if (recompute_floats || CreatesNewFormattingContext() ||
+      HasSelfPaintingLayer())
+    AddVisualOverflowFromFloats();
+
+  if (VisualOverflowRect() != previous_visual_overflow_rect) {
+    if (Layer())
+      Layer()->SetNeedsCompositingInputsUpdate();
+    GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_single_line.h b/third_party/blink/renderer/core/layout/layout_text_control_single_line.h
index 9132a9e..66c2a232 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control_single_line.h
+++ b/third_party/blink/renderer/core/layout/layout_text_control_single_line.h
@@ -78,10 +78,11 @@
       LayoutUnit line_height,
       LayoutUnit non_content_height) const override;
 
+  void ComputeVisualOverflow(const LayoutRect&, bool recompute_floats) override;
+
   // If the INPUT content height is smaller than the font height, the
   // inner-editor element overflows the INPUT box intentionally, however it
   // shouldn't affect outside of the INPUT box.  So we ignore child overflow.
-  void AddVisualOverflowFromChildren() final {}
   void AddLayoutOverflowFromChildren() final {}
 
   bool AllowsOverflowClip() const override { return false; }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 94888b4..813b7b0 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -888,12 +888,12 @@
   NGBfcOffset origin_bfc_offset = {ConstraintSpace().BfcOffset().line_offset,
                                    bfc_block_offset + content_size};
 
-  const NGPositionedFloatVector positioned_floats =
-      PositionFloats(ConstraintSpace().AvailableSize(),
-                     ConstraintSpace().PercentageResolutionSize(),
-                     ConstraintSpace().ReplacedPercentageResolutionSize(),
-                     origin_bfc_offset, bfc_block_offset, unpositioned_floats_,
-                     ConstraintSpace(), exclusion_space);
+  NGPositionedFloatVector positioned_floats;
+  PositionFloats(ConstraintSpace().AvailableSize(),
+                 ConstraintSpace().PercentageResolutionSize(),
+                 ConstraintSpace().ReplacedPercentageResolutionSize(),
+                 origin_bfc_offset, bfc_block_offset, unpositioned_floats_,
+                 ConstraintSpace(), exclusion_space, &positioned_floats);
 
   positioned_floats_.AppendVector(positioned_floats);
   unpositioned_floats_.clear();
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index d5f95c5..68530841 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_block_flow_painter.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 
 namespace blink {
 
@@ -66,6 +67,21 @@
 }
 
 template <typename Base>
+void LayoutNGMixin<Base>::ComputeVisualOverflow(
+    const LayoutRect& previous_visual_overflow_rect,
+    bool recompute_floats) {
+  Base::ComputeVisualOverflow(previous_visual_overflow_rect, recompute_floats);
+  AddVisualOverflowFromChildren();
+
+  if (Base::VisualOverflowRect() != previous_visual_overflow_rect) {
+    if (Base::Layer())
+      Base::Layer()->SetNeedsCompositingInputsUpdate();
+    Base::GetFrameView()->SetIntersectionObservationState(
+        LocalFrameView::kDesired);
+  }
+}
+
+template <typename Base>
 void LayoutNGMixin<Base>::AddVisualOverflowFromChildren() {
   // |ComputeOverflow()| calls this, which is called from
   // |CopyFragmentDataToLayoutBox()| and |RecalcOverflowAfterStyleChange()|.
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
index 477d924..d1c60bd1 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -83,7 +83,9 @@
  protected:
   bool IsOfType(LayoutObject::LayoutObjectType) const override;
 
-  void AddVisualOverflowFromChildren() final;
+  void ComputeVisualOverflow(const LayoutRect&, bool recompute_floats) final;
+
+  void AddVisualOverflowFromChildren();
   void AddLayoutOverflowFromChildren() final;
 
  private:
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 708eabe1..feac6a1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -2081,10 +2081,11 @@
           ? container_builder_.BfcBlockOffset().value()
           : ConstraintSpace().FloatsBfcBlockOffset().value();
 
-  const auto positioned_floats = PositionFloats(
-      child_available_size_, child_percentage_size_,
-      replaced_child_percentage_size_, origin_bfc_offset, bfc_block_offset,
-      unpositioned_floats_, ConstraintSpace(), &exclusion_space_);
+  NGPositionedFloatVector positioned_floats;
+  PositionFloats(child_available_size_, child_percentage_size_,
+                 replaced_child_percentage_size_, origin_bfc_offset,
+                 bfc_block_offset, unpositioned_floats_, ConstraintSpace(),
+                 &exclusion_space_, &positioned_floats);
 
   AddPositionedFloats(positioned_floats);
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 972033d..6437050 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -214,12 +214,6 @@
       }
       return layout_result;
     }
-    // Cached fragment was stale. Its fragment children might point to
-    // deleted LayoutObjects.
-    // Removing cached result to ensure that stale children cannot be
-    // reached through LayoutNGMixin::CurrentFragment.
-    if (box_->NeedsLayout())
-      ToLayoutBlockFlow(box_)->ClearCachedLayoutResult();
   }
 
   // This follows the code from LayoutBox::UpdateLogicalWidth
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 43fc62a..163f62d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -87,8 +87,8 @@
     return *this;
   }
 
-  virtual NGContainerFragmentBuilder& AddChild(const NGLayoutResult&,
-                                               const NGLogicalOffset&);
+  NGContainerFragmentBuilder& AddChild(const NGLayoutResult&,
+                                       const NGLogicalOffset&);
 
   // This version of AddChild will not propagate floats/out_of_flow.
   // Use the AddChild(NGLayoutResult) variant if NGLayoutResult is available.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc
index 6e4315d..49df543 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc
@@ -306,26 +306,24 @@
   return NGPositionedFloat(std::move(layout_result), float_bfc_offset);
 }
 
-const NGPositionedFloatVector PositionFloats(
-    const NGLogicalSize& float_available_size,
-    const NGLogicalSize& float_percentage_size,
-    const NGLogicalSize& float_replaced_percentage_size,
-    const NGBfcOffset& origin_bfc_offset,
-    LayoutUnit parent_bfc_block_offset,
-    NGUnpositionedFloatVector& unpositioned_floats,
-    const NGConstraintSpace& space,
-    NGExclusionSpace* exclusion_space) {
-  NGPositionedFloatVector positioned_floats;
-  positioned_floats.ReserveCapacity(unpositioned_floats.size());
+void PositionFloats(const NGLogicalSize& float_available_size,
+                    const NGLogicalSize& float_percentage_size,
+                    const NGLogicalSize& float_replaced_percentage_size,
+                    const NGBfcOffset& origin_bfc_offset,
+                    LayoutUnit parent_bfc_block_offset,
+                    NGUnpositionedFloatVector& unpositioned_floats,
+                    const NGConstraintSpace& space,
+                    NGExclusionSpace* exclusion_space,
+                    NGPositionedFloatVector* positioned_floats) {
+  positioned_floats->ReserveCapacity(positioned_floats->size() +
+                                     unpositioned_floats.size());
 
   for (NGUnpositionedFloat& unpositioned_float : unpositioned_floats) {
-    positioned_floats.push_back(PositionFloat(
+    positioned_floats->push_back(PositionFloat(
         float_available_size, float_percentage_size,
         float_replaced_percentage_size, origin_bfc_offset,
         parent_bfc_block_offset, &unpositioned_float, space, exclusion_space));
   }
-
-  return positioned_floats;
 }
 
 void AddUnpositionedFloat(NGUnpositionedFloatVector* unpositioned_floats,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h b/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h
index dd1c017..ffcc9b5 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h
@@ -53,15 +53,16 @@
 
 // Positions the list of {@code unpositioned_floats}. Adds them as exclusions to
 // {@code space}.
-CORE_EXPORT const NGPositionedFloatVector
-PositionFloats(const NGLogicalSize& float_available_size,
-               const NGLogicalSize& float_percentage_size,
-               const NGLogicalSize& float_replaced_percentage_size,
-               const NGBfcOffset& origin_bfc_offset,
-               LayoutUnit container_block_offset,
-               NGUnpositionedFloatVector& unpositioned_floats,
-               const NGConstraintSpace& space,
-               NGExclusionSpace* exclusion_space);
+CORE_EXPORT void PositionFloats(
+    const NGLogicalSize& float_available_size,
+    const NGLogicalSize& float_percentage_size,
+    const NGLogicalSize& float_replaced_percentage_size,
+    const NGBfcOffset& origin_bfc_offset,
+    LayoutUnit container_block_offset,
+    NGUnpositionedFloatVector& unpositioned_floats,
+    const NGConstraintSpace& space,
+    NGExclusionSpace* exclusion_space,
+    NGPositionedFloatVector* positioned_floats);
 
 // Add a pending float to the list. It will be committed (positioned) once we
 // have resolved the BFC block offset.
diff --git a/third_party/blink/renderer/core/page/touch_adjustment.cc b/third_party/blink/renderer/core/page/touch_adjustment.cc
index a218191..5e009e44 100644
--- a/third_party/blink/renderer/core/page/touch_adjustment.cc
+++ b/third_party/blink/renderer/core/page/touch_adjustment.cc
@@ -44,7 +44,8 @@
 namespace touch_adjustment {
 
 const float kZeroTolerance = 1e-6f;
-constexpr float kMaxAdjustmentSizeDips = 32.f;
+// The maximum adjustment range (diameters) in css pixel.
+constexpr float kMaxAdjustmentSize = 32.f;
 
 // Class for remembering absolute quads of a target node and what node they
 // represent.
@@ -513,10 +514,11 @@
       subtargets, touch_adjustment::HybridDistanceFunction);
 }
 
-LayoutSize GetHitTestRectForAdjustment(const LayoutSize& touch_area) {
-  const LayoutSize max_size(touch_adjustment::kMaxAdjustmentSizeDips,
-                            touch_adjustment::kMaxAdjustmentSizeDips);
-  return touch_area.ShrunkTo(max_size);
+LayoutSize GetHitTestRectForAdjustment(const LayoutSize& touch_area,
+                                       float zoom_factor) {
+  const LayoutSize max_size(touch_adjustment::kMaxAdjustmentSize,
+                            touch_adjustment::kMaxAdjustmentSize);
+  return touch_area.ShrunkTo(max_size * zoom_factor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/touch_adjustment.h b/third_party/blink/renderer/core/page/touch_adjustment.h
index 3c4f06d..44f4a30 100644
--- a/third_party/blink/renderer/core/page/touch_adjustment.h
+++ b/third_party/blink/renderer/core/page/touch_adjustment.h
@@ -43,7 +43,11 @@
                                   const IntRect& touch_area,
                                   const HeapVector<Member<Node>>&);
 
-LayoutSize GetHitTestRectForAdjustment(const LayoutSize& touch_area);
+// Applies an upper bound to the touch area as the adjustment rect. The
+// touch_area is in root frame coordinates, which is in physical pixel when
+// zoom-for-dsf is enabled, otherwise in dip (when page scale is 1). 
+LayoutSize GetHitTestRectForAdjustment(const LayoutSize& touch_area,
+                                       float zoom_factor);
 
 struct TouchAdjustmentResult {
   uint32_t unique_event_id;
diff --git a/third_party/blink/renderer/core/testing/sim/sim_compositor.cc b/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
index 30850ca..71b4296 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
@@ -23,19 +23,17 @@
 
 SimCompositor::SimCompositor() {
   LocalFrameView::SetInitialTracksPaintInvalidationsForTesting(true);
-
-  // SimCompositor overrides the LayerTreeViewDelegate to respond to
-  // BeginMainFrame(), which will update and paint the WebViewImpl given to
-  // SetWebView().
-  layer_tree_view_ = layer_tree_view_factory_.Initialize(this);
 }
 
 SimCompositor::~SimCompositor() {
   LocalFrameView::SetInitialTracksPaintInvalidationsForTesting(false);
 }
 
-void SimCompositor::SetWebView(WebViewImpl& web_view) {
+void SimCompositor::SetWebView(WebViewImpl& web_view,
+                               content::LayerTreeView& layer_tree_view) {
   web_view_ = &web_view;
+  layer_tree_view_ = &layer_tree_view;
+  DCHECK_EQ(&layer_tree_view, web_view_->LayerTreeView());
 
   // SimCompositor starts with defer commits enabled, but uses synchronous
   // compositing which does not use defer commits anyhow, it only uses it for
diff --git a/third_party/blink/renderer/core/testing/sim/sim_compositor.h b/third_party/blink/renderer/core/testing/sim/sim_compositor.h
index e1b033ee..b50f5478 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_compositor.h
+++ b/third_party/blink/renderer/core/testing/sim/sim_compositor.h
@@ -28,17 +28,18 @@
 // Note: This also does not support compositor driven animations.
 class SimCompositor final : public content::StubLayerTreeViewDelegate {
  public:
-  explicit SimCompositor();
+  SimCompositor();
   ~SimCompositor() override;
 
   // This compositor should be given to the WebViewImpl passed to SetWebView.
   content::LayerTreeView& layer_tree_view() { return *layer_tree_view_; }
 
   // When the compositor asks for a main frame, this WebViewImpl will have its
-  // lifecycle updated and be painted. The compositor() should have also been
-  // given to the WebViewImpl so that its using the same compositor() for its
-  // layer tree.
-  void SetWebView(WebViewImpl&);
+  // lifecycle updated and be painted. The WebLayerTreeView that is being used
+  // to composite the WebViewImpl is passed separately as the underlying
+  // content::LayerTreeView type, in order to bypass the Web* API surface
+  // provided to blink.
+  void SetWebView(WebViewImpl&, content::LayerTreeView&);
 
   // Executes the BeginMainFrame processing steps, an approximation of what
   // cc::ThreadProxy::BeginMainFrame would do.
@@ -93,7 +94,6 @@
   SimCanvas::Commands* paint_commands_;
 
   content::LayerTreeView* layer_tree_view_ = nullptr;
-  FrameTestHelpers::LayerTreeViewFactory layer_tree_view_factory_;
 
   std::unique_ptr<cc::ScopedDeferCommits> scoped_defer_commits_;
 };
diff --git a/third_party/blink/renderer/core/testing/sim/sim_test.cc b/third_party/blink/renderer/core/testing/sim/sim_test.cc
index c6a502e..8a3e27e6 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_test.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_test.cc
@@ -18,8 +18,11 @@
 namespace blink {
 
 SimTest::SimTest()
-    : web_view_client_(compositor_.layer_tree_view()),
-      web_frame_client_(*this) {
+    : web_frame_client_(*this),
+      // SimCompositor overrides the LayerTreeViewDelegate to respond to
+      // BeginMainFrame(), which will update and paint the WebViewImpl given to
+      // SetWebView().
+      web_view_client_(&compositor_) {
   Document::SetThreadedParsingEnabledForTesting(false);
   // Use the mock theme to get more predictable code paths, this also avoids
   // the OS callbacks in ScrollAnimatorMac which can schedule frames
@@ -53,7 +56,7 @@
   Test::SetUp();
 
   web_view_helper_.Initialize(&web_frame_client_, &web_view_client_);
-  compositor_.SetWebView(WebView());
+  compositor_.SetWebView(WebView(), *web_view_client_.layer_tree_view());
   page_.SetPage(WebView().GetPage());
 }
 
diff --git a/third_party/blink/renderer/core/testing/sim/sim_test.h b/third_party/blink/renderer/core/testing/sim/sim_test.h
index ed38fac..6a4753c 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_test.h
+++ b/third_party/blink/renderer/core/testing/sim/sim_test.h
@@ -52,8 +52,8 @@
 
   SimNetwork network_;
   SimCompositor compositor_;
-  SimWebViewClient web_view_client_;
   SimWebFrameClient web_frame_client_;
+  SimWebViewClient web_view_client_;
   SimPage page_;
   FrameTestHelpers::WebViewHelper web_view_helper_;
 
diff --git a/third_party/blink/renderer/core/testing/sim/sim_web_view_client.cc b/third_party/blink/renderer/core/testing/sim/sim_web_view_client.cc
index befdd59..52ba409 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_web_view_client.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_web_view_client.cc
@@ -8,11 +8,8 @@
 
 namespace blink {
 
-SimWebViewClient::SimWebViewClient(content::LayerTreeView& layer_tree_view)
-    : visually_non_empty_layout_count_(0),
-      finished_parsing_layout_count_(0),
-      finished_loading_layout_count_(0),
-      layer_tree_view_(&layer_tree_view) {}
+SimWebViewClient::SimWebViewClient(content::LayerTreeViewDelegate* delegate)
+    : FrameTestHelpers::TestWebViewClient(delegate) {}
 
 void SimWebViewClient::DidMeaningfulLayout(
     WebMeaningfulLayout meaningful_layout) {
@@ -29,10 +26,6 @@
   }
 }
 
-WebLayerTreeView* SimWebViewClient::InitializeLayerTreeView() {
-  return layer_tree_view_;
-}
-
 WebView* SimWebViewClient::CreateView(WebLocalFrame* opener,
                                       const WebURLRequest&,
                                       const WebWindowFeatures&,
diff --git a/third_party/blink/renderer/core/testing/sim/sim_web_view_client.h b/third_party/blink/renderer/core/testing/sim/sim_web_view_client.h
index 0fd8340c..f1a45d1b 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_web_view_client.h
+++ b/third_party/blink/renderer/core/testing/sim/sim_web_view_client.h
@@ -7,17 +7,11 @@
 
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 
-namespace content {
-class LayerTreeView;
-}
-
 namespace blink {
 
 class SimWebViewClient final : public FrameTestHelpers::TestWebViewClient {
  public:
-  // The LayerTreeView to be returned from InitializeLayerTreeView()
-  // must be constructed before this class, and given to it.
-  explicit SimWebViewClient(content::LayerTreeView&);
+  explicit SimWebViewClient(content::LayerTreeViewDelegate* delegate);
 
   int VisuallyNonEmptyLayoutCount() const {
     return visually_non_empty_layout_count_;
@@ -30,7 +24,6 @@
   }
 
   // WebViewClient implementation.
-  WebLayerTreeView* InitializeLayerTreeView() override;
   WebView* CreateView(WebLocalFrame* opener,
                       const WebURLRequest&,
                       const WebWindowFeatures&,
@@ -44,11 +37,10 @@
   // WebWidgetClient overrides.
   void DidMeaningfulLayout(WebMeaningfulLayout) override;
 
-  int visually_non_empty_layout_count_;
-  int finished_parsing_layout_count_;
-  int finished_loading_layout_count_;
+  int visually_non_empty_layout_count_ = 0;
+  int finished_parsing_layout_count_ = 0;
+  int finished_loading_layout_count_ = 0;
 
-  content::LayerTreeView* layer_tree_view_;
   FrameTestHelpers::WebViewHelper web_view_helper_;
 };
 
diff --git a/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js b/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
index 526b4c5d..42e8b41 100644
--- a/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
@@ -81,6 +81,7 @@
     const mainContainer = new UI.VBox();
     mainContainer.element.appendChild(this._mainToolbar.element);
     this._dataGrid.asWidget().show(mainContainer.element);
+    mainContainer.setMinimumSize(0, 72);
     this._splitWidget.setMainWidget(mainContainer);
 
     this._frameEmptyWidget = new UI.EmptyWidget(Common.UIString('Select frame to browse its content.'));
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js b/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js
index a00645cc6..e097ea2 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js
@@ -1151,12 +1151,13 @@
   async populateImageSource(image) {
     const {content, encoded} = await this.contentData();
     let imageSrc = Common.ContentProvider.contentAsDataURL(content, this._mimeType, encoded);
-    if (imageSrc === null) {
+    if (imageSrc === null && !this._failed) {
       const cacheControl = this.responseHeaderValue('cache-control') || '';
       if (!cacheControl.includes('no-cache'))
         imageSrc = this._url;
     }
-    image.src = imageSrc;
+    if (imageSrc !== null)
+      image.src = imageSrc;
   }
 
   /**
diff --git a/third_party/blink/renderer/modules/animationworklet/BUILD.gn b/third_party/blink/renderer/modules/animationworklet/BUILD.gn
index ddde73a..79387e8 100644
--- a/third_party/blink/renderer/modules/animationworklet/BUILD.gn
+++ b/third_party/blink/renderer/modules/animationworklet/BUILD.gn
@@ -28,5 +28,7 @@
     "worklet_animation.h",
     "worklet_animation_options.cc",
     "worklet_animation_options.h",
+    "worklet_group_effect_proxy.cc",
+    "worklet_group_effect_proxy.h",
   ]
 }
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
index c7912c9..4fc5352 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
@@ -25,8 +25,7 @@
                      WorkletAnimationId id,
                      double current_time,
                      AnimationWorkletDispatcherOutput* result) {
-  AnimationWorkletDispatcherOutput::AnimationState animation_output(
-      id, base::nullopt);
+  AnimationWorkletDispatcherOutput::AnimationState animation_output(id);
   if (animator->Animate(script_state, current_time, &animation_output)) {
     result->animations.push_back(std::move(animation_output));
   }
@@ -66,9 +65,10 @@
 Animator* AnimationWorkletGlobalScope::CreateAnimatorFor(
     int animation_id,
     const String& name,
-    WorkletAnimationOptions* options) {
+    WorkletAnimationOptions* options,
+    int num_effects) {
   DCHECK(!animators_.at(animation_id));
-  Animator* animator = CreateInstance(name, options);
+  Animator* animator = CreateInstance(name, options, num_effects);
   if (!animator)
     return nullptr;
   animators_.Set(animation_id, animator);
@@ -99,7 +99,8 @@
     WorkletAnimationOptions* options =
         static_cast<WorkletAnimationOptions*>(animation.options.get());
 
-    Animator* animator = CreateAnimatorFor(id, name, options);
+    Animator* animator =
+        CreateAnimatorFor(id, name, options, animation.num_effects);
     if (!animator)
       continue;
 
@@ -121,10 +122,13 @@
   for (const auto& worklet_animation_id : mutator_input.peeked_animations) {
     int id = worklet_animation_id.animation_id;
     Animator* animator = animators_.at(id);
+    if (!animator)
+      continue;
 
-    result->animations.emplace_back(
-        worklet_animation_id,
-        animator ? animator->GetLastLocalTime() : base::nullopt);
+    AnimationWorkletDispatcherOutput::AnimationState animation_output(
+        worklet_animation_id);
+    animation_output.local_times = animator->GetLocalTimes();
+    result->animations.push_back(animation_output);
   }
 
   return result;
@@ -185,7 +189,8 @@
 
 Animator* AnimationWorkletGlobalScope::CreateInstance(
     const String& name,
-    WorkletAnimationOptions* options) {
+    WorkletAnimationOptions* options,
+    int num_effects) {
   DCHECK(IsContextThread());
   AnimatorDefinition* definition = animator_definitions_.at(name);
   if (!definition)
@@ -206,7 +211,7 @@
            .ToLocal(&instance))
     return nullptr;
 
-  return new Animator(isolate, definition, instance);
+  return new Animator(isolate, definition, instance, num_effects);
 }
 
 AnimatorDefinition* AnimationWorkletGlobalScope::FindDefinitionForTest(
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
index c0ee79b..fbe305d 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
@@ -56,10 +56,12 @@
 
   void RegisterWithProxyClientIfNeeded();
   Animator* CreateInstance(const String& name,
-                           WorkletAnimationOptions* options);
+                           WorkletAnimationOptions* options,
+                           int num_effects);
   Animator* CreateAnimatorFor(int animation_id,
                               const String& name,
-                              WorkletAnimationOptions* options);
+                              WorkletAnimationOptions* options,
+                              int num_effects);
   typedef HeapHashMap<String, TraceWrapperMember<AnimatorDefinition>>
       DefinitionMap;
   DefinitionMap animator_definitions_;
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
index 1b5aea32..6b54362 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
@@ -222,7 +222,7 @@
     cc::WorkletAnimationId animation_id = {1, 1};
     AnimationWorkletInput state;
     state.added_and_updated_animations.emplace_back(animation_id, "test", 5000,
-                                                    nullptr);
+                                                    nullptr, 1);
 
     std::unique_ptr<AnimationWorkletOutput> output =
         global_scope->Mutate(state);
@@ -273,14 +273,14 @@
     cc::WorkletAnimationId animation_id = {1, 1};
     AnimationWorkletInput state;
     state.added_and_updated_animations.emplace_back(animation_id, "test", 5000,
-                                                    nullptr);
+                                                    nullptr, 1);
 
     std::unique_ptr<AnimationWorkletOutput> output =
         global_scope->Mutate(state);
     EXPECT_TRUE(output);
 
     EXPECT_EQ(output->animations.size(), 1ul);
-    EXPECT_EQ(output->animations[0].local_time,
+    EXPECT_EQ(output->animations[0].local_times[0],
               WTF::TimeDelta::FromMillisecondsD(123));
 
     waitable_event->Signal();
@@ -329,7 +329,7 @@
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
 
     state.added_and_updated_animations.push_back(
-        {animation_id, "test", 5000, nullptr});
+        {animation_id, "test", 5000, nullptr, 1});
     EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
     global_scope->Mutate(state);
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 1u);
@@ -366,7 +366,7 @@
     cc::WorkletAnimationId animation_id = {1, 1};
     AnimationWorkletInput state;
     state.added_and_updated_animations.push_back(
-        {animation_id, "test", 5000, nullptr});
+        {animation_id, "test", 5000, nullptr, 1});
     EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
     global_scope->Mutate(state);
     EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 1u);
diff --git a/third_party/blink/renderer/modules/animationworklet/animator.cc b/third_party/blink/renderer/modules/animationworklet/animator.cc
index 5faecda3..a8c3fe61 100644
--- a/third_party/blink/renderer/modules/animationworklet/animator.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animator.cc
@@ -15,16 +15,19 @@
 
 Animator::Animator(v8::Isolate* isolate,
                    AnimatorDefinition* definition,
-                   v8::Local<v8::Value> instance)
+                   v8::Local<v8::Value> instance,
+                   int num_effects)
     : definition_(definition),
       instance_(isolate, instance),
-      effect_(new EffectProxy()) {}
+      group_effect_(new WorkletGroupEffectProxy(num_effects)) {
+  DCHECK_GE(num_effects, 1);
+}
 
 Animator::~Animator() = default;
 
 void Animator::Trace(blink::Visitor* visitor) {
   visitor->Trace(definition_);
-  visitor->Trace(effect_);
+  visitor->Trace(group_effect_);
   visitor->Trace(instance_);
 }
 
@@ -46,8 +49,14 @@
 
   // Prepare arguments (i.e., current time and effect) and pass them to animate
   // callback.
-  v8::Local<v8::Value> v8_effect =
-      ToV8(effect_, script_state->GetContext()->Global(), isolate);
+  v8::Local<v8::Value> v8_effect;
+  if (group_effect_->getChildren().size() == 1) {
+    v8_effect = ToV8(group_effect_->getChildren()[0],
+                     script_state->GetContext()->Global(), isolate);
+  } else {
+    v8_effect =
+        ToV8(group_effect_, script_state->GetContext()->Global(), isolate);
+  }
 
   v8::Local<v8::Value> v8_current_time =
       ToV8(current_time, script_state->GetContext()->Global(), isolate);
@@ -62,8 +71,17 @@
   if (block.HasCaught())
     return false;
 
-  output->local_time = effect_->local_time();
+  output->local_times = GetLocalTimes();
   return true;
 }
 
+std::vector<base::Optional<TimeDelta>> Animator::GetLocalTimes() const {
+  std::vector<base::Optional<TimeDelta>> local_times;
+  local_times.reserve(group_effect_->getChildren().size());
+  for (const auto& effect : group_effect_->getChildren()) {
+    local_times.push_back(effect->local_time());
+  }
+  return local_times;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/animationworklet/animator.h b/third_party/blink/renderer/modules/animationworklet/animator.h
index c37bf14d..73e8e8c 100644
--- a/third_party/blink/renderer/modules/animationworklet/animator.h
+++ b/third_party/blink/renderer/modules/animationworklet/animator.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_ANIMATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_ANIMATOR_H_
 
-#include "third_party/blink/renderer/modules/animationworklet/effect_proxy.h"
+#include "third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
@@ -25,7 +25,10 @@
 class Animator final : public GarbageCollectedFinalized<Animator>,
                        public NameClient {
  public:
-  Animator(v8::Isolate*, AnimatorDefinition*, v8::Local<v8::Value> instance);
+  Animator(v8::Isolate*,
+           AnimatorDefinition*,
+           v8::Local<v8::Value> instance,
+           int num_effects);
   ~Animator();
   void Trace(blink::Visitor*);
   const char* NameInHeapSnapshot() const override { return "Animator"; }
@@ -36,9 +39,7 @@
   bool Animate(ScriptState*,
                double current_time,
                AnimationWorkletDispatcherOutput::AnimationState*);
-  base::Optional<TimeDelta> GetLastLocalTime() const {
-    return effect_->local_time();
-  }
+  std::vector<base::Optional<TimeDelta>> GetLocalTimes() const;
 
  private:
   // This object keeps the definition object, and animator instance alive.
@@ -46,7 +47,7 @@
   TraceWrapperMember<AnimatorDefinition> definition_;
   TraceWrapperV8Reference<v8::Value> instance_;
 
-  Member<EffectProxy> effect_;
+  Member<WorkletGroupEffectProxy> group_effect_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index 471ed16..e2a88e5 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -291,6 +291,7 @@
   for (auto& effect : effects_) {
     AnimationEffect* target_effect = effect;
     target_effect->Attach(this);
+    local_times_.push_back(base::nullopt);
   }
 
   if (timeline_->IsScrollTimeline())
@@ -338,7 +339,7 @@
     DestroyCompositorAnimation();
   }
 
-  local_time_ = base::nullopt;
+  local_times_.Fill(base::nullopt);
   start_time_ = base::nullopt;
   running_on_main_thread_ = false;
   // TODO(yigu): Because this animation has been detached and will not receive
@@ -382,16 +383,18 @@
   if (!start_time_)
     return;
 
-  // TODO(crbug.com/756539): For now we use 0 as inherited time for compositor
-  // worklet animations. Will need to get the inherited time from worklet
-  // context.
-  double inherited_time_seconds = 0;
+  DCHECK_EQ(effects_.size(), local_times_.size());
+  for (size_t i = 0; i < effects_.size(); ++i) {
+    // TODO(crbug.com/756539): For now we use 0 as inherited time for compositor
+    // worklet animations. Will need to get the inherited time from worklet
+    // context.
+    double inherited_time_seconds = 0;
 
-  if (local_time_)
-    inherited_time_seconds = local_time_->InSecondsF();
+    if (local_times_[i])
+      inherited_time_seconds = local_times_[i]->InSecondsF();
 
-  for (auto& effect : effects_)
-    effect->UpdateInheritedTime(inherited_time_seconds, reason);
+    effects_[i]->UpdateInheritedTime(inherited_time_seconds, reason);
+  }
 }
 
 bool WorkletAnimation::CheckCanStart(String* failure_message) {
@@ -573,7 +576,7 @@
     input_state->Add(
         {id_,
          std::string(animator_name_.Ascii().data(), animator_name_.length()),
-         current_time, CloneOptions()});
+         current_time, CloneOptions(), effects_.size()});
   } else if (was_active && is_active) {
     // Skip if the input time is not changed.
     if (did_time_change)
@@ -587,7 +590,13 @@
 void WorkletAnimation::SetOutputState(
     const AnimationWorkletOutput::AnimationState& state) {
   DCHECK(state.worklet_animation_id == id_);
-  local_time_ = state.local_time;
+  // The local times for composited effects, i.e. not running on main, are
+  // peeked and set via the main thread. If an animator is not ready upon
+  // peeking state.local_times will be empty.
+  DCHECK(local_times_.size() == state.local_times.size() ||
+         !running_on_main_thread_);
+  for (size_t i = 0; i < state.local_times.size(); ++i)
+    local_times_[i] = state.local_times[i];
 }
 
 void WorkletAnimation::Dispose() {
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
index 208f368..10536a4eb 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
@@ -155,7 +155,7 @@
   Animation::AnimationPlayState last_play_state_;
   // Start time in ms.
   base::Optional<base::TimeDelta> start_time_;
-  base::Optional<base::TimeDelta> local_time_;
+  Vector<base::Optional<base::TimeDelta>> local_times_;
   // We use this to skip updating if current time has not changed since last
   // update.
   base::Optional<base::TimeDelta> last_current_time_;
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.cc b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.cc
new file mode 100644
index 0000000..eeb27d8
--- /dev/null
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.cc
@@ -0,0 +1,20 @@
+// Copyright 2018 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 "third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.h"
+
+namespace blink {
+
+WorkletGroupEffectProxy::WorkletGroupEffectProxy(int num_effects)
+    : effects_(num_effects) {
+  for (int i = 0; i < num_effects; ++i)
+    effects_[i] = new EffectProxy();
+}
+
+void WorkletGroupEffectProxy::Trace(blink::Visitor* visitor) {
+  visitor->Trace(effects_);
+  ScriptWrappable::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.h b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.h
new file mode 100644
index 0000000..388534c
--- /dev/null
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.h
@@ -0,0 +1,29 @@
+// Copyright 2018 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 THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_WORKLET_GROUP_EFFECT_PROXY_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_WORKLET_GROUP_EFFECT_PROXY_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/modules/animationworklet/effect_proxy.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+
+namespace blink {
+
+class MODULES_EXPORT WorkletGroupEffectProxy : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  explicit WorkletGroupEffectProxy(int num_effects);
+  HeapVector<Member<EffectProxy>>& getChildren() { return effects_; }
+  void Trace(blink::Visitor*) override;
+
+ private:
+  HeapVector<Member<EffectProxy>> effects_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_WORKLET_GROUP_EFFECT_PROXY_H_
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.idl b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.idl
new file mode 100644
index 0000000..b2fd1ed
--- /dev/null
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_group_effect_proxy.idl
@@ -0,0 +1,9 @@
+// Copyright 2018 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.
+[
+    Exposed=AnimationWorklet,
+    OriginTrialEnabled=AnimationWorklet
+] interface WorkletGroupEffectProxy {
+  sequence<EffectProxy> getChildren();
+};
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index c97681e..2385d4f 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -63,6 +63,7 @@
           "animationworklet/animation_worklet_global_scope.idl",
           "animationworklet/effect_proxy.idl",
           "animationworklet/worklet_animation.idl",
+          "animationworklet/worklet_group_effect_proxy.idl",
           "app_banner/before_install_prompt_event.idl",
           "background_fetch/background_fetch_event.idl",
           "background_fetch/background_fetch_fetch.idl",
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
index e1895c44..890965a 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
@@ -232,11 +232,6 @@
   return stream;
 }
 
-P2PQuicStreamImpl* P2PQuicTransportImpl::CreateOutgoingUnidirectionalStream() {
-  DCHECK(false);
-  return nullptr;
-}
-
 P2PQuicStreamImpl* P2PQuicTransportImpl::CreateIncomingStream(
     quic::QuicStreamId id) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
index 60c85ef..2764b717 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
@@ -110,8 +110,7 @@
   // Creates a new outgoing stream. The caller does not own the
   // stream, so the stream is activated and ownership is moved to the
   // quic::QuicSession.
-  P2PQuicStreamImpl* CreateOutgoingBidirectionalStream() override;
-  P2PQuicStreamImpl* CreateOutgoingUnidirectionalStream() override;
+  P2PQuicStreamImpl* CreateOutgoingBidirectionalStream();
 
   void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
index 14d1064..0b8e7b8 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
@@ -4,153 +4,87 @@
 
 #include "third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
+
 namespace blink {
 
 namespace {
 
-template <typename T>
-bool AddPropertyValue(v8::Local<v8::Object>& v8_object,
-                      v8::Isolate* isolate,
-                      T name,
-                      v8::Local<v8::Value> value) {
-  return V8CallBoolean(v8_object->CreateDataProperty(
-      isolate->GetCurrentContext(), V8String(isolate, name), value));
-}
-
-bool AddPropertySequenceOfBooleans(v8::Local<v8::Object>& v8_object,
-                                   v8::Isolate* isolate,
-                                   WebString name,
-                                   const WebVector<int>& web_vector) {
-  v8::Local<v8::Array> v8_array = v8::Array::New(isolate, web_vector.size());
-  for (size_t i = 0; i < web_vector.size(); ++i) {
-    if (!V8CallBoolean(v8_array->CreateDataProperty(
-            isolate->GetCurrentContext(), static_cast<uint32_t>(i),
-            v8::Boolean::New(isolate, static_cast<bool>(web_vector[i])))))
-      return false;
-  }
-  return AddPropertyValue(v8_object, isolate, name, v8_array);
-}
-
-template <typename T>
-bool AddPropertySequenceOfNumbers(v8::Local<v8::Object>& v8_object,
-                                  v8::Isolate* isolate,
-                                  WebString name,
-                                  const WebVector<T>& web_vector) {
-  v8::Local<v8::Array> v8_array = v8::Array::New(isolate, web_vector.size());
-  for (size_t i = 0; i < web_vector.size(); ++i) {
-    if (!V8CallBoolean(v8_array->CreateDataProperty(
-            isolate->GetCurrentContext(), static_cast<uint32_t>(i),
-            v8::Number::New(isolate, static_cast<double>(web_vector[i])))))
-      return false;
-  }
-  return AddPropertyValue(v8_object, isolate, name, v8_array);
-}
-
-bool AddPropertySequenceOfStrings(v8::Local<v8::Object>& v8_object,
-                                  v8::Isolate* isolate,
-                                  WebString name,
-                                  const WebVector<WebString>& web_vector) {
-  v8::Local<v8::Array> v8_array = v8::Array::New(isolate, web_vector.size());
-  for (size_t i = 0; i < web_vector.size(); ++i) {
-    if (!V8CallBoolean(v8_array->CreateDataProperty(
-            isolate->GetCurrentContext(), static_cast<uint32_t>(i),
-            V8String(isolate, web_vector[i]))))
-      return false;
-  }
-  return AddPropertyValue(v8_object, isolate, name, v8_array);
-}
-
 v8::Local<v8::Value> WebRTCStatsToValue(ScriptState* script_state,
                                         const WebRTCStats* stats) {
-  v8::Isolate* isolate = script_state->GetIsolate();
-  v8::Local<v8::Object> v8_object = v8::Object::New(isolate);
+  V8ObjectBuilder builder(script_state);
 
-  bool success = true;
-  success &= AddPropertyValue(v8_object, isolate, "id",
-                              V8String(isolate, stats->Id()));
-  success &= AddPropertyValue(v8_object, isolate, "timestamp",
-                              v8::Number::New(isolate, stats->Timestamp()));
-  success &= AddPropertyValue(v8_object, isolate, "type",
-                              V8String(isolate, stats->GetType()));
-  for (size_t i = 0; i < stats->MembersCount() && success; ++i) {
+  builder.AddString("id", stats->Id());
+  builder.AddNumber("timestamp", stats->Timestamp());
+  builder.AddString("type", stats->GetType());
+
+  auto add_vector = [&builder](const WebString& name, auto web_vector) {
+    Vector<typename decltype(web_vector)::value_type> vector(web_vector.size());
+    std::move(web_vector.begin(), web_vector.end(), vector.begin());
+    builder.Add(name, vector);
+  };
+
+  for (size_t i = 0; i < stats->MembersCount(); ++i) {
     std::unique_ptr<WebRTCStatsMember> member = stats->GetMember(i);
     if (!member->IsDefined())
       continue;
     WebString name = member->GetName();
     switch (member->GetType()) {
       case kWebRTCStatsMemberTypeBool:
-        success &=
-            AddPropertyValue(v8_object, isolate, name,
-                             v8::Boolean::New(isolate, member->ValueBool()));
+        builder.AddBoolean(name, member->ValueBool());
         break;
       case kWebRTCStatsMemberTypeInt32:
-        success &= AddPropertyValue(
-            v8_object, isolate, name,
-            v8::Number::New(isolate,
-                            static_cast<double>(member->ValueInt32())));
+        builder.AddNumber(name, static_cast<double>(member->ValueInt32()));
         break;
       case kWebRTCStatsMemberTypeUint32:
-        success &= AddPropertyValue(
-            v8_object, isolate, name,
-            v8::Number::New(isolate,
-                            static_cast<double>(member->ValueUint32())));
+        builder.AddNumber(name, static_cast<double>(member->ValueUint32()));
         break;
       case kWebRTCStatsMemberTypeInt64:
-        success &= AddPropertyValue(
-            v8_object, isolate, name,
-            v8::Number::New(isolate,
-                            static_cast<double>(member->ValueInt64())));
+        builder.AddNumber(name, static_cast<double>(member->ValueInt64()));
         break;
       case kWebRTCStatsMemberTypeUint64:
-        success &= AddPropertyValue(
-            v8_object, isolate, name,
-            v8::Number::New(isolate,
-                            static_cast<double>(member->ValueUint64())));
+        builder.AddNumber(name, static_cast<double>(member->ValueUint64()));
         break;
       case kWebRTCStatsMemberTypeDouble:
-        success &=
-            AddPropertyValue(v8_object, isolate, name,
-                             v8::Number::New(isolate, member->ValueDouble()));
+        builder.AddNumber(name, member->ValueDouble());
         break;
       case kWebRTCStatsMemberTypeString:
-        success &= AddPropertyValue(v8_object, isolate, name,
-                                    V8String(isolate, member->ValueString()));
+        builder.AddString(name, member->ValueString());
         break;
-      case kWebRTCStatsMemberTypeSequenceBool:
-        success &= AddPropertySequenceOfBooleans(v8_object, isolate, name,
-                                                 member->ValueSequenceBool());
+      case kWebRTCStatsMemberTypeSequenceBool: {
+        WebVector<int> sequence = member->ValueSequenceBool();
+        Vector<bool> vector(sequence.size());
+        std::copy(sequence.begin(), sequence.end(), vector.begin());
+        builder.Add(name, vector);
         break;
+      }
       case kWebRTCStatsMemberTypeSequenceInt32:
-        success &= AddPropertySequenceOfNumbers(v8_object, isolate, name,
-                                                member->ValueSequenceInt32());
+        add_vector(name, member->ValueSequenceInt32());
         break;
       case kWebRTCStatsMemberTypeSequenceUint32:
-        success &= AddPropertySequenceOfNumbers(v8_object, isolate, name,
-                                                member->ValueSequenceUint32());
+        add_vector(name, member->ValueSequenceUint32());
         break;
       case kWebRTCStatsMemberTypeSequenceInt64:
-        success &= AddPropertySequenceOfNumbers(v8_object, isolate, name,
-                                                member->ValueSequenceInt64());
+        add_vector(name, member->ValueSequenceInt64());
         break;
       case kWebRTCStatsMemberTypeSequenceUint64:
-        success &= AddPropertySequenceOfNumbers(v8_object, isolate, name,
-                                                member->ValueSequenceUint64());
+        add_vector(name, member->ValueSequenceUint64());
         break;
       case kWebRTCStatsMemberTypeSequenceDouble:
-        success &= AddPropertySequenceOfNumbers(v8_object, isolate, name,
-                                                member->ValueSequenceDouble());
+        add_vector(name, member->ValueSequenceDouble());
         break;
       case kWebRTCStatsMemberTypeSequenceString:
-        success &= AddPropertySequenceOfStrings(v8_object, isolate, name,
-                                                member->ValueSequenceString());
+        add_vector(name, member->ValueSequenceString());
         break;
       default:
         NOTREACHED();
     }
   }
-  if (!success) {
+
+  v8::Local<v8::Object> v8_object = builder.V8Value();
+  if (v8_object.IsEmpty()) {
     NOTREACHED();
-    return v8::Undefined(isolate);
+    return v8::Undefined(script_state->GetIsolate());
   }
   return v8_object;
 }
diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
index afc9e46de..09177ea 100644
--- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
+++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
@@ -56,7 +56,7 @@
 #if DCHECK_IS_ON()
 static bool IsIsolatedWorldId(int world_id) {
   return DOMWrapperWorld::kMainWorldId < world_id &&
-         world_id < DOMWrapperWorld::kIsolatedWorldIdLimit;
+         world_id < DOMWrapperWorld::kDOMWrapperWorldIsolatedWorldIdLimit;
 }
 
 static bool IsMainWorldId(int world_id) {
diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
index 04ca480..76f79ae 100644
--- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
+++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
@@ -59,8 +59,10 @@
     kInvalidWorldId = -1,
     kMainWorldId = 0,
 
-    kEmbedderWorldIdLimit = IsolatedWorldId::kEmbedderWorldIdLimit,
-    kIsolatedWorldIdLimit = IsolatedWorldId::kIsolatedWorldIdLimit,
+    kDOMWrapperWorldEmbedderWorldIdLimit =
+        IsolatedWorldId::kEmbedderWorldIdLimit,
+    kDOMWrapperWorldIsolatedWorldIdLimit =
+        IsolatedWorldId::kIsolatedWorldIdLimit,
 
     // Other worlds can use IDs after this. Don't manually pick up an ID from
     // this range. generateWorldIdForType() picks it up on behalf of you.
diff --git a/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc b/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc
index 87ed9f7..e82a0e1d 100644
--- a/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc
+++ b/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc
@@ -82,6 +82,28 @@
   return events;
 }
 
+void WebCoalescedInputEvent::AddPredictedEvent(
+    const blink::WebInputEvent& event) {
+  predicted_events_.push_back(MakeWebScopedInputEvent(event));
+}
+
+size_t WebCoalescedInputEvent::PredictedEventSize() const {
+  return predicted_events_.size();
+}
+
+const WebInputEvent& WebCoalescedInputEvent::PredictedEvent(
+    size_t index) const {
+  return *predicted_events_[index].get();
+}
+
+std::vector<const WebInputEvent*>
+WebCoalescedInputEvent::GetPredictedEventsPointers() const {
+  std::vector<const WebInputEvent*> events;
+  for (const auto& event : predicted_events_)
+    events.push_back(event.get());
+  return events;
+}
+
 WebCoalescedInputEvent::WebCoalescedInputEvent(const WebInputEvent& event) {
   event_ = MakeWebScopedInputEvent(event);
   coalesced_events_.push_back(MakeWebScopedInputEvent(event));
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
index 70218ad..383fe84 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
@@ -94,10 +94,10 @@
 
 std::unique_ptr<AnimationWorkletDispatcherInput> CreateTestMutatorInput() {
   AnimationWorkletInput::AddAndUpdateState state1{
-      {11, 1}, "test1", 5000, nullptr};
+      {11, 1}, "test1", 5000, nullptr, 1};
 
   AnimationWorkletInput::AddAndUpdateState state2{
-      {22, 2}, "test2", 5000, nullptr};
+      {22, 2}, "test2", 5000, nullptr, 1};
 
   auto input = std::make_unique<AnimationWorkletDispatcherInput>();
   input->Add(std::move(state1));
@@ -146,7 +146,7 @@
   EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
 
   AnimationWorkletInput::AddAndUpdateState state2{
-      {22, 2}, "test2", 5000, nullptr};
+      {22, 2}, "test2", 5000, nullptr, 1};
 
   auto input = std::make_unique<AnimationWorkletDispatcherInput>();
   input->Add(std::move(state2));
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
index 16f6a142..42e7b288 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
@@ -562,22 +562,6 @@
   EXPECT_EQ(paint_image.FrameCount(), 3u);
 }
 
-TEST_F(BitmapImageTest, DecoderAndCacheMipLevels) {
-  // Tests that the supported sizes from the decoder match the mip level sizes
-  // in cc.
-  LoadImage("/images/resources/cat.jpg");
-  auto paint_image = image_->PaintImageForCurrentFrame();
-  // Jpeg decoder supports upto 1/8 downscales, or mip level 3.
-  for (int mip_level = 0; mip_level < 4; ++mip_level) {
-    SCOPED_TRACE(mip_level);
-    SkISize scaled_size = gfx::SizeToSkISize(cc::MipMapUtil::GetSizeForLevel(
-        gfx::Size(paint_image.width(), paint_image.height()), mip_level));
-    SkISize supported_size = paint_image.GetSupportedDecodeSize(scaled_size);
-    EXPECT_EQ(gfx::SkISizeToSize(supported_size),
-              gfx::SkISizeToSize(scaled_size));
-  }
-}
-
 class BitmapImageTestWithMockDecoder : public BitmapImageTest,
                                        public MockImageDecoderClient {
  public:
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
index 8858238..f08fbb3 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
@@ -78,7 +78,7 @@
 const int exifMarker = JPEG_APP0 + 1;
 
 // JPEG only supports a denominator of 8.
-const unsigned g_scale_denomiator = 8;
+const unsigned g_scale_denominator = 8;
 
 // Extracts the JPEG color space of an image for UMA purposes given |info| which
 // is assumed to have gone through a jpeg_read_header(). When the color space is
@@ -451,6 +451,21 @@
     ClearBuffer();
   }
 
+  bool ShouldDecodeToOriginalSize() const {
+    // We should decode only to original size if either dimension cannot fit a
+    // whole number of MCUs.
+    const int max_h_samp_factor = info_.max_h_samp_factor;
+    const int max_v_samp_factor = info_.max_v_samp_factor;
+    DCHECK_GE(max_h_samp_factor, 1);
+    DCHECK_GE(max_v_samp_factor, 1);
+    DCHECK_LE(max_h_samp_factor, 4);
+    DCHECK_LE(max_v_samp_factor, 4);
+    const int mcu_width = info_.max_h_samp_factor * DCTSIZE;
+    const int mcu_height = info_.max_v_samp_factor * DCTSIZE;
+    return info_.image_width % mcu_width != 0 ||
+           info_.image_height % mcu_height != 0;
+  }
+
   // Decode the JPEG data. If |only_size| is specified, then only the size
   // information will be decoded.
   bool Decode(bool only_size) {
@@ -496,16 +511,37 @@
 
         // Calculate and set decoded size.
         int max_numerator = decoder_->DesiredScaleNumerator();
-        info_.scale_denom = g_scale_denomiator;
+        info_.scale_denom = g_scale_denominator;
 
         if (decoder_->ShouldGenerateAllSizes()) {
+          // Some images should not be scaled down by libjpeg_turbo because
+          // doing so may cause artifacts. Specifically, if the image contains a
+          // non-whole number of MCUs in either dimension, it's possible that
+          // the encoder used bogus data to create the last row or column of
+          // MCUs. This data may manifest when downscaling using libjpeg_turbo.
+          // See https://crbug.com/890745 and
+          // https://github.com/libjpeg-turbo/libjpeg-turbo/issues/297. Hence,
+          // we'll only allow downscaling an image if both dimensions fit a
+          // whole number of MCUs or if decoding to the original size would
+          // cause us to exceed memory limits. The latter case is detected by
+          // checking the |max_numerator| returned by DesiredScaleNumerator():
+          // this method will return either |g_scale_denominator| if decoding to
+          // the original size won't exceed the memory limit (see
+          // |max_decoded_bytes_| in ImageDecoder) or something less than
+          // |g_scale_denominator| otherwise to ensure the image is downscaled.
           std::vector<SkISize> sizes;
-          sizes.reserve(max_numerator);
-          for (int numerator = 1; numerator <= max_numerator; ++numerator) {
-            info_.scale_num = numerator;
-            jpeg_calc_output_dimensions(&info_);
+          if (max_numerator == g_scale_denominator &&
+              ShouldDecodeToOriginalSize()) {
             sizes.push_back(
-                SkISize::Make(info_.output_width, info_.output_height));
+                SkISize::Make(info_.image_width, info_.image_height));
+          } else {
+            sizes.reserve(max_numerator);
+            for (int numerator = 1; numerator <= max_numerator; ++numerator) {
+              info_.scale_num = numerator;
+              jpeg_calc_output_dimensions(&info_);
+              sizes.push_back(
+                  SkISize::Make(info_.output_width, info_.output_height));
+            }
           }
           decoder_->SetSupportedDecodeSizes(std::move(sizes));
         }
@@ -868,13 +904,13 @@
   size_t original_bytes = Size().Width() * Size().Height() * 4;
 
   if (original_bytes <= max_decoded_bytes_)
-    return g_scale_denomiator;
+    return g_scale_denominator;
 
   // Downsample according to the maximum decoded size.
   unsigned scale_numerator = static_cast<unsigned>(floor(sqrt(
       // MSVC needs explicit parameter type for sqrt().
-      static_cast<float>(max_decoded_bytes_ * g_scale_denomiator *
-                         g_scale_denomiator / original_bytes))));
+      static_cast<float>(max_decoded_bytes_ * g_scale_denominator *
+                         g_scale_denominator / original_bytes))));
 
   return scale_numerator;
 }
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
index 899618c..734ee90 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
@@ -338,7 +338,10 @@
 }
 
 TEST(JPEGImageDecoderTest, SupportedSizesRectangle) {
-  const char* jpeg_file = "/images/resources/icc-v2-gbr.jpg";  // 275x207
+  // This 272x200 image uses 4:2:2 sampling format. The MCU is therefore 16x8.
+  // The width is a multiple of 16 and the height is a multiple of 8, so it's
+  // okay for the decoder to downscale it.
+  const char* jpeg_file = "/images/resources/icc-v2-gbr-422-whole-mcus.jpg";
 
   scoped_refptr<SharedBuffer> data = ReadFile(jpeg_file);
   ASSERT_TRUE(data);
@@ -349,9 +352,9 @@
   // This will decode the size and needs to be called to avoid DCHECKs
   ASSERT_TRUE(decoder->IsSizeAvailable());
   std::vector<SkISize> expected_sizes = {
-      SkISize::Make(35, 26),   SkISize::Make(69, 52),   SkISize::Make(104, 78),
-      SkISize::Make(138, 104), SkISize::Make(172, 130), SkISize::Make(207, 156),
-      SkISize::Make(241, 182), SkISize::Make(275, 207)};
+      SkISize::Make(34, 25),   SkISize::Make(68, 50),   SkISize::Make(102, 75),
+      SkISize::Make(136, 100), SkISize::Make(170, 125), SkISize::Make(204, 150),
+      SkISize::Make(238, 175), SkISize::Make(272, 200)};
 
   auto sizes = decoder->GetSupportedDecodeSizes();
   ASSERT_EQ(expected_sizes.size(), sizes.size());
@@ -363,6 +366,70 @@
   }
 }
 
+TEST(JPEGImageDecoderTest,
+     SupportedSizesRectangleNotMultipleOfMCUIfMemoryBound) {
+  // This 275x207 image uses 4:2:0 sampling format. The MCU is therefore 16x16.
+  // Neither the width nor the height is a multiple of the MCU, so downscaling
+  // should not be supported. However, we limit the memory so that the decoder
+  // is forced to support downscaling.
+  const char* jpeg_file = "/images/resources/icc-v2-gbr.jpg";
+
+  scoped_refptr<SharedBuffer> data = ReadFile(jpeg_file);
+  ASSERT_TRUE(data);
+
+  // Make the memory limit one fewer byte than what is needed in order to force
+  // downscaling.
+  std::unique_ptr<ImageDecoder> decoder = CreateJPEGDecoder(275 * 207 * 4 - 1);
+  decoder->SetData(data.get(), true);
+  // This will decode the size and needs to be called to avoid DCHECKs
+  ASSERT_TRUE(decoder->IsSizeAvailable());
+  std::vector<SkISize> expected_sizes = {
+      SkISize::Make(35, 26),   SkISize::Make(69, 52),   SkISize::Make(104, 78),
+      SkISize::Make(138, 104), SkISize::Make(172, 130), SkISize::Make(207, 156),
+      SkISize::Make(241, 182)};
+
+  auto sizes = decoder->GetSupportedDecodeSizes();
+  ASSERT_EQ(expected_sizes.size(), sizes.size());
+  for (size_t i = 0; i < sizes.size(); ++i) {
+    EXPECT_TRUE(expected_sizes[i] == sizes[i])
+        << "Expected " << expected_sizes[i].width() << "x"
+        << expected_sizes[i].height() << ". Got " << sizes[i].width() << "x"
+        << sizes[i].height();
+  }
+}
+
+TEST(JPEGImageDecoderTest, SupportedSizesRectangleNotMultipleOfMCU) {
+  struct {
+    const char* jpeg_file;
+    SkISize expected_size;
+  } recs[] = {
+      {// This 264x192 image uses 4:2:0 sampling format. The MCU is therefore
+       // 16x16. The height is a multiple of 16, but the width is not a
+       // multiple of 16, so it's not okay for the decoder to downscale it.
+       "/images/resources/icc-v2-gbr-420-width-not-whole-mcu.jpg",
+       SkISize::Make(264, 192)},
+      {// This 272x200 image uses 4:2:0 sampling format. The MCU is therefore
+       // 16x16. The width is a multiple of 16, but the width is not a multiple
+       // of 16, so it's not okay for the decoder to downscale it.
+       "/images/resources/icc-v2-gbr-420-height-not-whole-mcu.jpg",
+       SkISize::Make(272, 200)}};
+  for (const auto& rec : recs) {
+    scoped_refptr<SharedBuffer> data = ReadFile(rec.jpeg_file);
+    ASSERT_TRUE(data);
+    std::unique_ptr<ImageDecoder> decoder =
+        CreateJPEGDecoder(std::numeric_limits<int>::max());
+    decoder->SetData(data.get(), true);
+    // This will decode the size and needs to be called to avoid DCHECKs
+    ASSERT_TRUE(decoder->IsSizeAvailable());
+    auto sizes = decoder->GetSupportedDecodeSizes();
+    ASSERT_EQ(1u, sizes.size());
+    EXPECT_EQ(rec.expected_size, sizes[0])
+        << "Expected " << rec.expected_size.width() << "x"
+        << rec.expected_size.height() << ". Got " << sizes[0].width() << "x"
+        << sizes[0].height();
+  }
+}
+
 TEST(JPEGImageDecoderTest, SupportedSizesTruncatedIfMemoryBound) {
   const char* jpeg_file = "/images/resources/lenna.jpg";  // 256x256
   scoped_refptr<SharedBuffer> data = ReadFile(jpeg_file);
diff --git a/third_party/blink/renderer/platform/text/DEPS b/third_party/blink/renderer/platform/text/DEPS
index a7e3ce7..9ab8273 100644
--- a/third_party/blink/renderer/platform/text/DEPS
+++ b/third_party/blink/renderer/platform/text/DEPS
@@ -9,6 +9,7 @@
     "+third_party/blink/renderer/platform/date_components.h",
     "+third_party/blink/renderer/platform/heap",
     "+third_party/blink/renderer/platform/language.h",
+    "+third_party/blink/renderer/platform/mac/version_util_mac.h",
     "+third_party/blink/renderer/platform/layout_test_support.h",
     "+third_party/blink/renderer/platform/platform_export.h",
     "+third_party/blink/renderer/platform/runtime_enabled_features.h",
diff --git a/third_party/blink/renderer/platform/text/locale_mac_test.cc b/third_party/blink/renderer/platform/text/locale_mac_test.cc
index aad937b..661489c 100644
--- a/third_party/blink/renderer/platform/text/locale_mac_test.cc
+++ b/third_party/blink/renderer/platform/text/locale_mac_test.cc
@@ -29,6 +29,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/mac/version_util_mac.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
@@ -224,16 +225,26 @@
   // Do not test ja_JP locale. OS X 10.8 and 10.7 have different formats.
 }
 
-// http://crbug.com/811685 This test is flaky.
-TEST_F(LocaleMacTest, DISABLED_formatTime) {
+TEST_F(LocaleMacTest, formatTime) {
+  // On MacOS 10.13+, Arabic times (which contain spaces) use \xC2\xA0
+  // (which is a non-breaking space) instead of \x20 for those spaces. The
+  // 10.13+ behavior is probably more correct, but there does not appear to be a
+  // way to configure NSDateFormatter to behave that way on < 10.13.
+  bool expect_ar_nbsp = !IsOS10_10() && !IsOS10_11() && !IsOS10_12();
+
   EXPECT_STREQ("1:23 PM",
                FormatTime("en_US", 13, 23, 00, 000, true).Utf8().data());
   EXPECT_STREQ("13:23",
                FormatTime("fr_FR", 13, 23, 00, 000, true).Utf8().data());
   EXPECT_STREQ("13:23",
                FormatTime("ja_JP", 13, 23, 00, 000, true).Utf8().data());
-  EXPECT_STREQ("\xD9\xA1:\xD9\xA2\xD9\xA3 \xD9\x85",
-               FormatTime("ar", 13, 23, 00, 000, true).Utf8().data());
+  if (expect_ar_nbsp) {
+    EXPECT_STREQ("\xD9\xA1:\xD9\xA2\xD9\xA3\xC2\xA0\xD9\x85",
+                 FormatTime("ar", 13, 23, 00, 000, true).Utf8().data());
+  } else {
+    EXPECT_STREQ("\xD9\xA1:\xD9\xA2\xD9\xA3 \xD9\x85",
+                 FormatTime("ar", 13, 23, 00, 000, true).Utf8().data());
+  }
   EXPECT_STREQ("\xDB\xB1\xDB\xB3:\xDB\xB2\xDB\xB3",
                FormatTime("fa", 13, 23, 00, 000, true).Utf8().data());
 
@@ -243,8 +254,13 @@
                FormatTime("fr_FR", 00, 00, 00, 000, true).Utf8().data());
   EXPECT_STREQ("0:00",
                FormatTime("ja_JP", 00, 00, 00, 000, true).Utf8().data());
-  EXPECT_STREQ("\xD9\xA1\xD9\xA2:\xD9\xA0\xD9\xA0 \xD8\xB5",
-               FormatTime("ar", 00, 00, 00, 000, true).Utf8().data());
+  if (expect_ar_nbsp) {
+    EXPECT_STREQ("\xD9\xA1\xD9\xA2:\xD9\xA0\xD9\xA0\xC2\xA0\xD8\xB5",
+                 FormatTime("ar", 00, 00, 00, 000, true).Utf8().data());
+  } else {
+    EXPECT_STREQ("\xD9\xA1\xD9\xA2:\xD9\xA0\xD9\xA0 \xD8\xB5",
+                 FormatTime("ar", 00, 00, 00, 000, true).Utf8().data());
+  }
   EXPECT_STREQ("\xDB\xB0:\xDB\xB0\xDB\xB0",
                FormatTime("fa", 00, 00, 00, 000, true).Utf8().data());
 
@@ -254,10 +270,17 @@
                FormatTime("fr_FR", 07, 07, 07, 007, false).Utf8().data());
   EXPECT_STREQ("7:07:07.007",
                FormatTime("ja_JP", 07, 07, 07, 007, false).Utf8().data());
-  EXPECT_STREQ(
-      "\xD9\xA7:\xD9\xA0\xD9\xA7:"
-      "\xD9\xA0\xD9\xA7\xD9\xAB\xD9\xA0\xD9\xA0\xD9\xA7 \xD8\xB5",
-      FormatTime("ar", 07, 07, 07, 007, false).Utf8().data());
+  if (expect_ar_nbsp) {
+    EXPECT_STREQ(
+        "\xD9\xA7:\xD9\xA0\xD9\xA7:"
+        "\xD9\xA0\xD9\xA7\xD9\xAB\xD9\xA0\xD9\xA0\xD9\xA7\xC2\xA0\xD8\xB5",
+        FormatTime("ar", 07, 07, 07, 007, false).Utf8().data());
+  } else {
+    EXPECT_STREQ(
+        "\xD9\xA7:\xD9\xA0\xD9\xA7:"
+        "\xD9\xA0\xD9\xA7\xD9\xAB\xD9\xA0\xD9\xA0\xD9\xA7 \xD8\xB5",
+        FormatTime("ar", 07, 07, 07, 007, false).Utf8().data());
+  }
   EXPECT_STREQ(
       "\xDB\xB7:\xDB\xB0\xDB\xB7:"
       "\xDB\xB0\xDB\xB7\xD9\xAB\xDB\xB0\xDB\xB0\xDB\xB7",
diff --git a/third_party/blink/renderer/platform/text/text_run.h b/third_party/blink/renderer/platform/text/text_run.h
index df07c6f..0513011 100644
--- a/third_party/blink/renderer/platform/text/text_run.h
+++ b/third_party/blink/renderer/platform/text/text_run.h
@@ -50,8 +50,6 @@
     kAllowLeadingExpansion = 1 << 1,
   };
 
-  enum TextCodePath { kAuto = 0, kForceSimple = 1, kForceComplex = 2 };
-
   typedef unsigned ExpansionBehavior;
 
   TextRun(const LChar* c,
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index 84eeae0..36b7eb9 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -256,6 +256,16 @@
                 '--ignore-certificate-errors-spki-list=' + WPT_FINGERPRINT +
                 ',' + SXG_FINGERPRINT + ',' + SXG_WPT_FINGERPRINT,
                 '--user-data-dir']
+
+        # If we're already repeating the tests more than once, then we're not
+        # particularly concerned with speed. Resetting the shell between tests
+        # increases test run time by 2-5X, but provides more consistent results
+        # [less state leaks between tests].
+        if (self.get_option('reset_shell_between_tests') or
+            self.get_option('repeat_each') > 1 or
+            self.get_option('iterations') > 1):
+            flags += ['--reset-shell-between-tests']
+
         if TESTS_IN_BLINK:
             flags += ['--tests-in-blink']
         return flags
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py
index 2e81b05..ddcdeb9 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py
@@ -356,6 +356,14 @@
                 action='store',
                 help='Output per-test profile information, using the specified profiler.'),
             optparse.make_option(
+                '--reset-shell-between-tests',
+                action='store_true',
+                default=False,
+                help='Resetting the shell between tests causes the tests to '
+                     'take twice as long to run on average, but provides more '
+                     'consistent results. This is automatically enabled if '
+                     '--repeat-each or --gtest_repeat is specified'),
+            optparse.make_option(
                 '--repeat-each',
                 type='int',
                 default=1,
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index f57824f..256aca0 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -49,7 +49,8 @@
 
 # Windows' assembly is built with Yasm. The other platforms use the platform
 # assembler.
-if (is_win && !is_msan) {
+# Exclude Yasm for Windows ARM64 because Yasm targets to x86 and x64 only.
+if (is_win && !is_msan && current_cpu != "arm64") {
   import("//third_party/yasm/yasm_assemble.gni")
   yasm_assemble("boringssl_asm") {
     if (current_cpu == "x64") {
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium
index 0f23db2..4ce5373f 100644
--- a/third_party/feed/README.chromium
+++ b/third_party/feed/README.chromium
@@ -2,7 +2,7 @@
 Short name: feed
 URL: https://chromium.googlesource.com/feed
 Version: 0
-Revision: d97e635aa74d7d067a32cca5a8b65a39f4855e85
+Revision: 89e2c00bd3b9b2f0ce981f5c1d07a40c1e20eac1
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider.tlb b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider.tlb
index dbcd1c1..ae0734c 100644
--- a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider.tlb
+++ b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_i.c b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_i.c
index bb729ba..fdcf5eeb 100644
--- a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_i.c
+++ b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_i.c
@@ -8,7 +8,7 @@
  /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

-/* Compiler settings for ../../chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

+/* Compiler settings for gen/chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

     Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

@@ -82,13 +82,7 @@
 MIDL_DEFINE_GUID(IID, LIBID_GaiaCredentialProviderLib,0x4ADC3A52,0x8673,0x4CE3,0x81,0xF6,0x83,0x3D,0x18,0xBE,0xEB,0xA2);

 

 

-MIDL_DEFINE_GUID(CLSID, CLSID_GaiaCredentialProvider,0x0B5BFDF0,0x4594,0x47AC,0x94,0x0A,0xCF,0xC6,0x9A,0xBC,0x56,0x1C);

-

-

-MIDL_DEFINE_GUID(CLSID, CLSID_GaiaCredential,0x44AF95AC,0x6B23,0x4C54,0x94,0xBE,0xED,0xB1,0xCB,0x52,0xDA,0xFD);

-

-

-MIDL_DEFINE_GUID(CLSID, CLSID_ReauthCredential,0xE6CC5D8B,0x54C2,0x4586,0xAD,0xC3,0x74,0x8E,0xD1,0x62,0x84,0xB7);

+MIDL_DEFINE_GUID(CLSID, CLSID_GaiaCredentialProvider,0x89adae71,0xaee5,0x4ee2,0xbf,0xfb,0xe8,0x42,0x4e,0x06,0xf5,0x19);

 

 #undef MIDL_DEFINE_GUID

 

diff --git a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_i.h b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_i.h
index 11b1bc2..bb09b0db 100644
--- a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_i.h
+++ b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_i.h
@@ -6,7 +6,7 @@
  /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

-/* Compiler settings for ../../chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

+/* Compiler settings for gen/chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

     Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

@@ -85,30 +85,6 @@
 #endif 	/* __GaiaCredentialProvider_FWD_DEFINED__ */

 

 

-#ifndef __GaiaCredential_FWD_DEFINED__

-#define __GaiaCredential_FWD_DEFINED__

-

-#ifdef __cplusplus

-typedef class GaiaCredential GaiaCredential;

-#else

-typedef struct GaiaCredential GaiaCredential;

-#endif /* __cplusplus */

-

-#endif 	/* __GaiaCredential_FWD_DEFINED__ */

-

-

-#ifndef __ReauthCredential_FWD_DEFINED__

-#define __ReauthCredential_FWD_DEFINED__

-

-#ifdef __cplusplus

-typedef class ReauthCredential ReauthCredential;

-#else

-typedef struct ReauthCredential ReauthCredential;

-#endif /* __cplusplus */

-

-#endif 	/* __ReauthCredential_FWD_DEFINED__ */

-

-

 /* header files for imported files */

 #include "oaidl.h"

 #include "ocidl.h"

@@ -514,25 +490,9 @@
 

 #ifdef __cplusplus

 

-class DECLSPEC_UUID("0B5BFDF0-4594-47AC-940A-CFC69ABC561C")

+class DECLSPEC_UUID("89adae71-aee5-4ee2-bffb-e8424e06f519")

 GaiaCredentialProvider;

 #endif

-

-EXTERN_C const CLSID CLSID_GaiaCredential;

-

-#ifdef __cplusplus

-

-class DECLSPEC_UUID("44AF95AC-6B23-4C54-94BE-EDB1CB52DAFD")

-GaiaCredential;

-#endif

-

-EXTERN_C const CLSID CLSID_ReauthCredential;

-

-#ifdef __cplusplus

-

-class DECLSPEC_UUID("E6CC5D8B-54C2-4586-ADC3-748ED16284B7")

-ReauthCredential;

-#endif

 #endif /* __GaiaCredentialProviderLib_LIBRARY_DEFINED__ */

 

 /* Additional Prototypes for ALL interfaces */

diff --git a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_p.c b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_p.c
index 8198483..ed7fc05 100644
--- a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_p.c
+++ b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x64/gaia_credential_provider_p.c
@@ -6,7 +6,7 @@
  /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

-/* Compiler settings for ../../chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

+/* Compiler settings for gen/chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

     Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

diff --git a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider.tlb b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider.tlb
index cd1e751..aa92482 100644
--- a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider.tlb
+++ b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_i.c b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_i.c
index 48dc5c3..3584b52 100644
--- a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_i.c
+++ b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_i.c
@@ -8,7 +8,7 @@
  /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

-/* Compiler settings for ../../chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

+/* Compiler settings for gen/chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

     Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

@@ -82,13 +82,7 @@
 MIDL_DEFINE_GUID(IID, LIBID_GaiaCredentialProviderLib,0x4ADC3A52,0x8673,0x4CE3,0x81,0xF6,0x83,0x3D,0x18,0xBE,0xEB,0xA2);

 

 

-MIDL_DEFINE_GUID(CLSID, CLSID_GaiaCredentialProvider,0x0B5BFDF0,0x4594,0x47AC,0x94,0x0A,0xCF,0xC6,0x9A,0xBC,0x56,0x1C);

-

-

-MIDL_DEFINE_GUID(CLSID, CLSID_GaiaCredential,0x44AF95AC,0x6B23,0x4C54,0x94,0xBE,0xED,0xB1,0xCB,0x52,0xDA,0xFD);

-

-

-MIDL_DEFINE_GUID(CLSID, CLSID_ReauthCredential,0xE6CC5D8B,0x54C2,0x4586,0xAD,0xC3,0x74,0x8E,0xD1,0x62,0x84,0xB7);

+MIDL_DEFINE_GUID(CLSID, CLSID_GaiaCredentialProvider,0x89adae71,0xaee5,0x4ee2,0xbf,0xfb,0xe8,0x42,0x4e,0x06,0xf5,0x19);

 

 #undef MIDL_DEFINE_GUID

 

diff --git a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_i.h b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_i.h
index 0ac426f0..e428dfd 100644
--- a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_i.h
+++ b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_i.h
@@ -6,7 +6,7 @@
  /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

-/* Compiler settings for ../../chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

+/* Compiler settings for gen/chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

     Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

@@ -85,30 +85,6 @@
 #endif 	/* __GaiaCredentialProvider_FWD_DEFINED__ */

 

 

-#ifndef __GaiaCredential_FWD_DEFINED__

-#define __GaiaCredential_FWD_DEFINED__

-

-#ifdef __cplusplus

-typedef class GaiaCredential GaiaCredential;

-#else

-typedef struct GaiaCredential GaiaCredential;

-#endif /* __cplusplus */

-

-#endif 	/* __GaiaCredential_FWD_DEFINED__ */

-

-

-#ifndef __ReauthCredential_FWD_DEFINED__

-#define __ReauthCredential_FWD_DEFINED__

-

-#ifdef __cplusplus

-typedef class ReauthCredential ReauthCredential;

-#else

-typedef struct ReauthCredential ReauthCredential;

-#endif /* __cplusplus */

-

-#endif 	/* __ReauthCredential_FWD_DEFINED__ */

-

-

 /* header files for imported files */

 #include "oaidl.h"

 #include "ocidl.h"

@@ -514,25 +490,9 @@
 

 #ifdef __cplusplus

 

-class DECLSPEC_UUID("0B5BFDF0-4594-47AC-940A-CFC69ABC561C")

+class DECLSPEC_UUID("89adae71-aee5-4ee2-bffb-e8424e06f519")

 GaiaCredentialProvider;

 #endif

-

-EXTERN_C const CLSID CLSID_GaiaCredential;

-

-#ifdef __cplusplus

-

-class DECLSPEC_UUID("44AF95AC-6B23-4C54-94BE-EDB1CB52DAFD")

-GaiaCredential;

-#endif

-

-EXTERN_C const CLSID CLSID_ReauthCredential;

-

-#ifdef __cplusplus

-

-class DECLSPEC_UUID("E6CC5D8B-54C2-4586-ADC3-748ED16284B7")

-ReauthCredential;

-#endif

 #endif /* __GaiaCredentialProviderLib_LIBRARY_DEFINED__ */

 

 /* Additional Prototypes for ALL interfaces */

diff --git a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_p.c b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_p.c
index db38570..1489728 100644
--- a/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_p.c
+++ b/third_party/win_build_output/midl/chrome/credential_provider/gaiacp/x86/gaia_credential_provider_p.c
@@ -6,7 +6,7 @@
  /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

-/* Compiler settings for ../../chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

+/* Compiler settings for gen/chrome/credential_provider/gaiacp/gaia_credential_provider.idl:

     Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 8ce0439..271203f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -1114,6 +1114,8 @@
   <int value="15"
       label="Visible renderer in foreground app allocation failure"/>
   <int value="16" label="All renderer allocation failure"/>
+  <int value="17" label="Utility process foreground OOM"/>
+  <int value="18" label="All utility process crashes"/>
 </enum>
 
 <enum name="AndroidResourceExtractionStatus">
@@ -1898,6 +1900,17 @@
   <int value="11" label="READY_SCREEN_CONTINUED"/>
 </enum>
 
+<enum name="AssistantSource">
+  <int value="0" label="kUnspecified"/>
+  <int value="1" label="kDeepLink"/>
+  <int value="2" label="kHotkey"/>
+  <int value="3" label="kHotword"/>
+  <int value="4" label="kLauncherSearchBox"/>
+  <int value="5" label="kLongPressLauncher"/>
+  <int value="6" label="kSetup"/>
+  <int value="7" label="kStylus"/>
+</enum>
+
 <enum name="AsyncDNSConfigParsePosix">
   <int value="0" label="OK"/>
   <int value="1" label="RES_INIT_FAILED"/>
@@ -5287,6 +5300,15 @@
   <int value="31" label="Chooser Permissions Bubble"/>
 </enum>
 
+<enum name="CachedImageFetcherEvent">
+  <int value="0" label="Request for an image"/>
+  <int value="1" label="Cache hit"/>
+  <int value="2" label="Cache miss"/>
+  <int value="3" label="Cache decoding error"/>
+  <int value="4" label="Network transcoding error"/>
+  <int value="5" label="Failure to fetch the image from the network"/>
+</enum>
+
 <enum name="CacheResult">
   <int value="0" label="MEMORY_CACHE_HIT"/>
   <int value="1" label="DISK_CACHE_HIT"/>
@@ -29246,6 +29268,7 @@
   <int value="-1174267639" label="ClientLoFi:disabled"/>
   <int value="-1172572865" label="NTPShowGoogleGInOmnibox:enabled"/>
   <int value="-1172204005" label="enable-offline-auto-reload-visible-only"/>
+  <int value="-1166715563" label="ChromeOSAssistant:disabled"/>
   <int value="-1162944097" label="enable-color-correct-rendering"/>
   <int value="-1161409696" label="MediaRemotingEncrypted:enabled"/>
   <int value="-1161384421" label="ContextualSuggestionsAboveArticles:enabled"/>
@@ -30603,6 +30626,7 @@
   <int value="1481562816" label="disable-password-link"/>
   <int value="1486171015" label="disable-fill-on-account-select"/>
   <int value="1487341558" label="MacViewsAutofillPopup:enabled"/>
+  <int value="1488193175" label="ChromeOSAssistant:enabled"/>
   <int value="1488700164" label="password-import:disabled"/>
   <int value="1489915799" label="disable-permissions-blacklist"/>
   <int value="1490043732" label="enable-fill-on-account-select"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e1b8c905..168040c 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -5324,6 +5324,13 @@
   <summary>Record the status of the Assistant opt-in flow.</summary>
 </histogram>
 
+<histogram name="Assistant.QueryCountPerEntryPoint" enum="AssistantSource"
+    expires_after="2019-10-15">
+  <owner>xiaohuic@chromium.org</owner>
+  <owner>meilinw@chromium.org</owner>
+  <summary>Number of queries fired for each entry point.</summary>
+</histogram>
+
 <histogram name="Assistant.ServiceStartTime" units="ms">
   <owner>updowndota@chromium.org</owner>
   <summary>Amount of time spent in starting Assistant service.</summary>
@@ -11552,6 +11559,43 @@
   <summary>A bubble was given to the bubble manager but not shown.</summary>
 </histogram>
 
+<histogram name="CachedImageFetcher.Events" enum="CachedImageFetcherEvent">
+  <owner>fgorski@chromium.org</owner>
+  <owner>wylieb@chromium.org</owner>
+  <summary>
+    Events that track the lifecycle and performance of the cached_image_fetcher.
+    The events reported include: success/failure conditions, various recoverable
+    errors and a couple of dead-end errors.
+  </summary>
+</histogram>
+
+<histogram name="CachedImageFetcher.ImageLoadFromCacheTime" units="ms">
+  <owner>fgorski@chromium.org</owner>
+  <owner>wylieb@chromium.org</owner>
+  <summary>
+    The time it takes for cached_image_fetcher to load an image from the cache.
+  </summary>
+</histogram>
+
+<histogram name="CachedImageFetcher.ImageLoadFromNetworkAfterCacheHit"
+    units="ms">
+  <owner>fgorski@chromium.org</owner>
+  <owner>wylieb@chromium.org</owner>
+  <summary>
+    The time it takes for cached_image_fetcher to load an image from the network
+    after a cache hit.
+  </summary>
+</histogram>
+
+<histogram name="CachedImageFetcher.ImageLoadFromNetworkTime" units="ms">
+  <owner>fgorski@chromium.org</owner>
+  <owner>wylieb@chromium.org</owner>
+  <summary>
+    The time it takes for cached_image_fetcher to load an image from the
+    network.
+  </summary>
+</histogram>
+
 <histogram name="Canvas.ContextType" enum="CanvasContextType">
   <owner>junov@chromium.org</owner>
   <owner>kbr@chromium.org</owner>
@@ -84750,6 +84794,16 @@
   </summary>
 </histogram>
 
+<histogram name="Renderer4.Browser.RasterTaskTotalDuration"
+    units="microseconds">
+  <owner>khushalsagar@chromium.org</owner>
+  <summary>
+    Time spent completing all work for a compositor rasterization task. This
+    includes the time in the renderer process for sending GL or paint commands
+    to the GPU process and the time for flushing these commands to the driver.
+  </summary>
+</histogram>
+
 <histogram name="Renderer4.CommitToFinish" units="ms">
   <owner>wiltzius@chromium.org</owner>
   <summary>
@@ -85228,6 +85282,16 @@
   </summary>
 </histogram>
 
+<histogram name="Renderer4.Renderer.RasterTaskTotalDuration"
+    units="microseconds">
+  <owner>khushalsagar@chromium.org</owner>
+  <summary>
+    Time spent completing all work for a compositor rasterization task. This
+    includes the time in the renderer process for sending GL or paint commands
+    to the GPU process and the time for flushing these commands to the driver.
+  </summary>
+</histogram>
+
 <histogram name="Renderer4.renderPassCount">
   <owner>wiltzius@chromium.org</owner>
   <summary>
@@ -107259,6 +107323,15 @@
   <summary>Time it took sync to load models for USS datatypes.</summary>
 </histogram>
 
+<histogram base="true" name="Sync.USSMigrationEntityCount" units="entries"
+    expires_after="2020-02-01">
+  <owner>mastiz@chromium.org</owner>
+  <summary>
+    Counts the number of sync entities per model type successfully migrated from
+    directory to USS.
+  </summary>
+</histogram>
+
 <histogram name="Sync.USSMigrationFailure" enum="SyncModelTypes">
   <owner>maxbogue@chromium.org</owner>
   <summary>Counts directory to USS migration failures per model type.</summary>
@@ -131664,6 +131737,13 @@
   <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs.OutOfRaster"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="RasterTaskTypeGpu" separator=".">
+  <suffix name="Gpu" label="This metric is for only gpu raster."/>
+  <suffix name="Oop" label="This metric is for only oop raster."/>
+  <affected-histogram name="Renderer4.Browser.RasterTaskTotalDuration"/>
+  <affected-histogram name="Renderer4.Renderer.RasterTaskTotalDuration"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="ReadErrorSourceNetwork" separator=".">
   <suffix name="AnyNetwork"/>
   <suffix name="CurrentNetwork"
@@ -133851,6 +133931,7 @@
   <affected-histogram name="Sync.ModelTypeCount4"/>
   <affected-histogram name="Sync.ModelTypeEntityChange"/>
   <affected-histogram name="Sync.ModelTypeMemoryKB"/>
+  <affected-histogram name="Sync.USSMigrationEntityCount"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="SyncModelTypeByMacro" separator="" ordering="prefix">
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 23c0c69..7ead40f 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -17,7 +17,6 @@
 blink_perf.svg,"kouhei@chromium.org, fs@opera.com",Blink>SVG,https://bit.ly/blink-perf-benchmarks,
 components_perftests,csharrison@chromium.org,,,
 dromaeo,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",Blink>Bindings,,
-dummy_benchmark.histogram_benchmark_1,"eakuefner@chromium.org, simonhatch@chromium.org",,,
 dummy_benchmark.noisy_benchmark_1,nednguyen@google.com,Speed>Telemetry,,
 dummy_benchmark.stable_benchmark_1,nednguyen@google.com,Speed>Telemetry,,
 gpu_perftests,"reveman@chromium.org, chrome-gpu-perf-owners@chromium.org",Internals>GPU,,
diff --git a/tools/perf/benchmarks/dummy_benchmark.py b/tools/perf/benchmarks/dummy_benchmark.py
index da440613..b284ae50 100644
--- a/tools/perf/benchmarks/dummy_benchmark.py
+++ b/tools/perf/benchmarks/dummy_benchmark.py
@@ -15,7 +15,6 @@
 from telemetry import benchmark
 from telemetry.value import scalar
 from telemetry.page import legacy_page_test
-from telemetry.web_perf import timeline_based_measurement
 
 from page_sets import dummy_story_set
 
@@ -63,18 +62,3 @@
   def Name(cls):
     return 'dummy_benchmark.noisy_benchmark_1'
 
-
-@benchmark.Info(emails=['eakuefner@chromium.org', 'simonhatch@chromium.org'])
-class DummyBenchmarkThree(perf_benchmark.PerfBenchmark):
-  """A test benchmark for outputting histograms."""
-  page_set = dummy_story_set.DummyStorySet
-
-  def CreateCoreTimelineBasedMeasurementOptions(self):
-    options = timeline_based_measurement.Options(
-        timeline_based_measurement.DEBUG_OVERHEAD_LEVEL)
-    options.SetTimelineBasedMetrics(['sampleMetric'])
-    return options
-
-  @classmethod
-  def Name(cls):
-    return 'dummy_benchmark.histogram_benchmark_1'
diff --git a/tools/perf/core/shard_maps/android-go-perf_map.json b/tools/perf/core/shard_maps/android-go-perf_map.json
index 885370c..091808ab 100644
--- a/tools/perf/core/shard_maps/android-go-perf_map.json
+++ b/tools/perf/core/shard_maps/android-go-perf_map.json
@@ -49,22 +49,22 @@
         "benchmarks": {
             "system_health.common_mobile": {
                 "begin": 17,
-                "end": 33
+                "end": 34
             }
         }
     },
     "6": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 33,
-                "end": 59
+                "begin": 34,
+                "end": 61
             }
         }
     },
     "7": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 59
+                "begin": 61
             },
             "system_health.memory_mobile": {
                 "end": 6
@@ -99,38 +99,38 @@
         "benchmarks": {
             "system_health.memory_mobile": {
                 "begin": 24,
-                "end": 32
+                "end": 33
             }
         }
     },
     "12": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 32,
-                "end": 44
+                "begin": 33,
+                "end": 45
             }
         }
     },
     "13": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 44,
-                "end": 54
+                "begin": 45,
+                "end": 56
             }
         }
     },
     "14": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 54,
-                "end": 60
+                "begin": 56,
+                "end": 62
             }
         }
     },
     "15": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 60
+                "begin": 62
             },
             "system_health.webview_startup": {},
             "v8.browsing_mobile": {
@@ -162,7 +162,7 @@
         }
     },
     "extra_infos": {
-        "num_stories": 194,
+        "num_stories": 198,
         "predicted_min_shard_time": 2148.0,
         "predicted_min_shard_index": 10,
         "predicted_max_shard_time": 3000.0,
@@ -172,15 +172,15 @@
         "shard #2": 2538.0,
         "shard #3": 2356.0,
         "shard #4": 2546.0,
-        "shard #5": 2320.0,
-        "shard #6": 2318.0,
+        "shard #5": 2322.0,
+        "shard #6": 2320.0,
         "shard #7": 2452.0,
         "shard #8": 3000.0,
         "shard #9": 2220.0,
         "shard #10": 2148.0,
-        "shard #11": 2394.0,
+        "shard #11": 2400.0,
         "shard #12": 2538.0,
-        "shard #13": 2334.0,
+        "shard #13": 2340.0,
         "shard #14": 2538.0,
         "shard #15": 2214.0,
         "shard #16": 2574.0,
diff --git a/tools/perf/core/shard_maps/android-nexus5x-perf_map.json b/tools/perf/core/shard_maps/android-nexus5x-perf_map.json
index 509ccd0..0f758274 100644
--- a/tools/perf/core/shard_maps/android-nexus5x-perf_map.json
+++ b/tools/perf/core/shard_maps/android-nexus5x-perf_map.json
@@ -8,18 +8,22 @@
             "blink_perf.dom": {},
             "blink_perf.events": {},
             "blink_perf.image_decoder": {},
-            "blink_perf.layout": {}
+            "blink_perf.layout": {
+                "end": 59
+            }
         }
     },
     "1": {
         "benchmarks": {
+            "blink_perf.layout": {
+                "begin": 59
+            },
             "blink_perf.owp_storage": {},
             "blink_perf.paint": {},
             "blink_perf.parser": {},
             "blink_perf.shadow_dom": {},
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {},
             "jetstream": {},
@@ -34,22 +38,22 @@
         "benchmarks": {
             "loading.mobile": {
                 "begin": 16,
-                "end": 47
+                "end": 44
             }
         }
     },
     "3": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 47,
-                "end": 92
+                "begin": 44,
+                "end": 90
             }
         }
     },
     "4": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 92
+                "begin": 90
             },
             "media.desktop": {},
             "media.mobile": {},
@@ -57,14 +61,14 @@
             "memory.long_running_idle_gmail_background_tbmv2": {},
             "memory.long_running_idle_gmail_tbmv2": {},
             "memory.top_10_mobile": {
-                "end": 12
+                "end": 9
             }
         }
     },
     "5": {
         "benchmarks": {
             "memory.top_10_mobile": {
-                "begin": 12
+                "begin": 9
             },
             "octane": {},
             "oortonline_tbmv2": {},
@@ -72,52 +76,48 @@
             "power.typical_10_mobile": {},
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
-            "rendering.desktop": {},
-            "rendering.mobile": {
-                "end": 12
-            }
+            "rendering.desktop": {}
         }
     },
     "6": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 12,
-                "end": 116
+                "end": 101
             }
         }
     },
     "7": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 116,
-                "end": 220
+                "begin": 101,
+                "end": 228
             }
         }
     },
     "8": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 220,
-                "end": 356
+                "begin": 228,
+                "end": 359
             }
         }
     },
     "9": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 356
+                "begin": 359
             },
-            "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {
-                "end": 12
+            "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
+            "smoothness.tough_pinch_zoom_cases": {
+                "end": 13
             }
         }
     },
     "10": {
         "benchmarks": {
-            "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {
-                "begin": 12
+            "smoothness.tough_pinch_zoom_cases": {
+                "begin": 13
             },
-            "smoothness.tough_pinch_zoom_cases": {},
             "speedometer": {},
             "speedometer-future": {},
             "speedometer2": {},
@@ -125,33 +125,33 @@
             "startup.mobile": {},
             "system_health.common_desktop": {},
             "system_health.common_mobile": {
-                "end": 24
+                "end": 39
             }
         }
     },
     "11": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 24
+                "begin": 39
             },
             "system_health.memory_desktop": {},
             "system_health.memory_mobile": {
-                "end": 14
+                "end": 15
             }
         }
     },
     "12": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 14,
-                "end": 39
+                "begin": 15,
+                "end": 43
             }
         }
     },
     "13": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 39
+                "begin": 43
             },
             "system_health.webview_startup": {},
             "tab_switching.typical_25": {},
@@ -159,14 +159,14 @@
             "v8.browsing_desktop": {},
             "v8.browsing_desktop-future": {},
             "v8.browsing_mobile": {
-                "end": 7
+                "end": 8
             }
         }
     },
     "14": {
         "benchmarks": {
             "v8.browsing_mobile": {
-                "begin": 7
+                "begin": 8
             },
             "v8.browsing_mobile-future": {
                 "end": 7
@@ -184,26 +184,26 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 6850.0,
-        "predicted_min_shard_index": 13,
-        "predicted_max_shard_time": 7154.0,
-        "predicted_max_shard_index": 14,
-        "shard #0": 7030.0,
-        "shard #1": 7070.0,
-        "shard #2": 7016.0,
-        "shard #3": 7152.0,
-        "shard #4": 6970.0,
-        "shard #5": 6978.0,
-        "shard #6": 7020.0,
-        "shard #7": 7026.0,
-        "shard #8": 7008.0,
-        "shard #9": 7068.0,
-        "shard #10": 6956.0,
-        "shard #11": 7076.0,
-        "shard #12": 6984.0,
-        "shard #13": 6850.0,
-        "shard #14": 7154.0,
+        "num_stories": 1953,
+        "predicted_min_shard_time": 6540.0,
+        "predicted_min_shard_index": 2,
+        "predicted_max_shard_time": 7232.0,
+        "predicted_max_shard_index": 1,
+        "shard #0": 6832.0,
+        "shard #1": 7232.0,
+        "shard #2": 6540.0,
+        "shard #3": 7188.0,
+        "shard #4": 6610.0,
+        "shard #5": 6800.0,
+        "shard #6": 6944.0,
+        "shard #7": 6860.0,
+        "shard #8": 6916.0,
+        "shard #9": 6844.0,
+        "shard #10": 6850.0,
+        "shard #11": 6700.0,
+        "shard #12": 7068.0,
+        "shard #13": 6834.0,
+        "shard #14": 6630.0,
         "shard #15": 7136.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android_nexus5_perf_map.json b/tools/perf/core/shard_maps/android_nexus5_perf_map.json
index 51e35602..73d55558 100644
--- a/tools/perf/core/shard_maps/android_nexus5_perf_map.json
+++ b/tools/perf/core/shard_maps/android_nexus5_perf_map.json
@@ -24,102 +24,96 @@
             "blink_perf.shadow_dom": {},
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
-            "dummy_benchmark.stable_benchmark_1": {},
-            "jetstream": {}
+            "dummy_benchmark.stable_benchmark_1": {}
         }
     },
     "2": {
         "benchmarks": {
+            "jetstream": {},
             "kraken": {},
             "loading.desktop": {},
             "loading.mobile": {
-                "end": 24
+                "end": 23
             }
         }
     },
     "3": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 24,
-                "end": 63
+                "begin": 23,
+                "end": 59
             }
         }
     },
     "4": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 63
+                "begin": 59
             },
             "media.desktop": {},
             "media.mobile": {},
             "memory.desktop": {},
-            "memory.long_running_idle_gmail_background_tbmv2": {},
-            "memory.long_running_idle_gmail_tbmv2": {},
-            "memory.top_10_mobile": {
-                "end": 2
-            }
+            "memory.long_running_idle_gmail_background_tbmv2": {}
         }
     },
     "5": {
         "benchmarks": {
-            "memory.top_10_mobile": {
-                "begin": 2
-            },
+            "memory.long_running_idle_gmail_tbmv2": {},
+            "memory.top_10_mobile": {},
             "octane": {},
             "oortonline_tbmv2": {},
             "power.desktop": {},
             "power.typical_10_mobile": {},
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {
-                "end": 20
+                "end": 7
             }
         }
     },
     "6": {
         "benchmarks": {
             "rasterize_and_record_micro.top_25": {
-                "begin": 20
+                "begin": 7
             },
             "rendering.desktop": {},
             "rendering.mobile": {
-                "end": 96
+                "end": 81
             }
         }
     },
     "7": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 96,
-                "end": 210
+                "begin": 81,
+                "end": 218
             }
         }
     },
     "8": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 210,
-                "end": 358
+                "begin": 218,
+                "end": 360
             }
         }
     },
     "9": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 358
+                "begin": 360
             },
-            "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {
-                "end": 16
+            "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
+            "smoothness.tough_pinch_zoom_cases": {
+                "end": 14
             }
         }
     },
     "10": {
         "benchmarks": {
-            "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {
-                "begin": 16
+            "smoothness.tough_pinch_zoom_cases": {
+                "begin": 14
             },
-            "smoothness.tough_pinch_zoom_cases": {},
             "speedometer": {},
             "speedometer-future": {},
             "speedometer2": {},
@@ -127,33 +121,33 @@
             "startup.mobile": {},
             "system_health.common_desktop": {},
             "system_health.common_mobile": {
-                "end": 23
+                "end": 36
             }
         }
     },
     "11": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 23
+                "begin": 36
             },
             "system_health.memory_desktop": {},
             "system_health.memory_mobile": {
-                "end": 13
+                "end": 14
             }
         }
     },
     "12": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 13,
-                "end": 36
+                "begin": 14,
+                "end": 40
             }
         }
     },
     "13": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 36
+                "begin": 40
             },
             "system_health.webview_startup": {},
             "tab_switching.typical_25": {},
@@ -161,24 +155,24 @@
             "v8.browsing_desktop": {},
             "v8.browsing_desktop-future": {},
             "v8.browsing_mobile": {
-                "end": 1
+                "end": 2
             }
         }
     },
     "14": {
         "benchmarks": {
             "v8.browsing_mobile": {
-                "begin": 1
+                "begin": 2
             },
             "v8.browsing_mobile-future": {
-                "end": 3
+                "end": 6
             }
         }
     },
     "15": {
         "benchmarks": {
             "v8.browsing_mobile-future": {
-                "begin": 3
+                "begin": 6
             },
             "v8.runtime_stats.top_25": {},
             "wasm": {},
@@ -186,26 +180,26 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 7492.0,
-        "predicted_min_shard_index": 11,
-        "predicted_max_shard_time": 8258.0,
-        "predicted_max_shard_index": 1,
-        "shard #0": 7888.0,
-        "shard #1": 8258.0,
-        "shard #2": 7542.0,
-        "shard #3": 7740.0,
-        "shard #4": 7796.0,
-        "shard #5": 7906.0,
-        "shard #6": 7830.0,
-        "shard #7": 7882.0,
-        "shard #8": 7874.0,
-        "shard #9": 7874.0,
-        "shard #10": 7856.0,
-        "shard #11": 7492.0,
-        "shard #12": 8160.0,
-        "shard #13": 8008.0,
-        "shard #14": 7732.0,
-        "shard #15": 7848.0
+        "num_stories": 1953,
+        "predicted_min_shard_time": 7352.0,
+        "predicted_min_shard_index": 1,
+        "predicted_max_shard_time": 8142.0,
+        "predicted_max_shard_index": 2,
+        "shard #0": 7462.0,
+        "shard #1": 7352.0,
+        "shard #2": 8142.0,
+        "shard #3": 7580.0,
+        "shard #4": 7590.0,
+        "shard #5": 7974.0,
+        "shard #6": 7624.0,
+        "shard #7": 7710.0,
+        "shard #8": 7644.0,
+        "shard #9": 7724.0,
+        "shard #10": 7710.0,
+        "shard #11": 7566.0,
+        "shard #12": 7764.0,
+        "shard #13": 7754.0,
+        "shard #14": 7564.0,
+        "shard #15": 7712.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json b/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
index dcf2abcf..0f8ce77 100644
--- a/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
+++ b/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
@@ -9,14 +9,14 @@
             "blink_perf.events": {},
             "blink_perf.image_decoder": {},
             "blink_perf.layout": {
-                "end": 12
+                "end": 13
             }
         }
     },
     "1": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 12
+                "begin": 13
             },
             "blink_perf.owp_storage": {},
             "blink_perf.paint": {},
@@ -33,37 +33,36 @@
             "dromaeo": {
                 "begin": 2
             },
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {},
             "jetstream": {},
             "kraken": {},
             "loading.desktop": {},
             "loading.mobile": {
-                "end": 33
+                "end": 31
             }
         }
     },
     "3": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 33,
-                "end": 55
+                "begin": 31,
+                "end": 54
             }
         }
     },
     "4": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 55,
-                "end": 92
+                "begin": 54,
+                "end": 91
             }
         }
     },
     "5": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 92
+                "begin": 91
             },
             "media.desktop": {},
             "media.mobile": {},
@@ -71,14 +70,14 @@
             "memory.long_running_idle_gmail_background_tbmv2": {},
             "memory.long_running_idle_gmail_tbmv2": {},
             "memory.top_10_mobile": {
-                "end": 18
+                "end": 17
             }
         }
     },
     "6": {
         "benchmarks": {
             "memory.top_10_mobile": {
-                "begin": 18
+                "begin": 17
             },
             "octane": {},
             "oortonline_tbmv2": {},
@@ -88,22 +87,22 @@
             "rasterize_and_record_micro.top_25": {},
             "rendering.desktop": {},
             "rendering.mobile": {
-                "end": 22
+                "end": 17
             }
         }
     },
     "7": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 22,
-                "end": 187
+                "begin": 17,
+                "end": 174
             }
         }
     },
     "8": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 187,
+                "begin": 174,
                 "end": 403
             }
         }
@@ -122,37 +121,42 @@
             "startup.mobile": {},
             "system_health.common_desktop": {},
             "system_health.common_mobile": {
-                "end": 1
+                "end": 8
             }
         }
     },
     "10": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 1
+                "begin": 8
+            },
+            "system_health.memory_desktop": {
+                "end": 78
             }
         }
     },
     "11": {
         "benchmarks": {
-            "system_health.memory_desktop": {},
+            "system_health.memory_desktop": {
+                "begin": 78
+            },
             "system_health.memory_mobile": {
-                "end": 19
+                "end": 20
             }
         }
     },
     "12": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 19,
-                "end": 47
+                "begin": 20,
+                "end": 50
             }
         }
     },
     "13": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 47
+                "begin": 50
             },
             "system_health.webview_startup": {},
             "tab_switching.typical_25": {},
@@ -160,14 +164,14 @@
             "v8.browsing_desktop": {},
             "v8.browsing_desktop-future": {},
             "v8.browsing_mobile": {
-                "end": 6
+                "end": 7
             }
         }
     },
     "14": {
         "benchmarks": {
             "v8.browsing_mobile": {
-                "begin": 6
+                "begin": 7
             },
             "v8.browsing_mobile-future": {
                 "end": 7
@@ -185,26 +189,26 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 5702.0,
-        "predicted_min_shard_index": 13,
-        "predicted_max_shard_time": 6112.0,
-        "predicted_max_shard_index": 4,
-        "shard #0": 5910.0,
-        "shard #1": 5896.0,
-        "shard #2": 5950.0,
-        "shard #3": 5852.0,
-        "shard #4": 6112.0,
-        "shard #5": 5754.0,
-        "shard #6": 6004.0,
-        "shard #7": 5862.0,
-        "shard #8": 5950.0,
-        "shard #9": 5896.0,
-        "shard #10": 6030.0,
-        "shard #11": 5730.0,
-        "shard #12": 6054.0,
-        "shard #13": 5702.0,
-        "shard #14": 6104.0,
+        "num_stories": 1953,
+        "predicted_min_shard_time": 5582.0,
+        "predicted_min_shard_index": 14,
+        "predicted_max_shard_time": 6080.0,
+        "predicted_max_shard_index": 13,
+        "shard #0": 5832.0,
+        "shard #1": 5874.0,
+        "shard #2": 5836.0,
+        "shard #3": 5840.0,
+        "shard #4": 5816.0,
+        "shard #5": 5914.0,
+        "shard #6": 5700.0,
+        "shard #7": 5902.0,
+        "shard #8": 5872.0,
+        "shard #9": 5776.0,
+        "shard #10": 5874.0,
+        "shard #11": 5922.0,
+        "shard #12": 5700.0,
+        "shard #13": 6080.0,
+        "shard #14": 5582.0,
         "shard #15": 5914.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android_nexus6_webview_perf_map.json b/tools/perf/core/shard_maps/android_nexus6_webview_perf_map.json
index 2d415c3..7b0ec2a0 100644
--- a/tools/perf/core/shard_maps/android_nexus6_webview_perf_map.json
+++ b/tools/perf/core/shard_maps/android_nexus6_webview_perf_map.json
@@ -15,7 +15,6 @@
             "blink_perf.shadow_dom": {},
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {}
         }
@@ -26,14 +25,14 @@
             "kraken": {},
             "loading.desktop": {},
             "loading.mobile": {
-                "end": 77
+                "end": 70
             }
         }
     },
     "2": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 77
+                "begin": 70
             },
             "media.desktop": {},
             "media.mobile": {},
@@ -49,51 +48,46 @@
             "rasterize_and_record_micro.top_25": {},
             "rendering.desktop": {},
             "rendering.mobile": {
-                "end": 13
+                "end": 1
             }
         }
     },
     "3": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 13,
-                "end": 263
+                "begin": 1,
+                "end": 245
             }
         }
     },
     "4": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 263
+                "begin": 245
             },
             "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
-            "smoothness.tough_pinch_zoom_cases": {
-                "end": 9
-            }
+            "smoothness.tough_pinch_zoom_cases": {},
+            "speedometer": {},
+            "speedometer-future": {},
+            "speedometer2": {}
         }
     },
     "5": {
         "benchmarks": {
-            "smoothness.tough_pinch_zoom_cases": {
-                "begin": 9
-            },
-            "speedometer": {},
-            "speedometer-future": {},
-            "speedometer2": {},
             "speedometer2-future": {},
             "startup.mobile": {},
             "system_health.common_desktop": {},
             "system_health.common_mobile": {},
             "system_health.memory_desktop": {},
             "system_health.memory_mobile": {
-                "end": 16
+                "end": 18
             }
         }
     },
     "6": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 16
+                "begin": 18
             },
             "system_health.webview_startup": {},
             "tab_switching.typical_25": {},
@@ -101,14 +95,14 @@
             "v8.browsing_desktop": {},
             "v8.browsing_desktop-future": {},
             "v8.browsing_mobile": {
-                "end": 1
+                "end": 2
             }
         }
     },
     "7": {
         "benchmarks": {
             "v8.browsing_mobile": {
-                "begin": 1
+                "begin": 2
             },
             "v8.browsing_mobile-future": {},
             "v8.runtime_stats.top_25": {},
@@ -117,18 +111,18 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1944,
-        "predicted_min_shard_time": 13184.0,
-        "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 13580.0,
+        "num_stories": 1953,
+        "predicted_min_shard_time": 12832.0,
+        "predicted_min_shard_index": 6,
+        "predicted_max_shard_time": 13204.0,
         "predicted_max_shard_index": 1,
-        "shard #0": 13184.0,
-        "shard #1": 13580.0,
-        "shard #2": 13458.0,
-        "shard #3": 13400.0,
-        "shard #4": 13364.0,
-        "shard #5": 13324.0,
-        "shard #6": 13426.0,
-        "shard #7": 13452.0
+        "shard #0": 13022.0,
+        "shard #1": 13204.0,
+        "shard #2": 13022.0,
+        "shard #3": 13138.0,
+        "shard #4": 13204.0,
+        "shard #5": 13138.0,
+        "shard #6": 12832.0,
+        "shard #7": 13192.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/linux-perf_map.json b/tools/perf/core/shard_maps/linux-perf_map.json
index f1cd4cd..df0380f 100644
--- a/tools/perf/core/shard_maps/linux-perf_map.json
+++ b/tools/perf/core/shard_maps/linux-perf_map.json
@@ -9,44 +9,43 @@
             "blink_perf.events": {},
             "blink_perf.image_decoder": {},
             "blink_perf.layout": {
-                "end": 8
+                "end": 9
             }
         }
     },
     "1": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 8
+                "begin": 9
             },
             "blink_perf.owp_storage": {},
             "blink_perf.paint": {},
             "blink_perf.parser": {},
             "blink_perf.shadow_dom": {
-                "end": 35
+                "end": 37
             }
         }
     },
     "2": {
         "benchmarks": {
             "blink_perf.shadow_dom": {
-                "begin": 35
+                "begin": 37
             },
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {},
             "jetstream": {},
             "kraken": {},
             "loading.desktop": {
-                "end": 25
+                "end": 26
             }
         }
     },
     "3": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 25,
+                "begin": 26,
                 "end": 61
             }
         }
@@ -83,62 +82,62 @@
             "octane": {},
             "oortonline_tbmv2": {},
             "power.desktop": {
-                "end": 4
+                "end": 5
             }
         }
     },
     "7": {
         "benchmarks": {
             "power.desktop": {
-                "begin": 4
+                "begin": 5
             },
             "power.typical_10_mobile": {},
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {
-                "end": 23
+                "end": 24
             }
         }
     },
     "8": {
         "benchmarks": {
             "rasterize_and_record_micro.top_25": {
-                "begin": 23
+                "begin": 24
             },
             "rendering.desktop": {
-                "end": 52
+                "end": 53
             }
         }
     },
     "9": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 52,
-                "end": 109
+                "begin": 53,
+                "end": 111
             }
         }
     },
     "10": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 109,
-                "end": 173
+                "begin": 111,
+                "end": 178
             }
         }
     },
     "11": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 173
+                "begin": 178
             },
             "rendering.mobile": {
-                "end": 67
+                "end": 138
             }
         }
     },
     "12": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 67
+                "begin": 138
             },
             "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
             "smoothness.tough_pinch_zoom_cases": {},
@@ -148,65 +147,65 @@
             "speedometer2-future": {},
             "startup.mobile": {},
             "system_health.common_desktop": {
-                "end": 16
+                "end": 19
             }
         }
     },
     "13": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 16,
-                "end": 59
+                "begin": 19,
+                "end": 74
             }
         }
     },
     "14": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 59
+                "begin": 74
             },
             "system_health.common_mobile": {},
             "system_health.memory_desktop": {
-                "end": 7
+                "end": 9
             }
         }
     },
     "15": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 7,
-                "end": 17
+                "begin": 9,
+                "end": 19
             }
         }
     },
     "16": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 17,
-                "end": 35
+                "begin": 19,
+                "end": 43
             }
         }
     },
     "17": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 35,
-                "end": 57
+                "begin": 43,
+                "end": 72
             }
         }
     },
     "18": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 57,
-                "end": 68
+                "begin": 72,
+                "end": 88
             }
         }
     },
     "19": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 68
+                "begin": 88
             },
             "system_health.memory_mobile": {},
             "system_health.webview_startup": {},
@@ -255,50 +254,50 @@
         "benchmarks": {
             "v8.runtime_stats.top_25": {
                 "begin": 74,
-                "end": 114
+                "end": 113
             }
         }
     },
     "25": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 114
+                "begin": 113
             },
             "wasm": {},
             "webrtc": {}
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 2782.0,
-        "predicted_min_shard_index": 14,
-        "predicted_max_shard_time": 3088.0,
+        "num_stories": 1953,
+        "predicted_min_shard_time": 2784.0,
+        "predicted_min_shard_index": 15,
+        "predicted_max_shard_time": 3100.0,
         "predicted_max_shard_index": 21,
-        "shard #0": 2920.0,
-        "shard #1": 2912.0,
-        "shard #2": 2894.0,
-        "shard #3": 2972.0,
+        "shard #0": 2928.0,
+        "shard #1": 2916.0,
+        "shard #2": 2920.0,
+        "shard #3": 2888.0,
         "shard #4": 2940.0,
         "shard #5": 2878.0,
-        "shard #6": 2916.0,
-        "shard #7": 2862.0,
-        "shard #8": 2952.0,
-        "shard #9": 2904.0,
-        "shard #10": 2926.0,
-        "shard #11": 2930.0,
-        "shard #12": 2928.0,
-        "shard #13": 2944.0,
-        "shard #14": 2782.0,
-        "shard #15": 3054.0,
-        "shard #16": 2892.0,
-        "shard #17": 2892.0,
-        "shard #18": 3012.0,
-        "shard #19": 2832.0,
-        "shard #20": 2804.0,
-        "shard #21": 3088.0,
+        "shard #6": 3008.0,
+        "shard #7": 2926.0,
+        "shard #8": 2856.0,
+        "shard #9": 2910.0,
+        "shard #10": 2962.0,
+        "shard #11": 2908.0,
+        "shard #12": 2870.0,
+        "shard #13": 2862.0,
+        "shard #14": 3080.0,
+        "shard #15": 2784.0,
+        "shard #16": 3048.0,
+        "shard #17": 2814.0,
+        "shard #18": 3042.0,
+        "shard #19": 2850.0,
+        "shard #20": 2816.0,
+        "shard #21": 3100.0,
         "shard #22": 2828.0,
         "shard #23": 2946.0,
-        "shard #24": 2958.0,
-        "shard #25": 2882.0
+        "shard #24": 2882.0,
+        "shard #25": 2958.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-10_12_laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/mac-10_12_laptop_low_end-perf_map.json
index 2320a5ec..2985eee 100644
--- a/tools/perf/core/shard_maps/mac-10_12_laptop_low_end-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-10_12_laptop_low_end-perf_map.json
@@ -9,14 +9,14 @@
             "blink_perf.events": {},
             "blink_perf.image_decoder": {},
             "blink_perf.layout": {
-                "end": 59
+                "end": 60
             }
         }
     },
     "1": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 59
+                "begin": 60
             },
             "blink_perf.owp_storage": {},
             "blink_perf.paint": {},
@@ -24,20 +24,19 @@
             "blink_perf.shadow_dom": {},
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {},
             "jetstream": {},
             "kraken": {},
             "loading.desktop": {
-                "end": 9
+                "end": 10
             }
         }
     },
     "2": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 9,
+                "begin": 10,
                 "end": 42
             }
         }
@@ -95,116 +94,111 @@
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
             "rendering.desktop": {
-                "end": 21
+                "end": 23
             }
         }
     },
     "8": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 21,
-                "end": 71
+                "begin": 23,
+                "end": 72
             }
         }
     },
     "9": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 71,
-                "end": 121
+                "begin": 72,
+                "end": 123
             }
         }
     },
     "10": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 121,
-                "end": 200
+                "begin": 123,
+                "end": 203
             }
         }
     },
     "11": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 200
+                "begin": 203
             },
-            "rendering.mobile": {
-                "end": 421
-            }
+            "rendering.mobile": {},
+            "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
+            "smoothness.tough_pinch_zoom_cases": {}
         }
     },
     "12": {
         "benchmarks": {
-            "rendering.mobile": {
-                "begin": 421
-            },
-            "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
-            "smoothness.tough_pinch_zoom_cases": {},
             "speedometer": {},
             "speedometer-future": {},
             "speedometer2": {},
             "speedometer2-future": {},
             "startup.mobile": {},
             "system_health.common_desktop": {
-                "end": 22
+                "end": 26
             }
         }
     },
     "13": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 22,
-                "end": 62
+                "begin": 26,
+                "end": 80
             }
         }
     },
     "14": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 62
+                "begin": 80
             },
             "system_health.common_mobile": {},
             "system_health.memory_desktop": {
-                "end": 10
+                "end": 11
             }
         }
     },
     "15": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 10,
-                "end": 19
+                "begin": 11,
+                "end": 25
             }
         }
     },
     "16": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 19,
-                "end": 40
+                "begin": 25,
+                "end": 49
             }
         }
     },
     "17": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 40,
-                "end": 58
+                "begin": 49,
+                "end": 73
             }
         }
     },
     "18": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 58,
-                "end": 65
+                "begin": 73,
+                "end": 83
             }
         }
     },
     "19": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 65
+                "begin": 83
             },
             "system_health.memory_mobile": {},
             "system_health.webview_startup": {},
@@ -225,14 +219,14 @@
     "21": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "end": 20
+                "end": 25
             }
         }
     },
     "22": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "begin": 20
+                "begin": 25
             },
             "v8.browsing_mobile": {},
             "v8.browsing_mobile-future": {},
@@ -267,34 +261,34 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 2928.0,
+        "num_stories": 1953,
+        "predicted_min_shard_time": 2778.0,
         "predicted_min_shard_index": 17,
-        "predicted_max_shard_time": 3780.0,
+        "predicted_max_shard_time": 3798.0,
         "predicted_max_shard_index": 18,
-        "shard #0": 3246.0,
-        "shard #1": 3264.0,
-        "shard #2": 3300.0,
+        "shard #0": 3228.0,
+        "shard #1": 3322.0,
+        "shard #2": 3216.0,
         "shard #3": 3216.0,
         "shard #4": 3280.0,
         "shard #5": 3174.0,
         "shard #6": 3328.0,
-        "shard #7": 3174.0,
-        "shard #8": 3252.0,
-        "shard #9": 3280.0,
-        "shard #10": 3266.0,
-        "shard #11": 3244.0,
-        "shard #12": 3230.0,
-        "shard #13": 3264.0,
-        "shard #14": 3408.0,
-        "shard #15": 3174.0,
-        "shard #16": 3126.0,
-        "shard #17": 2928.0,
-        "shard #18": 3780.0,
-        "shard #19": 3178.0,
-        "shard #20": 3234.0,
-        "shard #21": 3226.0,
-        "shard #22": 3288.0,
+        "shard #7": 3286.0,
+        "shard #8": 3230.0,
+        "shard #9": 3256.0,
+        "shard #10": 3274.0,
+        "shard #11": 3192.0,
+        "shard #12": 3340.0,
+        "shard #13": 3230.0,
+        "shard #14": 3268.0,
+        "shard #15": 3204.0,
+        "shard #16": 3330.0,
+        "shard #17": 2778.0,
+        "shard #18": 3798.0,
+        "shard #19": 3208.0,
+        "shard #20": 3246.0,
+        "shard #21": 3236.0,
+        "shard #22": 3290.0,
         "shard #23": 3198.0,
         "shard #24": 3270.0,
         "shard #25": 3226.0
diff --git a/tools/perf/core/shard_maps/mac-10_13_laptop_high_end-perf_map.json b/tools/perf/core/shard_maps/mac-10_13_laptop_high_end-perf_map.json
index f741a28a..b3040eb 100644
--- a/tools/perf/core/shard_maps/mac-10_13_laptop_high_end-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-10_13_laptop_high_end-perf_map.json
@@ -9,14 +9,14 @@
             "blink_perf.events": {},
             "blink_perf.image_decoder": {},
             "blink_perf.layout": {
-                "end": 60
+                "end": 62
             }
         }
     },
     "1": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 60
+                "begin": 62
             },
             "blink_perf.owp_storage": {},
             "blink_perf.paint": {},
@@ -24,28 +24,27 @@
             "blink_perf.shadow_dom": {},
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {},
             "jetstream": {},
             "kraken": {},
             "loading.desktop": {
-                "end": 10
+                "end": 11
             }
         }
     },
     "2": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 10,
-                "end": 44
+                "begin": 11,
+                "end": 45
             }
         }
     },
     "3": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 44,
+                "begin": 45,
                 "end": 76
             }
         }
@@ -55,16 +54,11 @@
             "loading.desktop": {
                 "begin": 76
             },
-            "loading.mobile": {
-                "end": 84
-            }
+            "loading.mobile": {}
         }
     },
     "5": {
         "benchmarks": {
-            "loading.mobile": {
-                "begin": 84
-            },
             "media.desktop": {},
             "media.mobile": {},
             "memory.desktop": {}
@@ -81,48 +75,48 @@
             "power.typical_10_mobile": {},
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {
-                "end": 9
+                "end": 12
             }
         }
     },
     "7": {
         "benchmarks": {
             "rasterize_and_record_micro.top_25": {
-                "begin": 9
+                "begin": 12
             },
             "rendering.desktop": {
-                "end": 40
+                "end": 41
             }
         }
     },
     "8": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 40,
-                "end": 96
+                "begin": 41,
+                "end": 97
             }
         }
     },
     "9": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 96,
-                "end": 157
+                "begin": 97,
+                "end": 162
             }
         }
     },
     "10": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 157,
-                "end": 237
+                "begin": 162,
+                "end": 238
             }
         }
     },
     "11": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 237
+                "begin": 238
             },
             "rendering.mobile": {},
             "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
@@ -133,87 +127,87 @@
             "speedometer2-future": {},
             "startup.mobile": {},
             "system_health.common_desktop": {
-                "end": 12
+                "end": 14
             }
         }
     },
     "12": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 12,
-                "end": 52
+                "begin": 14,
+                "end": 69
             }
         }
     },
     "13": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 52
+                "begin": 69
             },
             "system_health.common_mobile": {},
             "system_health.memory_desktop": {
-                "end": 3
+                "end": 5
             }
         }
     },
     "14": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 3,
-                "end": 14
+                "begin": 5,
+                "end": 15
             }
         }
     },
     "15": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 14,
-                "end": 29
+                "begin": 15,
+                "end": 36
             }
         }
     },
     "16": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 29,
-                "end": 50
+                "begin": 36,
+                "end": 62
             }
         }
     },
     "17": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 50,
-                "end": 60
+                "begin": 62,
+                "end": 75
             }
         }
     },
     "18": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 60,
-                "end": 67
+                "begin": 75,
+                "end": 87
             }
         }
     },
     "19": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 67
+                "begin": 87
             },
             "system_health.memory_mobile": {},
             "system_health.webview_startup": {},
             "tab_switching.typical_25": {},
             "tracing.tracing_with_background_memory_infra": {},
             "v8.browsing_desktop": {
-                "end": 11
+                "end": 12
             }
         }
     },
     "20": {
         "benchmarks": {
             "v8.browsing_desktop": {
-                "begin": 11
+                "begin": 12
             },
             "v8.browsing_desktop-future": {
                 "end": 4
@@ -224,14 +218,14 @@
         "benchmarks": {
             "v8.browsing_desktop-future": {
                 "begin": 4,
-                "end": 22
+                "end": 26
             }
         }
     },
     "22": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "begin": 22
+                "begin": 26
             },
             "v8.browsing_mobile": {},
             "v8.browsing_mobile-future": {},
@@ -266,34 +260,34 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 2436.0,
+        "num_stories": 1953,
+        "predicted_min_shard_time": 2466.0,
         "predicted_min_shard_index": 18,
-        "predicted_max_shard_time": 3204.0,
+        "predicted_max_shard_time": 3222.0,
         "predicted_max_shard_index": 17,
-        "shard #0": 2874.0,
-        "shard #1": 2826.0,
-        "shard #2": 2880.0,
-        "shard #3": 2892.0,
-        "shard #4": 2848.0,
-        "shard #5": 2988.0,
-        "shard #6": 2728.0,
-        "shard #7": 2910.0,
-        "shard #8": 2840.0,
-        "shard #9": 2846.0,
-        "shard #10": 2906.0,
-        "shard #11": 2850.0,
-        "shard #12": 2844.0,
-        "shard #13": 2858.0,
-        "shard #14": 2868.0,
-        "shard #15": 2844.0,
-        "shard #16": 2976.0,
-        "shard #17": 3204.0,
-        "shard #18": 2436.0,
-        "shard #19": 2944.0,
-        "shard #20": 2752.0,
-        "shard #21": 2990.0,
-        "shard #22": 2802.0,
+        "shard #0": 2866.0,
+        "shard #1": 2866.0,
+        "shard #2": 2896.0,
+        "shard #3": 2812.0,
+        "shard #4": 2888.0,
+        "shard #5": 2948.0,
+        "shard #6": 2790.0,
+        "shard #7": 2898.0,
+        "shard #8": 2828.0,
+        "shard #9": 2882.0,
+        "shard #10": 2922.0,
+        "shard #11": 2838.0,
+        "shard #12": 2820.0,
+        "shard #13": 2918.0,
+        "shard #14": 2718.0,
+        "shard #15": 3036.0,
+        "shard #16": 2850.0,
+        "shard #17": 3222.0,
+        "shard #18": 2466.0,
+        "shard #19": 2964.0,
+        "shard #20": 2762.0,
+        "shard #21": 2808.0,
+        "shard #22": 2996.0,
         "shard #23": 2826.0,
         "shard #24": 2878.0,
         "shard #25": 2860.0
diff --git a/tools/perf/core/shard_maps/win-10-perf_map.json b/tools/perf/core/shard_maps/win-10-perf_map.json
index e8f67e8..dce7647e 100644
--- a/tools/perf/core/shard_maps/win-10-perf_map.json
+++ b/tools/perf/core/shard_maps/win-10-perf_map.json
@@ -9,14 +9,14 @@
             "blink_perf.events": {},
             "blink_perf.image_decoder": {},
             "blink_perf.layout": {
-                "end": 39
+                "end": 40
             }
         }
     },
     "1": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 39
+                "begin": 40
             },
             "blink_perf.owp_storage": {},
             "blink_perf.paint": {},
@@ -24,24 +24,27 @@
             "blink_perf.shadow_dom": {},
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {},
             "jetstream": {},
-            "kraken": {}
+            "kraken": {},
+            "loading.desktop": {
+                "end": 1
+            }
         }
     },
     "2": {
         "benchmarks": {
             "loading.desktop": {
-                "end": 36
+                "begin": 1,
+                "end": 37
             }
         }
     },
     "3": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 36,
+                "begin": 37,
                 "end": 65
             }
         }
@@ -73,12 +76,17 @@
                 "begin": 2
             },
             "memory.long_running_idle_gmail_background_tbmv2": {},
-            "memory.long_running_idle_gmail_tbmv2": {}
+            "memory.long_running_idle_gmail_tbmv2": {},
+            "memory.top_10_mobile": {
+                "end": 2
+            }
         }
     },
     "7": {
         "benchmarks": {
-            "memory.top_10_mobile": {},
+            "memory.top_10_mobile": {
+                "begin": 2
+            },
             "octane": {},
             "oortonline_tbmv2": {},
             "power.desktop": {},
@@ -86,48 +94,48 @@
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
             "rendering.desktop": {
-                "end": 1
+                "end": 2
             }
         }
     },
     "8": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 1,
-                "end": 56
+                "begin": 2,
+                "end": 57
             }
         }
     },
     "9": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 56,
-                "end": 114
+                "begin": 57,
+                "end": 116
             }
         }
     },
     "10": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 114,
-                "end": 181
+                "begin": 116,
+                "end": 185
             }
         }
     },
     "11": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 181
+                "begin": 185
             },
             "rendering.mobile": {
-                "end": 210
+                "end": 269
             }
         }
     },
     "12": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 210
+                "begin": 269
             },
             "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
             "smoothness.tough_pinch_zoom_cases": {},
@@ -137,65 +145,65 @@
             "speedometer2-future": {},
             "startup.mobile": {},
             "system_health.common_desktop": {
-                "end": 17
+                "end": 20
             }
         }
     },
     "13": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 17,
-                "end": 59
+                "begin": 20,
+                "end": 74
             }
         }
     },
     "14": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 59
+                "begin": 74
             },
             "system_health.common_mobile": {},
             "system_health.memory_desktop": {
-                "end": 7
+                "end": 8
             }
         }
     },
     "15": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 7,
-                "end": 16
+                "begin": 8,
+                "end": 19
             }
         }
     },
     "16": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 16,
-                "end": 33
+                "begin": 19,
+                "end": 40
             }
         }
     },
     "17": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 33,
-                "end": 54
+                "begin": 40,
+                "end": 69
             }
         }
     },
     "18": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 54,
-                "end": 63
+                "begin": 69,
+                "end": 80
             }
         }
     },
     "19": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 63
+                "begin": 80
             },
             "system_health.memory_mobile": {},
             "system_health.webview_startup": {},
@@ -216,14 +224,14 @@
     "21": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "end": 19
+                "end": 24
             }
         }
     },
     "22": {
         "benchmarks": {
             "v8.browsing_desktop-future": {
-                "begin": 19
+                "begin": 24
             },
             "v8.browsing_mobile": {},
             "v8.browsing_mobile-future": {},
@@ -244,50 +252,50 @@
         "benchmarks": {
             "v8.runtime_stats.top_25": {
                 "begin": 66,
-                "end": 106
+                "end": 105
             }
         }
     },
     "25": {
         "benchmarks": {
             "v8.runtime_stats.top_25": {
-                "begin": 106
+                "begin": 105
             },
             "wasm": {},
             "webrtc": {}
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 2570.0,
+        "num_stories": 1953,
+        "predicted_min_shard_time": 2606.0,
         "predicted_min_shard_index": 19,
-        "predicted_max_shard_time": 3012.0,
-        "predicted_max_shard_index": 18,
-        "shard #0": 2868.0,
-        "shard #1": 2770.0,
-        "shard #2": 2852.0,
-        "shard #3": 2872.0,
+        "predicted_max_shard_time": 3132.0,
+        "predicted_max_shard_index": 15,
+        "shard #0": 2856.0,
+        "shard #1": 2874.0,
+        "shard #2": 2820.0,
+        "shard #3": 2800.0,
         "shard #4": 2808.0,
         "shard #5": 2952.0,
-        "shard #6": 2770.0,
-        "shard #7": 2836.0,
-        "shard #8": 2802.0,
-        "shard #9": 2866.0,
-        "shard #10": 2842.0,
-        "shard #11": 2830.0,
-        "shard #12": 2870.0,
-        "shard #13": 2912.0,
-        "shard #14": 2666.0,
-        "shard #15": 2838.0,
-        "shard #16": 2970.0,
-        "shard #17": 2760.0,
-        "shard #18": 3012.0,
-        "shard #19": 2570.0,
-        "shard #20": 2836.0,
-        "shard #21": 2838.0,
-        "shard #22": 2978.0,
+        "shard #6": 2790.0,
+        "shard #7": 2828.0,
+        "shard #8": 2850.0,
+        "shard #9": 2858.0,
+        "shard #10": 2852.0,
+        "shard #11": 2832.0,
+        "shard #12": 2882.0,
+        "shard #13": 2784.0,
+        "shard #14": 2688.0,
+        "shard #15": 3132.0,
+        "shard #16": 2712.0,
+        "shard #17": 2808.0,
+        "shard #18": 3024.0,
+        "shard #19": 2606.0,
+        "shard #20": 2848.0,
+        "shard #21": 2848.0,
+        "shard #22": 2980.0,
         "shard #23": 2830.0,
-        "shard #24": 2856.0,
-        "shard #25": 2810.0
+        "shard #24": 2794.0,
+        "shard #25": 2872.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/win_7_nvidia_gpu_perf_map.json b/tools/perf/core/shard_maps/win_7_nvidia_gpu_perf_map.json
index 92d6488..7cf15934 100644
--- a/tools/perf/core/shard_maps/win_7_nvidia_gpu_perf_map.json
+++ b/tools/perf/core/shard_maps/win_7_nvidia_gpu_perf_map.json
@@ -15,21 +15,20 @@
             "blink_perf.shadow_dom": {},
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {},
             "jetstream": {},
             "kraken": {},
             "loading.desktop": {},
             "loading.mobile": {
-                "end": 82
+                "end": 91
             }
         }
     },
     "1": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 82
+                "begin": 91
             },
             "media.desktop": {},
             "media.mobile": {},
@@ -44,14 +43,14 @@
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
             "rendering.desktop": {
-                "end": 158
+                "end": 161
             }
         }
     },
     "2": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 158
+                "begin": 161
             },
             "rendering.mobile": {},
             "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
@@ -64,28 +63,28 @@
             "system_health.common_desktop": {},
             "system_health.common_mobile": {},
             "system_health.memory_desktop": {
-                "end": 16
+                "end": 19
             }
         }
     },
     "3": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 16
+                "begin": 19
             },
             "system_health.memory_mobile": {},
             "system_health.webview_startup": {},
             "tab_switching.typical_25": {},
             "tracing.tracing_with_background_memory_infra": {},
             "v8.browsing_desktop": {
-                "end": 20
+                "end": 25
             }
         }
     },
     "4": {
         "benchmarks": {
             "v8.browsing_desktop": {
-                "begin": 20
+                "begin": 25
             },
             "v8.browsing_desktop-future": {},
             "v8.browsing_mobile": {},
@@ -96,15 +95,15 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 15154.0,
-        "predicted_min_shard_index": 2,
-        "predicted_max_shard_time": 15372.0,
-        "predicted_max_shard_index": 3,
-        "shard #0": 15232.0,
-        "shard #1": 15240.0,
-        "shard #2": 15154.0,
-        "shard #3": 15372.0,
-        "shard #4": 15166.0
+        "num_stories": 1953,
+        "predicted_min_shard_time": 15180.0,
+        "predicted_min_shard_index": 4,
+        "predicted_max_shard_time": 15370.0,
+        "predicted_max_shard_index": 2,
+        "shard #0": 15254.0,
+        "shard #1": 15242.0,
+        "shard #2": 15370.0,
+        "shard #3": 15220.0,
+        "shard #4": 15180.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/win_7_perf_map.json b/tools/perf/core/shard_maps/win_7_perf_map.json
index 7297c940..c63d3d484 100644
--- a/tools/perf/core/shard_maps/win_7_perf_map.json
+++ b/tools/perf/core/shard_maps/win_7_perf_map.json
@@ -15,20 +15,19 @@
             "blink_perf.shadow_dom": {},
             "blink_perf.svg": {},
             "dromaeo": {},
-            "dummy_benchmark.histogram_benchmark_1": {},
             "dummy_benchmark.noisy_benchmark_1": {},
             "dummy_benchmark.stable_benchmark_1": {},
             "jetstream": {},
             "kraken": {},
             "loading.desktop": {
-                "end": 94
+                "end": 95
             }
         }
     },
     "1": {
         "benchmarks": {
             "loading.desktop": {
-                "begin": 94
+                "begin": 95
             },
             "loading.mobile": {},
             "media.desktop": {},
@@ -44,14 +43,14 @@
             "rasterize_and_record_micro.partial_invalidation": {},
             "rasterize_and_record_micro.top_25": {},
             "rendering.desktop": {
-                "end": 136
+                "end": 138
             }
         }
     },
     "2": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 136
+                "begin": 138
             },
             "rendering.mobile": {},
             "smoothness.gpu_rasterization.tough_pinch_zoom_cases": {},
@@ -64,28 +63,28 @@
             "system_health.common_desktop": {},
             "system_health.common_mobile": {},
             "system_health.memory_desktop": {
-                "end": 12
+                "end": 14
             }
         }
     },
     "3": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 12
+                "begin": 14
             },
             "system_health.memory_mobile": {},
             "system_health.webview_startup": {},
             "tab_switching.typical_25": {},
             "tracing.tracing_with_background_memory_infra": {},
             "v8.browsing_desktop": {
-                "end": 18
+                "end": 22
             }
         }
     },
     "4": {
         "benchmarks": {
             "v8.browsing_desktop": {
-                "begin": 18
+                "begin": 22
             },
             "v8.browsing_desktop-future": {},
             "v8.browsing_mobile": {},
@@ -96,15 +95,15 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1904,
-        "predicted_min_shard_time": 15542.0,
-        "predicted_min_shard_index": 2,
-        "predicted_max_shard_time": 15804.0,
-        "predicted_max_shard_index": 4,
-        "shard #0": 15666.0,
-        "shard #1": 15762.0,
-        "shard #2": 15542.0,
-        "shard #3": 15764.0,
-        "shard #4": 15804.0
+        "num_stories": 1953,
+        "predicted_min_shard_time": 15490.0,
+        "predicted_min_shard_index": 3,
+        "predicted_max_shard_time": 15888.0,
+        "predicted_max_shard_index": 2,
+        "shard #0": 15752.0,
+        "shard #1": 15698.0,
+        "shard #2": 15888.0,
+        "shard #3": 15490.0,
+        "shard #4": 15820.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/undocumented_benchmarks.py b/tools/perf/core/undocumented_benchmarks.py
index f20dfa7..e0b9659 100644
--- a/tools/perf/core/undocumented_benchmarks.py
+++ b/tools/perf/core/undocumented_benchmarks.py
@@ -9,7 +9,6 @@
   'angle_perftests',
   'components_perftests',
   'dromaeo',
-  'dummy_benchmark.histogram_benchmark_1',
   'dummy_benchmark.noisy_benchmark_1',
   'dummy_benchmark.stable_benchmark_1',
   'gpu_perftests',
diff --git a/ui/events/blink/prediction/kalman_predictor.cc b/ui/events/blink/prediction/kalman_predictor.cc
index c2d66ac..0d80f7a 100644
--- a/ui/events/blink/prediction/kalman_predictor.cc
+++ b/ui/events/blink/prediction/kalman_predictor.cc
@@ -47,15 +47,15 @@
   return x_predictor_.Stable() && y_predictor_.Stable();
 }
 
-bool KalmanPredictor::GeneratePrediction(base::TimeTicks frame_time,
+bool KalmanPredictor::GeneratePrediction(base::TimeTicks predict_time,
                                          InputData* result) const {
   std::vector<InputData> pred_points;
 
-  base::TimeDelta dt = frame_time - last_point_.time_stamp;
+  base::TimeDelta dt = predict_time - last_point_.time_stamp;
   // Kalman filter is not very good when predicting backwards. Besides,
   // predicting backwards means increasing latency. Thus disable prediction when
   // dt < 0.
-  if (!HasPrediction() || dt < base::TimeDelta::Min() || dt > kMaxResampleTime)
+  if (!HasPrediction() || dt < base::TimeDelta() || dt > kMaxResampleTime)
     return false;
 
   gfx::Vector2dF position(last_point_.pos.x(), last_point_.pos.y());
diff --git a/ui/events/blink/prediction/kalman_predictor.h b/ui/events/blink/prediction/kalman_predictor.h
index cf1edb33..07b783f0 100644
--- a/ui/events/blink/prediction/kalman_predictor.h
+++ b/ui/events/blink/prediction/kalman_predictor.h
@@ -33,7 +33,7 @@
 
   // Generate the prediction based on stored points and given time_stamp.
   // Return false if no prediction available.
-  bool GeneratePrediction(base::TimeTicks frame_time,
+  bool GeneratePrediction(base::TimeTicks predict_time,
                           InputData* result) const override;
 
  private:
diff --git a/ui/events/blink/prediction/least_squares_predictor.cc b/ui/events/blink/prediction/least_squares_predictor.cc
index 534d3de..f6126a5 100644
--- a/ui/events/blink/prediction/least_squares_predictor.cc
+++ b/ui/events/blink/prediction/least_squares_predictor.cc
@@ -75,14 +75,14 @@
   return x;
 }
 
-bool LeastSquaresPredictor::GeneratePrediction(base::TimeTicks frame_time,
+bool LeastSquaresPredictor::GeneratePrediction(base::TimeTicks predict_time,
                                                InputData* result) const {
-  if (!HasPrediction() || frame_time - time_.back() > kMaxResampleTime)
+  if (!HasPrediction() || predict_time - time_.back() > kMaxResampleTime)
     return false;
 
   gfx::Matrix3F time_matrix = GetXMatrix();
 
-  double dt = (frame_time - time_[0]).InMillisecondsF();
+  double dt = (predict_time - time_[0]).InMillisecondsF();
   if (dt > 0) {
     gfx::Vector3dF b1, b2;
     if (SolveLeastSquares(time_matrix, x_queue_, b1) &&
diff --git a/ui/events/blink/prediction/least_squares_predictor.h b/ui/events/blink/prediction/least_squares_predictor.h
index c5eb33b..edfe8296 100644
--- a/ui/events/blink/prediction/least_squares_predictor.h
+++ b/ui/events/blink/prediction/least_squares_predictor.h
@@ -33,7 +33,7 @@
 
   // Generate the prediction based on stored points and given time_stamp.
   // Return false if no prediction available.
-  bool GeneratePrediction(base::TimeTicks frame_time,
+  bool GeneratePrediction(base::TimeTicks predict_time,
                           InputData* result) const override;
 
  private:
diff --git a/ui/events/event.cc b/ui/events/event.cc
index e8748f6..4987bc3 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -1083,10 +1083,9 @@
     // Bit 30 of lParam represents the "previous key state". If set, the key
     // was already down, therefore this is an auto-repeat.
     is_repeat = (event.native_event().lParam & 0x40000000) != 0;
-  } else
+  }
 #endif
-  {
-    // Note that this is only reach for non-native events under Windows.
+  if (!is_repeat) {
     if (event.key_code() == (*last_key_event)->key_code() &&
         event.flags() == ((*last_key_event)->flags() & ~ui::EF_IS_REPEAT) &&
         (event.time_stamp() - (*last_key_event)->time_stamp())
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS
index 225898f..26eb0a7 100644
--- a/ui/gfx/OWNERS
+++ b/ui/gfx/OWNERS
@@ -28,16 +28,21 @@
 per-file gpu_fence*=reveman@chromium.org
 per-file gpu_fence*=dcastagna@chromium.org
 per-file gpu_fence*=rjkroege@chromium.org
+per-file gpu_fence*=spang@chromium.org
 per-file gpu_memory_buffer*=reveman@chromium.org
 per-file gpu_memory_buffer*=dcastagna@chromium.org
 per-file gpu_memory_buffer*=rjkroege@chromium.org
+per-file gpu_memory_buffer*=spang@chromium.org
 per-file buffer_format*=reveman@chromium.org
 per-file buffer_format*=dcastagna@chromium.org
 per-file buffer_format*=rjkroege@chromium.org
+per-file buffer_format*=spang@chromium.org
 per-file *buffer_types.*=reveman@chromium.org
 per-file *buffer_types.*=dcastagna@chromium.org
 per-file *buffer_types.*=rjkroege@chromium.org
+per-file *buffer_types.*=spang@chromium.org
 per-file *pixmap.*=rjkroege@chromium.org
+per-file *pixmap.*=spang@chromium.org
 
 # Vector icons.
 per-file *vector_icon*=estade@chromium.org
diff --git a/ui/gfx/color_utils.cc b/ui/gfx/color_utils.cc
index 6bb5a66..9edd09b 100644
--- a/ui/gfx/color_utils.cc
+++ b/ui/gfx/color_utils.cc
@@ -443,7 +443,7 @@
   g_color_utils_luma_midpoint = (GetLuma(color) + 255) / 2;
 }
 
-SkColor GetDarkestColorForTesting() {
+SkColor GetDarkestColor() {
   return g_color_utils_darkest;
 }
 
diff --git a/ui/gfx/color_utils.h b/ui/gfx/color_utils.h
index 75e2e83d..80b3504 100644
--- a/ui/gfx/color_utils.h
+++ b/ui/gfx/color_utils.h
@@ -189,7 +189,7 @@
 GFX_EXPORT void SetDarkestColor(SkColor color);
 
 // Returns the current color_utils darkest color so tests can clean up.
-GFX_EXPORT SkColor GetDarkestColorForTesting();
+GFX_EXPORT SkColor GetDarkestColor();
 
 }  // namespace color_utils
 
diff --git a/ui/gfx/color_utils_unittest.cc b/ui/gfx/color_utils_unittest.cc
index bb0a542..bb39cacb 100644
--- a/ui/gfx/color_utils_unittest.cc
+++ b/ui/gfx/color_utils_unittest.cc
@@ -205,7 +205,7 @@
 }
 
 TEST(ColorUtils, IsDarkDarkestColorChange) {
-  SkColor old_black_color = GetDarkestColorForTesting();
+  SkColor old_black_color = GetDarkestColor();
 
   ASSERT_FALSE(IsDark(SkColorSetARGB(255, 200, 200, 200)));
   SetDarkestColor(SkColorSetARGB(255, 200, 200, 200));
@@ -236,7 +236,7 @@
 }
 
 TEST(ColorUtils, GetColorWithMinimumContrast_StopsAtDarkestColor) {
-  SkColor old_black_color = GetDarkestColorForTesting();
+  SkColor old_black_color = GetDarkestColor();
 
   const SkColor darkest_color = SkColorSetRGB(0x44, 0x44, 0x44);
   SetDarkestColor(darkest_color);
diff --git a/ui/gfx/linux/OWNERS b/ui/gfx/linux/OWNERS
index 2d13a90..7543e31d 100644
--- a/ui/gfx/linux/OWNERS
+++ b/ui/gfx/linux/OWNERS
@@ -3,3 +3,4 @@
 dnicoara@chromium.org
 reveman@chromium.org
 rjkroege@chromium.org
+spang@chromium.org
diff --git a/ui/gl/gl_bindings.h b/ui/gl/gl_bindings.h
index 21fd6e30..ae88b309 100644
--- a/ui/gl/gl_bindings.h
+++ b/ui/gl/gl_bindings.h
@@ -440,6 +440,8 @@
 #define GL_MAX_VIEWS_OVR 0x9631
 #define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633
 
+#define GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT 0x8868
+
 // Forward declare EGL types.
 typedef uint64_t EGLuint64CHROMIUM;
 
diff --git a/ui/latency/ipc/latency_info_param_traits_macros.h b/ui/latency/ipc/latency_info_param_traits_macros.h
index b70087b..975d7fc 100644
--- a/ui/latency/ipc/latency_info_param_traits_macros.h
+++ b/ui/latency/ipc/latency_info_param_traits_macros.h
@@ -11,7 +11,6 @@
 IPC_ENUM_TRAITS_MAX_VALUE(ui::LatencyComponentType,
                           ui::LATENCY_COMPONENT_TYPE_LAST)
 
-IPC_ENUM_TRAITS_MAX_VALUE(ui::SourceEventType,
-                          ui::SourceEventType::SOURCE_EVENT_TYPE_LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(ui::SourceEventType, ui::SourceEventType::LAST)
 
 #endif  // UI_LATENCY_IPC_LATENCY_INFO_PARAM_TRAITS_MACROS_H_
diff --git a/ui/latency/latency_info.h b/ui/latency/latency_info.h
index eade9fb..f69bb917 100644
--- a/ui/latency/latency_info.h
+++ b/ui/latency/latency_info.h
@@ -93,7 +93,7 @@
   LATENCY_COMPONENT_TYPE_LAST = INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT,
 };
 
-enum SourceEventType {
+enum class SourceEventType {
   UNKNOWN,
   WHEEL,
   MOUSE,
@@ -104,7 +104,7 @@
   TOUCHPAD,
   FRAME,
   OTHER,
-  SOURCE_EVENT_TYPE_LAST = OTHER,
+  LAST = OTHER,
 };
 
 class LatencyInfo {
diff --git a/ui/latency/mojo/latency_info_struct_traits.cc b/ui/latency/mojo/latency_info_struct_traits.cc
index a1ac873..680356c 100644
--- a/ui/latency/mojo/latency_info_struct_traits.cc
+++ b/ui/latency/mojo/latency_info_struct_traits.cc
@@ -12,23 +12,23 @@
 
 ui::mojom::SourceEventType UISourceEventTypeToMojo(ui::SourceEventType type) {
   switch (type) {
-    case ui::UNKNOWN:
+    case ui::SourceEventType::UNKNOWN:
       return ui::mojom::SourceEventType::UNKNOWN;
-    case ui::WHEEL:
+    case ui::SourceEventType::WHEEL:
       return ui::mojom::SourceEventType::WHEEL;
-    case ui::MOUSE:
+    case ui::SourceEventType::MOUSE:
       return ui::mojom::SourceEventType::MOUSE;
-    case ui::TOUCH:
+    case ui::SourceEventType::TOUCH:
       return ui::mojom::SourceEventType::TOUCH;
-    case ui::INERTIAL:
+    case ui::SourceEventType::INERTIAL:
       return ui::mojom::SourceEventType::INERTIAL;
-    case ui::KEY_PRESS:
+    case ui::SourceEventType::KEY_PRESS:
       return ui::mojom::SourceEventType::KEY_PRESS;
-    case ui::TOUCHPAD:
+    case ui::SourceEventType::TOUCHPAD:
       return ui::mojom::SourceEventType::TOUCHPAD;
-    case ui::FRAME:
+    case ui::SourceEventType::FRAME:
       return ui::mojom::SourceEventType::FRAME;
-    case ui::OTHER:
+    case ui::SourceEventType::OTHER:
       return ui::mojom::SourceEventType::OTHER;
   }
   NOTREACHED();
@@ -38,23 +38,23 @@
 ui::SourceEventType MojoSourceEventTypeToUI(ui::mojom::SourceEventType type) {
   switch (type) {
     case ui::mojom::SourceEventType::UNKNOWN:
-      return ui::UNKNOWN;
+      return ui::SourceEventType::UNKNOWN;
     case ui::mojom::SourceEventType::WHEEL:
-      return ui::WHEEL;
+      return ui::SourceEventType::WHEEL;
     case ui::mojom::SourceEventType::MOUSE:
-      return ui::MOUSE;
+      return ui::SourceEventType::MOUSE;
     case ui::mojom::SourceEventType::TOUCH:
-      return ui::TOUCH;
+      return ui::SourceEventType::TOUCH;
     case ui::mojom::SourceEventType::INERTIAL:
-      return ui::INERTIAL;
+      return ui::SourceEventType::INERTIAL;
     case ui::mojom::SourceEventType::KEY_PRESS:
-      return ui::KEY_PRESS;
+      return ui::SourceEventType::KEY_PRESS;
     case ui::mojom::SourceEventType::TOUCHPAD:
-      return ui::TOUCHPAD;
+      return ui::SourceEventType::TOUCHPAD;
     case ui::mojom::SourceEventType::FRAME:
-      return ui::FRAME;
+      return ui::SourceEventType::FRAME;
     case ui::mojom::SourceEventType::OTHER:
-      return ui::OTHER;
+      return ui::SourceEventType::OTHER;
   }
   NOTREACHED();
   return ui::SourceEventType::UNKNOWN;
diff --git a/ui/latency/mojo/latency_info_struct_traits.h b/ui/latency/mojo/latency_info_struct_traits.h
index 00ab6f2..c6d9e7db 100644
--- a/ui/latency/mojo/latency_info_struct_traits.h
+++ b/ui/latency/mojo/latency_info_struct_traits.h
@@ -15,7 +15,7 @@
               "Enum size mismatch");
 
 static_assert(static_cast<int>(ui::mojom::SourceEventType::kMaxValue) ==
-                  static_cast<int>(ui::SOURCE_EVENT_TYPE_LAST),
+                  static_cast<int>(ui::SourceEventType::LAST),
               "Enum size mismatch");
 
 template <>
diff --git a/ui/latency/mojo/struct_traits_unittest.cc b/ui/latency/mojo/struct_traits_unittest.cc
index b764b6e5..4f03eecc 100644
--- a/ui/latency/mojo/struct_traits_unittest.cc
+++ b/ui/latency/mojo/struct_traits_unittest.cc
@@ -52,7 +52,7 @@
   EXPECT_EQ(10, latency.ukm_source_id());
   EXPECT_TRUE(latency.terminated());
 
-  latency.set_source_event_type(ui::TOUCH);
+  latency.set_source_event_type(ui::SourceEventType::TOUCH);
 
   mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
   LatencyInfo output;
diff --git a/ui/views/controls/webview/unhandled_keyboard_event_handler.cc b/ui/views/controls/webview/unhandled_keyboard_event_handler.cc
index d7bd9365..80ce19e 100644
--- a/ui/views/controls/webview/unhandled_keyboard_event_handler.cc
+++ b/ui/views/controls/webview/unhandled_keyboard_event_handler.cc
@@ -16,12 +16,12 @@
 UnhandledKeyboardEventHandler::~UnhandledKeyboardEventHandler() {
 }
 
-void UnhandledKeyboardEventHandler::HandleKeyboardEvent(
+bool UnhandledKeyboardEventHandler::HandleKeyboardEvent(
     const content::NativeWebKeyboardEvent& event,
     FocusManager* focus_manager) {
   if (!focus_manager) {
     NOTREACHED();
-    return;
+    return false;
   }
   // Previous calls to TranslateMessage can generate Char events as well as
   // RawKeyDown events, even if the latter triggered an accelerator.  In these
@@ -29,7 +29,7 @@
   if (event.GetType() == blink::WebInputEvent::kChar &&
       ignore_next_char_event_) {
     ignore_next_char_event_ = false;
-    return;
+    return false;
   }
   // It's necessary to reset this flag, because a RawKeyDown event may not
   // always generate a Char event.
@@ -45,9 +45,8 @@
     // set the flag and fix it if no event was handled.
     ignore_next_char_event_ = true;
 
-    if (focus_manager->ProcessAccelerator(accelerator)) {
-      return;
-    }
+    if (focus_manager->ProcessAccelerator(accelerator))
+      return true;
 
     // ProcessAccelerator didn't handle the accelerator, so we know both
     // that |this| is still valid, and that we didn't want to set the flag.
@@ -55,7 +54,9 @@
   }
 
   if (event.os_event && !event.skip_in_browser)
-    HandleNativeKeyboardEvent(event.os_event, focus_manager);
+    return HandleNativeKeyboardEvent(event.os_event, focus_manager);
+
+  return false;
 }
 
 }  // namespace views
diff --git a/ui/views/controls/webview/unhandled_keyboard_event_handler.h b/ui/views/controls/webview/unhandled_keyboard_event_handler.h
index 8f06105..67cb6563 100644
--- a/ui/views/controls/webview/unhandled_keyboard_event_handler.h
+++ b/ui/views/controls/webview/unhandled_keyboard_event_handler.h
@@ -23,12 +23,12 @@
   UnhandledKeyboardEventHandler();
   ~UnhandledKeyboardEventHandler();
 
-  void HandleKeyboardEvent(const content::NativeWebKeyboardEvent& event,
+  bool HandleKeyboardEvent(const content::NativeWebKeyboardEvent& event,
                            FocusManager* focus_manager);
 
  private:
   // Platform specific handling for unhandled keyboard events.
-  static void HandleNativeKeyboardEvent(gfx::NativeEvent event,
+  static bool HandleNativeKeyboardEvent(gfx::NativeEvent event,
                                         FocusManager* focus_manager);
 
   // Whether to ignore the next Char keyboard event.
diff --git a/ui/views/controls/webview/unhandled_keyboard_event_handler_default.cc b/ui/views/controls/webview/unhandled_keyboard_event_handler_default.cc
index 8d30e36..f6cb4a38 100644
--- a/ui/views/controls/webview/unhandled_keyboard_event_handler_default.cc
+++ b/ui/views/controls/webview/unhandled_keyboard_event_handler_default.cc
@@ -10,10 +10,10 @@
 namespace views {
 
 // static
-void UnhandledKeyboardEventHandler::HandleNativeKeyboardEvent(
+bool UnhandledKeyboardEventHandler::HandleNativeKeyboardEvent(
     gfx::NativeEvent event,
     FocusManager* focus_manager) {
-  focus_manager->OnKeyEvent(*static_cast<ui::KeyEvent*>(event));
+  return !focus_manager->OnKeyEvent(*(event->AsKeyEvent()));
 }
 
 }  // namespace views
diff --git a/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm b/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm
index e87f913..5e004e41 100644
--- a/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm
+++ b/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm
@@ -10,11 +10,12 @@
 namespace views {
 
 // static
-void UnhandledKeyboardEventHandler::HandleNativeKeyboardEvent(
+bool UnhandledKeyboardEventHandler::HandleNativeKeyboardEvent(
     gfx::NativeEvent event,
     FocusManager* focus_manager) {
   [[base::mac::ObjCCastStrict<NativeWidgetMacNSWindow>([event window])
       commandDispatcher] redispatchKeyEvent:event];
+  return true;
 }
 
 }  // namespace views
diff --git a/ui/views/controls/webview/unhandled_keyboard_event_handler_win.cc b/ui/views/controls/webview/unhandled_keyboard_event_handler_win.cc
index e31aabb..04f7604c 100644
--- a/ui/views/controls/webview/unhandled_keyboard_event_handler_win.cc
+++ b/ui/views/controls/webview/unhandled_keyboard_event_handler_win.cc
@@ -9,13 +9,14 @@
 namespace views {
 
 // static
-void UnhandledKeyboardEventHandler::HandleNativeKeyboardEvent(
+bool UnhandledKeyboardEventHandler::HandleNativeKeyboardEvent(
     gfx::NativeEvent event,
     FocusManager* focus_manager) {
   // Any unhandled keyboard/character messages should be defproced.
   // This allows stuff like F10, etc to work correctly.
   const MSG& message(event->native_event());
   DefWindowProc(message.hwnd, message.message, message.wParam, message.lParam);
+  return true;
 }
 
 }  // namespace views
diff --git a/ui/views/controls/webview/web_dialog_view.cc b/ui/views/controls/webview/web_dialog_view.cc
index 56fc98b2..e2023e4 100644
--- a/ui/views/controls/webview/web_dialog_view.cc
+++ b/ui/views/controls/webview/web_dialog_view.cc
@@ -287,12 +287,13 @@
 // A simplified version of BrowserView::HandleKeyboardEvent().
 // We don't handle global keyboard shortcuts here, but that's fine since
 // they're all browser-specific. (This may change in the future.)
-void WebDialogView::HandleKeyboardEvent(content::WebContents* source,
+bool WebDialogView::HandleKeyboardEvent(content::WebContents* source,
                                         const NativeWebKeyboardEvent& event) {
   if (!event.os_event)
-    return;
+    return false;
 
   GetWidget()->native_widget_private()->RepostNativeEvent(event.os_event);
+  return true;
 }
 
 void WebDialogView::CloseContents(WebContents* source) {
diff --git a/ui/views/controls/webview/web_dialog_view.h b/ui/views/controls/webview/web_dialog_view.h
index 29d18cb..6467993 100644
--- a/ui/views/controls/webview/web_dialog_view.h
+++ b/ui/views/controls/webview/web_dialog_view.h
@@ -97,7 +97,7 @@
   // Overridden from content::WebContentsDelegate:
   void SetContentsBounds(content::WebContents* source,
                          const gfx::Rect& bounds) override;
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
   void CloseContents(content::WebContents* source) override;
diff --git a/ui/views/examples/webview_example.cc b/ui/views/examples/webview_example.cc
index f23b646f..3910b87 100644
--- a/ui/views/examples/webview_example.cc
+++ b/ui/views/examples/webview_example.cc
@@ -31,10 +31,10 @@
   webview_->GetWebContents()->Focus();
 }
 
-void WebViewExample::HandleKeyboardEvent(
+bool WebViewExample::HandleKeyboardEvent(
     content::WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
-  unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+  return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
       event, webview_->GetFocusManager());
 }
 
diff --git a/ui/views/examples/webview_example.h b/ui/views/examples/webview_example.h
index f04c87e..600feaa1 100644
--- a/ui/views/examples/webview_example.h
+++ b/ui/views/examples/webview_example.h
@@ -28,7 +28,7 @@
   void CreateExampleView(View* container) override;
 
   // content::WebContentsDelegate:
-  void HandleKeyboardEvent(
+  bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
 
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 19b02d6..5fef993 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1915,10 +1915,12 @@
 
   // View:
   void OnEvent(ui::Event* event) override {
-    // Guard against attempting to close the widget twice.
-    if (widget_)
-      widget_->CloseNow();
-    widget_ = nullptr;
+    // Guard against closing twice and writing to freed memory.
+    if (widget_ && event->type() == ui::ET_MOUSE_PRESSED) {
+      Widget* widget = widget_;
+      widget_ = nullptr;
+      widget->CloseNow();
+    }
   }
 
  private:
diff --git a/ui/views_bridge_mac/bridge_factory_impl.mm b/ui/views_bridge_mac/bridge_factory_impl.mm
index 323abef..47e1e56 100644
--- a/ui/views_bridge_mac/bridge_factory_impl.mm
+++ b/ui/views_bridge_mac/bridge_factory_impl.mm
@@ -67,7 +67,8 @@
 
 void BridgeFactoryImpl::BindRequest(
     mojom::BridgeFactoryAssociatedRequest request) {
-  binding_.Bind(std::move(request));
+  binding_.Bind(std::move(request),
+                ui::WindowResizeHelperMac::Get()->task_runner());
 }
 
 void BridgeFactoryImpl::CreateBridgedNativeWidget(