diff --git a/DEPS b/DEPS
index cfdcf844..d295bd97 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '1bfece8556cc03b6aa904b2445d2332136bce037',
+  'skia_revision': 'd5bee5d50c60eedda697ac305658d3817125e147',
   # 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': '60d37ab26372b10eef25127e1ae3dd5270f73f68',
+  'v8_revision': '539f7ab310f8865090c494b0251c45e7c52ae5f5',
   # 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.
@@ -56,7 +56,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
-  'buildtools_revision': '104574186c17cd4701857454feba8872e52a7d82',
+  'buildtools_revision': '31d4daad5d9af672d4e234570a24f3fd844bb713',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '8cb884102c17ef0530277126fd8da054d329d065',
+  'pdfium_revision': '1d95c68f912102dfda5d6e9ad7ca79411cda5900',
   # 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.
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index 882f183..6a1061f 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -258,6 +258,7 @@
   } else if (is_linux) {
     libs = [
       "dl",
+      "pthread",
       "rt",
     ]
   }
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 95572936..38a6119 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -509,7 +509,6 @@
   "//build/config:feature_flags",
   "//build/config/compiler:afdo",
   "//build/config/compiler:compiler",
-  "//build/config/compiler:pthread",
   "//build/config/compiler:clang_stackrealign",
   "//build/config/compiler:compiler_arm_fpu",
   "//build/config/compiler:compiler_arm_thumb",
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 7eb4fb3f..95996ab 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -421,6 +421,13 @@
     cflags += [ "-B$binutils_path" ]
   }
 
+  if (is_linux) {
+    cflags += [ "-pthread" ]
+    # Do not use the -pthread ldflag here since it becomes a no-op
+    # when using -nodefaultlibs, which would cause an unused argument
+    # error.  "-lpthread" is added in //build/config:default_libs.
+  }
+
   # Clang-specific compiler flags setup.
   # ------------------------------------
   if (is_clang) {
@@ -545,16 +552,6 @@
   }
 }
 
-# This is separate from :compiler (and not even a sub-config there)
-# so that some targets can remove it from the list with:
-#   configs -= [ "//build/config/compiler:pthread" ]
-config("pthread") {
-  if (is_linux) {
-    cflags = [ "-pthread" ]
-    ldflags = [ "-pthread" ]
-  }
-}
-
 # This provides the basic options to select the target CPU and ABI.
 # It is factored out of "compiler" so that special cases can use this
 # without using everything that "compiler" brings in.  Options that
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
index 8df94d23..e7e2b789 100755
--- a/build/fuchsia/test_runner.py
+++ b/build/fuchsia/test_runner.py
@@ -124,6 +124,9 @@
   if gtest_filter:
     autorun_file.write(' --gtest_filter=' + gtest_filter)
   autorun_file.write('\n')
+  # If shutdown happens too soon after the test completion, log statements from
+  # the end of the run will be lost, so sleep for a bit before shutting down.
+  autorun_file.write('sleep 3\n')
   autorun_file.write('dm poweroff\n')
   autorun_file.flush()
   os.chmod(autorun_file.name, 0750)
@@ -188,12 +191,17 @@
 
   qemu_path = os.path.join(SDK_ROOT, 'qemu', 'bin', 'qemu-system-x86_64')
 
+  if int(os.environ.get('CHROME_HEADLESS', 0)) == 0:
+    args_for_kvm_and_cpu = ['-enable-kvm', '-cpu', 'host,migratable=no']
+  else:
+    args_for_kvm_and_cpu = ['-cpu', 'Haswell,+smap,-check']
   RunAndCheck(args.dry_run,
       [qemu_path, '-m', '2048', '-nographic', '-net', 'none', '-smp', '4',
-       '-machine', 'q35', '-kernel',
-       os.path.join(SDK_ROOT, 'kernel', 'magenta.bin'),
-       '-cpu', 'Haswell,+smap,-check', '-initrd', bootfs,
-       '-append', 'TERM=xterm-256color'])
+       '-machine', 'q35',
+       '-kernel', os.path.join(SDK_ROOT, 'kernel', 'magenta.bin'),
+       '-initrd', bootfs,
+       '-append', 'TERM=xterm-256color kernel.halt_on_panic=true'] +
+       args_for_kvm_and_cpu)
 
   return 0
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index 9ed4e76..a6f0500 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -66,8 +66,10 @@
     private static final float WEBVR_DPR = 1.0f;
     // Fairly arbitrary values that put a good amount of content on the screen without making the
     // text too small to read.
-    private static final float DEFAULT_CONTENT_WIDTH = 960f;
-    private static final float DEFAULT_CONTENT_HEIGHT = 640f;
+    @VisibleForTesting
+    public static final float DEFAULT_CONTENT_WIDTH = 960f;
+    @VisibleForTesting
+    public static final float DEFAULT_CONTENT_HEIGHT = 640f;
 
     // Make full screen 16:9 until we get exact dimensions from playing video.
     private static final float FULLSCREEN_CONTENT_WIDTH = 1024f;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
index d6e9154e..69cabcc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
@@ -27,6 +27,7 @@
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content.browser.test.util.DOMUtils;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.display.DisplayAndroid;
 
 import java.util.concurrent.TimeoutException;
 
@@ -260,4 +261,36 @@
         assertState(mVrTestRule.getFirstTabWebContents(), Page.PAGE_WEBVR,
                 PresentationMode.NON_PRESENTING, FullscreenMode.NON_FULLSCREENED);
     }
+
+    /**
+     * Tests exit presentation transition from WebVR to VrShell
+     */
+    @Test
+    @CommandLineFlags.Add("enable-webvr")
+    @MediumTest
+    public void testExitPresentationWebVrToVrShell()
+            throws IllegalArgumentException, InterruptedException, TimeoutException {
+        mVrTestRule.loadUrlAndAwaitInitialization(TEST_PAGE_WEBVR_URL, PAGE_LOAD_TIMEOUT_S);
+        enterPresentationOrFail(mVrTestRule.getFirstTabCvc());
+
+        // Validate our size is what we expect.
+        DisplayAndroid primaryDisplay =
+                DisplayAndroid.getNonMultiDisplay(mVrTestRule.getActivity());
+        float expectedWidth = primaryDisplay.getDisplayWidth();
+        float expectedHeight = primaryDisplay.getDisplayHeight();
+        Assert.assertTrue(mVrTestRule.pollJavaScriptBoolean(
+                "screen.width == " + expectedWidth + " && screen.height == " + expectedHeight,
+                POLL_TIMEOUT_LONG_MS, mVrTestRule.getFirstTabWebContents()));
+
+        // Exit presentation through JavaScript.
+        mVrTestRule.runJavaScriptOrFail("vrDisplay.exitPresent();", POLL_TIMEOUT_SHORT_MS,
+                mVrTestRule.getFirstTabWebContents());
+
+        // Validate our size is what we expect.
+        expectedWidth = VrShellImpl.DEFAULT_CONTENT_WIDTH;
+        expectedHeight = VrShellImpl.DEFAULT_CONTENT_HEIGHT;
+        Assert.assertTrue(mVrTestRule.pollJavaScriptBoolean(
+                "screen.width == " + expectedWidth + " && screen.height == " + expectedHeight,
+                POLL_TIMEOUT_LONG_MS, mVrTestRule.getFirstTabWebContents()));
+    }
 }
diff --git a/chrome/browser/extensions/extension_install_ui_browsertest.cc b/chrome/browser/extensions/extension_install_ui_browsertest.cc
index 8902bd59..3941ca78 100644
--- a/chrome/browser/extensions/extension_install_ui_browsertest.cc
+++ b/chrome/browser/extensions/extension_install_ui_browsertest.cc
@@ -26,7 +26,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/app_sorting.h"
-#include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 
 using content::WebContents;
@@ -35,9 +34,6 @@
 
 class ExtensionInstallUIBrowserTest : public ExtensionBrowserTest {
  public:
-  ExtensionInstallUIBrowserTest() {}
-  ~ExtensionInstallUIBrowserTest() override {}
-
   // Checks that a theme info bar is currently visible and issues an undo to
   // revert to the previous theme.
   void VerifyThemeInfoBarAndUndoInstall() {
@@ -51,29 +47,18 @@
         infobar_service->infobar_at(0)->delegate()->AsConfirmInfoBarDelegate();
     ASSERT_TRUE(delegate);
     delegate->Cancel();
-    WaitForThemeChange();
     ASSERT_EQ(0U, infobar_service->infobar_count());
   }
 
   // Install the given theme from the data dir and verify expected name.
   void InstallThemeAndVerify(const char* theme_name,
                              const std::string& expected_name) {
+    // If there is already a theme installed, the current theme should be
+    // disabled and the new one installed + enabled.
+    int expected_change = GetTheme() ? 0 : 1;
     const base::FilePath theme_path = test_data_dir_.AppendASCII(theme_name);
-    const bool theme_exists = GetTheme();
-    // Themes install asynchronously so we must check the number of enabled
-    // extensions after theme install completes.
-    size_t num_before = extensions::ExtensionRegistry::Get(profile())
-                            ->enabled_extensions()
-                            .size();
-    ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_path, 1, browser()));
-    WaitForThemeChange();
-    size_t num_after = extensions::ExtensionRegistry::Get(profile())
-                           ->enabled_extensions()
-                           .size();
-    // If a theme was already installed, we're just swapping one for another, so
-    // no change in extension count.
-    EXPECT_EQ(num_before + (theme_exists ? 0 : 1), num_after);
-
+    ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_path, expected_change,
+        browser()));
     const Extension* theme = GetTheme();
     ASSERT_TRUE(theme);
     ASSERT_EQ(theme->name(), expected_name);
@@ -82,17 +67,6 @@
   const Extension* GetTheme() const {
     return ThemeServiceFactory::GetThemeForProfile(browser()->profile());
   }
-
-  void WaitForThemeChange() {
-    content::WindowedNotificationObserver theme_change_observer(
-        chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
-        content::Source<ThemeService>(
-            ThemeServiceFactory::GetForProfile(browser()->profile())));
-    theme_change_observer.Wait();
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ExtensionInstallUIBrowserTest);
 };
 
 // Fails on Linux and Windows (http://crbug.com/580907).
@@ -101,7 +75,6 @@
   // Install theme once and undo to verify we go back to default theme.
   base::FilePath theme_crx = PackExtension(test_data_dir_.AppendASCII("theme"));
   ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_crx, 1, browser()));
-  WaitForThemeChange();
   const Extension* theme = GetTheme();
   ASSERT_TRUE(theme);
   std::string theme_id = theme->id();
@@ -110,12 +83,10 @@
 
   // Set the same theme twice and undo to verify we go back to default theme.
   ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_crx, 0, browser()));
-  WaitForThemeChange();
   theme = GetTheme();
   ASSERT_TRUE(theme);
   ASSERT_EQ(theme_id, theme->id());
   ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_crx, 0, browser()));
-  WaitForThemeChange();
   theme = GetTheme();
   ASSERT_TRUE(theme);
   ASSERT_EQ(theme_id, theme->id());
@@ -133,7 +104,7 @@
   // Then install second theme.
   InstallThemeAndVerify("theme2", "snowflake theme");
   const Extension* theme2 = GetTheme();
-  EXPECT_NE(theme_id, theme2->id());
+  EXPECT_FALSE(theme_id == theme2->id());
 
   // Undo second theme will revert to first theme.
   VerifyThemeInfoBarAndUndoInstall();
diff --git a/chrome/browser/extensions/extension_service_sync_unittest.cc b/chrome/browser/extensions/extension_service_sync_unittest.cc
index 8d91faa8..1b810fa 100644
--- a/chrome/browser/extensions/extension_service_sync_unittest.cc
+++ b/chrome/browser/extensions/extension_service_sync_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/test/mock_entropy_provider.h"
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -29,8 +28,6 @@
 #include "chrome/browser/extensions/scripting_permissions_modifier.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/themes/theme_service.h"
-#include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/sync_helper.h"
@@ -1566,11 +1563,6 @@
   // Installing a theme should not result in a sync change (themes are handled
   // separately by ThemeSyncableService).
   InstallCRX(data_dir().AppendASCII("theme.crx"), INSTALL_NEW);
-  content::WindowedNotificationObserver theme_change_observer(
-      chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
-      content::Source<ThemeService>(
-          ThemeServiceFactory::GetForProfile(profile())));
-  theme_change_observer.Wait();
   EXPECT_TRUE(processor->changes().empty());
 }
 
diff --git a/chrome/browser/extensions/theme_installed_infobar_delegate.cc b/chrome/browser/extensions/theme_installed_infobar_delegate.cc
index e432bbdc..16d349d 100644
--- a/chrome/browser/extensions/theme_installed_infobar_delegate.cc
+++ b/chrome/browser/extensions/theme_installed_infobar_delegate.cc
@@ -144,9 +144,7 @@
     const extensions::Extension* previous_theme =
         extension_service_->GetExtensionById(previous_theme_id_, true);
     if (previous_theme) {
-      theme_service_->RevertToTheme(previous_theme);
-      // TODO(estade): while we're waiting to close, it would be nice to
-      // indicate that the theme is busy reverting.
+      theme_service_->SetTheme(previous_theme);
       return false;  // The theme change will close us.
     }
   }
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index a887674..090f91b 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -76,8 +76,8 @@
       chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
         content::NotificationService::AllSources());
   InstallExtension("theme2.crx");
-  infobar_removed_1.Wait();
   infobar_added_2.Wait();
+  infobar_removed_1.Wait();
   EXPECT_EQ(
       0u,
       InfoBarService::FromWebContents(
diff --git a/chrome/browser/resources/chromeos/login/encryption_migration.html b/chrome/browser/resources/chromeos/login/encryption_migration.html
index 99b35ad..14afc2a 100644
--- a/chrome/browser/resources/chromeos/login/encryption_migration.html
+++ b/chrome/browser/resources/chromeos/login/encryption_migration.html
@@ -90,7 +90,7 @@
             <div>$i18n{migrationButtonReportAnIssue}</div>
           </oobe-text-button>
 </if>
-          <oobe-text-button inverse on-tap="onRestart_">
+          <oobe-text-button inverse on-tap="onRestartOnFailure_">
             <div>$i18n{migrationButtonRestart}</div>
           </oobe-text-button>
         </div>
@@ -117,7 +117,7 @@
             </oobe-text-button>
           </template>
           <template is="dom-if" if="[[isResuming]]">
-            <oobe-text-button inverse on-tap="onRestart_">
+            <oobe-text-button inverse on-tap="onRestartOnLowStorage_">
               <div>$i18n{migrationButtonRestart}</div>
             </oobe-text-button>
           </template>
diff --git a/chrome/browser/resources/chromeos/login/encryption_migration.js b/chrome/browser/resources/chromeos/login/encryption_migration.js
index 5a99b34b..c166910 100644
--- a/chrome/browser/resources/chromeos/login/encryption_migration.js
+++ b/chrome/browser/resources/chromeos/login/encryption_migration.js
@@ -216,8 +216,16 @@
    * Handles tap on RESTART button.
    * @private
    */
-  onRestart_: function() {
-    this.fire('restart');
+  onRestartOnLowStorage_: function() {
+    this.fire('restart-on-low-storage');
+  },
+
+  /**
+   * Handles tap on RESTART button on the migration failure screen.
+   * @private
+   */
+  onRestartOnFailure_: function() {
+    this.fire('restart-on-failure');
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/login/screen_encryption_migration.js b/chrome/browser/resources/chromeos/login/screen_encryption_migration.js
index 6163d1e..c6b5415 100644
--- a/chrome/browser/resources/chromeos/login/screen_encryption_migration.js
+++ b/chrome/browser/resources/chromeos/login/screen_encryption_migration.js
@@ -28,8 +28,12 @@
       encryptionMigration.addEventListener('skip', function() {
         chrome.send('skipMigration');
       });
-      encryptionMigration.addEventListener('restart', function() {
-        chrome.send('requestRestart');
+      encryptionMigration.addEventListener(
+          'restart-on-low-storage', function() {
+            chrome.send('requestRestartOnLowStorage');
+          });
+      encryptionMigration.addEventListener('restart-on-failure', function() {
+        chrome.send('requestRestartOnFailure');
       });
       encryptionMigration.addEventListener('openFeedbackDialog', function() {
         chrome.send('openFeedbackDialog');
diff --git a/chrome/browser/resources/md_bookmarks/OWNERS b/chrome/browser/resources/md_bookmarks/OWNERS
index d0663e0..b02ce2c 100644
--- a/chrome/browser/resources/md_bookmarks/OWNERS
+++ b/chrome/browser/resources/md_bookmarks/OWNERS
@@ -1,5 +1,4 @@
 calamity@chromium.org
-dbeam@chromium.org
 tsergeant@chromium.org
 
 # COMPONENT: UI>Browser>Bookmarks
diff --git a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
index fa5b140d..ce5099f 100644
--- a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
@@ -4,25 +4,20 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/themes_helper.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
-#include "chrome/browser/themes/theme_service_factory.h"
 #include "components/browser_sync/profile_sync_service.h"
-#include "content/public/test/test_utils.h"
 
 using themes_helper::GetCustomTheme;
 using themes_helper::GetThemeID;
+using themes_helper::UseCustomTheme;
 using themes_helper::UseDefaultTheme;
 using themes_helper::UseSystemTheme;
 using themes_helper::UsingCustomTheme;
 using themes_helper::UsingDefaultTheme;
 using themes_helper::UsingSystemTheme;
 
-namespace {
-
 class SingleClientThemesSyncTest : public SyncTest {
  public:
   SingleClientThemesSyncTest() : SyncTest(SINGLE_CLIENT) {}
@@ -39,18 +34,18 @@
 IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, CustomTheme) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-  EXPECT_FALSE(UsingCustomTheme(GetProfile(0)));
-  EXPECT_FALSE(UsingCustomTheme(verifier()));
+  ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
+  ASSERT_FALSE(UsingCustomTheme(verifier()));
 
-  SetCustomTheme(GetProfile(0));
-  SetCustomTheme(verifier());
-  EXPECT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
-  EXPECT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+  UseCustomTheme(GetProfile(0), 0);
+  UseCustomTheme(verifier(), 0);
+  ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+  ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
 
-  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
-  EXPECT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
-  EXPECT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+  ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+  ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
 }
 
 // TODO(sync): Fails on Chrome OS. See http://crbug.com/84575.
@@ -61,43 +56,41 @@
 #endif  // OS_CHROMEOS
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-  SetCustomTheme(GetProfile(0));
-  SetCustomTheme(verifier());
-  EXPECT_FALSE(UsingSystemTheme(GetProfile(0)));
-  EXPECT_FALSE(UsingSystemTheme(verifier()));
+  UseCustomTheme(GetProfile(0), 0);
+  UseCustomTheme(verifier(), 0);
+  ASSERT_FALSE(UsingSystemTheme(GetProfile(0)));
+  ASSERT_FALSE(UsingSystemTheme(verifier()));
 
-  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
   UseSystemTheme(GetProfile(0));
   UseSystemTheme(verifier());
-  EXPECT_TRUE(UsingSystemTheme(GetProfile(0)));
-  EXPECT_TRUE(UsingSystemTheme(verifier()));
+  ASSERT_TRUE(UsingSystemTheme(GetProfile(0)));
+  ASSERT_TRUE(UsingSystemTheme(verifier()));
 
-  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
-  EXPECT_TRUE(UsingSystemTheme(GetProfile(0)));
-  EXPECT_TRUE(UsingSystemTheme(verifier()));
+  ASSERT_TRUE(UsingSystemTheme(GetProfile(0)));
+  ASSERT_TRUE(UsingSystemTheme(verifier()));
 }
 
 IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, DefaultTheme) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-  SetCustomTheme(GetProfile(0));
-  EXPECT_FALSE(UsingDefaultTheme(GetProfile(0)));
+  UseCustomTheme(GetProfile(0), 0);
+  UseCustomTheme(verifier(), 0);
+  ASSERT_FALSE(UsingDefaultTheme(GetProfile(0)));
+  ASSERT_FALSE(UsingDefaultTheme(verifier()));
 
-  SetCustomTheme(verifier());
-  EXPECT_FALSE(UsingDefaultTheme(verifier()));
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
-  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
   UseDefaultTheme(GetProfile(0));
-  EXPECT_TRUE(UsingDefaultTheme(GetProfile(0)));
   UseDefaultTheme(verifier());
-  EXPECT_TRUE(UsingDefaultTheme(verifier()));
+  ASSERT_TRUE(UsingDefaultTheme(GetProfile(0)));
+  ASSERT_TRUE(UsingDefaultTheme(verifier()));
 
-  EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
 
-  EXPECT_TRUE(UsingDefaultTheme(GetProfile(0)));
-  EXPECT_TRUE(UsingDefaultTheme(verifier()));
+  ASSERT_TRUE(UsingDefaultTheme(GetProfile(0)));
+  ASSERT_TRUE(UsingDefaultTheme(verifier()));
 }
-
-}  // namespace
diff --git a/chrome/browser/sync/test/integration/sync_integration_test_util.cc b/chrome/browser/sync/test/integration/sync_integration_test_util.cc
index d9afa063..42ef3e24 100644
--- a/chrome/browser/sync/test/integration/sync_integration_test_util.cc
+++ b/chrome/browser/sync/test/integration/sync_integration_test_util.cc
@@ -5,20 +5,7 @@
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 
 #include "base/strings/stringprintf.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/sync/test/integration/themes_helper.h"
-#include "chrome/browser/themes/theme_service_factory.h"
 #include "components/browser_sync/profile_sync_service.h"
-#include "content/public/test/test_utils.h"
-
-void SetCustomTheme(Profile* profile, int theme_index) {
-  themes_helper::UseCustomTheme(profile, theme_index);
-  content::WindowedNotificationObserver theme_change_observer(
-      chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
-      content::Source<ThemeService>(
-          ThemeServiceFactory::GetForProfile(profile)));
-  theme_change_observer.Wait();
-}
 
 ServerCountMatchStatusChecker::ServerCountMatchStatusChecker(
     syncer::ModelType type,
diff --git a/chrome/browser/sync/test/integration/sync_integration_test_util.h b/chrome/browser/sync/test/integration/sync_integration_test_util.h
index 63df4c9..c3af2d7 100644
--- a/chrome/browser/sync/test/integration/sync_integration_test_util.h
+++ b/chrome/browser/sync/test/integration/sync_integration_test_util.h
@@ -11,15 +11,10 @@
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "components/sync/base/model_type.h"
 
-class Profile;
-
 namespace browser_sync {
 class ProfileSyncService;
 }  // namespace browser_sync
 
-// Sets a custom theme and wait until the asynchronous process is done.
-void SetCustomTheme(Profile* profile, int theme_index = 0);
-
 // Checker to block until the server has a given number of entities.
 class ServerCountMatchStatusChecker
     : public fake_server::FakeServerMatchStatusChecker {
diff --git a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
index 68beb142..530f209 100644
--- a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
@@ -38,7 +38,7 @@
   ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
   ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
 
-  SetCustomTheme(GetProfile(0));
+  UseCustomTheme(GetProfile(0), 0);
   ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
 
   // TODO(sync): Add functions to simulate when a pending extension
@@ -57,8 +57,8 @@
                        E2E_ENABLED(CustomThenSyncNative)) {
   ASSERT_TRUE(SetupClients());
 
-  SetCustomTheme(GetProfile(0));
-  SetCustomTheme(GetProfile(1));
+  UseCustomTheme(GetProfile(0), 0);
+  UseCustomTheme(GetProfile(1), 0);
 
   ASSERT_TRUE(SetupSync());
 
@@ -77,8 +77,8 @@
                        E2E_ENABLED(CustomThenSyncDefault)) {
   ASSERT_TRUE(SetupClients());
 
-  SetCustomTheme(GetProfile(0));
-  SetCustomTheme(GetProfile(1));
+  UseCustomTheme(GetProfile(0), 0);
+  UseCustomTheme(GetProfile(1), 0);
 
   ASSERT_TRUE(SetupSync());
 
@@ -97,7 +97,7 @@
 IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, E2E_ENABLED(CycleOptions)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
-  SetCustomTheme(GetProfile(0));
+  UseCustomTheme(GetProfile(0), 0);
 
   ASSERT_TRUE(
       ThemePendingInstallChecker(GetProfile(1), GetCustomTheme(0)).Wait());
@@ -115,7 +115,7 @@
   EXPECT_TRUE(UsingDefaultTheme(GetProfile(0)));
   EXPECT_TRUE(UsingDefaultTheme(GetProfile(1)));
 
-  SetCustomTheme(GetProfile(0), 1);
+  UseCustomTheme(GetProfile(0), 1);
   ASSERT_TRUE(
       ThemePendingInstallChecker(GetProfile(1), GetCustomTheme(1)).Wait());
   EXPECT_EQ(GetCustomTheme(1), GetThemeID(GetProfile(0)));
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 4f3da008..a352c67 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -546,13 +546,13 @@
 }
 
 // static
-void BrowserThemePack::BuildFromExtension(
-    const extensions::Extension* extension,
-    scoped_refptr<BrowserThemePack> pack) {
+scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromExtension(
+    const Extension* extension) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   DCHECK(extension->is_theme());
-  DCHECK(!pack->is_valid());
 
+  scoped_refptr<BrowserThemePack> pack(new BrowserThemePack);
   pack->BuildHeader(extension);
   pack->BuildTintsFromJSON(extensions::ThemeInfo::GetTints(extension));
   pack->BuildColorsFromJSON(extensions::ThemeInfo::GetColors(extension));
@@ -567,14 +567,14 @@
       &file_paths);
   pack->BuildSourceImagesArray(file_paths);
 
-  if (!pack->LoadRawBitmapsTo(file_paths, &pack->images_))
-    return;
+  if (!pack->LoadRawBitmapsTo(file_paths, &pack->images_on_ui_thread_))
+    return NULL;
 
-  pack->CreateImages(&pack->images_);
+  pack->CreateImages(&pack->images_on_ui_thread_);
 
   // Make sure the |images_on_file_thread_| has bitmaps for supported
   // scale factors before passing to FILE thread.
-  pack->images_on_file_thread_ = pack->images_;
+  pack->images_on_file_thread_ = pack->images_on_ui_thread_;
   for (ImageCache::iterator it = pack->images_on_file_thread_.begin();
        it != pack->images_on_file_thread_.end(); ++it) {
     gfx::ImageSkia* image_skia =
@@ -582,13 +582,13 @@
     image_skia->MakeThreadSafe();
   }
 
-  // Set ThemeImageSource on |images_| to resample the source
+  // Set ThemeImageSource on |images_on_ui_thread_| to resample the source
   // image if a caller of BrowserThemePack::GetImageNamed() requests an
   // ImageSkiaRep for a scale factor not specified by the theme author.
   // Callers of BrowserThemePack::GetImageNamed() to be able to retrieve
   // ImageSkiaReps for all supported scale factors.
-  for (ImageCache::iterator it = pack->images_.begin();
-       it != pack->images_.end(); ++it) {
+  for (ImageCache::iterator it = pack->images_on_ui_thread_.begin();
+       it != pack->images_on_ui_thread_.end(); ++it) {
     const gfx::ImageSkia source_image_skia = it->second.AsImageSkia();
     ThemeImageSource* source = new ThemeImageSource(source_image_skia);
     // image_skia takes ownership of source.
@@ -603,7 +603,7 @@
   }
 
   // The BrowserThemePack is now in a consistent state.
-  pack->is_valid_ = true;
+  return pack;
 }
 
 // static
@@ -671,7 +671,6 @@
                 << "from those supported by platform.";
     return NULL;
   }
-  pack->is_valid_ = true;
   return pack;
 }
 
@@ -684,13 +683,6 @@
   return false;
 }
 
-BrowserThemePack::BrowserThemePack() : CustomThemeSupplier(EXTENSION) {
-  scale_factors_ = ui::GetSupportedScaleFactors();
-  // On Windows HiDPI SCALE_FACTOR_100P may not be supported by default.
-  if (!base::ContainsValue(scale_factors_, ui::SCALE_FACTOR_100P))
-    scale_factors_.push_back(ui::SCALE_FACTOR_100P);
-}
-
 bool BrowserThemePack::WriteToDisk(const base::FilePath& path) const {
   // Add resources for each of the property arrays.
   RawDataForWriting resources;
@@ -779,8 +771,8 @@
     return gfx::Image();
 
   // Check if the image is cached.
-  ImageCache::const_iterator image_iter = images_.find(prs_id);
-  if (image_iter != images_.end())
+  ImageCache::const_iterator image_iter = images_on_ui_thread_.find(prs_id);
+  if (image_iter != images_on_ui_thread_.end())
     return image_iter->second;
 
   ThemeImagePngSource::PngMap png_map;
@@ -794,7 +786,7 @@
     gfx::ImageSkia image_skia(new ThemeImagePngSource(png_map), 1.0f);
     // |image_skia| takes ownership of ThemeImagePngSource.
     gfx::Image ret = gfx::Image(image_skia);
-    images_[prs_id] = ret;
+    images_on_ui_thread_[prs_id] = ret;
     return ret;
   }
 
@@ -838,6 +830,19 @@
 
 // private:
 
+BrowserThemePack::BrowserThemePack()
+    : CustomThemeSupplier(EXTENSION),
+      header_(NULL),
+      tints_(NULL),
+      colors_(NULL),
+      display_properties_(NULL),
+      source_images_(NULL) {
+  scale_factors_ = ui::GetSupportedScaleFactors();
+  // On Windows HiDPI SCALE_FACTOR_100P may not be supported by default.
+  if (!base::ContainsValue(scale_factors_, ui::SCALE_FACTOR_100P))
+    scale_factors_.push_back(ui::SCALE_FACTOR_100P);
+}
+
 void BrowserThemePack::BuildHeader(const Extension* extension) {
   header_ = new BrowserThemePackHeader;
   header_->version = kThemePackVersion;
diff --git a/chrome/browser/themes/browser_theme_pack.h b/chrome/browser/themes/browser_theme_pack.h
index d527e973..cc975cc 100644
--- a/chrome/browser/themes/browser_theme_pack.h
+++ b/chrome/browser/themes/browser_theme_pack.h
@@ -53,11 +53,11 @@
 // will trip our IO on the UI thread detector.
 class BrowserThemePack : public CustomThemeSupplier {
  public:
-  // Builds the theme from |extension| into |pack|. This may be done on a
-  // separate thread as it takes so long. This can fail in the case where the
-  // theme has invalid data, in which case |pack->is_valid()| will be false.
-  static void BuildFromExtension(const extensions::Extension* extension,
-                                 scoped_refptr<BrowserThemePack> pack);
+  // Builds the theme pack from all data from |extension|. This is often done
+  // on a separate thread as it takes so long. This can fail and return NULL in
+  // the case where the theme has invalid data.
+  static scoped_refptr<BrowserThemePack> BuildFromExtension(
+      const extensions::Extension* extension);
 
   // Builds the theme pack from a previously performed WriteToDisk(). This
   // operation should be relatively fast, as it should be an mmap() and some
@@ -69,11 +69,6 @@
   // in the data pack.
   static bool IsPersistentImageID(int id);
 
-  // Default. Everything is empty.
-  BrowserThemePack();
-
-  bool is_valid() const { return is_valid_; }
-
   // Builds a data pack on disk at |path| for future quick loading by
   // BuildFromDataPack(). Often (but not always) called from the file thread;
   // implementation should be threadsafe because neither thread will write to
@@ -108,6 +103,9 @@
   // Maps image ids to maps of scale factors to file paths.
   typedef std::map<int, ScaleFactorToFileMap> FilePathMap;
 
+  // Default. Everything is empty.
+  BrowserThemePack();
+
   ~BrowserThemePack() override;
 
   // Builds a header ready to write to disk.
@@ -219,7 +217,7 @@
 
     // theme_id without NULL terminator.
     uint8_t theme_id[16];
-  }* header_ = nullptr;
+  } *header_;
 
   // The remaining structs represent individual entries in an array. For the
   // following three structs, BrowserThemePack will either allocate an array or
@@ -229,21 +227,21 @@
     double h;
     double s;
     double l;
-  }* tints_ = nullptr;
+  } *tints_;
 
   struct ColorPair {
     int32_t id;
     SkColor color;
-  }* colors_ = nullptr;
+  } *colors_;
 
   struct DisplayPropertyPair {
     int32_t id;
     int32_t property;
-  }* display_properties_ = nullptr;
+  } *display_properties_;
 
   // A list of included source images. A pointer to a -1 terminated array of
   // our persistent IDs.
-  int* source_images_ = nullptr;
+  int* source_images_;
 #pragma pack(pop)
 
   // The scale factors represented by the images in the theme pack.
@@ -256,8 +254,9 @@
   RawImages image_memory_;
 
   // Loaded images. These are loaded from |image_memory_|, from |data_pack_|,
-  // and by BuildFromExtension().
-  ImageCache images_;
+  // and by BuildFromExtension(). These images should only be accessed on the UI
+  // thread.
+  ImageCache images_on_ui_thread_;
 
   // Cache of images created in BuildFromExtension(). Once the theme pack is
   // created, this cache should only be accessed on the file thread. There
@@ -265,10 +264,6 @@
   // or vice versa.
   ImageCache images_on_file_thread_;
 
-  // Whether the theme pack has been succesfully initialized and is ready to
-  // use.
-  bool is_valid_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(BrowserThemePack);
 };
 
diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc
index 22e9d2f..1180081 100644
--- a/chrome/browser/themes/browser_theme_pack_unittest.cc
+++ b/chrome/browser/themes/browser_theme_pack_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/json/json_file_value_serializer.h"
 #include "base/json/json_reader.h"
 #include "base/path_service.h"
-#include "base/synchronization/waitable_event.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/themes/theme_properties.h"
@@ -35,16 +34,14 @@
 
 class BrowserThemePackTest : public ::testing::Test {
  public:
-  BrowserThemePackTest()
-      : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD),
-        theme_pack_(new BrowserThemePack()) {
+  BrowserThemePackTest() {
     std::vector<ui::ScaleFactor> scale_factors;
     scale_factors.push_back(ui::SCALE_FACTOR_100P);
     scale_factors.push_back(ui::SCALE_FACTOR_200P);
     scoped_set_supported_scale_factors_.reset(
-        new ui::test::ScopedSetSupportedScaleFactors(scale_factors));
+      new ui::test::ScopedSetSupportedScaleFactors(scale_factors));
+    theme_pack_ = new BrowserThemePack();
   }
-  ~BrowserThemePackTest() override {}
 
   // Transformation for link underline colors.
   SkColor BuildThirdOpacity(SkColor color_link) {
@@ -145,22 +142,33 @@
   }
 
   bool LoadRawBitmapsTo(const TestFilePathMap& out_file_paths) {
-    return theme_pack_->LoadRawBitmapsTo(out_file_paths, &theme_pack_->images_);
+    return theme_pack_->LoadRawBitmapsTo(out_file_paths,
+                                         &theme_pack_->images_on_ui_thread_);
   }
 
   // This function returns void in order to be able use ASSERT_...
   // The BrowserThemePack is returned in |pack|.
   void BuildFromUnpackedExtension(const base::FilePath& extension_path,
-                                  scoped_refptr<BrowserThemePack>* pack) {
-    io_waiter_.reset(new base::WaitableEvent(
-        base::WaitableEvent::ResetPolicy::AUTOMATIC,
-        base::WaitableEvent::InitialState::NOT_SIGNALED));
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::Bind(&BrowserThemePackTest::DoBuildFromUnpackedExtension,
-                   base::Unretained(this), extension_path, pack));
-    io_waiter_->Wait();
-    ASSERT_TRUE((*pack)->is_valid());
+                                  scoped_refptr<BrowserThemePack>& pack) {
+    base::FilePath manifest_path =
+        extension_path.AppendASCII("manifest.json");
+    std::string error;
+    JSONFileValueDeserializer deserializer(manifest_path);
+    std::unique_ptr<base::DictionaryValue> valid_value =
+        base::DictionaryValue::From(deserializer.Deserialize(NULL, &error));
+    EXPECT_EQ("", error);
+    ASSERT_TRUE(valid_value.get());
+    scoped_refptr<Extension> extension(
+        Extension::Create(
+            extension_path,
+            extensions::Manifest::INVALID_LOCATION,
+            *valid_value,
+            Extension::REQUIRE_KEY,
+            &error));
+    ASSERT_TRUE(extension.get());
+    ASSERT_EQ("", error);
+    pack = BrowserThemePack::BuildFromExtension(extension.get());
+    ASSERT_TRUE(pack.get());
   }
 
   base::FilePath GetStarGazingPath() {
@@ -339,33 +347,12 @@
     }
   }
 
- protected:
+  content::TestBrowserThreadBundle test_browser_thread_bundle_;
+
   typedef std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors>
       ScopedSetSupportedScaleFactors;
   ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
-
-  void DoBuildFromUnpackedExtension(const base::FilePath& extension_path,
-                                    scoped_refptr<BrowserThemePack>* pack) {
-    base::FilePath manifest_path = extension_path.AppendASCII("manifest.json");
-    std::string error;
-    JSONFileValueDeserializer deserializer(manifest_path);
-    std::unique_ptr<base::DictionaryValue> valid_value =
-        base::DictionaryValue::From(deserializer.Deserialize(NULL, &error));
-    EXPECT_EQ("", error);
-    ASSERT_TRUE(valid_value.get());
-    scoped_refptr<Extension> extension(Extension::Create(
-        extension_path, extensions::Manifest::INVALID_LOCATION, *valid_value,
-        Extension::REQUIRE_KEY, &error));
-    ASSERT_TRUE(extension.get());
-    ASSERT_EQ("", error);
-    *pack = new BrowserThemePack;
-    BrowserThemePack::BuildFromExtension(extension.get(), *pack);
-    io_waiter_->Signal();
-  }
-
-  content::TestBrowserThreadBundle thread_bundle_;
   scoped_refptr<BrowserThemePack> theme_pack_;
-  std::unique_ptr<base::WaitableEvent> io_waiter_;
 };
 
 // 'ntp_section' used to correspond to ThemeProperties::COLOR_NTP_SECTION,
@@ -582,7 +569,7 @@
   {
     base::FilePath star_gazing_path = GetStarGazingPath();
     scoped_refptr<BrowserThemePack> pack;
-    BuildFromUnpackedExtension(star_gazing_path, &pack);
+    BuildFromUnpackedExtension(star_gazing_path, pack);
     ASSERT_TRUE(pack->WriteToDisk(file));
     VerifyStarGazing(pack.get());
   }
@@ -606,7 +593,7 @@
   {
     base::FilePath hidpi_path = GetHiDpiThemePath();
     scoped_refptr<BrowserThemePack> pack;
-    BuildFromUnpackedExtension(hidpi_path, &pack);
+    BuildFromUnpackedExtension(hidpi_path, pack);
     ASSERT_TRUE(pack->WriteToDisk(file));
     VerifyHiDpiTheme(pack.get());
   }
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 34cd105..6a9d0e1 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -17,12 +17,10 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/theme_installed_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/browser_theme_pack.h"
 #include "chrome/browser/themes/custom_theme_supplier.h"
@@ -264,7 +262,7 @@
     case extensions::NOTIFICATION_EXTENSION_ENABLED: {
       const Extension* extension = Details<const Extension>(details).ptr();
       if (extension->is_theme())
-        DoSetTheme(extension, true);
+        SetTheme(extension);
       break;
     }
     default:
@@ -273,18 +271,40 @@
 }
 
 void ThemeService::SetTheme(const Extension* extension) {
-  DoSetTheme(extension, false);
-}
-
-void ThemeService::RevertToTheme(const Extension* extension) {
   DCHECK(extension->is_theme());
   ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
-  DCHECK(!service->IsExtensionEnabled(extension->id()));
-  // |extension| is disabled when reverting to the previous theme via an
-  // infobar.
-  service->EnableExtension(extension->id());
-  // Enabling the extension will call back to SetTheme().
+  if (!service->IsExtensionEnabled(extension->id())) {
+    // |extension| is disabled when reverting to the previous theme via an
+    // infobar.
+    service->EnableExtension(extension->id());
+    // Enabling the extension will call back to SetTheme().
+    return;
+  }
+
+  std::string previous_theme_id = GetThemeID();
+
+  // Clear our image cache.
+  FreePlatformCaches();
+
+  BuildFromExtension(extension);
+  SaveThemeID(extension->id());
+
+  NotifyThemeChanged();
+  base::RecordAction(UserMetricsAction("Themes_Installed"));
+
+  if (previous_theme_id != kDefaultThemeID &&
+      previous_theme_id != extension->id() &&
+      service->GetInstalledExtension(previous_theme_id)) {
+    // Do not disable the previous theme if it is already uninstalled. Sending
+    // NOTIFICATION_BROWSER_THEME_CHANGED causes the previous theme to be
+    // uninstalled when the notification causes the remaining infobar to close
+    // and does not open any new infobars. See crbug.com/468280.
+
+    // Disable the old theme.
+    service->DisableExtension(previous_theme_id,
+                              extensions::Extension::DISABLE_USER_ACTION);
+  }
 }
 
 void ThemeService::UseDefaultTheme() {
@@ -329,10 +349,8 @@
 void ThemeService::OnInfobarDestroyed() {
   number_of_infobars_--;
 
-  if (number_of_infobars_ == 0 &&
-      !build_extension_task_tracker_.HasTrackedTasks()) {
+  if (number_of_infobars_ == 0)
     RemoveUnusedThemes(false);
-  }
 }
 
 void ThemeService::RemoveUnusedThemes(bool ignore_infobars) {
@@ -357,8 +375,8 @@
   for (extensions::ExtensionSet::const_iterator it = extensions->begin();
        it != extensions->end(); ++it) {
     const extensions::Extension* extension = it->get();
-    if (extension->is_theme() && extension->id() != current_theme &&
-        extension->id() != building_extension_id_) {
+    if (extension->is_theme() &&
+        extension->id() != current_theme) {
       // Only uninstall themes which are not disabled or are disabled with
       // reason DISABLE_USER_ACTION. We cannot blanket uninstall all disabled
       // themes because externally installed themes are initially disabled.
@@ -560,7 +578,7 @@
 
   // If we don't have a file pack, we're updating from an old version.
   base::FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename);
-  if (!path.empty()) {
+  if (path != base::FilePath()) {
     path = path.Append(chrome::kThemePackFilename);
     SwapThemeSupplier(BrowserThemePack::BuildFromDataPack(path, current_id));
     if (theme_supplier_)
@@ -667,15 +685,6 @@
   return SkColorSetA(separator_color, alpha);
 }
 
-void ThemeService::DoSetTheme(const Extension* extension,
-                              bool suppress_infobar) {
-  DCHECK(extension->is_theme());
-  DCHECK(extensions::ExtensionSystem::Get(profile_)
-             ->extension_service()
-             ->IsExtensionEnabled(extension->id()));
-  BuildFromExtension(extension, suppress_infobar);
-}
-
 gfx::ImageSkia* ThemeService::GetImageSkiaNamed(int id, bool incognito) const {
   gfx::Image image = GetImageNamed(id, incognito);
   if (image.IsEmpty())
@@ -776,6 +785,10 @@
     // be recreated from the extension.
     MigrateTheme();
     set_ready();
+
+    // Send notification in case anyone requested data and cached it when the
+    // theme service was not ready yet.
+    NotifyThemeChanged();
   }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -793,18 +806,15 @@
 }
 
 void ThemeService::MigrateTheme() {
+  // TODO(erg): We need to pop up a dialog informing the user that their
+  // theme is being migrated.
   ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   const Extension* extension =
       service ? service->GetExtensionById(GetThemeID(), false) : nullptr;
   if (extension) {
     DLOG(ERROR) << "Migrating theme";
-    // Theme migration is done on the UI thread. Blocking the UI from appearing
-    // until it's ready is deemed better than showing a blip of the default
-    // theme.
-    scoped_refptr<BrowserThemePack> pack(new BrowserThemePack);
-    BrowserThemePack::BuildFromExtension(extension, pack);
-    OnThemeBuiltFromExtension(extension->id(), pack, true);
+    BuildFromExtension(extension);
     base::RecordAction(UserMetricsAction("Themes.Migrated"));
   } else {
     DLOG(ERROR) << "Theme is mysteriously gone.";
@@ -831,26 +841,10 @@
   profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id);
 }
 
-void ThemeService::BuildFromExtension(const Extension* extension,
-                                      bool suppress_infobar) {
-  build_extension_task_tracker_.TryCancelAll();
-  building_extension_id_ = extension->id();
-  scoped_refptr<BrowserThemePack> pack(new BrowserThemePack);
-  auto task_runner = base::CreateTaskRunnerWithTraits(
-      {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
-  build_extension_task_tracker_.PostTaskAndReply(
-      task_runner.get(), FROM_HERE,
-      base::Bind(&BrowserThemePack::BuildFromExtension, extension, pack),
-      base::Bind(&ThemeService::OnThemeBuiltFromExtension,
-                 weak_ptr_factory_.GetWeakPtr(), extension->id(), pack,
-                 suppress_infobar));
-}
-
-void ThemeService::OnThemeBuiltFromExtension(
-    const extensions::ExtensionId& extension_id,
-    scoped_refptr<BrowserThemePack> pack,
-    bool suppress_infobar) {
-  if (!pack->is_valid()) {
+void ThemeService::BuildFromExtension(const Extension* extension) {
+  scoped_refptr<BrowserThemePack> pack(
+      BrowserThemePack::BuildFromExtension(extension));
+  if (!pack.get()) {
     // TODO(erg): We've failed to install the theme; perhaps we should tell the
     // user? http://crbug.com/34780
     LOG(ERROR) << "Could not load theme.";
@@ -861,57 +855,16 @@
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   if (!service)
     return;
-  const Extension* extension = extensions::ExtensionRegistry::Get(profile_)
-                                   ->enabled_extensions()
-                                   .GetByID(extension_id);
-  if (!extension)
-    return;
 
   // Write the packed file to disk.
   service->GetFileTaskRunner()->PostTask(
       FROM_HERE, base::Bind(&WritePackToDiskCallback, base::RetainedRef(pack),
                             extension->path()));
 
-  const std::string previous_theme_id = GetThemeID();
-  const bool previous_using_system_theme = UsingSystemTheme();
-
   // Save only the extension path. The packed file will be loaded via
   // LoadThemePrefs().
   SavePackName(extension->path());
   SwapThemeSupplier(pack);
-
-  // Clear our image cache.
-  FreePlatformCaches();
-  SaveThemeID(extension->id());
-  NotifyThemeChanged();
-
-  // Same old theme, but the theme has changed (migrated) or auto-updated.
-  if (previous_theme_id == extension->id())
-    return;
-
-  base::RecordAction(UserMetricsAction("Themes_Installed"));
-
-  bool can_revert_theme = previous_theme_id == kDefaultThemeID;
-  if (previous_theme_id != kDefaultThemeID &&
-      service->GetInstalledExtension(previous_theme_id)) {
-    // Do not disable the previous theme if it is already uninstalled. Sending
-    // NOTIFICATION_BROWSER_THEME_CHANGED causes the previous theme to be
-    // uninstalled when the notification causes the remaining infobar to close
-    // and does not open any new infobars. See crbug.com/468280.
-
-    // Disable the old theme.
-    service->DisableExtension(previous_theme_id,
-                              extensions::Extension::DISABLE_USER_ACTION);
-
-    can_revert_theme = true;
-  }
-
-  // Offer to revert to the old theme.
-  if (can_revert_theme && !suppress_infobar) {
-    ThemeInstalledInfoBarDelegate::Create(
-        extension, profile_, previous_theme_id, previous_using_system_theme);
-  }
-  building_extension_id_.clear();
 }
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chrome/browser/themes/theme_service.h b/chrome/browser/themes/theme_service.h
index c941a63..51c676c 100644
--- a/chrome/browser/themes/theme_service.h
+++ b/chrome/browser/themes/theme_service.h
@@ -16,17 +16,14 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
-#include "base/task/cancelable_task_tracker.h"
 #include "build/build_config.h"
 #include "chrome/common/features.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
-#include "extensions/common/extension_id.h"
 #include "extensions/features/features.h"
 #include "ui/base/theme_provider.h"
 
-class BrowserThemePack;
 class CustomThemeSupplier;
 class ThemeSyncableService;
 class Profile;
@@ -85,9 +82,6 @@
   // ExtensionService.
   virtual void SetTheme(const extensions::Extension* extension);
 
-  // Similar to SetTheme, but doesn't show an undo infobar.
-  void RevertToTheme(const extensions::Extension* extension);
-
   // Reset the theme to default.
   virtual void UseDefaultTheme();
 
@@ -236,9 +230,6 @@
   // and contrasting with the foreground tab is the most important).
   static SkColor GetSeparatorColor(SkColor tab_color, SkColor frame_color);
 
-  void DoSetTheme(const extensions::Extension* extension,
-                  bool suppress_infobar);
-
   // These methods provide the implementation for ui::ThemeProvider (exposed
   // via BrowserThemeProvider).
   gfx::ImageSkia* GetImageSkiaNamed(int id, bool incognito) const;
@@ -279,15 +270,8 @@
   void SaveThemeID(const std::string& id);
 
   // Implementation of SetTheme() (and the fallback from LoadThemePrefs() in
-  // case we don't have a theme pack). |new_theme| indicates whether this is a
-  // newly installed theme or a migration.
-  void BuildFromExtension(const extensions::Extension* extension,
-                          bool new_theme);
-
-  // Callback when |pack| has finished or failed building.
-  void OnThemeBuiltFromExtension(const extensions::ExtensionId& extension_id,
-                                 scoped_refptr<BrowserThemePack> pack,
-                                 bool new_theme);
+  // case we don't have a theme pack).
+  void BuildFromExtension(const extensions::Extension* extension);
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   // Returns true if the profile belongs to a supervised user.
@@ -344,14 +328,6 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  // Allows us to cancel building a theme pack from an extension.
-  base::CancelableTaskTracker build_extension_task_tracker_;
-
-  // The ID of the theme that's currently being built on a different thread.
-  // We hold onto this just to be sure not to uninstall the extension view
-  // RemoveUnusedThemes while it's still being built.
-  std::string building_extension_id_;
-
   base::WeakPtrFactory<ThemeService> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ThemeService);
diff --git a/chrome/browser/themes/theme_service_browsertest.cc b/chrome/browser/themes/theme_service_browsertest.cc
index d9d19f9..7ed17415 100644
--- a/chrome/browser/themes/theme_service_browsertest.cc
+++ b/chrome/browser/themes/theme_service_browsertest.cc
@@ -7,7 +7,6 @@
 #include "base/macros.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_restrictions.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/profiles/profile.h"
@@ -16,7 +15,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "content/public/test/test_utils.h"
 
 namespace {
 
@@ -61,14 +59,6 @@
             profile->GetPrefs()->GetFilePath(prefs::kCurrentThemePackFilename));
 
   InstallExtension(test_data_dir_.AppendASCII("theme"), 1);
-  // The theme isn't installed synchronously.
-  EXPECT_FALSE(UsingCustomTheme(*theme_service));
-
-  // Wait for the theme to be loaded.
-  content::WindowedNotificationObserver theme_change_observer(
-      chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
-      content::Source<ThemeService>(theme_service));
-  theme_change_observer.Wait();
 
   // Check that the theme was installed.
   EXPECT_TRUE(UsingCustomTheme(*theme_service));
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index 83114e6..7f6807ee 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
-#include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -78,7 +77,8 @@
     installer->Load(temp_dir);
     std::string extension_id = observer.WaitForExtensionLoaded()->id();
 
-    WaitForThemeInstall();
+    // Let the ThemeService finish creating the theme pack.
+    base::RunLoop().RunUntilIdle();
 
     return extension_id;
   }
@@ -111,14 +111,6 @@
     return theme_service->get_theme_supplier();
   }
 
-  void WaitForThemeInstall() {
-    content::WindowedNotificationObserver theme_change_observer(
-        chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
-        content::Source<ThemeService>(
-            ThemeServiceFactory::GetForProfile(profile())));
-    theme_change_observer.Wait();
-  }
-
   // Alpha blends a non-opaque foreground color against an opaque background.
   // This is not the same as color_utils::AlphaBlend() since it gets the opacity
   // from the foreground color and then does not blend the two colors' alpha
@@ -171,6 +163,8 @@
   ThemeService* theme_service =
       ThemeServiceFactory::GetForProfile(profile_.get());
   theme_service->UseDefaultTheme();
+  // Let the ThemeService uninstall unused themes.
+  base::RunLoop().RunUntilIdle();
 
   base::ScopedTempDir temp_dir1;
   ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
@@ -194,19 +188,19 @@
 
   // 2) Enabling a disabled theme extension should swap the current theme.
   service_->EnableExtension(extension1_id);
-  WaitForThemeInstall();
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(extension1_id, theme_service->GetThemeID());
   EXPECT_TRUE(service_->IsExtensionEnabled(extension1_id));
   EXPECT_TRUE(registry_->GetExtensionById(extension2_id,
                                           ExtensionRegistry::DISABLED));
 
-  // 3) Using RevertToTheme() with a disabled theme should enable and set the
+  // 3) Using SetTheme() with a disabled theme should enable and set the
   // theme. This is the case when the user reverts to the previous theme
   // via an infobar.
   const extensions::Extension* extension2 =
       service_->GetInstalledExtension(extension2_id);
-  theme_service->RevertToTheme(extension2);
-  WaitForThemeInstall();
+  theme_service->SetTheme(extension2);
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(extension2_id, theme_service->GetThemeID());
   EXPECT_TRUE(service_->IsExtensionEnabled(extension2_id));
   EXPECT_TRUE(registry_->GetExtensionById(extension1_id,
@@ -346,21 +340,18 @@
   // Show an infobar.
   theme_service->OnInfobarDisplayed();
 
-  // Install another theme. The first extension shouldn't be uninstalled yet as
-  // it should be possible to revert to it. Emulate the infobar destroying
-  // itself as a result of the NOTIFICATION_BROWSER_THEME_CHANGED notification.
+  // Install another theme. Emulate the infobar destroying itself (and
+  // causing unused themes to be uninstalled) as a result of the
+  // NOTIFICATION_BROWSER_THEME_CHANGED notification.
   {
     InfobarDestroyerOnThemeChange destroyer(profile_.get());
     const std::string& extension2_id = LoadUnpackedThemeAt(temp_dir2.GetPath());
-    EXPECT_EQ(extension2_id, theme_service->GetThemeID());
+    ASSERT_EQ(extension2_id, theme_service->GetThemeID());
+    ASSERT_FALSE(service_->GetInstalledExtension(extension1_id));
   }
 
-  auto* extension1 = service_->GetInstalledExtension(extension1_id);
-  ASSERT_TRUE(extension1);
-
   // Check that it is possible to reinstall extension1.
-  ThemeServiceFactory::GetForProfile(profile_.get())->RevertToTheme(extension1);
-  WaitForThemeInstall();
+  ASSERT_EQ(extension1_id, LoadUnpackedThemeAt(temp_dir1.GetPath()));
   EXPECT_EQ(extension1_id, theme_service->GetThemeID());
 }
 
diff --git a/chrome/browser/ui/bookmarks/bookmark_bar_constants.h b/chrome/browser/ui/bookmarks/bookmark_bar_constants.h
index d6e35ea..f80b23f 100644
--- a/chrome/browser/ui/bookmarks/bookmark_bar_constants.h
+++ b/chrome/browser/ui/bookmarks/bookmark_bar_constants.h
@@ -10,7 +10,7 @@
 namespace chrome {
 
 // The height of Bookmarks Bar, when visible in "New Tab Page" mode.
-const int kNTPBookmarkBarHeight = 40;
+const int kNTPBookmarkBarHeight = 39;
 
 // The minimum height of Bookmarks Bar, when attached to the toolbar. The
 // height of the toolbar may grow to more than this value if the embedded
@@ -20,9 +20,9 @@
 // points) because of the visual overlap with the main toolbar. When using this
 // to compute values other than the actual height of the toolbar, be sure to add
 // |kVisualHeightOffset|.
-const int kMinimumBookmarkBarHeight = 26;
+const int kMinimumBookmarkBarHeight = 25;
 #elif defined(TOOLKIT_VIEWS)
-const int kMinimumBookmarkBarHeight = 28;
+const int kMinimumBookmarkBarHeight = 27;
 #endif
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index 0416c62d..01d1e5e6 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -22,6 +22,7 @@
 #include "chrome/browser/shell_integration.h"
 #include "chrome/browser/signin/chrome_signin_helper.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
+#include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -842,7 +843,7 @@
 BrowserWindowCocoa::GetRenderViewHeightInsetWithDetachedBookmarkBar() {
   if (browser_->bookmark_bar_state() != BookmarkBar::DETACHED)
     return 0;
-  return 40;
+  return chrome::kNTPBookmarkBarHeight;
 }
 
 void BrowserWindowCocoa::ExecuteExtensionCommand(
diff --git a/chrome/browser/ui/extensions/extension_install_ui_default.cc b/chrome/browser/ui/extensions/extension_install_ui_default.cc
index 6114b11..53fda5b 100644
--- a/chrome/browser/ui/extensions/extension_install_ui_default.cc
+++ b/chrome/browser/ui/extensions/extension_install_ui_default.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/theme_installed_infobar_delegate.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
@@ -140,13 +141,25 @@
     content::BrowserContext* context)
     : profile_(Profile::FromBrowserContext(context)),
       skip_post_install_ui_(false),
-      use_app_installed_bubble_(false) {}
+      previous_using_system_theme_(false),
+      use_app_installed_bubble_(false) {
+  // |profile| can be NULL during tests.
+  if (profile_) {
+    // Remember the current theme in case the user presses undo.
+    const Extension* previous_theme =
+        ThemeServiceFactory::GetThemeForProfile(profile_);
+    if (previous_theme)
+      previous_theme_id_ = previous_theme->id();
+    previous_using_system_theme_ =
+        ThemeServiceFactory::GetForProfile(profile_)->UsingSystemTheme();
+  }
+}
 
 ExtensionInstallUIDefault::~ExtensionInstallUIDefault() {}
 
 void ExtensionInstallUIDefault::OnInstallSuccess(const Extension* extension,
                                                  const SkBitmap* icon) {
-  if (skip_post_install_ui_ || extension->is_theme())
+  if (skip_post_install_ui_)
     return;
 
   if (!profile_) {
@@ -156,6 +169,12 @@
     return;
   }
 
+  if (extension->is_theme()) {
+    ThemeInstalledInfoBarDelegate::Create(
+        extension, profile_, previous_theme_id_, previous_using_system_theme_);
+    return;
+  }
+
   // Extensions aren't enabled by default in incognito so we confirm
   // the install in a normal window.
   Profile* current_profile = profile_->GetOriginalProfile();
diff --git a/chrome/browser/ui/extensions/extension_install_ui_default.h b/chrome/browser/ui/extensions/extension_install_ui_default.h
index 2586f2e..32d40e1 100644
--- a/chrome/browser/ui/extensions/extension_install_ui_default.h
+++ b/chrome/browser/ui/extensions/extension_install_ui_default.h
@@ -35,6 +35,10 @@
   // Whether or not to show the default UI after completing the installation.
   bool skip_post_install_ui_;
 
+  // Used to undo theme installation.
+  std::string previous_theme_id_;
+  bool previous_using_system_theme_;
+
   // Whether to show an installed bubble on app install, or use the default
   // action of opening a new tab page.
   bool use_app_installed_bubble_;
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
index e61e99b4..7bc09d7 100644
--- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
+++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
@@ -291,7 +291,8 @@
   void InstallThemeAndVerify(const std::string& theme_dir,
                              const std::string& theme_name) {
     const extensions::Extension* theme =
-        ThemeServiceFactory::GetThemeForProfile(profile());
+        ThemeServiceFactory::GetThemeForProfile(
+            ExtensionBrowserTest::browser()->profile());
     // If there is already a theme installed, the current theme should be
     // disabled and the new one installed + enabled.
     int expected_change = theme ? 0 : 1;
@@ -299,13 +300,9 @@
     const base::FilePath theme_path = test_data_dir_.AppendASCII(theme_dir);
     ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(
         theme_path, expected_change, ExtensionBrowserTest::browser()));
-    content::WindowedNotificationObserver theme_change_observer(
-        chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
-        content::Source<ThemeService>(
-            ThemeServiceFactory::GetForProfile(profile())));
-    theme_change_observer.Wait();
     const extensions::Extension* new_theme =
-        ThemeServiceFactory::GetThemeForProfile(profile());
+        ThemeServiceFactory::GetThemeForProfile(
+            ExtensionBrowserTest::browser()->profile());
     ASSERT_NE(static_cast<extensions::Extension*>(NULL), new_theme);
     ASSERT_EQ(new_theme->name(), theme_name);
   }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 9e4f912..14def1c78 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -605,7 +605,6 @@
   // Don't let the bookmarks show on top of the location bar while animating.
   SetPaintToLayer();
   layer()->SetMasksToBounds(true);
-  layer()->SetFillsBoundsOpaquely(false);
 
   size_animation_.Reset(1);
 }
@@ -799,23 +798,19 @@
 }
 
 int BookmarkBarView::GetToolbarOverlap() const {
-  int attached_overlap = kToolbarAttachedBookmarkBarOverlap +
-      views::NonClientFrameView::kClientEdgeThickness;
+  int attached_overlap = kToolbarAttachedBookmarkBarOverlap;
+
   if (!IsDetached())
     return attached_overlap;
 
-  int detached_overlap = views::NonClientFrameView::kClientEdgeThickness;
-
   // Do not animate the overlap when the infobar is above us (i.e. when we're
   // detached), since drawing over the infobar looks weird.
   if (infobar_visible_)
-    return detached_overlap;
+    return 0;
 
   // When detached with no infobar, animate the overlap between the attached and
   // detached states.
-  return detached_overlap + static_cast<int>(
-      (attached_overlap - detached_overlap) *
-          size_animation_.GetCurrentValue());
+  return static_cast<int>(attached_overlap * size_animation_.GetCurrentValue());
 }
 
 int BookmarkBarView::GetPreferredHeight() const {
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index e530c8d..e7e19ede 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -197,10 +197,6 @@
   // Paint background for detached state; if animating, this is fade in/out.
   const ui::ThemeProvider* tp = view->GetThemeProvider();
   gfx::Rect fill_rect = view->GetLocalBounds();
-  // We have to not color the top 1dp, because that should be painted by the
-  // toolbar. We will, however, paint the 1px separator at the bottom of the
-  // first dp. See crbug.com/610359
-  fill_rect.Inset(0, 1, 0, 0);
 
   // In detached mode, the bar is meant to overlap with |contents_container_|.
   // The detached background color may be partially transparent, but the layer
@@ -2547,8 +2543,7 @@
   }
   // Don't use bookmark_bar_view_->height() which won't be the final height if
   // the bookmark bar is animating.
-  return chrome::kNTPBookmarkBarHeight -
-         views::NonClientFrameView::kClientEdgeThickness;
+  return chrome::kNTPBookmarkBarHeight;
 }
 
 void BrowserView::ExecuteExtensionCommand(
diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc
index f781e84..0b8410c 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout.cc
@@ -512,8 +512,7 @@
   }
 
   // Offset for the detached bookmark bar.
-  return bookmark_bar_->height() -
-      views::NonClientFrameView::kClientEdgeThickness;
+  return bookmark_bar_->height();
 }
 
 int BrowserViewLayout::LayoutDownloadShelf(int bottom) {
diff --git a/chrome/browser/ui/views/frame/browser_view_unittest.cc b/chrome/browser/ui/views/frame/browser_view_unittest.cc
index a12611c..3a13912 100644
--- a/chrome/browser/ui/views/frame/browser_view_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_unittest.cc
@@ -140,17 +140,10 @@
 
   // Bookmark bar layout on NTP.
   EXPECT_EQ(0, bookmark_bar->x());
-  EXPECT_EQ(tabstrip->bounds().bottom() + toolbar->height() -
-                views::NonClientFrameView::kClientEdgeThickness,
-            bookmark_bar->y());
+  EXPECT_EQ(tabstrip->bounds().bottom() + toolbar->height(), bookmark_bar->y());
   EXPECT_EQ(toolbar->bounds().bottom(), contents_container->y());
-  // Contents view has a "top margin" pushing it below the bookmark bar.
-  EXPECT_EQ(bookmark_bar->height() -
-                views::NonClientFrameView::kClientEdgeThickness,
-            devtools_web_view->y());
-  EXPECT_EQ(bookmark_bar->height() -
-                views::NonClientFrameView::kClientEdgeThickness,
-            contents_web_view->y());
+  EXPECT_EQ(bookmark_bar->height(), devtools_web_view->y());
+  EXPECT_EQ(bookmark_bar->height(), contents_web_view->y());
 
   // Bookmark bar is parented back to top container on normal page.
   NavigateAndCommitActiveTabWithTitle(browser,
diff --git a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
index f6e284a1..c57c266e 100644
--- a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
@@ -16,6 +16,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/sys_info.h"
 #include "base/task_scheduler/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/arc/arc_migration_constants.h"
 #include "chrome/browser/chromeos/login/ui/login_feedback.h"
@@ -46,18 +48,25 @@
 // JS API callbacks names.
 constexpr char kJsApiStartMigration[] = "startMigration";
 constexpr char kJsApiSkipMigration[] = "skipMigration";
-constexpr char kJsApiRequestRestart[] = "requestRestart";
+constexpr char kJsApiRequestRestartOnLowStorage[] =
+    "requestRestartOnLowStorage";
+constexpr char kJsApiRequestRestartOnFailure[] = "requestRestartOnFailure";
 constexpr char kJsApiOpenFeedbackDialog[] = "openFeedbackDialog";
 
 // UMA names.
 constexpr char kUmaNameFirstScreen[] = "Cryptohome.MigrationUI.FirstScreen";
 constexpr char kUmaNameUserChoice[] = "Cryptohome.MigrationUI.UserChoice";
+constexpr char kUmaNameMigrationResult[] =
+    "Cryptohome.MigrationUI.MigrationResult";
+constexpr char kUmaNameRemoveCryptohomeResult[] =
+    "Cryptohome.MigrationUI.RemoveCryptohomeResult";
 constexpr char kUmaNameConsumedBatteryPercent[] =
     "Cryptohome.MigrationUI.ConsumedBatteryPercent";
+constexpr char kUmaNameVisibleScreen[] = "Cryptohome.MigrationUI.VisibleScreen";
 
-// This enum must match the numbering for Cryptohome.MigrationUI.FirstScreen in
-// histograms.xml. Do not reorder or remove items, only add new items before
-// FIRST_SCREEN_MAX.
+// This enum must match the numbering for MigrationUIFirstScreen in
+// histograms/enums.xml. Do not reorder or remove items, only add new items
+// before FIRST_SCREEN_COUNT.
 enum class FirstScreen {
   FIRST_SCREEN_READY = 0,
   FIRST_SCREEN_RESUME = 1,
@@ -65,15 +74,44 @@
   FIRST_SCREEN_COUNT
 };
 
-// This enum must match the numbering for Cryptohome.MigrationUI.UserChoice in
-// histograms.xml. Do not reorder or remove items, only add new items before
-// FIRST_SCREEN_MAX.
+// This enum must match the numbering for MigrationUIUserChoice in
+// histograms/enums.xml. Do not reorder or remove items, only add new items
+// before USER_CHOICE_COUNT.
 enum class UserChoice {
   USER_CHOICE_UPDATE = 0,
   USER_CHOICE_SKIP = 1,
+  USER_CHOICE_RESTART_ON_FAILURE = 2,
+  USER_CHOICE_RESTART_ON_LOW_STORAGE = 3,
+  USER_CHOICE_REPORT_AN_ISSUE = 4,
   USER_CHOICE_COUNT
 };
 
+// This enum must match the numbering for MigrationUIMigrationResult in
+// histograms/enums.xml. Do not reorder or remove items, only add new items
+// before COUNT.
+enum class MigrationResult {
+  SUCCESS_IN_NEW_MIGRATION = 0,
+  SUCCESS_IN_RESUMED_MIGRATION = 1,
+  GENERAL_FAILURE_IN_NEW_MIGRATION = 2,
+  GENERAL_FAILURE_IN_RESUMED_MIGRATION = 3,
+  REQUEST_FAILURE_IN_NEW_MIGRATION = 4,
+  REQUEST_FAILURE_IN_RESUMED_MIGRATION = 5,
+  MOUNT_FAILURE_IN_NEW_MIGRATION = 6,
+  MOUNT_FAILURE_IN_RESUMED_MIGRATION = 7,
+  COUNT
+};
+
+// This enum must match the numbering for MigrationUIRemoveCryptohomeResult in
+// histograms/enums.xml. Do not reorder or remove items, only add new items
+// before COUNT.
+enum class RemoveCryptohomeResult {
+  SUCCESS_IN_NEW_MIGRATION = 0,
+  SUCCESS_IN_RESUMED_MIGRATION = 1,
+  FAILURE_IN_NEW_MIGRATION = 2,
+  FAILURE_IN_RESUMED_MIGRATION = 3,
+  COUNT
+};
+
 bool IsTestingUI() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       chromeos::switches::kTestEncryptionMigrationUI);
@@ -81,13 +119,30 @@
 
 // Wrapper functions for histogram macros to avoid duplication of expanded code.
 void RecordFirstScreen(FirstScreen first_screen) {
-  UMA_HISTOGRAM_ENUMERATION(kUmaNameFirstScreen, static_cast<int>(first_screen),
-                            static_cast<int>(FirstScreen::FIRST_SCREEN_COUNT));
+  UMA_HISTOGRAM_ENUMERATION(kUmaNameFirstScreen, first_screen,
+                            FirstScreen::FIRST_SCREEN_COUNT);
 }
 
 void RecordUserChoice(UserChoice user_choice) {
-  UMA_HISTOGRAM_ENUMERATION(kUmaNameUserChoice, static_cast<int>(user_choice),
-                            static_cast<int>(UserChoice::USER_CHOICE_COUNT));
+  UMA_HISTOGRAM_ENUMERATION(kUmaNameUserChoice, user_choice,
+                            UserChoice::USER_CHOICE_COUNT);
+}
+
+void RecordMigrationResult(MigrationResult migration_result) {
+  UMA_HISTOGRAM_ENUMERATION(kUmaNameMigrationResult, migration_result,
+                            MigrationResult::COUNT);
+}
+
+void RecordRemoveCryptohomeResult(bool success, bool is_resumed_migration) {
+  RemoveCryptohomeResult result =
+      success ? (is_resumed_migration
+                     ? RemoveCryptohomeResult::SUCCESS_IN_RESUMED_MIGRATION
+                     : RemoveCryptohomeResult::SUCCESS_IN_NEW_MIGRATION)
+              : (is_resumed_migration
+                     ? RemoveCryptohomeResult::FAILURE_IN_RESUMED_MIGRATION
+                     : RemoveCryptohomeResult::FAILURE_IN_NEW_MIGRATION);
+  UMA_HISTOGRAM_ENUMERATION(kUmaNameRemoveCryptohomeResult, result,
+                            RemoveCryptohomeResult::COUNT);
 }
 
 }  // namespace
@@ -204,8 +259,11 @@
               &EncryptionMigrationScreenHandler::HandleStartMigration);
   AddCallback(kJsApiSkipMigration,
               &EncryptionMigrationScreenHandler::HandleSkipMigration);
-  AddCallback(kJsApiRequestRestart,
-              &EncryptionMigrationScreenHandler::HandleRequestRestart);
+  AddCallback(
+      kJsApiRequestRestartOnLowStorage,
+      &EncryptionMigrationScreenHandler::HandleRequestRestartOnLowStorage);
+  AddCallback(kJsApiRequestRestartOnFailure,
+              &EncryptionMigrationScreenHandler::HandleRequestRestartOnFailure);
   AddCallback(kJsApiOpenFeedbackDialog,
               &EncryptionMigrationScreenHandler::HandleOpenFeedbackDialog);
 }
@@ -258,11 +316,18 @@
   }
 }
 
-void EncryptionMigrationScreenHandler::HandleRequestRestart() {
+void EncryptionMigrationScreenHandler::HandleRequestRestartOnLowStorage() {
+  RecordUserChoice(UserChoice::USER_CHOICE_RESTART_ON_LOW_STORAGE);
+  DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+}
+
+void EncryptionMigrationScreenHandler::HandleRequestRestartOnFailure() {
+  RecordUserChoice(UserChoice::USER_CHOICE_RESTART_ON_FAILURE);
   DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
 }
 
 void EncryptionMigrationScreenHandler::HandleOpenFeedbackDialog() {
+  RecordUserChoice(UserChoice::USER_CHOICE_REPORT_AN_ISSUE);
   const std::string description = base::StringPrintf(
       "Auto generated feedback for http://crbug.com/719266.\n"
       "(uniquifier:%s)",
@@ -291,6 +356,16 @@
     StopBlockingPowerSave();
     PowerPolicyController::Get()->SetEncryptionMigrationActive(false);
   }
+
+  // Record which screen is visible to the user.
+  // We record it after delay to make sure that the user was actually able
+  // to see the screen (i.e. the screen is not just a flash).
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          &EncryptionMigrationScreenHandler::OnDelayedRecordVisibleScreen,
+          weak_ptr_factory_.GetWeakPtr(), state),
+      base::TimeDelta::FromSeconds(1));
 }
 
 void EncryptionMigrationScreenHandler::CheckAvailableStorage() {
@@ -351,6 +426,9 @@
     cryptohome::MountError return_code,
     const std::string& mount_hash) {
   if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) {
+    RecordMigrationResult(
+        should_resume_ ? MigrationResult::MOUNT_FAILURE_IN_RESUMED_MIGRATION
+                       : MigrationResult::MOUNT_FAILURE_IN_NEW_MIGRATION);
     UpdateUIState(UIState::MIGRATION_FAILED);
     return;
   }
@@ -402,6 +480,7 @@
     cryptohome::MountError return_code) {
   LOG_IF(ERROR, !success) << "Removing cryptohome failed. return code: "
                           << return_code;
+  RecordRemoveCryptohomeResult(success, should_resume_);
   UpdateUIState(UIState::MIGRATION_FAILED);
 }
 
@@ -433,6 +512,9 @@
       CallJS("setMigrationProgress", static_cast<double>(current) / total);
       break;
     case cryptohome::DIRCRYPTO_MIGRATION_SUCCESS:
+      RecordMigrationResult(should_resume_
+                                ? MigrationResult::SUCCESS_IN_RESUMED_MIGRATION
+                                : MigrationResult::SUCCESS_IN_NEW_MIGRATION);
       // If the battery level decreased during migration, record the consumed
       // battery level.
       if (*current_battery_percent_ < initial_battery_percent_) {
@@ -445,6 +527,9 @@
       DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
       break;
     case cryptohome::DIRCRYPTO_MIGRATION_FAILED:
+      RecordMigrationResult(
+          should_resume_ ? MigrationResult::GENERAL_FAILURE_IN_RESUMED_MIGRATION
+                         : MigrationResult::GENERAL_FAILURE_IN_NEW_MIGRATION);
       // Stop listening to the progress updates.
       DBusThreadManager::Get()
           ->GetCryptohomeClient()
@@ -461,8 +546,21 @@
 void EncryptionMigrationScreenHandler::OnMigrationRequested(bool success) {
   if (!success) {
     LOG(ERROR) << "Requesting MigrateToDircrypto failed.";
+    RecordMigrationResult(
+        should_resume_ ? MigrationResult::REQUEST_FAILURE_IN_RESUMED_MIGRATION
+                       : MigrationResult::REQUEST_FAILURE_IN_NEW_MIGRATION);
     UpdateUIState(UIState::MIGRATION_FAILED);
   }
 }
 
+void EncryptionMigrationScreenHandler::OnDelayedRecordVisibleScreen(
+    UIState ui_state) {
+  if (current_ui_state_ != ui_state)
+    return;
+
+  // If |current_ui_state_| is not changed for a second, record the current
+  // screen as a "visible" screen.
+  UMA_HISTOGRAM_ENUMERATION(kUmaNameVisibleScreen, ui_state, UIState::COUNT);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h
index 968a9ebd..5dba39d5 100644
--- a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h
@@ -47,14 +47,17 @@
   void Initialize() override;
 
  private:
-  // Enumeration for the migration state. These values must be kept in sync with
-  // EncryptionMigrationUIState in JS code.
+  // Enumeration for migration UI state. These values must be kept in sync with
+  // EncryptionMigrationUIState in JS code, and match the numbering for
+  // MigrationUIScreen in histograms/enums.xml. Do not reorder or remove items,
+  // only add new items before COUNT.
   enum UIState {
     INITIAL = 0,
     READY = 1,
     MIGRATING = 2,
     MIGRATION_FAILED = 3,
     NOT_ENOUGH_STORAGE = 4,
+    COUNT
   };
 
   // WebUIMessageHandler implementation:
@@ -66,7 +69,8 @@
   // Handlers for JS API callbacks.
   void HandleStartMigration();
   void HandleSkipMigration();
-  void HandleRequestRestart();
+  void HandleRequestRestartOnLowStorage();
+  void HandleRequestRestartOnFailure();
   void HandleOpenFeedbackDialog();
 
   // Updates UI state.
@@ -94,6 +98,9 @@
                            uint64_t total);
   void OnMigrationRequested(bool success);
 
+  // Records UMA about visible screen after delay.
+  void OnDelayedRecordVisibleScreen(UIState state);
+
   Delegate* delegate_ = nullptr;
   bool show_on_init_ = false;
 
diff --git a/components/cronet/android/cronet_bidirectional_stream_adapter.cc b/components/cronet/android/cronet_bidirectional_stream_adapter.cc
index a637d490..104cb43 100644
--- a/components/cronet/android/cronet_bidirectional_stream_adapter.cc
+++ b/components/cronet/android/cronet_bidirectional_stream_adapter.cc
@@ -398,15 +398,9 @@
 
   pending_write_data_ = std::move(pending_write_data);
   bool end_of_stream = pending_write_data_->jwrite_end_of_stream == JNI_TRUE;
-  if (pending_write_data_->write_buffer_list.size() == 1) {
-    bidi_stream_->SendData(pending_write_data_->write_buffer_list[0],
-                           pending_write_data_->write_buffer_len_list[0],
-                           end_of_stream);
-  } else {
-    bidi_stream_->SendvData(pending_write_data_->write_buffer_list,
-                            pending_write_data_->write_buffer_len_list,
-                            end_of_stream);
-  }
+  bidi_stream_->SendvData(pending_write_data_->write_buffer_list,
+                          pending_write_data_->write_buffer_len_list,
+                          end_of_stream);
 }
 
 void CronetBidirectionalStreamAdapter::DestroyOnNetworkThread(
diff --git a/device/generic_sensor/public/interfaces/BUILD.gn b/device/generic_sensor/public/interfaces/BUILD.gn
index bd043fd..9ffc6b3 100644
--- a/device/generic_sensor/public/interfaces/BUILD.gn
+++ b/device/generic_sensor/public/interfaces/BUILD.gn
@@ -17,9 +17,6 @@
     "sensor_provider.mojom",
   ]
 
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
-
   # TODO(crbug.com/699569): Convert to use the new JS bindings.
   use_new_js_bindings = false
 }
diff --git a/device/generic_sensor/sensor_impl.cc b/device/generic_sensor/sensor_impl.cc
index 1cf9eb8..33e9cd0b 100644
--- a/device/generic_sensor/sensor_impl.cc
+++ b/device/generic_sensor/sensor_impl.cc
@@ -25,21 +25,21 @@
 
 void SensorImpl::AddConfiguration(
     const PlatformSensorConfiguration& configuration,
-    const AddConfigurationCallback& callback) {
+    AddConfigurationCallback callback) {
   // TODO(Mikhail): To avoid overflowing browser by repeated AddConfigs
   // (maybe limit the number of configs per client).
-  callback.Run(sensor_->StartListening(this, configuration));
+  std::move(callback).Run(sensor_->StartListening(this, configuration));
 }
 
 void SensorImpl::GetDefaultConfiguration(
-    const GetDefaultConfigurationCallback& callback) {
-  callback.Run(sensor_->GetDefaultConfiguration());
+    GetDefaultConfigurationCallback callback) {
+  std::move(callback).Run(sensor_->GetDefaultConfiguration());
 }
 
 void SensorImpl::RemoveConfiguration(
     const PlatformSensorConfiguration& configuration,
-    const RemoveConfigurationCallback& callback) {
-  callback.Run(sensor_->StopListening(this, configuration));
+    RemoveConfigurationCallback callback) {
+  std::move(callback).Run(sensor_->StopListening(this, configuration));
 }
 
 void SensorImpl::Suspend() {
diff --git a/device/generic_sensor/sensor_impl.h b/device/generic_sensor/sensor_impl.h
index 3730fc6..4a6ecc95 100644
--- a/device/generic_sensor/sensor_impl.h
+++ b/device/generic_sensor/sensor_impl.h
@@ -23,12 +23,11 @@
  private:
   // Sensor implementation.
   void AddConfiguration(const PlatformSensorConfiguration& configuration,
-                        const AddConfigurationCallback& callback) override;
+                        AddConfigurationCallback callback) override;
   void GetDefaultConfiguration(
-      const GetDefaultConfigurationCallback& callback) override;
-  void RemoveConfiguration(
-      const PlatformSensorConfiguration& configuration,
-      const RemoveConfigurationCallback& callback) override;
+      GetDefaultConfigurationCallback callback) override;
+  void RemoveConfiguration(const PlatformSensorConfiguration& configuration,
+                           RemoveConfigurationCallback callback) override;
   void Suspend() override;
   void Resume() override;
 
diff --git a/device/generic_sensor/sensor_provider_impl.cc b/device/generic_sensor/sensor_provider_impl.cc
index 8f36c47f..3e78e3a1 100644
--- a/device/generic_sensor/sensor_provider_impl.cc
+++ b/device/generic_sensor/sensor_provider_impl.cc
@@ -18,17 +18,16 @@
 
 void RunCallback(mojom::SensorInitParamsPtr init_params,
                  mojom::SensorClientRequest client,
-                 const SensorProviderImpl::GetSensorCallback& callback) {
-  callback.Run(std::move(init_params), std::move(client));
+                 SensorProviderImpl::GetSensorCallback callback) {
+  std::move(callback).Run(std::move(init_params), std::move(client));
 }
 
-void NotifySensorCreated(
-    mojom::SensorInitParamsPtr init_params,
-    mojom::SensorClientRequest client,
-    const SensorProviderImpl::GetSensorCallback& callback) {
+void NotifySensorCreated(mojom::SensorInitParamsPtr init_params,
+                         mojom::SensorClientRequest client,
+                         SensorProviderImpl::GetSensorCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(RunCallback, base::Passed(&init_params),
-                            base::Passed(&client), callback));
+      FROM_HERE, base::BindOnce(&RunCallback, std::move(init_params),
+                                std::move(client), std::move(callback)));
 }
 
 }  // namespace
@@ -54,10 +53,10 @@
 
 void SensorProviderImpl::GetSensor(mojom::SensorType type,
                                    mojom::SensorRequest sensor_request,
-                                   const GetSensorCallback& callback) {
+                                   GetSensorCallback callback) {
   auto cloned_handle = provider_->CloneSharedBufferHandle();
   if (!cloned_handle.is_valid()) {
-    NotifySensorCreated(nullptr, nullptr, callback);
+    NotifySensorCreated(nullptr, nullptr, std::move(callback));
     return;
   }
 
@@ -66,23 +65,23 @@
     PlatformSensorProviderBase::CreateSensorCallback cb = base::Bind(
         &SensorProviderImpl::SensorCreated, weak_ptr_factory_.GetWeakPtr(),
         type, base::Passed(&cloned_handle), base::Passed(&sensor_request),
-        callback);
+        base::Passed(&callback));
     provider_->CreateSensor(type, cb);
     return;
   }
 
   SensorCreated(type, std::move(cloned_handle), std::move(sensor_request),
-                callback, std::move(sensor));
+                std::move(callback), std::move(sensor));
 }
 
 void SensorProviderImpl::SensorCreated(
     mojom::SensorType type,
     mojo::ScopedSharedBufferHandle cloned_handle,
     mojom::SensorRequest sensor_request,
-    const GetSensorCallback& callback,
+    GetSensorCallback callback,
     scoped_refptr<PlatformSensor> sensor) {
   if (!sensor) {
-    NotifySensorCreated(nullptr, nullptr, callback);
+    NotifySensorCreated(nullptr, nullptr, std::move(callback));
     return;
   }
 
@@ -104,7 +103,7 @@
   DCHECK_GT(init_params->minimum_frequency, 0.0);
 
   NotifySensorCreated(std::move(init_params), sensor_impl->GetClient(),
-                      callback);
+                      std::move(callback));
 
   mojo::MakeStrongBinding(std::move(sensor_impl), std::move(sensor_request));
 }
diff --git a/device/generic_sensor/sensor_provider_impl.h b/device/generic_sensor/sensor_provider_impl.h
index 3435f12..2dd3728b 100644
--- a/device/generic_sensor/sensor_provider_impl.h
+++ b/device/generic_sensor/sensor_provider_impl.h
@@ -33,13 +33,13 @@
   // SensorProvider implementation.
   void GetSensor(mojom::SensorType type,
                  mojom::SensorRequest sensor_request,
-                 const GetSensorCallback& callback) override;
+                 GetSensorCallback callback) override;
 
   // Helper callback method to return created sensors.
   void SensorCreated(mojom::SensorType type,
                      mojo::ScopedSharedBufferHandle cloned_handle,
                      mojom::SensorRequest sensor_request,
-                     const GetSensorCallback& callback,
+                     GetSensorCallback callback,
                      scoped_refptr<PlatformSensor> sensor);
 
   PlatformSensorProvider* provider_;
diff --git a/media/base/key_system_properties.cc b/media/base/key_system_properties.cc
index 5c6bcb5..0a15064 100644
--- a/media/base/key_system_properties.cc
+++ b/media/base/key_system_properties.cc
@@ -9,12 +9,11 @@
 
 namespace media {
 
+#if defined(OS_ANDROID)
 SupportedCodecs KeySystemProperties::GetSupportedSecureCodecs() const {
-#if !defined(OS_ANDROID)
-  NOTREACHED();
-#endif
   return EME_CODEC_NONE;
 }
+#endif
 
 bool KeySystemProperties::UseAesDecryptor() const {
   return false;
diff --git a/media/base/key_system_properties.h b/media/base/key_system_properties.h
index a5f890b..b00147d 100644
--- a/media/base/key_system_properties.h
+++ b/media/base/key_system_properties.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "build/build_config.h"
 #include "media/base/eme_constants.h"
 #include "media/base/media_export.h"
 
@@ -27,8 +28,10 @@
   // Returns the codecs supported by this key system.
   virtual SupportedCodecs GetSupportedCodecs() const = 0;
 
+#if defined(OS_ANDROID)
   // Returns the codecs with hardware-secure support in this key system.
   virtual SupportedCodecs GetSupportedSecureCodecs() const;
+#endif
 
   // Returns the configuration rule for supporting a robustness requirement.
   virtual EmeConfigRule GetRobustnessConfigRule(
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc
index 773cc62..691bc03 100644
--- a/media/base/key_systems.cc
+++ b/media/base/key_systems.cc
@@ -238,7 +238,7 @@
   void UpdateSupportedKeySystems();
 
   void AddSupportedKeySystems(
-      std::vector<std::unique_ptr<KeySystemProperties>>* key_systems);
+      std::vector<std::unique_ptr<KeySystemProperties>> key_systems);
 
   void RegisterMimeType(const std::string& mime_type, EmeCodec codecs_mask);
   bool IsValidMimeTypeCodecsCombination(const std::string& mime_type,
@@ -340,7 +340,7 @@
   // Clear Key is always supported.
   key_systems_properties.emplace_back(new ClearKeyProperties());
 
-  AddSupportedKeySystems(&key_systems_properties);
+  AddSupportedKeySystems(std::move(key_systems_properties));
 }
 
 // Returns whether distinctive identifiers and persistent state can be reliably
@@ -371,11 +371,11 @@
 }
 
 void KeySystemsImpl::AddSupportedKeySystems(
-    std::vector<std::unique_ptr<KeySystemProperties>>* key_systems) {
+    std::vector<std::unique_ptr<KeySystemProperties>> key_systems) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(key_system_properties_map_.empty());
 
-  for (auto& properties : *key_systems) {
+  for (auto& properties : key_systems) {
     DCHECK(!properties->GetKeySystemName().empty());
     DCHECK(properties->GetPersistentLicenseSessionSupport() !=
            EmeSessionTypeSupport::INVALID);
@@ -393,7 +393,7 @@
       continue;
     }
 
-    // Supporting persistent state is a prerequsite for supporting persistent
+    // Supporting persistent state is a prerequisite for supporting persistent
     // sessions.
     if (properties->GetPersistentStateSupport() ==
         EmeFeatureSupport::NOT_SUPPORTED) {
diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc
index e5676cf9..9eea543 100644
--- a/media/base/key_systems_unittest.cc
+++ b/media/base/key_systems_unittest.cc
@@ -37,6 +37,10 @@
 const char kAudioFoo[] = "audio/foo";
 const char kVideoFoo[] = "video/foo";
 
+const char kRobustnessSupported[] = "supported";
+const char kRobustnessSecureCodecsRequired[] = "secure-codecs-required";
+const char kRobustnessNotSupported[] = "not-supported";
+
 // Pick some arbitrary bit fields as long as they are not in conflict with the
 // real ones.
 enum TestCodec : uint32_t {
@@ -50,71 +54,111 @@
 static_assert((TEST_CODEC_FOO_ALL & EME_CODEC_ALL) == EME_CODEC_NONE,
               "test codec masks should only use invalid codec masks");
 
-class TestKeySystemProperties : public KeySystemProperties {
+// Base class to provide default implementations.
+class TestKeySystemPropertiesBase : public KeySystemProperties {
  public:
   bool IsSupportedInitDataType(EmeInitDataType init_data_type) const override {
     return init_data_type == EmeInitDataType::WEBM;
   }
+
   SupportedCodecs GetSupportedCodecs() const override {
     return EME_CODEC_WEBM_ALL | TEST_CODEC_FOO_ALL;
   }
+
   EmeConfigRule GetRobustnessConfigRule(
       EmeMediaType media_type,
       const std::string& requested_robustness) const override {
     return requested_robustness.empty() ? EmeConfigRule::SUPPORTED
                                         : EmeConfigRule::NOT_SUPPORTED;
   }
+
   EmeSessionTypeSupport GetPersistentReleaseMessageSessionSupport()
       const override {
     return EmeSessionTypeSupport::NOT_SUPPORTED;
   }
 };
 
-class AesKeySystemProperties : public TestKeySystemProperties {
+class AesKeySystemProperties : public TestKeySystemPropertiesBase {
  public:
   AesKeySystemProperties(const std::string& name) : name_(name) {}
 
   std::string GetKeySystemName() const override { return name_; }
+
   EmeSessionTypeSupport GetPersistentLicenseSessionSupport() const override {
     return EmeSessionTypeSupport::NOT_SUPPORTED;
   }
+
   EmeFeatureSupport GetPersistentStateSupport() const override {
     return EmeFeatureSupport::NOT_SUPPORTED;
   }
+
   EmeFeatureSupport GetDistinctiveIdentifierSupport() const override {
     return EmeFeatureSupport::NOT_SUPPORTED;
   }
+
   bool UseAesDecryptor() const override { return true; }
 
  private:
   std::string name_;
 };
 
-class ExternalKeySystemProperties : public TestKeySystemProperties {
+class ExternalKeySystemProperties : public TestKeySystemPropertiesBase {
  public:
   std::string GetKeySystemName() const override { return kExternal; }
+
+#if defined(OS_ANDROID)
+  // We have hw-secure FOO_VIDEO codec support.
+  SupportedCodecs GetSupportedSecureCodecs() const override {
+    return TEST_CODEC_FOO_VIDEO;
+  }
+#endif
+
+  EmeConfigRule GetRobustnessConfigRule(
+      EmeMediaType media_type,
+      const std::string& requested_robustness) const override {
+    if (requested_robustness == kRobustnessSupported)
+      return EmeConfigRule::SUPPORTED;
+    else if (requested_robustness == kRobustnessSecureCodecsRequired)
+      return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
+    else if (requested_robustness == kRobustnessNotSupported)
+      return EmeConfigRule::NOT_SUPPORTED;
+    else
+      NOTREACHED();
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+
   EmeSessionTypeSupport GetPersistentLicenseSessionSupport() const override {
     return EmeSessionTypeSupport::SUPPORTED;
   }
+
   EmeFeatureSupport GetPersistentStateSupport() const override {
     return EmeFeatureSupport::ALWAYS_ENABLED;
   }
+
   EmeFeatureSupport GetDistinctiveIdentifierSupport() const override {
     return EmeFeatureSupport::ALWAYS_ENABLED;
   }
+
   std::string GetPepperType() const override {
     return "application/x-ppapi-external-cdm";
   }
 };
 
+static EmeConfigRule GetVideoContentTypeConfigRule(
+    const std::string& mime_type,
+    const std::vector<std::string>& codecs,
+    const std::string& key_system) {
+  return KeySystems::GetInstance()->GetContentTypeConfigRule(
+      key_system, EmeMediaType::VIDEO, mime_type, codecs);
+}
+
 // Adapt IsSupportedKeySystemWithMediaMimeType() to the new API,
 // IsSupportedCodecCombination().
 static bool IsSupportedKeySystemWithMediaMimeType(
     const std::string& mime_type,
     const std::vector<std::string>& codecs,
     const std::string& key_system) {
-  return (KeySystems::GetInstance()->GetContentTypeConfigRule(
-              key_system, EmeMediaType::VIDEO, mime_type, codecs) !=
+  return (GetVideoContentTypeConfigRule(mime_type, codecs, key_system) !=
           EmeConfigRule::NOT_SUPPORTED);
 }
 
@@ -131,6 +175,12 @@
   return KeySystems::GetInstance()->IsSupportedKeySystem(key_system);
 }
 
+static EmeConfigRule GetRobustnessConfigRule(
+    const std::string& requested_robustness) {
+  return KeySystems::GetInstance()->GetRobustnessConfigRule(
+      kExternal, EmeMediaType::VIDEO, requested_robustness);
+}
+
 // Adds test container and codec masks.
 // This function must be called after SetMediaClient() if a MediaClient will be
 // provided.
@@ -695,4 +745,35 @@
     EXPECT_FALSE(IsSupportedKeySystem(kExternal));
 }
 
+TEST_F(KeySystemsTest, GetContentTypeConfigRule) {
+  if (!CanRunExternalKeySystemTests())
+    return;
+
+  EXPECT_EQ(EmeConfigRule::SUPPORTED,
+            GetRobustnessConfigRule(kRobustnessSupported));
+  EXPECT_EQ(EmeConfigRule::NOT_SUPPORTED,
+            GetRobustnessConfigRule(kRobustnessNotSupported));
+  EXPECT_EQ(EmeConfigRule::HW_SECURE_CODECS_REQUIRED,
+            GetRobustnessConfigRule(kRobustnessSecureCodecsRequired));
+}
+
+#if defined(OS_ANDROID)
+TEST_F(KeySystemsTest, HardwareSecureCodecs) {
+  if (!CanRunExternalKeySystemTests())
+    return;
+
+  EXPECT_EQ(EmeConfigRule::HW_SECURE_CODECS_NOT_ALLOWED,
+            GetVideoContentTypeConfigRule(kVideoWebM, vp8_codec(), kUsesAes));
+  EXPECT_EQ(
+      EmeConfigRule::HW_SECURE_CODECS_NOT_ALLOWED,
+      GetVideoContentTypeConfigRule(kVideoFoo, foovideo_codec(), kUsesAes));
+
+  EXPECT_EQ(EmeConfigRule::HW_SECURE_CODECS_NOT_ALLOWED,
+            GetVideoContentTypeConfigRule(kVideoWebM, vp8_codec(), kExternal));
+  EXPECT_EQ(
+      EmeConfigRule::SUPPORTED,
+      GetVideoContentTypeConfigRule(kVideoFoo, foovideo_codec(), kExternal));
+}
+#endif
+
 }  // namespace media
diff --git a/net/BUILD.gn b/net/BUILD.gn
index ac40a7a..cbe13593 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -4724,6 +4724,8 @@
     "quic/platform/impl/quic_chromium_clock_test.cc",
     "quic/platform/impl/quic_test_impl.cc",
     "quic/platform/impl/quic_test_impl.h",
+    "quic/quartc/quartc_session_test.cc",
+    "quic/quartc/quartc_stream_test.cc",
     "quic/test_tools/crypto_test_utils.cc",
     "quic/test_tools/crypto_test_utils.h",
     "quic/test_tools/crypto_test_utils_test.cc",
diff --git a/net/cert/internal/nist_pkits_unittest.cc b/net/cert/internal/nist_pkits_unittest.cc
index 6697ded9..8980709 100644
--- a/net/cert/internal/nist_pkits_unittest.cc
+++ b/net/cert/internal/nist_pkits_unittest.cc
@@ -57,18 +57,15 @@
 }
 
 void PkitsTestInfo::SetInitialExplicitPolicy(bool b) {
-  initial_explicit_policy =
-      b ? InitialExplicitPolicy::kTrue : InitialExplicitPolicy::kFalse;
+  initial_explicit_policy = b;
 }
 
 void PkitsTestInfo::SetInitialPolicyMappingInhibit(bool b) {
-  initial_policy_mapping_inhibit = b ? InitialPolicyMappingInhibit::kTrue
-                                     : InitialPolicyMappingInhibit::kFalse;
+  initial_policy_mapping_inhibit = b;
 }
 
 void PkitsTestInfo::SetInitialInhibitAnyPolicy(bool b) {
-  initial_inhibit_any_policy =
-      b ? InitialAnyPolicyInhibit::kTrue : InitialAnyPolicyInhibit::kFalse;
+  initial_inhibit_any_policy = b;
 }
 
 PkitsTestInfo::~PkitsTestInfo() = default;
diff --git a/net/cert/internal/nist_pkits_unittest.h b/net/cert/internal/nist_pkits_unittest.h
index 8f9ef42..adc064b6 100644
--- a/net/cert/internal/nist_pkits_unittest.h
+++ b/net/cert/internal/nist_pkits_unittest.h
@@ -46,15 +46,13 @@
   std::set<der::Input> initial_policy_set;
 
   // The value of "initial-explicit-policy".
-  InitialExplicitPolicy initial_explicit_policy = InitialExplicitPolicy::kFalse;
+  bool initial_explicit_policy = false;
 
   // The value of "initial-policy-mapping-inhibit".
-  InitialPolicyMappingInhibit initial_policy_mapping_inhibit =
-      InitialPolicyMappingInhibit::kFalse;
+  bool initial_policy_mapping_inhibit = false;
 
   // The value of "initial-inhibit-any-policy".
-  InitialAnyPolicyInhibit initial_inhibit_any_policy =
-      InitialAnyPolicyInhibit::kFalse;
+  bool initial_inhibit_any_policy = false;
 
   // This is the time when PKITS was published.
   der::GeneralizedTime time = {2011, 4, 15, 0, 0, 0};
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
index 05b66d6..d1ce0bdb 100644
--- a/net/cert/internal/path_builder.cc
+++ b/net/cert/internal/path_builder.cc
@@ -604,12 +604,9 @@
 
   // Verify the entire certificate chain.
   auto result_path = base::MakeUnique<ResultPath>();
-  // TODO(eroman): don't pass placeholder for policy.
-  VerifyCertificateChain(
-      next_path_.certs, next_path_.last_cert_trust, signature_policy_, time_,
-      key_purpose_, InitialExplicitPolicy::kFalse, {AnyPolicy()},
-      InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse,
-      nullptr /*user_constrained_policy_set*/, &result_path->errors);
+  VerifyCertificateChain(next_path_.certs, next_path_.last_cert_trust,
+                         signature_policy_, time_, key_purpose_,
+                         &result_path->errors);
   bool verify_result = !result_path->errors.ContainsHighSeverityErrors();
 
   DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = "
diff --git a/net/cert/internal/test_helpers.cc b/net/cert/internal/test_helpers.cc
index 70bd6447..16c39cf1 100644
--- a/net/cert/internal/test_helpers.cc
+++ b/net/cert/internal/test_helpers.cc
@@ -125,8 +125,7 @@
   return ::testing::AssertionSuccess();
 }
 
-VerifyCertChainTest::VerifyCertChainTest()
-    : user_initial_policy_set{AnyPolicy()} {}
+VerifyCertChainTest::VerifyCertChainTest() = default;
 VerifyCertChainTest::~VerifyCertChainTest() = default;
 
 bool VerifyCertChainTest::HasHighSeverityErrors() const {
diff --git a/net/cert/internal/test_helpers.h b/net/cert/internal/test_helpers.h
index d8b1c69..dceb068 100644
--- a/net/cert/internal/test_helpers.h
+++ b/net/cert/internal/test_helpers.h
@@ -95,16 +95,6 @@
   // The Key Purpose to use when verifying the chain.
   KeyPurpose key_purpose = KeyPurpose::ANY_EKU;
 
-  InitialExplicitPolicy initial_explicit_policy = InitialExplicitPolicy::kFalse;
-
-  std::set<der::Input> user_initial_policy_set;
-
-  InitialPolicyMappingInhibit initial_policy_mapping_inhibit =
-      InitialPolicyMappingInhibit::kFalse;
-
-  InitialAnyPolicyInhibit initial_any_policy_inhibit =
-      InitialAnyPolicyInhibit::kFalse;
-
   // The expected errors/warnings from verification (as a string).
   std::string expected_errors;
 
diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc
index 3985cd8a..2560b942 100644
--- a/net/cert/internal/verify_certificate_chain.cc
+++ b/net/cert/internal/verify_certificate_chain.cc
@@ -4,7 +4,6 @@
 
 #include "net/cert/internal/verify_certificate_chain.h"
 
-#include <algorithm>
 #include <memory>
 
 #include "base/logging.h"
@@ -64,9 +63,6 @@
                      "The extended key usage does not include client auth");
 DEFINE_CERT_ERROR_ID(kCertIsNotTrustAnchor,
                      "Certificate is not a trust anchor");
-DEFINE_CERT_ERROR_ID(kNoValidPolicy, "No valid policy");
-DEFINE_CERT_ERROR_ID(kPolicyMappingAnyPolicy,
-                     "PolicyMappings must not map anyPolicy");
 
 bool IsHandledCriticalExtensionOid(const der::Input& oid) {
   if (oid == BasicConstraintsOid())
@@ -82,19 +78,8 @@
     return true;
   if (oid == SubjectAltNameOid())
     return true;
-  // TODO(eroman): The policy qualifiers are not processed (or in some cases
-  // even parsed). This is fine when the policies extension is non-critical,
-  // however if it is critical the code should also ensure that the policy
-  // qualifiers are only recognized ones (CPS and User Notice).
-  if (oid == CertificatePoliciesOid())
-    return true;
-  if (oid == PolicyMappingsOid())
-    return true;
-  if (oid == PolicyConstraintsOid())
-    return true;
-  if (oid == InhibitAnyPolicyOid())
-    return true;
 
+  // TODO(eroman): Make this more complete.
   return false;
 }
 
@@ -135,7 +120,7 @@
 //    The validity period for a certificate is the period of time from
 //    notBefore through notAfter, inclusive.
 void VerifyTimeValidity(const ParsedCertificate& cert,
-                        const der::GeneralizedTime& time,
+                        const der::GeneralizedTime time,
                         CertErrors* errors) {
   if (time < cert.tbs().validity_not_before)
     errors->AddError(kValidityFailedNotBefore);
@@ -231,545 +216,16 @@
   }
 }
 
-// Returns |true| if |policies| contains the OID |search_oid|.
-bool SetContains(const std::set<der::Input>& policies,
-                 const der::Input& search_oid) {
-  return policies.count(search_oid) > 0;
-}
-
-// Representation of RFC 5280's "valid_policy_tree", used to keep track of the
-// valid policies and policy re-mappings.
-//
-// ValidPolicyTree differs slightly from RFC 5280's description in that:
-//
-//  (1) It does not track "qualifier_set". This is not needed as it is not
-//      output by this implementation.
-//
-//  (2) It only stores the most recent level of the policy tree rather than
-//      the full tree of nodes.
-class ValidPolicyTree {
- public:
-  ValidPolicyTree() {}
-
-  struct Node {
-    // |root_policy| is equivalent to |valid_policy|, but in the domain of the
-    // caller.
-    //
-    // The reason for this distinction is the Policy Mappings extension.
-    //
-    // So whereas |valid_policy| is in the remapped domain defined by the
-    // issuing certificate, |root_policy| is in the fixed domain of the caller.
-    //
-    // OIDs in "user_initial_policy_set" and "user_constrained_policy_set" are
-    // directly comparable to |root_policy| values, but not necessarily to
-    // |valid_policy|.
-    //
-    // In terms of the valid policy tree, |root_policy| can be found by
-    // starting at the node's root ancestor, and finding the first node with a
-    // valid_policy other than anyPolicy. This is effectively the same process
-    // as used during policy tree intersection in RFC 5280 6.1.5.g.iii.1
-    der::Input root_policy;
-
-    // The same as RFC 5280's "valid_policy" variable.
-    der::Input valid_policy;
-
-    // The same as RFC 5280s "expected_policy_set" variable.
-    std::set<der::Input> expected_policy_set;
-
-    // Note that RFC 5280's "qualifier_set" is omitted.
-  };
-
-  // Level represents all the nodes at depth "i" in the valid_policy_tree.
-  using Level = std::vector<Node>;
-
-  // Initializes the ValidPolicyTree for the given "user_initial_policy_set".
-  //
-  // In RFC 5280, the valid_policy_tree is initialized to a root node at depth
-  // 0 of "anyPolicy"; the intersection with the "user_initial_policy_set" is
-  // done at the end (Wrap Up) as described in section 6.1.5 step g.
-  //
-  // Whereas in this implementation, the restriction on policies is added here,
-  // and intersecting the valid policy tree during Wrap Up is no longer needed.
-  //
-  // The final "user_constrained_policy_set" obtained will be the same. The
-  // advantages of this approach is simpler code.
-  void Init(const std::set<der::Input>& user_initial_policy_set) {
-    Clear();
-    for (const der::Input& policy_oid : user_initial_policy_set)
-      AddRootNode(policy_oid);
-  }
-
-  // Returns the current level (i.e. all nodes at depth i in the valid
-  // policy tree).
-  const Level& current_level() const { return current_level_; }
-  Level& current_level() { return current_level_; }
-
-  // In RFC 5280 valid_policy_tree may be set to null. That is represented here
-  // by emptiness.
-  bool IsNull() const { return current_level_.empty(); }
-  void SetNull() { Clear(); }
-
-  // This implementation keeps only the last level of the valid policy
-  // tree. Calling StartLevel() returns the nodes for the previous
-  // level, and starts a new level.
-  Level StartLevel() {
-    Level prev_level;
-    std::swap(prev_level, current_level_);
-    return prev_level;
-  }
-
-  // Gets the set of policies (in terms of root authority's policy domain) that
-  // are valid at the curent level of the policy tree.
-  //
-  // For example:
-  //
-  //  * If the valid policy tree was initialized with anyPolicy, then this
-  //    function returns what X.509 calls "authorities-constrained-policy-set".
-  //
-  //  * If the valid policy tree was instead initialized with the
-  //    "user-initial-policy_set", then this function returns what X.509
-  //    calls "user-constrained-policy-set"
-  //    ("authorities-constrained-policy-set" intersected with the
-  //    "user-initial-policy-set").
-  void GetValidRootPolicySet(std::set<der::Input>* policy_set) {
-    policy_set->clear();
-    for (const Node& node : current_level_)
-      policy_set->insert(node.root_policy);
-
-    // If the result includes anyPolicy, simplify it to a set of size 1.
-    if (policy_set->size() > 1 && SetContains(*policy_set, AnyPolicy()))
-      *policy_set = {AnyPolicy()};
-  }
-
-  // Adds a node |n| to the current level which is a child of |parent|
-  // such that:
-  //   * n.valid_policy = policy_oid
-  //   * n.expected_policy_set = {policy_oid}
-  void AddNode(const Node& parent, const der::Input& policy_oid) {
-    AddNodeWithExpectedPolicySet(parent, policy_oid, {policy_oid});
-  }
-
-  // Adds a node |n| to the current level which is a child of |parent|
-  // such that:
-  //   * n.valid_policy = policy_oid
-  //   * n.expected_policy_set = expected_policy_set
-  void AddNodeWithExpectedPolicySet(
-      const Node& parent,
-      const der::Input& policy_oid,
-      const std::set<der::Input>& expected_policy_set) {
-    Node new_node;
-    new_node.valid_policy = policy_oid;
-    new_node.expected_policy_set = expected_policy_set;
-
-    // Consider the root policy as the first policy other than anyPolicy (or
-    // anyPolicy if it hasn't been restricted yet).
-    new_node.root_policy =
-        (parent.root_policy == AnyPolicy()) ? policy_oid : parent.root_policy;
-
-    current_level_.push_back(std::move(new_node));
-  }
-
-  // Returns the first node having valid_policy == anyPolicy in |level|, or
-  // nullptr if there is none.
-  static const Node* FindAnyPolicyNode(const Level& level) {
-    for (const Node& node : level) {
-      if (node.valid_policy == AnyPolicy())
-        return &node;
-    }
-    return nullptr;
-  }
-
-  // Deletes all nodes |n| in |level| where |n.valid_policy| matches the given
-  // |valid_policy|. This may re-order the nodes in |level|.
-  static void DeleteNodesMatchingValidPolicy(const der::Input& valid_policy,
-                                             Level* level) {
-    // This works by swapping nodes to the end of the vector, and then doing a
-    // single resize to delete them all.
-    auto cur = level->begin();
-    auto end = level->end();
-    while (cur != end) {
-      bool should_delete_node = cur->valid_policy == valid_policy;
-      if (should_delete_node) {
-        end = std::prev(end);
-        std::iter_swap(cur, end);
-      } else {
-        ++cur;
-      }
-    }
-    level->erase(end, level->end());
-  }
-
- private:
-  // Deletes all nodes in the valid policy tree.
-  void Clear() { current_level_.clear(); }
-
-  // Adds a node to the current level for OID |policy_oid|. The current level
-  // is assumed to be the root level.
-  void AddRootNode(const der::Input& policy_oid) {
-    Node new_node;
-    new_node.root_policy = policy_oid;
-    new_node.valid_policy = policy_oid;
-    new_node.expected_policy_set = {policy_oid};
-    current_level_.push_back(std::move(new_node));
-  }
-
-  Level current_level_;
-
-  DISALLOW_COPY_AND_ASSIGN(ValidPolicyTree);
-};
-
-// Class that encapsulates the state variables used by certificate path
-// validation.
-class PathVerifier {
- public:
-  // Same parameters and meaning as VerifyCertificateChain().
-  void Run(const ParsedCertificateList& certs,
-           const CertificateTrust& last_cert_trust,
-           const SignaturePolicy* signature_policy,
-           const der::GeneralizedTime& time,
-           KeyPurpose required_key_purpose,
-           InitialExplicitPolicy initial_explicit_policy,
-           const std::set<der::Input>& user_initial_policy_set,
-           InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
-           InitialAnyPolicyInhibit initial_any_policy_inhibit,
-           std::set<der::Input>* user_constrained_policy_set,
-           CertPathErrors* errors);
-
- private:
-  // Verifies and updates the valid policies. This corresponds with RFC 5280
-  // section 6.1.3 steps d-f.
-  void VerifyPolicies(const ParsedCertificate& cert,
-                      bool is_target_cert,
-                      CertErrors* errors);
-
-  // Applies the policy mappings. This corresponds with RFC 5280 section 6.1.4
-  // steps a-b.
-  void VerifyPolicyMappings(const ParsedCertificate& cert, CertErrors* errors);
-
-  // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
-  // Processing" procedure.
-  void BasicCertificateProcessing(const ParsedCertificate& cert,
-                                  bool is_target_cert,
-                                  const SignaturePolicy* signature_policy,
-                                  const der::GeneralizedTime& time,
-                                  KeyPurpose required_key_purpose,
-                                  CertErrors* errors);
-
-  // This function corresponds to RFC 5280 section 6.1.4's "Preparation for
-  // Certificate i+1" procedure. |cert| is expected to be an intermediate.
-  void PrepareForNextCertificate(const ParsedCertificate& cert,
-                                 CertErrors* errors);
-
-  // This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up
-  // Procedure". It does processing for the final certificate (the target cert).
-  void WrapUp(const ParsedCertificate& cert, CertErrors* errors);
-
-  // Enforces trust anchor constraints compatibile with RFC 5937.
-  //
-  // Note that the anchor constraints are encoded via the attached certificate
-  // itself.
-  void ApplyTrustAnchorConstraints(const ParsedCertificate& cert,
-                                   KeyPurpose required_key_purpose,
-                                   CertErrors* errors);
-
-  // Initializes the path validation algorithm given anchor constraints. This
-  // follows the description in RFC 5937
-  void ProcessRootCertificate(const ParsedCertificate& cert,
-                              const CertificateTrust& trust,
-                              KeyPurpose required_key_purpose,
-                              CertErrors* errors);
-
-  ValidPolicyTree valid_policy_tree_;
-
-  // Will contain a NameConstraints for each previous cert in the chain which
-  // had nameConstraints. This corresponds to the permitted_subtrees and
-  // excluded_subtrees state variables from RFC 5280.
-  std::vector<const NameConstraints*> name_constraints_list_;
-
-  // |explicit_policy_| corresponds with the same named variable from RFC 5280
-  // section 6.1.2:
-  //
-  //   explicit_policy:  an integer that indicates if a non-NULL
-  //   valid_policy_tree is required.  The integer indicates the
-  //   number of non-self-issued certificates to be processed before
-  //   this requirement is imposed.  Once set, this variable may be
-  //   decreased, but may not be increased.  That is, if a certificate in the
-  //   path requires a non-NULL valid_policy_tree, a later certificate cannot
-  //   remove this requirement.  If initial-explicit-policy is set, then the
-  //   initial value is 0, otherwise the initial value is n+1.
-  size_t explicit_policy_;
-
-  // |inhibit_any_policy_| corresponds with the same named variable from RFC
-  // 5280 section 6.1.2:
-  //
-  //   inhibit_anyPolicy:  an integer that indicates whether the
-  //   anyPolicy policy identifier is considered a match.  The
-  //   integer indicates the number of non-self-issued certificates
-  //   to be processed before the anyPolicy OID, if asserted in a
-  //   certificate other than an intermediate self-issued
-  //   certificate, is ignored.  Once set, this variable may be
-  //   decreased, but may not be increased.  That is, if a
-  //   certificate in the path inhibits processing of anyPolicy, a
-  //   later certificate cannot permit it.  If initial-any-policy-
-  //   inhibit is set, then the initial value is 0, otherwise the
-  //   initial value is n+1.
-  size_t inhibit_any_policy_;
-
-  // |policy_mapping_| corresponds with the same named variable from RFC 5280
-  // section 6.1.2:
-  //
-  //   policy_mapping:  an integer that indicates if policy mapping
-  //   is permitted.  The integer indicates the number of non-self-
-  //   issued certificates to be processed before policy mapping is
-  //   inhibited.  Once set, this variable may be decreased, but may
-  //   not be increased.  That is, if a certificate in the path
-  //   specifies that policy mapping is not permitted, it cannot be
-  //   overridden by a later certificate.  If initial-policy-
-  //   mapping-inhibit is set, then the initial value is 0,
-  //   otherwise the initial value is n+1.
-  size_t policy_mapping_;
-
-  // |working_spki_| is an amalgamation of 3 separate variables from RFC 5280:
-  //    * working_public_key
-  //    * working_public_key_algorithm
-  //    * working_public_key_parameters
-  //
-  // They are combined for simplicity since the signature verification takes an
-  // SPKI, and the parameter inheritence is not applicable for the supported
-  // key types.
-  //
-  // An approximate explanation of |working_spki| is this description from RFC
-  // 5280 section 6.1.2:
-  //
-  //    working_public_key:  the public key used to verify the
-  //    signature of a certificate.
-  der::Input working_spki_;
-
-  // |working_normalized_issuer_name_| is the normalized value of the
-  // working_issuer_name variable in RFC 5280 section 6.1.2:
-  //
-  //    working_issuer_name:  the issuer distinguished name expected
-  //    in the next certificate in the chain.
-  der::Input working_normalized_issuer_name_;
-
-  // |max_path_length_| corresponds with the same named variable in RFC 5280
-  // section 6.1.2.
-  //
-  //    max_path_length:  this integer is initialized to n, is
-  //    decremented for each non-self-issued certificate in the path,
-  //    and may be reduced to the value in the path length constraint
-  //    field within the basic constraints extension of a CA
-  //    certificate.
-  size_t max_path_length_;
-};
-
-void PathVerifier::VerifyPolicies(const ParsedCertificate& cert,
-                                  bool is_target_cert,
-                                  CertErrors* errors) {
-  // From RFC 5280 section 6.1.3:
-  //
-  //  (d)  If the certificate policies extension is present in the
-  //       certificate and the valid_policy_tree is not NULL, process
-  //       the policy information by performing the following steps in
-  //       order:
-  if (cert.has_policy_oids() && !valid_policy_tree_.IsNull()) {
-    ValidPolicyTree::Level previous_level = valid_policy_tree_.StartLevel();
-
-    // Identify if there was a node with valid_policy == anyPolicy at depth i-1.
-    const ValidPolicyTree::Node* any_policy_node_prev_level =
-        ValidPolicyTree::FindAnyPolicyNode(previous_level);
-
-    //     (1)  For each policy P not equal to anyPolicy in the
-    //          certificate policies extension, let P-OID denote the OID
-    //          for policy P and P-Q denote the qualifier set for policy
-    //          P.  Perform the following steps in order:
-    bool cert_has_any_policy = false;
-    for (const der::Input& p_oid : cert.policy_oids()) {
-      if (p_oid == AnyPolicy()) {
-        cert_has_any_policy = true;
-        continue;
-      }
-
-      //        (i)   For each node of depth i-1 in the valid_policy_tree
-      //              where P-OID is in the expected_policy_set, create a
-      //              child node as follows: set the valid_policy to P-OID,
-      //              set the qualifier_set to P-Q, and set the
-      //              expected_policy_set to {P-OID}.
-      bool found_match = false;
-      for (const ValidPolicyTree::Node& prev_node : previous_level) {
-        if (SetContains(prev_node.expected_policy_set, p_oid)) {
-          valid_policy_tree_.AddNode(prev_node, p_oid);
-          found_match = true;
-        }
-      }
-
-      //        (ii)  If there was no match in step (i) and the
-      //              valid_policy_tree includes a node of depth i-1 with
-      //              the valid_policy anyPolicy, generate a child node with
-      //              the following values: set the valid_policy to P-OID,
-      //              set the qualifier_set to P-Q, and set the
-      //              expected_policy_set to  {P-OID}.
-      if (!found_match && any_policy_node_prev_level)
-        valid_policy_tree_.AddNode(*any_policy_node_prev_level, p_oid);
-    }
-
-    //     (2)  If the certificate policies extension includes the policy
-    //          anyPolicy with the qualifier set AP-Q and either (a)
-    //          inhibit_anyPolicy is greater than 0 or (b) i<n and the
-    //          certificate is self-issued, then:
-    //
-    //          For each node in the valid_policy_tree of depth i-1, for
-    //          each value in the expected_policy_set (including
-    //          anyPolicy) that does not appear in a child node, create a
-    //          child node with the following values: set the valid_policy
-    //          to the value from the expected_policy_set in the parent
-    //          node, set the qualifier_set to AP-Q, and set the
-    //          expected_policy_set to the value in the valid_policy from
-    //          this node.
-    if (cert_has_any_policy && ((inhibit_any_policy_ > 0) ||
-                                (!is_target_cert && IsSelfIssued(cert)))) {
-      // Keep track of the existing policies at depth i.
-      std::set<der::Input> child_node_policies;
-      for (const ValidPolicyTree::Node& node :
-           valid_policy_tree_.current_level())
-        child_node_policies.insert(node.valid_policy);
-
-      for (const ValidPolicyTree::Node& prev_node : previous_level) {
-        for (const der::Input& expected_policy :
-             prev_node.expected_policy_set) {
-          if (!SetContains(child_node_policies, expected_policy)) {
-            child_node_policies.insert(expected_policy);
-            valid_policy_tree_.AddNode(prev_node, expected_policy);
-          }
-        }
-      }
-    }
-
-    //     (3)  If there is a node in the valid_policy_tree of depth i-1
-    //          or less without any child nodes, delete that node.  Repeat
-    //          this step until there are no nodes of depth i-1 or less
-    //          without children.
-    //
-    // Nothing needs to be done for this step, since this implementation only
-    // stores the nodes at depth i, and the entire level has already been
-    // calculated.
-  }
-
-  //  (e)  If the certificate policies extension is not present, set the
-  //       valid_policy_tree to NULL.
-  if (!cert.has_policy_oids())
-    valid_policy_tree_.SetNull();
-
-  //  (f)  Verify that either explicit_policy is greater than 0 or the
-  //       valid_policy_tree is not equal to NULL;
-  if (!((explicit_policy_ > 0) || !valid_policy_tree_.IsNull()))
-    errors->AddError(kNoValidPolicy);
-}
-
-void PathVerifier::VerifyPolicyMappings(const ParsedCertificate& cert,
-                                        CertErrors* errors) {
-  if (!cert.has_policy_mappings())
-    return;
-
-  // From RFC 5280 section 6.1.4:
-  //
-  //  (a)  If a policy mappings extension is present, verify that the
-  //       special value anyPolicy does not appear as an
-  //       issuerDomainPolicy or a subjectDomainPolicy.
-  for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
-    if (mapping.issuer_domain_policy == AnyPolicy() ||
-        mapping.subject_domain_policy == AnyPolicy()) {
-      // Because this implementation continues processing certificates after
-      // this error, clear the valid policy tree to ensure the
-      // "user_constrained_policy_set" output upon failure is empty.
-      valid_policy_tree_.SetNull();
-      errors->AddError(kPolicyMappingAnyPolicy);
-    }
-  }
-
-  //  (b)  If a policy mappings extension is present, then for each
-  //       issuerDomainPolicy ID-P in the policy mappings extension:
-  //
-  //     (1)  If the policy_mapping variable is greater than 0, for each
-  //          node in the valid_policy_tree of depth i where ID-P is the
-  //          valid_policy, set expected_policy_set to the set of
-  //          subjectDomainPolicy values that are specified as
-  //          equivalent to ID-P by the policy mappings extension.
-  //
-  //          If no node of depth i in the valid_policy_tree has a
-  //          valid_policy of ID-P but there is a node of depth i with a
-  //          valid_policy of anyPolicy, then generate a child node of
-  //          the node of depth i-1 that has a valid_policy of anyPolicy
-  //          as follows:
-  //
-  //        (i)    set the valid_policy to ID-P;
-  //
-  //        (ii)   set the qualifier_set to the qualifier set of the
-  //               policy anyPolicy in the certificate policies
-  //               extension of certificate i; and
-  //
-  //        (iii)  set the expected_policy_set to the set of
-  //               subjectDomainPolicy values that are specified as
-  //               equivalent to ID-P by the policy mappings extension.
-  //
-  if (policy_mapping_ > 0) {
-    const ValidPolicyTree::Node* any_policy_node =
-        ValidPolicyTree::FindAnyPolicyNode(valid_policy_tree_.current_level());
-
-    // Group mappings by issuer domain policy.
-    std::map<der::Input, std::set<der::Input>> mappings;
-    for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
-      mappings[mapping.issuer_domain_policy].insert(
-          mapping.subject_domain_policy);
-    }
-
-    for (const auto& it : mappings) {
-      const der::Input& issuer_domain_policy = it.first;
-      const std::set<der::Input>& subject_domain_policies = it.second;
-      bool found_node = false;
-
-      for (ValidPolicyTree::Node& node : valid_policy_tree_.current_level()) {
-        if (node.valid_policy == issuer_domain_policy) {
-          node.expected_policy_set = subject_domain_policies;
-          found_node = true;
-        }
-      }
-
-      if (!found_node && any_policy_node) {
-        valid_policy_tree_.AddNodeWithExpectedPolicySet(
-            *any_policy_node, issuer_domain_policy, subject_domain_policies);
-      }
-    }
-  }
-
-  //  (b)  If a policy mappings extension is present, then for each
-  //       issuerDomainPolicy ID-P in the policy mappings extension:
-  //
-  //  ...
-  //
-  //     (2)  If the policy_mapping variable is equal to 0:
-  //
-  //        (i)    delete each node of depth i in the valid_policy_tree
-  //               where ID-P is the valid_policy.
-  //
-  //        (ii)   If there is a node in the valid_policy_tree of depth
-  //               i-1 or less without any child nodes, delete that
-  //               node.  Repeat this step until there are no nodes of
-  //               depth i-1 or less without children.
-  if (policy_mapping_ == 0) {
-    for (const ParsedPolicyMapping& mapping : cert.policy_mappings()) {
-      ValidPolicyTree::DeleteNodesMatchingValidPolicy(
-          mapping.issuer_domain_policy, &valid_policy_tree_.current_level());
-    }
-  }
-}
-
-void PathVerifier::BasicCertificateProcessing(
+// This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
+// Processing" procedure.
+void BasicCertificateProcessing(
     const ParsedCertificate& cert,
     bool is_target_cert,
     const SignaturePolicy* signature_policy,
     const der::GeneralizedTime& time,
-    KeyPurpose required_key_purpose,
+    const der::Input& working_spki,
+    const der::Input& working_normalized_issuer_name,
+    const std::vector<const NameConstraints*>& name_constraints_list,
     CertErrors* errors) {
   // Check that the signature algorithms in Certificate vs TBSCertificate
   // match. This isn't part of RFC 5280 section 6.1.3, but is mandated by
@@ -779,7 +235,7 @@
   // Verify the digital signature using the previous certificate's key (RFC
   // 5280 section 6.1.3 step a.1).
   if (!VerifySignedData(cert.signature_algorithm(), cert.tbs_certificate_tlv(),
-                        cert.signature_value(), working_spki_, signature_policy,
+                        cert.signature_value(), working_spki, signature_policy,
                         errors)) {
     errors->AddError(kVerifySignedDataFailed);
   }
@@ -793,15 +249,15 @@
 
   // Verify the certificate's issuer name matches the issuing certificate's
   // subject name. (RFC 5280 section 6.1.3 step a.4)
-  if (cert.normalized_issuer() != working_normalized_issuer_name_)
+  if (cert.normalized_issuer() != working_normalized_issuer_name)
     errors->AddError(kSubjectDoesNotMatchIssuer);
 
   // Name constraints (RFC 5280 section 6.1.3 step b & c)
   // If certificate i is self-issued and it is not the final certificate in the
   // path, skip this step for certificate i.
-  if (!name_constraints_list_.empty() &&
+  if (!name_constraints_list.empty() &&
       (!IsSelfIssued(cert) || is_target_cert)) {
-    for (const NameConstraints* nc : name_constraints_list_) {
+    for (const NameConstraints* nc : name_constraints_list) {
       if (!nc->IsPermittedCert(cert.normalized_subject(),
                                cert.subject_alt_names())) {
         errors->AddError(kNotPermittedByNameConstraints);
@@ -809,30 +265,31 @@
     }
   }
 
-  // RFC 5280 section 6.1.3 step d - f.
-  VerifyPolicies(cert, is_target_cert, errors);
-
-  // The key purpose is checked not just for the end-entity certificate, but
-  // also interpreted as a constraint when it appears in intermediates. This
-  // goes beyond what RFC 5280 describes, but is the de-facto standard. See
-  // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questions
-  VerifyExtendedKeyUsage(cert, required_key_purpose, errors);
+  // TODO(eroman): Steps d-f are omitted, as policy constraints are not yet
+  // implemented.
 }
 
-void PathVerifier::PrepareForNextCertificate(const ParsedCertificate& cert,
-                                             CertErrors* errors) {
-  // RFC 5280 section 6.1.4 step a-b
-  VerifyPolicyMappings(cert, errors);
+// This function corresponds to RFC 5280 section 6.1.4's "Preparation for
+// Certificate i+1" procedure. |cert| is expected to be an intermediate.
+void PrepareForNextCertificate(
+    const ParsedCertificate& cert,
+    size_t* max_path_length_ptr,
+    der::Input* working_spki,
+    der::Input* working_normalized_issuer_name,
+    std::vector<const NameConstraints*>* name_constraints_list,
+    CertErrors* errors) {
+  // TODO(crbug.com/634456): Steps a-b are omitted, as policy mappings are not
+  // yet implemented.
 
   // From RFC 5280 section 6.1.4 step c:
   //
   //    Assign the certificate subject name to working_normalized_issuer_name.
-  working_normalized_issuer_name_ = cert.normalized_subject();
+  *working_normalized_issuer_name = cert.normalized_subject();
 
   // From RFC 5280 section 6.1.4 step d:
   //
   //    Assign the certificate subjectPublicKey to working_public_key.
-  working_spki_ = cert.tbs().spki_tlv;
+  *working_spki = cert.tbs().spki_tlv;
 
   // Note that steps e and f are omitted as they are handled by
   // the assignment to |working_spki| above. See the definition
@@ -840,53 +297,10 @@
 
   // From RFC 5280 section 6.1.4 step g:
   if (cert.has_name_constraints())
-    name_constraints_list_.push_back(&cert.name_constraints());
+    name_constraints_list->push_back(&cert.name_constraints());
 
-  //     (h)  If certificate i is not self-issued:
-  if (!IsSelfIssued(cert)) {
-    //         (1)  If explicit_policy is not 0, decrement explicit_policy by
-    //              1.
-    if (explicit_policy_ > 0)
-      explicit_policy_ -= 1;
-
-    //         (2)  If policy_mapping is not 0, decrement policy_mapping by 1.
-    if (policy_mapping_ > 0)
-      policy_mapping_ -= 1;
-
-    //         (3)  If inhibit_anyPolicy is not 0, decrement inhibit_anyPolicy
-    //              by 1.
-    if (inhibit_any_policy_ > 0)
-      inhibit_any_policy_ -= 1;
-  }
-
-  //      (i)  If a policy constraints extension is included in the
-  //           certificate, modify the explicit_policy and policy_mapping
-  //           state variables as follows:
-  if (cert.has_policy_constraints()) {
-    //         (1)  If requireExplicitPolicy is present and is less than
-    //              explicit_policy, set explicit_policy to the value of
-    //              requireExplicitPolicy.
-    if (cert.policy_constraints().has_require_explicit_policy &&
-        cert.policy_constraints().require_explicit_policy < explicit_policy_) {
-      explicit_policy_ = cert.policy_constraints().require_explicit_policy;
-    }
-
-    //         (2)  If inhibitPolicyMapping is present and is less than
-    //              policy_mapping, set policy_mapping to the value of
-    //              inhibitPolicyMapping.
-    if (cert.policy_constraints().has_inhibit_policy_mapping &&
-        cert.policy_constraints().inhibit_policy_mapping < policy_mapping_) {
-      policy_mapping_ = cert.policy_constraints().inhibit_policy_mapping;
-    }
-  }
-
-  //      (j)  If the inhibitAnyPolicy extension is included in the
-  //           certificate and is less than inhibit_anyPolicy, set
-  //           inhibit_anyPolicy to the value of inhibitAnyPolicy.
-  if (cert.has_inhibit_any_policy() &&
-      cert.inhibit_any_policy() < inhibit_any_policy_) {
-    inhibit_any_policy_ = cert.inhibit_any_policy();
-  }
+  // TODO(eroman): Steps h-j are omitted as policy
+  // constraints/mappings/inhibitAnyPolicy are not yet implemented.
 
   // From RFC 5280 section 6.1.4 step k:
   //
@@ -913,10 +327,10 @@
   //    max_path_length is greater than zero and decrement
   //    max_path_length by 1.
   if (!IsSelfIssued(cert)) {
-    if (max_path_length_ == 0) {
+    if (*max_path_length_ptr == 0) {
       errors->AddError(kMaxPathLengthViolated);
     } else {
-      --max_path_length_;
+      --(*max_path_length_ptr);
     }
   }
 
@@ -926,8 +340,8 @@
   //    less than max_path_length, set max_path_length to the value
   //    of pathLenConstraint.
   if (cert.has_basic_constraints() && cert.basic_constraints().has_path_len &&
-      cert.basic_constraints().path_len < max_path_length_) {
-    max_path_length_ = cert.basic_constraints().path_len;
+      cert.basic_constraints().path_len < *max_path_length_ptr) {
+    *max_path_length_ptr = cert.basic_constraints().path_len;
   }
 
   // From RFC 5280 section 6.1.4 step n:
@@ -994,22 +408,13 @@
   }
 }
 
-void PathVerifier::WrapUp(const ParsedCertificate& cert, CertErrors* errors) {
-  // From RFC 5280 section 6.1.5:
-  //      (a)  If explicit_policy is not 0, decrement explicit_policy by 1.
-  if (explicit_policy_ > 0)
-    explicit_policy_ -= 1;
+// This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up Procedure".
+// It does processing for the final certificate (the target cert).
+void WrapUp(const ParsedCertificate& cert, CertErrors* errors) {
+  // TODO(crbug.com/634452): Steps a-b are omitted as policy constraints are not
+  // yet implemented.
 
-  //      (b)  If a policy constraints extension is included in the
-  //           certificate and requireExplicitPolicy is present and has a
-  //           value of 0, set the explicit_policy state variable to 0.
-  if (cert.has_policy_constraints() &&
-      cert.policy_constraints().has_require_explicit_policy &&
-      cert.policy_constraints().require_explicit_policy == 0) {
-    explicit_policy_ = 0;
-  }
-
-  // Note step c-e are omitted as the verification function does
+  // Note step c-e are omitted the verification function does
   // not output the working public key.
 
   // From RFC 5280 section 6.1.5 step f:
@@ -1023,24 +428,24 @@
   // directly match the procedures in RFC 5280's section 6.1.
   VerifyNoUnconsumedCriticalExtensions(cert, errors);
 
-  // RFC 5280 section 6.1.5 step g is skipped, as the intersection of valid
-  // policies was computed during previous steps.
-  //
-  //    If either (1) the value of explicit_policy variable is greater than
-  //    zero or (2) the valid_policy_tree is not NULL, then path processing
-  //   has succeeded.
-  if (!(explicit_policy_ > 0 || !valid_policy_tree_.IsNull())) {
-    errors->AddError(kNoValidPolicy);
-  }
+  // TODO(eroman): Step g is omitted, as policy constraints are not yet
+  // implemented.
 
   // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure",
   // however is implied by RFC 5280 section 4.2.1.9.
   VerifyTargetCertHasConsistentCaBits(cert, errors);
 }
 
-void PathVerifier::ApplyTrustAnchorConstraints(const ParsedCertificate& cert,
-                                               KeyPurpose required_key_purpose,
-                                               CertErrors* errors) {
+// Enforces trust anchor constraints compatibile with RFC 5937.
+//
+// Note that the anchor constraints are encoded via the attached certificate
+// itself.
+void ApplyTrustAnchorConstraints(
+    const ParsedCertificate& cert,
+    KeyPurpose required_key_purpose,
+    size_t* max_path_length_ptr,
+    std::vector<const NameConstraints*>* name_constraints_list,
+    CertErrors* errors) {
   // This is not part of RFC 5937 nor RFC 5280, but matches the EKU handling
   // done for intermediates (described in Web PKI's Baseline Requirements).
   VerifyExtendedKeyUsage(cert, required_key_purpose, errors);
@@ -1049,7 +454,7 @@
 
   // Initialize name constraints initial-permitted/excluded-subtrees.
   if (cert.has_name_constraints())
-    name_constraints_list_.push_back(&cert.name_constraints());
+    name_constraints_list->push_back(&cert.name_constraints());
 
   // TODO(eroman): Initialize user-initial-policy-set based on anchor
   // constraints.
@@ -1072,7 +477,7 @@
   // NOTE: RFC 5937 does not say to enforce the CA=true part of basic
   // constraints.
   if (cert.has_basic_constraints() && cert.basic_constraints().has_path_len)
-    max_path_length_ = cert.basic_constraints().path_len;
+    *max_path_length_ptr = cert.basic_constraints().path_len;
 
   // From RFC 5937 section 2:
   //
@@ -1082,15 +487,22 @@
   VerifyNoUnconsumedCriticalExtensions(cert, errors);
 }
 
-void PathVerifier::ProcessRootCertificate(const ParsedCertificate& cert,
-                                          const CertificateTrust& trust,
-                                          KeyPurpose required_key_purpose,
-                                          CertErrors* errors) {
+// Initializes the path validation algorithm given anchor constraints. This
+// follows the description in RFC 5937
+void ProcessRootCertificate(
+    const ParsedCertificate& cert,
+    const CertificateTrust& trust,
+    KeyPurpose required_key_purpose,
+    size_t* max_path_length_ptr,
+    std::vector<const NameConstraints*>* name_constraints_list,
+    der::Input* working_spki,
+    der::Input* working_normalized_issuer_name,
+    CertErrors* errors) {
   // Use the certificate's SPKI and subject when verifying the next certificate.
   // Note this is initialized even in the case of untrusted roots (they already
   // emit an error for the distrust).
-  working_spki_ = cert.tbs().spki_tlv;
-  working_normalized_issuer_name_ = cert.normalized_subject();
+  *working_spki = cert.tbs().spki_tlv;
+  *working_normalized_issuer_name = cert.normalized_subject();
 
   switch (trust.type) {
     case CertificateTrustType::UNSPECIFIED:
@@ -1105,26 +517,24 @@
     case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
       // If the trust anchor has constraints, enforce them.
       if (trust.type == CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS) {
-        ApplyTrustAnchorConstraints(cert, required_key_purpose, errors);
+        ApplyTrustAnchorConstraints(cert, required_key_purpose,
+                                    max_path_length_ptr, name_constraints_list,
+                                    errors);
       }
       break;
   }
 }
 
-void PathVerifier::Run(
-    const ParsedCertificateList& certs,
-    const CertificateTrust& last_cert_trust,
-    const SignaturePolicy* signature_policy,
-    const der::GeneralizedTime& time,
-    KeyPurpose required_key_purpose,
-    InitialExplicitPolicy initial_explicit_policy,
-    const std::set<der::Input>& user_initial_policy_set,
-    InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
-    InitialAnyPolicyInhibit initial_any_policy_inhibit,
-    std::set<der::Input>* user_constrained_policy_set,
-    CertPathErrors* errors) {
-  // This implementation is structured to mimic the description of certificate
-  // path verification given by RFC 5280 section 6.1.
+}  // namespace
+
+// This implementation is structured to mimic the description of certificate
+// path verification given by RFC 5280 section 6.1.
+void VerifyCertificateChain(const ParsedCertificateList& certs,
+                            const CertificateTrust& last_cert_trust,
+                            const SignaturePolicy* signature_policy,
+                            const der::GeneralizedTime& time,
+                            KeyPurpose required_key_purpose,
+                            CertPathErrors* errors) {
   DCHECK(signature_policy);
   DCHECK(errors);
 
@@ -1141,48 +551,50 @@
     return;
   }
 
-  // RFC 5280's "n" variable is the length of the path, which does not count
-  // the trust anchor. (Although in practice it doesn't really change behaviors
-  // if n is used in place of n+1).
-  const size_t n = certs.size() - 1;
+  // Will contain a NameConstraints for each previous cert in the chain which
+  // had nameConstraints. This corresponds to the permitted_subtrees and
+  // excluded_subtrees state variables from RFC 5280.
+  std::vector<const NameConstraints*> name_constraints_list;
 
-  valid_policy_tree_.Init(user_initial_policy_set);
-
-  // RFC 5280 section section 6.1.2:
+  // |working_spki| is an amalgamation of 3 separate variables from RFC 5280:
+  //    * working_public_key
+  //    * working_public_key_algorithm
+  //    * working_public_key_parameters
   //
-  // If initial-explicit-policy is set, then the initial value
-  // [of explicit_policy] is 0, otherwise the initial value is n+1.
-  explicit_policy_ =
-      initial_explicit_policy == InitialExplicitPolicy::kTrue ? 0 : n + 1;
-
-  // RFC 5280 section section 6.1.2:
+  // They are combined for simplicity since the signature verification takes an
+  // SPKI, and the parameter inheritence is not applicable for the supported
+  // key types.
   //
-  // If initial-any-policy-inhibit is set, then the initial value
-  // [of inhibit_anyPolicy] is 0, otherwise the initial value is n+1.
-  inhibit_any_policy_ =
-      initial_any_policy_inhibit == InitialAnyPolicyInhibit::kTrue ? 0 : n + 1;
-
-  // RFC 5280 section section 6.1.2:
+  // An approximate explanation of |working_spki| is this description from RFC
+  // 5280 section 6.1.2:
   //
-  // If initial-policy-mapping-inhibit is set, then the initial value
-  // [of policy_mapping] is 0, otherwise the initial value is n+1.
-  policy_mapping_ =
-      initial_policy_mapping_inhibit == InitialPolicyMappingInhibit::kTrue
-          ? 0
-          : n + 1;
+  //    working_public_key:  the public key used to verify the
+  //    signature of a certificate.
+  der::Input working_spki;
 
-  // RFC 5280 section section 6.1.2:
+  // |working_normalized_issuer_name| is the normalized value of the
+  // working_issuer_name variable in RFC 5280 section 6.1.2:
   //
-  // max_path_length:  this integer is initialized to n, ...
-  max_path_length_ = n;
+  //    working_issuer_name:  the issuer distinguished name expected
+  //    in the next certificate in the chain.
+  der::Input working_normalized_issuer_name;
+
+  // |max_path_length| corresponds with the same named variable in RFC 5280
+  // section 6.1.2:
+  //
+  //    max_path_length:  this integer is initialized to n, is
+  //    decremented for each non-self-issued certificate in the path,
+  //    and may be reduced to the value in the path length constraint
+  //    field within the basic constraints extension of a CA
+  //    certificate.
+  size_t max_path_length = certs.size();
 
   // Iterate over all the certificates in the reverse direction: starting from
   // the root certificate and progressing towards the target certificate.
   //
-  //   * i=0  :  Root certificate (i.e. trust anchor)
-  //   * i=1  :  Certificate issued by root
-  //   * i=x  :  Certificate i=x is issued by certificate i=x-1
-  //   * i=n  :  Target certificate.
+  //   * i=0               :  Root certificate (i.e. trust anchor)
+  //   * i=1               :  Certificated signed by the root certificate
+  //   * i=certs.size()-1  :  Target certificate.
   for (size_t i = 0; i < certs.size(); ++i) {
     const size_t index_into_certs = certs.size() - i - 1;
 
@@ -1200,6 +612,8 @@
 
     if (is_root_cert) {
       ProcessRootCertificate(cert, last_cert_trust, required_key_purpose,
+                             &max_path_length, &name_constraints_list,
+                             &working_spki, &working_normalized_issuer_name,
                              cert_errors);
 
       // Don't do any other checks for root certificates.
@@ -1212,45 +626,28 @@
     //     - Then run "Wrap up"
     //     - Otherwise run "Prepare for Next cert"
     BasicCertificateProcessing(cert, is_target_cert, signature_policy, time,
-                               required_key_purpose, cert_errors);
+                               working_spki, working_normalized_issuer_name,
+                               name_constraints_list, cert_errors);
+
+    // The key purpose is checked not just for the end-entity certificate, but
+    // also interpreted as a constraint when it appears in intermediates. This
+    // goes beyond what RFC 5280 describes, but is the de-facto standard. See
+    // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questions
+    VerifyExtendedKeyUsage(cert, required_key_purpose, cert_errors);
+
     if (!is_target_cert) {
-      PrepareForNextCertificate(cert, cert_errors);
+      PrepareForNextCertificate(cert, &max_path_length, &working_spki,
+                                &working_normalized_issuer_name,
+                                &name_constraints_list, cert_errors);
     } else {
       WrapUp(cert, cert_errors);
     }
   }
 
-  if (user_constrained_policy_set) {
-    // valid_policy_tree_ already contains the intersection of valid policies
-    // with user_initial_policy_set.
-    valid_policy_tree_.GetValidRootPolicySet(user_constrained_policy_set);
-  }
-
   // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1:
   //
   //    A certificate MUST NOT appear more than once in a prospective
   //    certification path.
 }
 
-}  // namespace
-
-void VerifyCertificateChain(
-    const ParsedCertificateList& certs,
-    const CertificateTrust& last_cert_trust,
-    const SignaturePolicy* signature_policy,
-    const der::GeneralizedTime& time,
-    KeyPurpose required_key_purpose,
-    InitialExplicitPolicy initial_explicit_policy,
-    const std::set<der::Input>& user_initial_policy_set,
-    InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
-    InitialAnyPolicyInhibit initial_any_policy_inhibit,
-    std::set<der::Input>* user_constrained_policy_set,
-    CertPathErrors* errors) {
-  PathVerifier verifier;
-  verifier.Run(certs, last_cert_trust, signature_policy, time,
-               required_key_purpose, initial_explicit_policy,
-               user_initial_policy_set, initial_policy_mapping_inhibit,
-               initial_any_policy_inhibit, user_constrained_policy_set, errors);
-}
-
 }  // namespace net
diff --git a/net/cert/internal/verify_certificate_chain.h b/net/cert/internal/verify_certificate_chain.h
index a8b2917..8d1f044 100644
--- a/net/cert/internal/verify_certificate_chain.h
+++ b/net/cert/internal/verify_certificate_chain.h
@@ -5,7 +5,7 @@
 #ifndef NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_H_
 #define NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_H_
 
-#include <set>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
@@ -30,160 +30,72 @@
   CLIENT_AUTH,
 };
 
-enum class InitialExplicitPolicy {
-  kFalse,
-  kTrue,
-};
-
-enum class InitialPolicyMappingInhibit {
-  kFalse,
-  kTrue,
-};
-
-enum class InitialAnyPolicyInhibit {
-  kFalse,
-  kTrue,
-};
-
 // VerifyCertificateChain() verifies an ordered certificate path in accordance
-// with RFC 5280's "Certification Path Validation" algorithm (section 6).
+// with RFC 5280 (with some modifications [1]).
 //
-// -----------------------------------------
-// Deviations from RFC 5280
-// -----------------------------------------
+// [1] Deviations from RFC 5280:
 //
-//   * If Extended Key Usage appears on intermediates, it is treated as
-//     a restriction on subordinate certificates.
+//   * If Extended Key Usage appears on intermediates it is treated as a
+//     restriction on subordinate certificates.
 //
-// -----------------------------------------
-// Additional responsibilities of the caller
-// -----------------------------------------
-//
-// After successful path verification, the caller is responsible for
-// subsequently checking:
+// The caller is responsible for additionally checking:
 //
 //  * The end-entity's KeyUsage before using its SPKI.
-//  * The end-entity's name/subjectAltName. Name constraints from intermediates
-//    will have already been applied, so it is sufficient to check the
-//    end-entity for a match.
+//  * The end-entity's name/subjectAltName (note that name constraints from
+//    intermediates will have already been applied, so just need to check
+//    the end-entity for a match).
+//  * Policies
+//
+// WARNING: This implementation is in progress, and is currently incomplete.
+// Consult an OWNER before using it.
+//
+// TODO(eroman): Take a CertPath instead of ParsedCertificateList +
+//               TrustAnchor.
 //
 // ---------
 // Inputs
 // ---------
 //
-//   certs:
-//     A non-empty chain of DER-encoded certificates, listed in the
-//     "forward" direction. The first certificate is the target
-//     certificate to verify, and the last certificate has trustedness
-//     given by |last_cert_trust| (generally a trust anchor).
+//   cert_chain:
+//     A non-empty chain of N DER-encoded certificates, listed in the
+//     "forward" direction. The first certificate is the target certificate to
+//     verify, and the last certificate has trustedness given by
+//     |last_cert_trust|.
 //
-//      * certs[0] is the target certificate to verify.
-//      * certs[i+1] holds the certificate that issued cert_chain[i].
-//      * certs[N-1] the root certificate
-//
-//     Note that THIS IS NOT identical in meaning to the same named
-//     "certs" input defined in RFC 5280 section 6.1.1.a. The differences
-//     are:
-//
-//      * The order of certificates is reversed
-//      * In RFC 5280 "certs" DOES NOT include the trust anchor
+//      * cert_chain[0] is the target certificate to verify.
+//      * cert_chain[i+1] holds the certificate that issued cert_chain[i].
+//      * cert_chain[N-1] the root certificate
 //
 //   last_cert_trust:
-//     Trustedness of |certs.back()|. The trustedness of |certs.back()|
-//     MUST BE decided by the caller -- this function takes it purely as
-//     an input. Moreover, the CertificateTrust can be used to specify
-//     trust anchor constraints.
-//
-//     This combined with |certs.back()| (the root certificate) fills a
-//     similar role to "trust anchor information" defined in RFC 5280
-//     section 6.1.1.d.
+//     Trustedness of certs.back(). The trustedness of certs.back() MUST BE
+//     decided by the caller -- this function takes it purely as an input.
+//     Moreover, the CertificateTrust can be used to specify trust anchor
+//     constraints [1]
 //
 //   signature_policy:
 //     The policy to use when verifying signatures (what hash algorithms are
 //     allowed, what length keys, what named curves, etc).
 //
 //   time:
-//     The UTC time to use for expiration checks. This is equivalent to
-//     the input from RFC 5280 section 6.1.1:
+//     The UTC time to use for expiration checks.
 //
-//       (b)  the current date/time.
-//
-//   required_key_purpose:
+//   key_purpose:
 //     The key purpose that the target certificate needs to be valid for.
 //
-//   user_initial_policy_set:
-//     This is equivalent to the same named input in RFC 5280 section
-//     6.1.1:
-//
-//       (c)  user-initial-policy-set: A set of certificate policy
-//            identifiers naming the policies that are acceptable to the
-//            certificate user. The user-initial-policy-set contains the
-//            special value any-policy if the user is not concerned about
-//            certificate policy.
-//
-//   initial_policy_mapping_inhibit:
-//     This is equivalent to the same named input in RFC 5280 section
-//     6.1.1:
-//
-//       (e)  initial-policy-mapping-inhibit, which indicates if policy
-//            mapping is allowed in the certification path.
-//
-//   initial_explicit_policy:
-//     This is equivalent to the same named input in RFC 5280 section
-//     6.1.1:
-//
-//       (f)  initial-explicit-policy, which indicates if the path must be
-//            valid for at least one of the certificate policies in the
-//            user-initial-policy-set.
-//
-//   initial_any_policy_inhibit:
-//     This is equivalent to the same named input in RFC 5280 section
-//     6.1.1:
-//
-//       (g)  initial-any-policy-inhibit, which indicates whether the
-//            anyPolicy OID should be processed if it is included in a
-//            certificate.
-//
 // ---------
 // Outputs
 // ---------
-//
-//   user_constrained_policy_set:
-//     Can be null. If non-null, |user_constrained_policy_set| will be filled
-//     with the matching policies (intersected with user_initial_policy_set).
-//     This is equivalent to the same named output in X.509 section 10.2.
-//     Note that it is OK for this to point to input user_initial_policy_set.
-//
 //   errors:
 //     Must be non-null. The set of errors/warnings encountered while
 //     validating the path are appended to this structure. If verification
 //     failed, then there is guaranteed to be at least 1 high severity error
 //     written to |errors|.
 //
-// -------------------------
-// Trust Anchor constraints
-// -------------------------
-//
-// Conceptually, VerifyCertificateChain() sets RFC 5937's
-// "enforceTrustAnchorConstraints" to true.
-//
-// One specifies trust anchor constraints using the |last_cert_trust|
-// parameter in conjunction with extensions appearing in |certs.back()|.
-//
-// The trust anchor |certs.back()| is always passed as a certificate to
-// this function, however the manner in which that certificate is
-// interpreted depends on |last_cert_trust|:
-//
-// TRUSTED_ANCHOR:
-//
-// No properties from the root certificate, other than its Subject and
-// SPKI, are checked during verification. This is the usual
-// interpretation for a "trust anchor".
-//
-// TRUSTED_ANCHOR_WITH_CONSTRAINTS:
-//
-// Only a subset of extensions and properties from the certificate are checked,
-// as described by RFC 5937.
+// [1] Conceptually VerifyCertificateChain() sets RFC 5937's
+// "enforceTrustAnchorConstraints" to true. And one specifies whether to
+// interpret a root certificate as having trust anchor constraints through the
+// |last_cert_trust| parameter. The constraints are just a subset of the
+// extensions present in the certificate:
 //
 //  * Signature:             No
 //  * Validity (expiration): No
@@ -192,24 +104,17 @@
 //  * Basic constraints:     Yes, but only the pathlen (CA=false is accepted)
 //  * Name constraints:      Yes
 //  * Certificate policies:  Not currently, TODO(crbug.com/634453)
-//  * Policy Mappings:       No
 //  * inhibitAnyPolicy:      Not currently, TODO(crbug.com/634453)
 //  * PolicyConstraints:     Not currently, TODO(crbug.com/634452)
 //
 // The presence of any other unrecognized extension marked as critical fails
 // validation.
-NET_EXPORT void VerifyCertificateChain(
-    const ParsedCertificateList& certs,
-    const CertificateTrust& last_cert_trust,
-    const SignaturePolicy* signature_policy,
-    const der::GeneralizedTime& time,
-    KeyPurpose required_key_purpose,
-    InitialExplicitPolicy initial_explicit_policy,
-    const std::set<der::Input>& user_initial_policy_set,
-    InitialPolicyMappingInhibit initial_policy_mapping_inhibit,
-    InitialAnyPolicyInhibit initial_any_policy_inhibit,
-    std::set<der::Input>* user_constrained_policy_set,
-    CertPathErrors* errors);
+NET_EXPORT void VerifyCertificateChain(const ParsedCertificateList& certs,
+                                       const CertificateTrust& last_cert_trust,
+                                       const SignaturePolicy* signature_policy,
+                                       const der::GeneralizedTime& time,
+                                       KeyPurpose required_key_purpose,
+                                       CertPathErrors* errors);
 
 // TODO(crbug.com/634443): Move exported errors to a central location?
 extern CertErrorId kValidityFailedNotAfter;
diff --git a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
index f86e1e9e..8138dae9 100644
--- a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
@@ -67,19 +67,12 @@
 
     SimpleSignaturePolicy signature_policy(1024);
 
-    std::set<der::Input> user_constrained_policy_set;
-
     CertPathErrors path_errors;
-    VerifyCertificateChain(
-        input_chain, CertificateTrust::ForTrustAnchor(), &signature_policy,
-        info.time, KeyPurpose::ANY_EKU, info.initial_explicit_policy,
-        info.initial_policy_set, info.initial_policy_mapping_inhibit,
-        info.initial_inhibit_any_policy, &user_constrained_policy_set,
-        &path_errors);
+    VerifyCertificateChain(input_chain, CertificateTrust::ForTrustAnchor(),
+                           &signature_policy, info.time, KeyPurpose::ANY_EKU,
+                           &path_errors);
     bool did_succeed = !path_errors.ContainsHighSeverityErrors();
 
-    EXPECT_EQ(info.user_constrained_policy_set, user_constrained_policy_set);
-
     //  TODO(crbug.com/634443): Test errors on failure?
     if (info.should_validate != did_succeed) {
       ASSERT_EQ(info.should_validate, did_succeed)
@@ -229,21 +222,6 @@
                               PkitsTest07KeyUsage,
                               VerifyCertificateChainPkitsTestDelegate);
 INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
-                              PkitsTest08CertificatePolicies,
-                              VerifyCertificateChainPkitsTestDelegate);
-INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
-                              PkitsTest09RequireExplicitPolicy,
-                              VerifyCertificateChainPkitsTestDelegate);
-INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
-                              PkitsTest10PolicyMappings,
-                              VerifyCertificateChainPkitsTestDelegate);
-INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
-                              PkitsTest11InhibitPolicyMapping,
-                              VerifyCertificateChainPkitsTestDelegate);
-INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
-                              PkitsTest12InhibitAnyPolicy,
-                              VerifyCertificateChainPkitsTestDelegate);
-INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
                               PkitsTest13NameConstraints,
                               VerifyCertificateChainPkitsTestDelegate);
 INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
@@ -254,4 +232,8 @@
 // PkitsTest05VerifyingPathswithSelfIssuedCertificates,
 // PkitsTest14DistributionPoints, PkitsTest15DeltaCRLs
 
+// TODO(mattm): Certificate Policies support: PkitsTest08CertificatePolicies,
+// PkitsTest09RequireExplicitPolicy PkitsTest10PolicyMappings,
+// PkitsTest11InhibitPolicyMapping, PkitsTest12InhibitAnyPolicy
+
 }  // namespace net
diff --git a/net/cert/internal/verify_certificate_chain_unittest.cc b/net/cert/internal/verify_certificate_chain_unittest.cc
index 82851d0..13c689f 100644
--- a/net/cert/internal/verify_certificate_chain_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_unittest.cc
@@ -19,13 +19,8 @@
     SimpleSignaturePolicy signature_policy(1024);
 
     CertPathErrors errors;
-    // TODO(eroman): Check user_constrained_policy_set.
-    VerifyCertificateChain(
-        test.chain, test.last_cert_trust, &signature_policy, test.time,
-        test.key_purpose, test.initial_explicit_policy,
-        test.user_initial_policy_set, test.initial_policy_mapping_inhibit,
-        test.initial_any_policy_inhibit,
-        nullptr /*user_constrained_policy_set*/, &errors);
+    VerifyCertificateChain(test.chain, test.last_cert_trust, &signature_policy,
+                           test.time, test.key_purpose, &errors);
     EXPECT_EQ(test.expected_errors, errors.ToDebugString(test.chain))
         << "Test file: " << test_file_path;
   }
diff --git a/net/http/bidirectional_stream.cc b/net/http/bidirectional_stream.cc
index f76dee08..a050bb72 100644
--- a/net/http/bidirectional_stream.cc
+++ b/net/http/bidirectional_stream.cc
@@ -167,21 +167,6 @@
   return rv;
 }
 
-void BidirectionalStream::SendData(const scoped_refptr<IOBuffer>& data,
-                                   int length,
-                                   bool end_stream) {
-  DCHECK(stream_impl_);
-  DCHECK(write_buffer_list_.empty());
-  DCHECK(write_buffer_len_list_.empty());
-
-  if (net_log_.IsCapturing()) {
-    net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_SEND_DATA);
-  }
-  stream_impl_->SendData(data, length, end_stream);
-  write_buffer_list_.push_back(data);
-  write_buffer_len_list_.push_back(length);
-}
-
 void BidirectionalStream::SendvData(
     const std::vector<scoped_refptr<IOBuffer>>& buffers,
     const std::vector<int>& lengths,
diff --git a/net/http/bidirectional_stream.h b/net/http/bidirectional_stream.h
index 09b809cb..aaab6e4e 100644
--- a/net/http/bidirectional_stream.h
+++ b/net/http/bidirectional_stream.h
@@ -147,11 +147,6 @@
   // invoked, and should not be called again until Delegate::OnDataSent is
   // invoked. If |end_stream| is true, the DATA frame will have an END_STREAM
   // flag.
-  void SendData(const scoped_refptr<IOBuffer>& data,
-                int length,
-                bool end_stream);
-
-  // Same as SendData except this takes in a vector of IOBuffers.
   void SendvData(const std::vector<scoped_refptr<IOBuffer>>& buffers,
                  const std::vector<int>& lengths,
                  bool end_stream);
diff --git a/net/http/bidirectional_stream_impl.h b/net/http/bidirectional_stream_impl.h
index 54f9d04..9b5ffab7 100644
--- a/net/http/bidirectional_stream_impl.h
+++ b/net/http/bidirectional_stream_impl.h
@@ -128,10 +128,6 @@
   // Delegate::OnHeadersSent is invoked, and should not be called again until
   // Delegate::OnDataSent is invoked. If |end_stream| is true, the DATA frame
   // will have an END_STREAM flag.
-  virtual void SendData(const scoped_refptr<IOBuffer>& data,
-                        int length,
-                        bool end_stream) = 0;
-
   virtual void SendvData(const std::vector<scoped_refptr<IOBuffer>>& buffers,
                          const std::vector<int>& lengths,
                          bool end_stream) = 0;
diff --git a/net/http/bidirectional_stream_unittest.cc b/net/http/bidirectional_stream_unittest.cc
index 6d62d57..037a21e 100644
--- a/net/http/bidirectional_stream_unittest.cc
+++ b/net/http/bidirectional_stream_unittest.cc
@@ -184,9 +184,7 @@
   void SendData(const scoped_refptr<IOBuffer>& data,
                 int length,
                 bool end_of_stream) {
-    not_expect_callback_ = true;
-    stream_->SendData(data, length, end_of_stream);
-    not_expect_callback_ = false;
+    SendvData({data}, {length}, end_of_stream);
   }
 
   void SendvData(const std::vector<scoped_refptr<IOBuffer>>& data,
@@ -769,7 +767,7 @@
       entries, index, NetLogEventType::BIDIRECTIONAL_STREAM_RECV_TRAILERS,
       NetLogEventPhase::NONE);
   index = ExpectLogContainsSomewhere(
-      entries, index, NetLogEventType::BIDIRECTIONAL_STREAM_SEND_DATA,
+      entries, index, NetLogEventType::BIDIRECTIONAL_STREAM_SENDV_DATA,
       NetLogEventPhase::NONE);
   index = ExpectLogContainsSomewhere(
       entries, index, NetLogEventType::BIDIRECTIONAL_STREAM_READ_DATA,
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index 4d0dc5b..014bb5f1 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -1249,9 +1249,6 @@
 // }
 EVENT_TYPE(BIDIRECTIONAL_STREAM_READ_DATA)
 
-// Marks the SendData call of a net::BidirectionalStream.
-EVENT_TYPE(BIDIRECTIONAL_STREAM_SEND_DATA)
-
 // Marks the SendvData call of a net::BidirectionalStream.
 // The following parameters are attached:
 // {
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl.cc b/net/quic/chromium/bidirectional_stream_quic_impl.cc
index df82cec1..f07bc68 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl.cc
+++ b/net/quic/chromium/bidirectional_stream_quic_impl.cc
@@ -145,43 +145,6 @@
   return rv;
 }
 
-void BidirectionalStreamQuicImpl::SendData(const scoped_refptr<IOBuffer>& data,
-                                           int length,
-                                           bool end_stream) {
-  DCHECK(length > 0 || (length == 0 && end_stream));
-  if (!stream_) {
-    LOG(ERROR) << "Trying to send data after stream has been destroyed.";
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::NotifyError,
-                              weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
-    return;
-  }
-
-  std::unique_ptr<QuicConnection::ScopedPacketBundler> bundler;
-  if (!has_sent_headers_) {
-    DCHECK(!send_request_headers_automatically_);
-    // Creates a bundler only if there are headers to be sent along with the
-    // single data buffer.
-    bundler =
-        session_->CreatePacketBundler(QuicConnection::SEND_ACK_IF_PENDING);
-    // Sending the request might result in the stream being closed.
-    if (!WriteHeaders())
-      return;
-  }
-
-  QuicStringPiece string_data(data->data(), length);
-  int rv = stream_->WriteStreamData(
-      string_data, end_stream,
-      base::Bind(&BidirectionalStreamQuicImpl::OnSendDataComplete,
-                 weak_factory_.GetWeakPtr()));
-  DCHECK(rv == OK || rv == ERR_IO_PENDING);
-  if (rv == OK) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&BidirectionalStreamQuicImpl::OnSendDataComplete,
-                              weak_factory_.GetWeakPtr(), OK));
-  }
-}
-
 void BidirectionalStreamQuicImpl::SendvData(
     const std::vector<scoped_refptr<IOBuffer>>& buffers,
     const std::vector<int>& lengths,
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl.h b/net/quic/chromium/bidirectional_stream_quic_impl.h
index af5bcf4..f2a4055 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl.h
+++ b/net/quic/chromium/bidirectional_stream_quic_impl.h
@@ -45,9 +45,6 @@
              std::unique_ptr<base::Timer> timer) override;
   void SendRequestHeaders() override;
   int ReadData(IOBuffer* buffer, int buffer_len) override;
-  void SendData(const scoped_refptr<IOBuffer>& data,
-                int length,
-                bool end_stream) override;
   void SendvData(const std::vector<scoped_refptr<IOBuffer>>& buffers,
                  const std::vector<int>& lengths,
                  bool end_stream) override;
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
index 2efcb02..bd81ec3 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
@@ -173,9 +173,7 @@
   void SendData(const scoped_refptr<IOBuffer>& data,
                 int length,
                 bool end_of_stream) {
-    not_expect_callback_ = true;
-    stream_->SendData(data, length, end_of_stream);
-    not_expect_callback_ = false;
+    SendvData({data}, {length}, end_of_stream);
   }
 
   void SendvData(const std::vector<scoped_refptr<IOBuffer>>& data,
diff --git a/net/quic/core/frames/quic_ack_frame.cc b/net/quic/core/frames/quic_ack_frame.cc
index 8dcb1373..0741a15 100644
--- a/net/quic/core/frames/quic_ack_frame.cc
+++ b/net/quic/core/frames/quic_ack_frame.cc
@@ -80,13 +80,6 @@
   packet_number_intervals_.Difference(*packet_number_intervals_.begin());
 }
 
-void PacketNumberQueue::Complement() {
-  if (Empty()) {
-    return;
-  }
-  packet_number_intervals_.Complement(Min(), Max() + 1);
-}
-
 bool PacketNumberQueue::Contains(QuicPacketNumber packet_number) const {
   return packet_number_intervals_.Contains(packet_number);
 }
diff --git a/net/quic/core/frames/quic_ack_frame.h b/net/quic/core/frames/quic_ack_frame.h
index 0a8991a..ef4829c 100644
--- a/net/quic/core/frames/quic_ack_frame.h
+++ b/net/quic/core/frames/quic_ack_frame.h
@@ -52,11 +52,6 @@
   // Removes the smallest interval in the queue.
   void RemoveSmallestInterval();
 
-  // Mutates packet number set so that it contains only those packet numbers
-  // from minimum to maximum packet number not currently in the set. Do nothing
-  // if packet number set is empty.
-  void Complement();
-
   // Returns true if the queue contains |packet_number|.
   bool Contains(QuicPacketNumber packet_number) const;
 
diff --git a/net/quic/core/frames/quic_frames_test.cc b/net/quic/core/frames/quic_frames_test.cc
index 67ff6f8..b0a77377 100644
--- a/net/quic/core/frames/quic_frames_test.cc
+++ b/net/quic/core/frames/quic_frames_test.cc
@@ -278,19 +278,6 @@
   EXPECT_FALSE(queue.Contains(20));
 }
 
-TEST_F(PacketNumberQueueTest, Complement) {
-  PacketNumberQueue queue;
-  queue.Add(1, 10);
-  queue.Add(12, 20);
-  queue.Add(22, 30);
-  queue.Complement();
-  EXPECT_EQ(2u, queue.NumIntervals());
-  EXPECT_TRUE(queue.Contains(10));
-  EXPECT_TRUE(queue.Contains(11));
-  EXPECT_TRUE(queue.Contains(20));
-  EXPECT_TRUE(queue.Contains(21));
-}
-
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/core/quic_bandwidth.cc b/net/quic/core/quic_bandwidth.cc
index 86f0664..f831087 100644
--- a/net/quic/core/quic_bandwidth.cc
+++ b/net/quic/core/quic_bandwidth.cc
@@ -5,111 +5,11 @@
 #include "net/quic/core/quic_bandwidth.h"
 
 #include <cinttypes>
-#include <limits>
 
-#include "net/quic/core/quic_constants.h"
-#include "net/quic/core/quic_time.h"
-#include "net/quic/core/quic_types.h"
-#include "net/quic/platform/api/quic_bug_tracker.h"
 #include "net/quic/platform/api/quic_str_cat.h"
 
 namespace net {
 
-// Highest number that QuicBandwidth can hold.
-const int64_t kQuicInfiniteBandwidth = INT64_C(0x7fffffffffffffff);
-
-// static
-QuicBandwidth QuicBandwidth::Zero() {
-  return QuicBandwidth(0);
-}
-
-// static
-QuicBandwidth QuicBandwidth::Infinite() {
-  return QuicBandwidth(std::numeric_limits<int64_t>::max());
-}
-
-// static
-QuicBandwidth QuicBandwidth::FromBitsPerSecond(int64_t bits_per_second) {
-  return QuicBandwidth(bits_per_second);
-}
-
-// static
-QuicBandwidth QuicBandwidth::FromKBitsPerSecond(int64_t k_bits_per_second) {
-  DCHECK(k_bits_per_second < kQuicInfiniteBandwidth / 1000);
-  return QuicBandwidth(k_bits_per_second * 1000);
-}
-
-// static
-QuicBandwidth QuicBandwidth::FromBytesPerSecond(int64_t bytes_per_second) {
-  DCHECK(bytes_per_second < kQuicInfiniteBandwidth / 8);
-  return QuicBandwidth(bytes_per_second * 8);
-}
-
-// static
-QuicBandwidth QuicBandwidth::FromKBytesPerSecond(int64_t k_bytes_per_second) {
-  DCHECK(k_bytes_per_second < kQuicInfiniteBandwidth / 8000);
-  return QuicBandwidth(k_bytes_per_second * 8000);
-}
-
-// static
-QuicBandwidth QuicBandwidth::FromBytesAndTimeDelta(QuicByteCount bytes,
-                                                   QuicTime::Delta delta) {
-  DCHECK_LT(bytes, static_cast<uint64_t>(kQuicInfiniteBandwidth /
-                                         (8 * kNumMicrosPerSecond)));
-  int64_t bytes_per_second =
-      (bytes * kNumMicrosPerSecond) / delta.ToMicroseconds();
-  return QuicBandwidth(bytes_per_second * 8);
-}
-
-QuicBandwidth::QuicBandwidth(int64_t bits_per_second)
-    : bits_per_second_(bits_per_second) {
-  if (bits_per_second < 0) {
-    QUIC_BUG << "Can't set negative bandwidth " << bits_per_second;
-    bits_per_second_ = 0;
-    return;
-  }
-  bits_per_second_ = bits_per_second;
-}
-
-int64_t QuicBandwidth::ToBitsPerSecond() const {
-  return bits_per_second_;
-}
-
-int64_t QuicBandwidth::ToKBitsPerSecond() const {
-  return bits_per_second_ / 1000;
-}
-
-int64_t QuicBandwidth::ToBytesPerSecond() const {
-  return bits_per_second_ / 8;
-}
-
-int64_t QuicBandwidth::ToKBytesPerSecond() const {
-  return bits_per_second_ / 8000;
-}
-
-QuicByteCount QuicBandwidth::ToBytesPerPeriod(
-    QuicTime::Delta time_period) const {
-  return ToBytesPerSecond() * time_period.ToMicroseconds() /
-         kNumMicrosPerSecond;
-}
-
-int64_t QuicBandwidth::ToKBytesPerPeriod(QuicTime::Delta time_period) const {
-  return ToKBytesPerSecond() * time_period.ToMicroseconds() /
-         kNumMicrosPerSecond;
-}
-
-bool QuicBandwidth::IsZero() const {
-  return (bits_per_second_ == 0);
-}
-
-QuicTime::Delta QuicBandwidth::TransferTime(QuicByteCount bytes) const {
-  if (bits_per_second_ == 0) {
-    return QuicTime::Delta::Zero();
-  }
-  return QuicTime::Delta::FromMicroseconds(bytes * 8 * kNumMicrosPerSecond /
-                                           bits_per_second_);
-}
-
 std::string QuicBandwidth::ToDebugValue() const {
   if (bits_per_second_ < 80000) {
     return QuicStringPrintf("%" PRId64 " bits/s (%" PRId64 " bytes/s)",
diff --git a/net/quic/core/quic_bandwidth.h b/net/quic/core/quic_bandwidth.h
index 4e11d5f..e710b2edb 100644
--- a/net/quic/core/quic_bandwidth.h
+++ b/net/quic/core/quic_bandwidth.h
@@ -9,9 +9,11 @@
 
 #include <cmath>
 #include <cstdint>
+#include <limits>
 #include <ostream>
 
 #include "base/compiler_specific.h"
+#include "net/quic/core/quic_constants.h"
 #include "net/quic/core/quic_time.h"
 #include "net/quic/core/quic_types.h"
 #include "net/quic/platform/api/quic_export.h"
@@ -21,47 +23,75 @@
 class QUIC_EXPORT_PRIVATE QuicBandwidth {
  public:
   // Creates a new QuicBandwidth with an internal value of 0.
-  static QuicBandwidth Zero();
+  static constexpr QuicBandwidth Zero() { return QuicBandwidth(0); }
 
   // Creates a new QuicBandwidth with an internal value of INT64_MAX.
-  static QuicBandwidth Infinite();
+  static constexpr QuicBandwidth Infinite() {
+    return QuicBandwidth(std::numeric_limits<int64_t>::max());
+  }
 
   // Create a new QuicBandwidth holding the bits per second.
-  static QuicBandwidth FromBitsPerSecond(int64_t bits_per_second);
+  static constexpr QuicBandwidth FromBitsPerSecond(int64_t bits_per_second) {
+    return QuicBandwidth(bits_per_second);
+  }
 
   // Create a new QuicBandwidth holding the kilo bits per second.
-  static QuicBandwidth FromKBitsPerSecond(int64_t k_bits_per_second);
+  static constexpr QuicBandwidth FromKBitsPerSecond(int64_t k_bits_per_second) {
+    return QuicBandwidth(k_bits_per_second * 1000);
+  }
 
   // Create a new QuicBandwidth holding the bytes per second.
-  static QuicBandwidth FromBytesPerSecond(int64_t bytes_per_second);
+  static constexpr QuicBandwidth FromBytesPerSecond(int64_t bytes_per_second) {
+    return QuicBandwidth(bytes_per_second * 8);
+  }
 
   // Create a new QuicBandwidth holding the kilo bytes per second.
-  static QuicBandwidth FromKBytesPerSecond(int64_t k_bytes_per_second);
+  static constexpr QuicBandwidth FromKBytesPerSecond(
+      int64_t k_bytes_per_second) {
+    return QuicBandwidth(k_bytes_per_second * 8000);
+  }
 
   // Create a new QuicBandwidth based on the bytes per the elapsed delta.
-  static QuicBandwidth FromBytesAndTimeDelta(QuicByteCount bytes,
-                                             QuicTime::Delta delta);
+  static inline QuicBandwidth FromBytesAndTimeDelta(QuicByteCount bytes,
+                                                    QuicTime::Delta delta) {
+    return QuicBandwidth((bytes * kNumMicrosPerSecond) /
+                         delta.ToMicroseconds() * 8);
+  }
 
-  int64_t ToBitsPerSecond() const;
+  inline int64_t ToBitsPerSecond() const { return bits_per_second_; }
 
-  int64_t ToKBitsPerSecond() const;
+  inline int64_t ToKBitsPerSecond() const { return bits_per_second_ / 1000; }
 
-  int64_t ToBytesPerSecond() const;
+  inline int64_t ToBytesPerSecond() const { return bits_per_second_ / 8; }
 
-  int64_t ToKBytesPerSecond() const;
+  inline int64_t ToKBytesPerSecond() const { return bits_per_second_ / 8000; }
 
-  QuicByteCount ToBytesPerPeriod(QuicTime::Delta time_period) const;
+  inline QuicByteCount ToBytesPerPeriod(QuicTime::Delta time_period) const {
+    return ToBytesPerSecond() * time_period.ToMicroseconds() /
+           kNumMicrosPerSecond;
+  }
 
-  int64_t ToKBytesPerPeriod(QuicTime::Delta time_period) const;
+  inline int64_t ToKBytesPerPeriod(QuicTime::Delta time_period) const {
+    return ToKBytesPerSecond() * time_period.ToMicroseconds() /
+           kNumMicrosPerSecond;
+  }
 
-  bool IsZero() const;
+  inline bool IsZero() const { return bits_per_second_ == 0; }
 
-  QuicTime::Delta TransferTime(QuicByteCount bytes) const;
+  inline QuicTime::Delta TransferTime(QuicByteCount bytes) const {
+    if (bits_per_second_ == 0) {
+      return QuicTime::Delta::Zero();
+    }
+    return QuicTime::Delta::FromMicroseconds(bytes * 8 * kNumMicrosPerSecond /
+                                             bits_per_second_);
+  }
 
   std::string ToDebugValue() const;
 
  private:
-  explicit QuicBandwidth(int64_t bits_per_second);
+  explicit constexpr QuicBandwidth(int64_t bits_per_second)
+      : bits_per_second_(bits_per_second >= 0 ? bits_per_second : 0) {}
+
   int64_t bits_per_second_;
 
   friend QuicBandwidth operator+(QuicBandwidth lhs, QuicBandwidth rhs);
diff --git a/net/quic/core/quic_connection.cc b/net/quic/core/quic_connection.cc
index 6ca5955..26f097d 100644
--- a/net/quic/core/quic_connection.cc
+++ b/net/quic/core/quic_connection.cc
@@ -253,7 +253,12 @@
       time_of_last_received_packet_(clock_->ApproximateNow()),
       time_of_last_sent_new_packet_(clock_->ApproximateNow()),
       last_send_for_timeout_(clock_->ApproximateNow()),
-      sent_packet_manager_(perspective, clock_, &stats_, kCubicBytes, kNack),
+      sent_packet_manager_(
+          perspective,
+          clock_,
+          &stats_,
+          FLAGS_quic_reloadable_flag_quic_default_to_bbr ? kBBR : kCubicBytes,
+          kNack),
       version_negotiation_state_(START_NEGOTIATION),
       perspective_(perspective),
       connected_(true),
@@ -2431,4 +2436,9 @@
   }
 }
 
+void QuicConnection::SetStreamNotifier(
+    StreamNotifierInterface* stream_notifier) {
+  sent_packet_manager_.SetStreamNotifier(stream_notifier);
+}
+
 }  // namespace net
diff --git a/net/quic/core/quic_connection.h b/net/quic/core/quic_connection.h
index fd78c93..4503c6c 100644
--- a/net/quic/core/quic_connection.h
+++ b/net/quic/core/quic_connection.h
@@ -659,6 +659,9 @@
   // the MTU discovery alarm.
   void DiscoverMtu();
 
+  // Sets the stream notifer on the SentPacketManager.
+  void SetStreamNotifier(StreamNotifierInterface* stream_notifier);
+
   // Return the name of the cipher of the primary decrypter of the framer.
   const char* cipher_name() const { return framer_.decrypter()->cipher_name(); }
   // Return the id of the cipher of the primary decrypter of the framer.
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h
index 56c7420..56c7860 100644
--- a/net/quic/core/quic_flags_list.h
+++ b/net/quic/core/quic_flags_list.h
@@ -156,7 +156,7 @@
 // written in big endian.
 QUIC_FLAG(bool,
           FLAGS_quic_restart_flag_quic_big_endian_connection_id_client,
-          false)
+          true)
 
 // If true, on server side, 8-byte connection ID in public header is read and
 // written in big endian.
@@ -170,7 +170,7 @@
 
 // If true, enable random padding of size [1, 256] when response body is
 // compressed for QUIC version >= 38.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_random_padding, false)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_random_padding, true)
 
 // Use conservation in PROBE_BW ouside of super-unity gain and immediately
 // preceeding cycle.
@@ -219,3 +219,10 @@
 
 // If true, use the more CPU efficient bandwidth sampler datastructure.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_faster_bandwidth_sampler, false)
+
+// In QUIC, notify StreamNotifier instead of per-packet AckNotifier on
+// every ack or retransmitted.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_stream_notifier, false)
+
+// When true, defaults to BBR congestion control instead of Cubic.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_default_to_bbr, false)
diff --git a/net/quic/core/quic_framer_test.cc b/net/quic/core/quic_framer_test.cc
index 64feb881..a04fdc6 100644
--- a/net/quic/core/quic_framer_test.cc
+++ b/net/quic/core/quic_framer_test.cc
@@ -4763,7 +4763,7 @@
       static_cast<unsigned char>(
           FLAGS_quic_reloadable_flag_quic_remove_v33_hacks2 ? 0x39 : 0x3D),
       // connection_id
-          0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+      0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
       // version tag
       'Q', '0',  GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(),
       // packet number
@@ -4805,7 +4805,7 @@
       static_cast<unsigned char>(
           FLAGS_quic_reloadable_flag_quic_remove_v33_hacks2 ? 0x39 : 0x3D),
       // connection_id
-          0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+      0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
       // version tag
       'Q', '0',  GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(),
       // packet number
diff --git a/net/quic/core/quic_headers_stream.cc b/net/quic/core/quic_headers_stream.cc
index 38e3ff48..b6dfec1 100644
--- a/net/quic/core/quic_headers_stream.cc
+++ b/net/quic/core/quic_headers_stream.cc
@@ -9,6 +9,20 @@
 
 namespace net {
 
+QuicHeadersStream::CompressedHeaderInfo::CompressedHeaderInfo(
+    QuicStreamOffset headers_stream_offset,
+    QuicStreamOffset full_length,
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener)
+    : headers_stream_offset(headers_stream_offset),
+      full_length(full_length),
+      unacked_length(full_length),
+      ack_listener(std::move(ack_listener)) {}
+
+QuicHeadersStream::CompressedHeaderInfo::CompressedHeaderInfo(
+    const CompressedHeaderInfo& other) = default;
+
+QuicHeadersStream::CompressedHeaderInfo::~CompressedHeaderInfo() {}
+
 QuicHeadersStream::QuicHeadersStream(QuicSpdySession* session)
     : QuicStream(kHeadersStreamId, session), spdy_session_(session) {
   // The headers stream is exempt from connection level flow control.
@@ -43,4 +57,95 @@
   }
 }
 
+QuicConsumedData QuicHeadersStream::WritevDataInner(
+    QuicIOVector iov,
+    QuicStreamOffset offset,
+    bool fin,
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+  if (!session()->use_stream_notifier()) {
+    return QuicStream::WritevDataInner(iov, offset, fin,
+                                       std::move(ack_listener));
+  }
+  QuicConsumedData consumed =
+      QuicStream::WritevDataInner(iov, offset, fin, nullptr);
+  if (consumed.bytes_consumed == 0 || ack_listener == nullptr) {
+    // No need to update unacked_headers_ if no byte is consumed or there is no
+    // ack listener.
+    return consumed;
+  }
+
+  if (!unacked_headers_.empty() &&
+      (offset == unacked_headers_.back().headers_stream_offset +
+                     unacked_headers_.back().full_length) &&
+      ack_listener == unacked_headers_.back().ack_listener) {
+    // Try to combine with latest inserted entry if they belong to the same
+    // header (i.e., having contiguous offset and the same ack listener).
+    unacked_headers_.back().full_length += consumed.bytes_consumed;
+    unacked_headers_.back().unacked_length += consumed.bytes_consumed;
+  } else {
+    unacked_headers_.push_back(CompressedHeaderInfo(
+        offset, consumed.bytes_consumed, std::move(ack_listener)));
+  }
+  return consumed;
+}
+
+void QuicHeadersStream::OnStreamFrameAcked(const QuicStreamFrame& frame,
+                                           QuicTime::Delta ack_delay_time) {
+  for (CompressedHeaderInfo& header : unacked_headers_) {
+    if (frame.offset < header.headers_stream_offset) {
+      // This header frame offset belongs to headers with smaller offset, stop
+      // processing.
+      break;
+    }
+
+    if (frame.offset >= header.headers_stream_offset + header.full_length) {
+      // This header frame belongs to headers with larger offset.
+      continue;
+    }
+
+    if (header.unacked_length < frame.data_length) {
+      // This header frame is out of range.
+      CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+                                 "Unsent stream data is acked");
+      return;
+    }
+
+    header.unacked_length -= frame.data_length;
+
+    if (header.ack_listener != nullptr) {
+      header.ack_listener->OnPacketAcked(frame.data_length, ack_delay_time);
+    }
+    break;
+  }
+
+  // Remove headers which are fully acked. Please note, header frames can be
+  // acked out of order, but unacked_headers_ is cleaned up in order.
+  while (!unacked_headers_.empty() &&
+         unacked_headers_.front().unacked_length == 0) {
+    unacked_headers_.pop_front();
+  }
+  QuicStream::OnStreamFrameAcked(frame, ack_delay_time);
+}
+
+void QuicHeadersStream::OnStreamFrameRetransmitted(
+    const QuicStreamFrame& frame) {
+  for (CompressedHeaderInfo& header : unacked_headers_) {
+    if (frame.offset < header.headers_stream_offset) {
+      // This header frame offset belongs to headers with smaller offset, stop
+      // processing.
+      break;
+    }
+
+    if (frame.offset >= header.headers_stream_offset + header.full_length) {
+      // This header frame belongs to headers with larger offset.
+      continue;
+    }
+
+    if (header.ack_listener != nullptr) {
+      header.ack_listener->OnPacketRetransmitted(frame.data_length);
+    }
+    break;
+  }
+}
+
 }  // namespace net
diff --git a/net/quic/core/quic_headers_stream.h b/net/quic/core/quic_headers_stream.h
index b28a7a4..f74b869 100644
--- a/net/quic/core/quic_headers_stream.h
+++ b/net/quic/core/quic_headers_stream.h
@@ -6,6 +6,7 @@
 #define NET_QUIC_CORE_QUIC_HEADERS_STREAM_H_
 
 #include <cstddef>
+#include <deque>
 #include <memory>
 
 #include "base/macros.h"
@@ -37,14 +38,53 @@
   // Release underlying buffer if allowed.
   void MaybeReleaseSequencerBuffer();
 
+  void OnStreamFrameAcked(const QuicStreamFrame& frame,
+                          QuicTime::Delta ack_delay_time) override;
+
+  void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override;
+
  private:
   friend class test::QuicHeadersStreamPeer;
 
+  // Override to store mapping from offset, length to ack_listener. This
+  // ack_listener is notified once data within [offset, offset + length] is
+  // acked or retransmitted.
+  QuicConsumedData WritevDataInner(
+      QuicIOVector iov,
+      QuicStreamOffset offset,
+      bool fin,
+      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener)
+      override;
+
+  // CompressedHeaderInfo includes simple information of a header, including
+  // offset in headers stream, unacked length and ack listener of this header.
+  struct QUIC_EXPORT_PRIVATE CompressedHeaderInfo {
+    CompressedHeaderInfo(
+        QuicStreamOffset headers_stream_offset,
+        QuicStreamOffset full_length,
+        QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+    CompressedHeaderInfo(const CompressedHeaderInfo& other);
+    ~CompressedHeaderInfo();
+
+    // Offset the header was sent on the headers stream.
+    QuicStreamOffset headers_stream_offset;
+    // The full length of the header.
+    QuicByteCount full_length;
+    // The remaining bytes to be acked.
+    QuicByteCount unacked_length;
+    // Ack listener of this header, and it is notified once any of the bytes has
+    // been acked or retransmitted.
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener;
+  };
+
   // Returns true if the session is still connected.
   bool IsConnected();
 
   QuicSpdySession* spdy_session_;
 
+  // Headers that have not been fully acked.
+  std::deque<CompressedHeaderInfo> unacked_headers_;
+
   DISALLOW_COPY_AND_ASSIGN(QuicHeadersStream);
 };
 
diff --git a/net/quic/core/quic_headers_stream_test.cc b/net/quic/core/quic_headers_stream_test.cc
index a2e16a42..14401ee6 100644
--- a/net/quic/core/quic_headers_stream_test.cc
+++ b/net/quic/core/quic_headers_stream_test.cc
@@ -972,7 +972,7 @@
       framer_->ProcessInput(saved_data_.data(), saved_data_.length());
       EXPECT_EQ(saved_payloads_, data);
 
-      if (use_ack_listener) {
+      if (use_ack_listener && !session_.use_stream_notifier()) {
         // Notice, acked bytes doesn't include extra bytes used by
         // HTTP/2 DATA frame headers.
         EXPECT_EQ(ack_listener->total_acked_bytes(), data_len);
@@ -1041,6 +1041,65 @@
   EXPECT_EQ(consumed_data.fin_consumed, false);
 }
 
+TEST_P(QuicHeadersStreamTest, AckSentData) {
+  if (!session_.use_stream_notifier()) {
+    return;
+  }
+  EXPECT_CALL(session_,
+              WritevData(headers_stream_, kHeadersStreamId, _, _, NO_FIN, _))
+      .WillRepeatedly(
+          WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
+  InSequence s;
+  QuicReferenceCountedPointer<MockAckListener> ack_listener1(
+      new MockAckListener());
+  QuicReferenceCountedPointer<MockAckListener> ack_listener2(
+      new MockAckListener());
+  QuicReferenceCountedPointer<MockAckListener> ack_listener3(
+      new MockAckListener());
+
+  // Packet 1.
+  headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
+  headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
+  headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
+
+  // Packet 2.
+  headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
+  headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
+
+  // Packet 3.
+  headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
+
+  QuicStreamFrame frame1(kHeadersStreamId, false, 0, "Header5");
+  QuicStreamFrame frame2(kHeadersStreamId, false, 7, "Header5");
+  // This is a bad frame3.
+  QuicStreamFrame frame3(kHeadersStreamId, false, 14, "BadHeader7");
+  QuicStreamFrame frame4(kHeadersStreamId, false, 21, "Header9");
+  QuicStreamFrame frame5(kHeadersStreamId, false, 28, "Header7");
+  QuicStreamFrame frame6(kHeadersStreamId, false, 35, "Header9");
+  // Packet 2 gets retransmitted.
+  EXPECT_CALL(*ack_listener3, OnPacketRetransmitted(7)).Times(1);
+  EXPECT_CALL(*ack_listener2, OnPacketRetransmitted(7)).Times(1);
+  headers_stream_->OnStreamFrameRetransmitted(frame4);
+  headers_stream_->OnStreamFrameRetransmitted(frame5);
+
+  // Packets are acked in order: 2, 3, 1.
+  EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
+  EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
+  headers_stream_->OnStreamFrameAcked(frame4, QuicTime::Delta::Zero());
+  headers_stream_->OnStreamFrameAcked(frame5, QuicTime::Delta::Zero());
+
+  EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
+  headers_stream_->OnStreamFrameAcked(frame6, QuicTime::Delta::Zero());
+
+  EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
+  EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
+  headers_stream_->OnStreamFrameAcked(frame1, QuicTime::Delta::Zero());
+  headers_stream_->OnStreamFrameAcked(frame2, QuicTime::Delta::Zero());
+  // Unsent data is acked.
+  EXPECT_CALL(*connection_, CloseConnection(QUIC_INTERNAL_ERROR, _, _));
+  headers_stream_->OnStreamFrameAcked(frame3, QuicTime::Delta::Zero());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/core/quic_sent_packet_manager.cc b/net/quic/core/quic_sent_packet_manager.cc
index 9b3b3dc9..7233e52 100644
--- a/net/quic/core/quic_sent_packet_manager.cc
+++ b/net/quic/core/quic_sent_packet_manager.cc
@@ -117,6 +117,9 @@
     }
   } else if (config.HasClientRequestedIndependentOption(kBYTE, perspective_)) {
     SetSendAlgorithm(kCubic);
+  } else if (FLAGS_quic_reloadable_flag_quic_default_to_bbr &&
+             config.HasClientRequestedIndependentOption(kQBIC, perspective_)) {
+    SetSendAlgorithm(kCubicBytes);
   } else if (FLAGS_quic_reloadable_flag_quic_enable_pcc &&
              config.HasClientRequestedIndependentOption(kTPCC, perspective_)) {
     SetSendAlgorithm(kPCC);
@@ -464,6 +467,7 @@
   // The AckListener needs to be notified about the most recent
   // transmission, since that's the one only one it tracks.
   if (newest_transmission == packet_number) {
+    unacked_packets_.NotifyStreamFramesAcked(*info, ack_delay_time);
     unacked_packets_.NotifyAndClearListeners(&info->ack_listeners,
                                              ack_delay_time);
   } else {
@@ -478,6 +482,8 @@
     // only handle nullptr encrypted packets in a special way.
     const QuicTransmissionInfo& newest_transmission_info =
         unacked_packets_.GetTransmissionInfo(newest_transmission);
+    unacked_packets_.NotifyStreamFramesAcked(newest_transmission_info,
+                                             ack_delay_time);
     if (HasCryptoHandshake(newest_transmission_info)) {
       unacked_packets_.RemoveFromInFlight(newest_transmission);
     }
@@ -962,4 +968,9 @@
   return send_algorithm_.get();
 }
 
+void QuicSentPacketManager::SetStreamNotifier(
+    StreamNotifierInterface* stream_notifier) {
+  unacked_packets_.SetStreamNotifier(stream_notifier);
+}
+
 }  // namespace net
diff --git a/net/quic/core/quic_sent_packet_manager.h b/net/quic/core/quic_sent_packet_manager.h
index acdf795d..268a54c4 100644
--- a/net/quic/core/quic_sent_packet_manager.h
+++ b/net/quic/core/quic_sent_packet_manager.h
@@ -224,6 +224,8 @@
 
   const SendAlgorithmInterface* GetSendAlgorithm() const;
 
+  void SetStreamNotifier(StreamNotifierInterface* stream_notifier);
+
   QuicPacketNumber largest_packet_peer_knows_is_acked() const {
     return largest_packet_peer_knows_is_acked_;
   }
diff --git a/net/quic/core/quic_session.cc b/net/quic/core/quic_session.cc
index 3afc985..7c1bf263 100644
--- a/net/quic/core/quic_session.cc
+++ b/net/quic/core/quic_session.cc
@@ -45,10 +45,15 @@
                        perspective() == Perspective::IS_SERVER,
                        nullptr),
       currently_writing_stream_id_(0),
-      respect_goaway_(true) {}
+      respect_goaway_(true),
+      use_stream_notifier_(
+          FLAGS_quic_reloadable_flag_quic_use_stream_notifier) {}
 
 void QuicSession::Initialize() {
   connection_->set_visitor(this);
+  if (use_stream_notifier_) {
+    connection_->SetStreamNotifier(this);
+  }
   connection_->SetFromConfig(config_);
 
   DCHECK_EQ(kCryptoStreamId, GetMutableCryptoStream()->id());
@@ -66,6 +71,7 @@
       << "Surprisingly high number of locally closed self initiated streams"
          "still waiting for final byte offset: "
       << GetNumLocallyClosedOutgoingStreamsHighestOffset();
+  QUIC_LOG_IF(WARNING, !zombie_streams_.empty()) << "Still have zombie streams";
 }
 
 void QuicSession::OnStreamFrame(const QuicStreamFrame& frame) {
@@ -129,6 +135,13 @@
     }
   }
 
+  // Cleanup zombie stream map on connection close.
+  while (!zombie_streams_.empty()) {
+    ZombieStreamMap::iterator it = zombie_streams_.begin();
+    closed_streams_.push_back(std::move(it->second));
+    zombie_streams_.erase(it);
+  }
+
   if (visitor_) {
     visitor_->OnConnectionClosed(connection_->connection_id(), error,
                                  error_details);
@@ -369,7 +382,11 @@
     stream->set_rst_sent(true);
   }
 
-  closed_streams_.push_back(std::move(it->second));
+  if (stream->IsWaitingForAcks()) {
+    zombie_streams_[stream->id()] = std::move(it->second);
+  } else {
+    closed_streams_.push_back(std::move(it->second));
+  }
 
   // If we haven't received a FIN or RST for this stream, we need to keep track
   // of the how many bytes the stream's flow controller believes it has
@@ -944,4 +961,45 @@
   return stream_ptr;
 }
 
+void QuicSession::OnStreamDoneWaitingForAcks(QuicStreamId id) {
+  auto it = zombie_streams_.find(id);
+  if (it == zombie_streams_.end()) {
+    return;
+  }
+
+  closed_streams_.push_back(std::move(it->second));
+  zombie_streams_.erase(it);
+}
+
+QuicStream* QuicSession::GetStream(QuicStreamId id) const {
+  auto static_stream = static_stream_map_.find(id);
+  if (static_stream != static_stream_map_.end()) {
+    return static_stream->second;
+  }
+  auto active_stream = dynamic_stream_map_.find(id);
+  if (active_stream != dynamic_stream_map_.end()) {
+    return active_stream->second.get();
+  }
+  auto zombie_stream = zombie_streams_.find(id);
+  if (zombie_stream != zombie_streams_.end()) {
+    return zombie_stream->second.get();
+  }
+  return nullptr;
+}
+
+void QuicSession::OnStreamFrameAcked(const QuicStreamFrame& frame,
+                                     QuicTime::Delta ack_delay_time) {
+  QuicStream* stream = GetStream(frame.stream_id);
+  if (stream != nullptr) {
+    stream->OnStreamFrameAcked(frame, ack_delay_time);
+  }
+}
+
+void QuicSession::OnStreamFrameRetransmitted(const QuicStreamFrame& frame) {
+  QuicStream* stream = GetStream(frame.stream_id);
+  if (stream != nullptr) {
+    stream->OnStreamFrameRetransmitted(frame);
+  }
+}
+
 }  // namespace net
diff --git a/net/quic/core/quic_session.h b/net/quic/core/quic_session.h
index 403f0e5..d70e4bdf 100644
--- a/net/quic/core/quic_session.h
+++ b/net/quic/core/quic_session.h
@@ -22,6 +22,7 @@
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/core/quic_stream.h"
 #include "net/quic/core/quic_write_blocked_list.h"
+#include "net/quic/core/stream_notifier_interface.h"
 #include "net/quic/platform/api/quic_containers.h"
 #include "net/quic/platform/api/quic_export.h"
 #include "net/quic/platform/api/quic_socket_address.h"
@@ -36,7 +37,8 @@
 class QuicSessionPeer;
 }  // namespace test
 
-class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
+class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface,
+                                        public StreamNotifierInterface {
  public:
   // An interface from the session to the entity owning the session.
   // This lets the session notify its owner (the Dispatcher) when the connection
@@ -107,6 +109,11 @@
   bool HasOpenDynamicStreams() const override;
   void OnPathDegrading() override;
 
+  // StreamNotifierInterface methods:
+  void OnStreamFrameAcked(const QuicStreamFrame& frame,
+                          QuicTime::Delta ack_delay_time) override;
+  void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override;
+
   // Called on every incoming packet. Passes |packet| through to |connection_|.
   virtual void ProcessUdpPacket(const QuicSocketAddress& self_address,
                                 const QuicSocketAddress& peer_address,
@@ -208,6 +215,11 @@
   // WINDOW_UPDATE arrives.
   void MarkConnectionLevelWriteBlocked(QuicStreamId id);
 
+  // Called when stream |id| is done waiting for acks either because all data
+  // gets acked or is not interested in data being acked (which happens when
+  // a stream is reset because of an error).
+  void OnStreamDoneWaitingForAcks(QuicStreamId id);
+
   // Returns true if the session has data to be sent, either queued in the
   // connection, or in a write-blocked stream.
   bool HasDataToWrite() const;
@@ -254,6 +266,8 @@
     respect_goaway_ = respect_goaway;
   }
 
+  bool use_stream_notifier() const { return use_stream_notifier_; }
+
  protected:
   using StaticStreamMap = QuicSmallMap<QuicStreamId, QuicStream*, 2>;
 
@@ -262,6 +276,9 @@
 
   using ClosedStreams = std::vector<std::unique_ptr<QuicStream>>;
 
+  using ZombieStreamMap =
+      QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>;
+
   // TODO(ckrasic) - For all *DynamicStream2 below, rename after
   // quic_reloadable_flag_quic_refactor_stream_creation is deprecated.
 
@@ -347,6 +364,8 @@
 
   ClosedStreams* closed_streams() { return &closed_streams_; }
 
+  const ZombieStreamMap& zombie_streams() const { return zombie_streams_; }
+
   void set_max_open_incoming_streams(size_t max_open_incoming_streams);
   void set_max_open_outgoing_streams(size_t max_open_outgoing_streams);
 
@@ -416,6 +435,10 @@
   // starting with larger flow control receive windows.
   void AdjustInitialFlowControlWindows(size_t stream_window);
 
+  // Find stream with |id|, returns nullptr if the stream does not exist or
+  // closed.
+  QuicStream* GetStream(QuicStreamId id) const;
+
   // Keep track of highest received byte offset of locally closed streams, while
   // waiting for a definitive final highest offset from the peer.
   std::map<QuicStreamId, QuicStreamOffset>
@@ -428,6 +451,10 @@
 
   ClosedStreams closed_streams_;
 
+  // Streams which are closed, but need to be kept alive. Currently, the only
+  // reason is the stream's sent data (including FIN) does not get fully acked.
+  ZombieStreamMap zombie_streams_;
+
   QuicConfig config_;
 
   // The maximum number of outgoing streams this connection can open.
@@ -485,6 +512,9 @@
   // chance they will fail.
   bool respect_goaway_;
 
+  // This session is notified on every ack or loss.
+  const bool use_stream_notifier_;
+
   DISALLOW_COPY_AND_ASSIGN(QuicSession);
 };
 
diff --git a/net/quic/core/quic_session_test.cc b/net/quic/core/quic_session_test.cc
index a502562..8a251a7 100644
--- a/net/quic/core/quic_session_test.cc
+++ b/net/quic/core/quic_session_test.cc
@@ -228,6 +228,8 @@
   }
 
   using QuicSession::PostProcessAfterData;
+  using QuicSession::closed_streams;
+  using QuicSession::zombie_streams;
 
  private:
   StrictMock<TestCryptoStream> crypto_stream_;
@@ -1346,6 +1348,24 @@
   }
 }
 
+TEST_P(QuicSessionTestServer, ZombieStreams) {
+  if (!session_.use_stream_notifier()) {
+    return;
+  }
+  TestStream* stream2 = session_.CreateOutgoingDynamicStream(kDefaultPriority);
+  QuicStreamPeer::SetStreamBytesWritten(3, stream2);
+  EXPECT_TRUE(stream2->IsWaitingForAcks());
+
+  EXPECT_CALL(*connection_, SendRstStream(2, _, _));
+  session_.CloseStream(2);
+  EXPECT_TRUE(QuicContainsKey(session_.zombie_streams(), 2));
+  EXPECT_TRUE(session_.closed_streams()->empty());
+  session_.OnStreamDoneWaitingForAcks(2);
+  EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), 2));
+  EXPECT_EQ(1u, session_.closed_streams()->size());
+  EXPECT_EQ(2u, session_.closed_streams()->front()->id());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/core/quic_spdy_session.cc b/net/quic/core/quic_spdy_session.cc
index ec533eade..be9aa55d 100644
--- a/net/quic/core/quic_spdy_session.cc
+++ b/net/quic/core/quic_spdy_session.cc
@@ -360,6 +360,9 @@
   for (auto& stream : *closed_streams()) {
     static_cast<QuicSpdyStream*>(stream.get())->ClearSession();
   }
+  for (auto const& kv : zombie_streams()) {
+    static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
+  }
   for (auto const& kv : dynamic_streams()) {
     static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
   }
diff --git a/net/quic/core/quic_spdy_stream_test.cc b/net/quic/core/quic_spdy_stream_test.cc
index 63aaa89..2a87a4d 100644
--- a/net/quic/core/quic_spdy_stream_test.cc
+++ b/net/quic/core/quic_spdy_stream_test.cc
@@ -11,11 +11,13 @@
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/core/quic_write_blocked_list.h"
 #include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_map_util.h"
 #include "net/quic/platform/api/quic_ptr_util.h"
 #include "net/quic/platform/api/quic_string_piece.h"
 #include "net/quic/platform/api/quic_test.h"
 #include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
 #include "net/quic/test_tools/quic_spdy_session_peer.h"
 #include "net/quic/test_tools/quic_stream_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
@@ -55,6 +57,7 @@
 
   using QuicStream::WriteOrBufferData;
   using QuicStream::CloseWriteSide;
+  using QuicSpdyStream::set_ack_listener;
 
   const string& data() const { return data_; }
 
@@ -963,6 +966,67 @@
                   "Trailers cannot be sent after a FIN");
 }
 
+TEST_P(QuicSpdyStreamTest, HeaderStreamNotiferCorrespondingSpdyStream) {
+  Initialize(kShouldProcessData);
+  if (!session_->use_stream_notifier()) {
+    return;
+  }
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+      .Times(AnyNumber())
+      .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
+  testing::InSequence s;
+  QuicReferenceCountedPointer<MockAckListener> ack_listener1(
+      new MockAckListener());
+  QuicReferenceCountedPointer<MockAckListener> ack_listener2(
+      new MockAckListener());
+  stream_->set_ack_listener(ack_listener1);
+  stream2_->set_ack_listener(ack_listener2);
+
+  session_->headers_stream()->WriteOrBufferData("Header1", false,
+                                                ack_listener1);
+  stream_->WriteOrBufferData("Test1", true, nullptr);
+
+  session_->headers_stream()->WriteOrBufferData("Header2", false,
+                                                ack_listener2);
+  stream2_->WriteOrBufferData("Test2", false, nullptr);
+
+  QuicStreamFrame frame1(kHeadersStreamId, false, 0, "Header1");
+  QuicStreamFrame frame2(stream_->id(), true, 0, "Test1");
+  QuicStreamFrame frame3(kHeadersStreamId, false, 7, "Header2");
+  QuicStreamFrame frame4(stream2_->id(), false, 0, "Test2");
+
+  EXPECT_CALL(*ack_listener1, OnPacketRetransmitted(7));
+  session_->OnStreamFrameRetransmitted(frame1);
+
+  EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
+  session_->OnStreamFrameAcked(frame1, QuicTime::Delta::Zero());
+  EXPECT_CALL(*ack_listener1, OnPacketAcked(5, _));
+  session_->OnStreamFrameAcked(frame2, QuicTime::Delta::Zero());
+  EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
+  session_->OnStreamFrameAcked(frame3, QuicTime::Delta::Zero());
+  EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _));
+  session_->OnStreamFrameAcked(frame4, QuicTime::Delta::Zero());
+}
+
+TEST_P(QuicSpdyStreamTest, StreamBecomesZombieWithWriteThatCloses) {
+  Initialize(kShouldProcessData);
+  if (!session_->use_stream_notifier()) {
+    return;
+  }
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+      .Times(AnyNumber())
+      .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
+  QuicStreamPeer::CloseReadSide(stream_);
+  // This write causes stream to be closed.
+  stream_->WriteOrBufferData("Test1", true, nullptr);
+  // stream_ has unacked data and should become zombie.
+  EXPECT_TRUE(QuicContainsKey(QuicSessionPeer::zombie_streams(session_.get()),
+                              stream_->id()));
+  EXPECT_TRUE(QuicSessionPeer::closed_streams(session_.get()).empty());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/core/quic_stream.cc b/net/quic/core/quic_stream.cc
index 733a805..95d91e4 100644
--- a/net/quic/core/quic_stream.cc
+++ b/net/quic/core/quic_stream.cc
@@ -54,12 +54,14 @@
       session_(session),
       stream_bytes_read_(0),
       stream_bytes_written_(0),
+      stream_bytes_acked_(0),
       stream_error_(QUIC_STREAM_NO_ERROR),
       connection_error_(QUIC_NO_ERROR),
       read_side_closed_(false),
       write_side_closed_(false),
       fin_buffered_(false),
       fin_sent_(false),
+      fin_acked_(false),
       fin_received_(false),
       rst_sent_(false),
       rst_received_(false),
@@ -74,11 +76,15 @@
       connection_flow_controller_(session_->flow_controller()),
       stream_contributes_to_connection_flow_control_(true),
       busy_counter_(0),
-      add_random_padding_after_fin_(false) {
+      add_random_padding_after_fin_(false),
+      ack_listener_(nullptr) {
   SetFromConfig();
 }
 
-QuicStream::~QuicStream() {}
+QuicStream::~QuicStream() {
+  QUIC_LOG_IF(WARNING, !IsWaitingForAcks())
+      << "Stream destroyed while waiting for acks.";
+}
 
 void QuicStream::SetFromConfig() {}
 
@@ -171,6 +177,9 @@
   // Sending a RstStream results in calling CloseStream.
   session()->SendRstStream(id(), error, stream_bytes_written_);
   rst_sent_ = true;
+  if (session()->use_stream_notifier() && !IsWaitingForAcks()) {
+    session_->OnStreamDoneWaitingForAcks(id_);
+  }
 }
 
 void QuicStream::CloseConnectionWithDetails(QuicErrorCode error,
@@ -492,4 +501,48 @@
   add_random_padding_after_fin_ = true;
 }
 
+void QuicStream::OnStreamFrameAcked(const QuicStreamFrame& frame,
+                                    QuicTime::Delta ack_delay_time) {
+  DCHECK_EQ(frame.stream_id, id());
+  stream_bytes_acked_ += frame.data_length;
+  if (stream_bytes_acked_ > stream_bytes_written_) {
+    CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+                               "Unsent stream data is acked");
+    return;
+  }
+  if (frame.fin) {
+    fin_acked_ = true;
+  }
+  if (ack_listener_ != nullptr) {
+    ack_listener_->OnPacketAcked(frame.data_length, ack_delay_time);
+  }
+  if (!IsWaitingForAcks()) {
+    session_->OnStreamDoneWaitingForAcks(id_);
+  }
+}
+
+void QuicStream::OnStreamFrameRetransmitted(const QuicStreamFrame& frame) {
+  if (ack_listener_ != nullptr) {
+    ack_listener_->OnPacketRetransmitted(frame.data_length);
+  }
+}
+
+bool QuicStream::IsWaitingForAcks() const {
+  if (rst_sent_ && stream_error_ != QUIC_STREAM_NO_ERROR) {
+    // RST_STREAM sent because of error.
+    return false;
+  }
+  if (connection_error_ != QUIC_NO_ERROR) {
+    // Connection encounters error and is going to close.
+    return false;
+  }
+  if (stream_bytes_acked_ == stream_bytes_written_ &&
+      ((fin_sent_ && fin_acked_) || !fin_sent_)) {
+    // All sent data has been acked.
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace net
diff --git a/net/quic/core/quic_stream.h b/net/quic/core/quic_stream.h
index df1ebf02..e88c684b 100644
--- a/net/quic/core/quic_stream.h
+++ b/net/quic/core/quic_stream.h
@@ -29,6 +29,7 @@
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/core/quic_stream_sequencer.h"
 #include "net/quic/core/quic_types.h"
+#include "net/quic/core/stream_notifier_interface.h"
 #include "net/quic/platform/api/quic_export.h"
 #include "net/quic/platform/api/quic_reference_counted.h"
 #include "net/quic/platform/api/quic_string_piece.h"
@@ -41,11 +42,11 @@
 
 class QuicSession;
 
-class QUIC_EXPORT_PRIVATE QuicStream {
+class QUIC_EXPORT_PRIVATE QuicStream : public StreamNotifierInterface {
  public:
   QuicStream(QuicStreamId id, QuicSession* session);
 
-  virtual ~QuicStream();
+  ~QuicStream() override;
 
   // Not in use currently.
   void SetFromConfig();
@@ -91,6 +92,12 @@
   virtual void CloseConnectionWithDetails(QuicErrorCode error,
                                           const std::string& details);
 
+  // Returns true if this stream is still waiting for acks of sent data.
+  // This will return false if all data has been acked, or if the stream
+  // is no longer interested in data being acked (which happens when
+  // a stream is reset because of an error).
+  bool IsWaitingForAcks() const;
+
   QuicStreamId id() const { return id_; }
 
   QuicRstStreamErrorCode stream_error() const { return stream_error_; }
@@ -189,6 +196,11 @@
   // Adds random padding after the fin is consumed for this stream.
   void AddRandomPaddingAfterFin();
 
+  // StreamNotifierInterface methods:
+  void OnStreamFrameAcked(const QuicStreamFrame& frame,
+                          QuicTime::Delta ack_delay_time) override;
+  void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override;
+
  protected:
   // Sends as many bytes in the first |count| buffers of |iov| to the connection
   // as the connection will consume.
@@ -226,6 +238,11 @@
     stream_contributes_to_connection_flow_control_ = false;
   }
 
+  void set_ack_listener(
+      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+    ack_listener_ = std::move(ack_listener);
+  }
+
  private:
   friend class test::QuicStreamPeer;
   friend class QuicStreamUtils;
@@ -270,6 +287,8 @@
   // framing, encryption overhead etc.
   uint64_t stream_bytes_read_;
   uint64_t stream_bytes_written_;
+  // Written bytes which have been acked.
+  uint64_t stream_bytes_acked_;
 
   // Stream error code received from a RstStreamFrame or error code sent by the
   // visitor or sequencer in the RstStreamFrame.
@@ -289,6 +308,8 @@
   bool fin_buffered_;
   // True if a FIN has been sent to the session.
   bool fin_sent_;
+  // True if a FIN has been acked.
+  bool fin_acked_;
 
   // True if this stream has received (and the sequencer has accepted) a
   // StreamFrame with the FIN set.
@@ -324,6 +345,10 @@
   // stream.
   bool add_random_padding_after_fin_;
 
+  // Ack listener of this stream, and it is notified when any of written bytes
+  // are acked.
+  QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener_;
+
   DISALLOW_COPY_AND_ASSIGN(QuicStream);
 };
 
diff --git a/net/quic/core/quic_stream_test.cc b/net/quic/core/quic_stream_test.cc
index 6e83dae..a2aebf5 100644
--- a/net/quic/core/quic_stream_test.cc
+++ b/net/quic/core/quic_stream_test.cc
@@ -714,6 +714,124 @@
   EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
 }
 
+TEST_F(QuicStreamTest, StreamWaitsForAcks) {
+  Initialize(kShouldProcessData);
+  if (!session_->use_stream_notifier()) {
+    return;
+  }
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+      .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
+  // Stream is not waiting for acks initially.
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+  // Send kData1.
+  stream_->WriteOrBufferData(kData1, false, nullptr);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+  QuicStreamFrame frame1(stream_->id(), false, 0, kData1);
+  stream_->OnStreamFrameAcked(frame1, QuicTime::Delta::Zero());
+  // Stream is not waiting for acks as all sent data is acked.
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+  // Send kData2.
+  stream_->WriteOrBufferData(kData2, false, nullptr);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+  // Send FIN.
+  stream_->WriteOrBufferData("", true, nullptr);
+
+  // kData2 is acked.
+  QuicStreamFrame frame2(stream_->id(), false, 9, kData2);
+  stream_->OnStreamFrameAcked(frame2, QuicTime::Delta::Zero());
+  // Stream is waiting for acks as FIN is not acked.
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+  // FIN is acked.
+  QuicStreamFrame frame3(stream_->id(), true, 18, "");
+  stream_->OnStreamFrameAcked(frame3, QuicTime::Delta::Zero());
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+TEST_F(QuicStreamTest, CancelStream) {
+  Initialize(kShouldProcessData);
+  if (!session_->use_stream_notifier()) {
+    return;
+  }
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+      .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+  stream_->WriteOrBufferData(kData1, false, nullptr);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+  // Cancel stream.
+  stream_->Reset(QUIC_STREAM_NO_ERROR);
+  // stream still waits for acks as the error code is QUIC_STREAM_NO_ERROR, and
+  // data is going to be retransmitted.
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+  stream_->Reset(QUIC_STREAM_CANCELLED);
+  // Stream stops waiting for acks as data is not going to be retransmitted.
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+TEST_F(QuicStreamTest, RstFrameReceivedStreamNotFinishSending) {
+  Initialize(kShouldProcessData);
+  if (!session_->use_stream_notifier()) {
+    return;
+  }
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+      .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+  stream_->WriteOrBufferData(kData1, false, nullptr);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+  // RST_STREAM received.
+  QuicRstStreamFrame rst_frame(stream_->id(), QUIC_STREAM_CANCELLED, 1234);
+  stream_->OnStreamReset(rst_frame);
+  // Stream stops waiting for acks as it does not finish sending and rst is
+  // sent.
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+TEST_F(QuicStreamTest, RstFrameReceivedStreamFinishSending) {
+  Initialize(kShouldProcessData);
+  if (!session_->use_stream_notifier()) {
+    return;
+  }
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+      .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+  stream_->WriteOrBufferData(kData1, true, nullptr);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+  // RST_STREAM received.
+  QuicRstStreamFrame rst_frame(stream_->id(), QUIC_STREAM_CANCELLED, 1234);
+  stream_->OnStreamReset(rst_frame);
+  // Stream stops waiting for acks as it has unacked data.
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+}
+
+TEST_F(QuicStreamTest, ConnectionClosed) {
+  Initialize(kShouldProcessData);
+  if (!session_->use_stream_notifier()) {
+    return;
+  }
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+      .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+  stream_->WriteOrBufferData(kData1, false, nullptr);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+  stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR,
+                              ConnectionCloseSource::FROM_SELF);
+  // Stream stops waiting for acks as connection is going to close.
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/core/quic_unacked_packet_map.cc b/net/quic/core/quic_unacked_packet_map.cc
index 8c5fbf1..4c5f8b2d 100644
--- a/net/quic/core/quic_unacked_packet_map.cc
+++ b/net/quic/core/quic_unacked_packet_map.cc
@@ -16,7 +16,8 @@
       largest_observed_(0),
       least_unacked_(1),
       bytes_in_flight_(0),
-      pending_crypto_packet_count_(0) {}
+      pending_crypto_packet_count_(0),
+      stream_notifier_(nullptr) {}
 
 QuicUnackedPacketMap::~QuicUnackedPacketMap() {
   for (QuicTransmissionInfo& transmission_info : unacked_packets_) {
@@ -102,6 +103,13 @@
   QuicTransmissionInfo* transmission_info =
       &unacked_packets_.at(old_packet_number - least_unacked_);
   QuicFrames* frames = &transmission_info->retransmittable_frames;
+  if (stream_notifier_ != nullptr) {
+    for (const QuicFrame& frame : *frames) {
+      if (frame.type == STREAM_FRAME) {
+        stream_notifier_->OnStreamFrameRetransmitted(*frame.stream_frame);
+      }
+    }
+  }
   for (AckListenerWrapper& wrapper : transmission_info->ack_listeners) {
     wrapper.ack_listener->OnPacketRetransmitted(wrapper.length);
   }
@@ -350,4 +358,23 @@
   return least_unacked_;
 }
 
+void QuicUnackedPacketMap::SetStreamNotifier(
+    StreamNotifierInterface* stream_notifier) {
+  stream_notifier_ = stream_notifier;
+}
+
+void QuicUnackedPacketMap::NotifyStreamFramesAcked(
+    const QuicTransmissionInfo& info,
+    QuicTime::Delta ack_delay) {
+  if (stream_notifier_ == nullptr) {
+    return;
+  }
+
+  for (const QuicFrame& frame : info.retransmittable_frames) {
+    if (frame.type == STREAM_FRAME) {
+      stream_notifier_->OnStreamFrameAcked(*frame.stream_frame, ack_delay);
+    }
+  }
+}
+
 }  // namespace net
diff --git a/net/quic/core/quic_unacked_packet_map.h b/net/quic/core/quic_unacked_packet_map.h
index 1901f49e..70549b5 100644
--- a/net/quic/core/quic_unacked_packet_map.h
+++ b/net/quic/core/quic_unacked_packet_map.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/core/quic_transmission_info.h"
+#include "net/quic/core/stream_notifier_interface.h"
 #include "net/quic/platform/api/quic_export.h"
 
 namespace net {
@@ -50,6 +51,10 @@
   void NotifyAndClearListeners(QuicPacketNumber newest_transmission,
                                QuicTime::Delta delta_largest_observed);
 
+  // Notifies stream_notifier that stream frames have been acked.
+  void NotifyStreamFramesAcked(const QuicTransmissionInfo& info,
+                               QuicTime::Delta ack_delay);
+
   // Marks |info| as no longer in flight.
   void RemoveFromInFlight(QuicTransmissionInfo* info);
 
@@ -146,6 +151,8 @@
   // RTT measurement purposes.
   void RemoveObsoletePackets();
 
+  void SetStreamNotifier(StreamNotifierInterface* stream_notifier);
+
  private:
   // Called when a packet is retransmitted with a new packet number.
   // |old_packet_number| will remain unacked, but will have no
@@ -194,6 +201,10 @@
   // Number of retransmittable crypto handshake packets.
   size_t pending_crypto_packet_count_;
 
+  // Receives notifications of stream frames being retransmitted or
+  // acknowledged.
+  StreamNotifierInterface* stream_notifier_;
+
   DISALLOW_COPY_AND_ASSIGN(QuicUnackedPacketMap);
 };
 
diff --git a/net/quic/core/stream_notifier_interface.h b/net/quic/core/stream_notifier_interface.h
new file mode 100644
index 0000000..34d90be9
--- /dev/null
+++ b/net/quic/core/stream_notifier_interface.h
@@ -0,0 +1,29 @@
+// 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_QUIC_CORE_STREAM_NOTIFIER_INTERFACE_H_
+#define NET_QUIC_CORE_STREAM_NOTIFIER_INTERFACE_H_
+
+#include "net/quic/core/frames/quic_stream_frame.h"
+#include "net/quic/core/quic_time.h"
+
+namespace net {
+
+// Pure virtual class to be notified when a packet containing a stream frame is
+// acked or lost.
+class QUIC_EXPORT_PRIVATE StreamNotifierInterface {
+ public:
+  virtual ~StreamNotifierInterface() {}
+
+  // Called when |frame| is acked.
+  virtual void OnStreamFrameAcked(const QuicStreamFrame& frame,
+                                  QuicTime::Delta ack_delay_time) = 0;
+
+  // Called when |frame| is retransmitted.
+  virtual void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) = 0;
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_CORE_STREAM_NOTIFIER_INTERFACE_H_
diff --git a/net/quic/quartc/quartc_factory.cc b/net/quic/quartc/quartc_factory.cc
index 2591214..c65a86e 100644
--- a/net/quic/quartc/quartc_factory.cc
+++ b/net/quic/quartc/quartc_factory.cc
@@ -112,7 +112,14 @@
                                 : Perspective::IS_CLIENT;
   std::unique_ptr<QuicConnection> quic_connection =
       CreateQuicConnection(quartc_session_config, perspective);
+  QuicTagVector copt;
+  if (quartc_session_config.congestion_control ==
+      QuartcCongestionControl::kBBR) {
+    copt.push_back(kTBBR);
+  }
   QuicConfig quic_config;
+  quic_config.SetConnectionOptionsToSend(copt);
+  quic_config.SetClientConnectionOptions(copt);
   return std::unique_ptr<QuartcSessionInterface>(new QuartcSession(
       std::move(quic_connection), quic_config,
       quartc_session_config.unique_remote_server_id, perspective,
diff --git a/net/quic/quartc/quartc_factory.h b/net/quic/quartc/quartc_factory.h
index d6601d9..99268a5 100644
--- a/net/quic/quartc/quartc_factory.h
+++ b/net/quic/quartc/quartc_factory.h
@@ -18,9 +18,9 @@
 // QuartcSessionInterface. Implements the QuicAlarmFactory to create alarms
 // using the QuartcTaskRunner. Implements the QuicConnectionHelperInterface used
 // by the QuicConnections. Only one QuartcFactory is expected to be created.
-class QuartcFactory : public QuartcFactoryInterface,
-                      public QuicAlarmFactory,
-                      public QuicConnectionHelperInterface {
+class QUIC_EXPORT_PRIVATE QuartcFactory : public QuartcFactoryInterface,
+                                          public QuicAlarmFactory,
+                                          public QuicConnectionHelperInterface {
  public:
   QuartcFactory(const QuartcFactoryConfig& factory_config);
   ~QuartcFactory() override;
diff --git a/net/quic/quartc/quartc_factory_interface.h b/net/quic/quartc/quartc_factory_interface.h
index 1d4db2f..d9e6c1f 100644
--- a/net/quic/quartc/quartc_factory_interface.h
+++ b/net/quic/quartc/quartc_factory_interface.h
@@ -16,8 +16,14 @@
 
 namespace net {
 
+// Algorithm to use for congestion control.
+enum class QuartcCongestionControl {
+  kDefault,  // Use an arbitrary algorithm chosen by QUIC.
+  kBBR,      // Use BBR.
+};
+
 // Used to create instances for Quartc objects such as QuartcSession.
-class QuartcFactoryInterface {
+class QUIC_EXPORT_PRIVATE QuartcFactoryInterface {
  public:
   virtual ~QuartcFactoryInterface() {}
 
@@ -36,6 +42,10 @@
     // The maximum size of the packet can be written with the packet writer.
     // 1200 bytes by default.
     uint64_t max_packet_size = 1200;
+    // Algorithm to use for congestion control.  By default, uses an arbitrary
+    // congestion control algorithm chosen by QUIC.
+    QuartcCongestionControl congestion_control =
+        QuartcCongestionControl::kDefault;
   };
 
   virtual std::unique_ptr<QuartcSessionInterface> CreateQuartcSession(
diff --git a/net/quic/quartc/quartc_packet_writer.h b/net/quic/quartc/quartc_packet_writer.h
index 0eda2710..6a18cd4 100644
--- a/net/quic/quartc/quartc_packet_writer.h
+++ b/net/quic/quartc/quartc_packet_writer.h
@@ -13,7 +13,7 @@
 // Implements a QuicPacketWriter using a
 // QuartcSessionInterface::PacketTransport, which allows a QuicConnection to
 // use(for example), a WebRTC IceTransport.
-class QuartcPacketWriter : public QuicPacketWriter {
+class QUIC_EXPORT_PRIVATE QuartcPacketWriter : public QuicPacketWriter {
  public:
   QuartcPacketWriter(QuartcSessionInterface::PacketTransport* packet_transport,
                      QuicByteCount max_packet_size);
diff --git a/net/quic/quartc/quartc_session.cc b/net/quic/quartc/quartc_session.cc
index 0e839c9..7e5c5af6 100644
--- a/net/quic/quartc/quartc_session.cc
+++ b/net/quic/quartc/quartc_session.cc
@@ -149,7 +149,8 @@
 
 QuartcStream* QuartcSession::CreateOutgoingDynamicStream(
     SpdyPriority priority) {
-  return CreateDataStream(GetNextOutgoingStreamId(), priority);
+  return ActivateDataStream(
+      CreateDataStream(GetNextOutgoingStreamId(), priority));
 }
 
 void QuartcSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
@@ -173,6 +174,21 @@
   QuicSession::CloseStream(stream_id);
 }
 
+void QuartcSession::CancelStream(QuicStreamId stream_id) {
+  ResetStream(stream_id, QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
+}
+
+void QuartcSession::ResetStream(QuicStreamId stream_id,
+                                QuicRstStreamErrorCode error) {
+  if (!IsOpenStream(stream_id)) {
+    return;
+  }
+  QuicStream* stream = QuicSession::GetOrCreateStream(stream_id);
+  if (stream) {
+    stream->Reset(error);
+  }
+}
+
 void QuartcSession::OnConnectionClosed(QuicErrorCode error,
                                        const string& error_details,
                                        ConnectionCloseSource source) {
@@ -267,35 +283,50 @@
 }
 
 QuicStream* QuartcSession::CreateIncomingDynamicStream(QuicStreamId id) {
-  QuartcStream* stream = CreateDataStream(id, kDefaultPriority);
-  if (stream) {
-    DCHECK(session_delegate_);
-    session_delegate_->OnIncomingStream(stream);
-  }
-  return stream;
+  return ActivateDataStream(CreateDataStream(id, kDefaultPriority));
 }
 
 std::unique_ptr<QuicStream> QuartcSession::CreateStream(QuicStreamId id) {
-  QuartcStream* stream = CreateDataStream(id, kDefaultPriority);
-  return QuicWrapUnique(stream);
+  return CreateDataStream(id, kDefaultPriority);
 }
 
-QuartcStream* QuartcSession::CreateDataStream(QuicStreamId id,
-                                              SpdyPriority priority) {
+std::unique_ptr<QuartcStream> QuartcSession::CreateDataStream(
+    QuicStreamId id,
+    SpdyPriority priority) {
   if (crypto_stream_ == nullptr || !crypto_stream_->encryption_established()) {
     // Encryption not active so no stream created
     return nullptr;
   }
-  QuartcStream* stream = new QuartcStream(id, this);
+  auto stream = QuicMakeUnique<QuartcStream>(id, this);
   if (stream) {
-    // Make QuicSession take ownership of the stream.
-    ActivateStream(std::unique_ptr<QuicStream>(stream));
     // Register the stream to the QuicWriteBlockedList. |priority| is clamped
     // between 0 and 7, with 0 being the highest priority and 7 the lowest
     // priority.
     write_blocked_streams()->RegisterStream(stream->id(), priority);
+
+    if (IsIncomingStream(id)) {
+      DCHECK(session_delegate_);
+      // Incoming streams need to be registered with the session_delegate_.
+      session_delegate_->OnIncomingStream(stream.get());
+      // Quartc doesn't send on incoming streams.
+      stream->set_fin_sent(true);
+    } else {
+      // Quartc doesn't receive on outgoing streams.
+      stream->set_fin_received(true);
+    }
   }
   return stream;
 }
 
+QuartcStream* QuartcSession::ActivateDataStream(
+    std::unique_ptr<QuartcStream> stream) {
+  // Transfer ownership of the data stream to the session via ActivateStream().
+  QuartcStream* raw = stream.release();
+  if (raw) {
+    // Make QuicSession take ownership of the stream.
+    ActivateStream(std::unique_ptr<QuicStream>(raw));
+  }
+  return raw;
+}
+
 }  // namespace net
diff --git a/net/quic/quartc/quartc_session.h b/net/quic/quartc/quartc_session.h
index 98a3650..de347aa 100644
--- a/net/quic/quartc/quartc_session.h
+++ b/net/quic/quartc/quartc_session.h
@@ -8,6 +8,7 @@
 #include "net/quic/core/quic_crypto_client_stream.h"
 #include "net/quic/core/quic_crypto_server_stream.h"
 #include "net/quic/core/quic_crypto_stream.h"
+#include "net/quic/core/quic_error_codes.h"
 #include "net/quic/core/quic_session.h"
 #include "net/quic/quartc/quartc_clock_interface.h"
 #include "net/quic/quartc/quartc_session_interface.h"
@@ -26,9 +27,10 @@
                             std::string* error_details) const override;
 };
 
-class QuartcSession : public QuicSession,
-                      public QuartcSessionInterface,
-                      public QuicCryptoClientStream::ProofHandler {
+class QUIC_EXPORT_PRIVATE QuartcSession
+    : public QuicSession,
+      public QuartcSessionInterface,
+      public QuicCryptoClientStream::ProofHandler {
  public:
   QuartcSession(std::unique_ptr<QuicConnection> connection,
                 const QuicConfig& config,
@@ -67,6 +69,8 @@
   QuartcStreamInterface* CreateOutgoingStream(
       const OutgoingStreamParameters& param) override;
 
+  void CancelStream(QuicStreamId stream_id) override;
+
   void SetDelegate(QuartcSessionInterface::Delegate* session_delegate) override;
 
   void OnTransportCanWrite() override;
@@ -94,7 +98,13 @@
   QuicStream* CreateIncomingDynamicStream(QuicStreamId id) override;
   std::unique_ptr<QuicStream> CreateStream(QuicStreamId id) override;
 
-  QuartcStream* CreateDataStream(QuicStreamId id, SpdyPriority priority);
+  std::unique_ptr<QuartcStream> CreateDataStream(QuicStreamId id,
+                                                 SpdyPriority priority);
+  // Activates a QuartcStream.  The session takes ownership of the stream, but
+  // returns an unowned pointer to the stream for convenience.
+  QuartcStream* ActivateDataStream(std::unique_ptr<QuartcStream> stream);
+
+  void ResetStream(QuicStreamId stream_id, QuicRstStreamErrorCode error);
 
  private:
   // For crypto handshake.
diff --git a/net/quic/quartc/quartc_session_interface.h b/net/quic/quartc/quartc_session_interface.h
index f4d88453..03996e6 100644
--- a/net/quic/quartc/quartc_session_interface.h
+++ b/net/quic/quartc/quartc_session_interface.h
@@ -9,6 +9,8 @@
 #include <stdint.h>
 #include <string>
 
+#include "net/quic/core/quic_error_codes.h"
+#include "net/quic/core/quic_types.h"
 #include "net/quic/quartc/quartc_stream_interface.h"
 
 namespace net {
@@ -16,7 +18,7 @@
 // Given a PacketTransport, provides a way to send and receive separate streams
 // of reliable, in-order, encrypted data. For example, this can build on top of
 // a WebRTC IceTransport for sending and receiving data over QUIC.
-class QuartcSessionInterface {
+class QUIC_EXPORT_PRIVATE QuartcSessionInterface {
  public:
   virtual ~QuartcSessionInterface() {}
 
@@ -48,6 +50,13 @@
   virtual QuartcStreamInterface* CreateOutgoingStream(
       const OutgoingStreamParameters& params) = 0;
 
+  // If the given stream is still open, sends a reset frame to cancel it.
+  // Note:  This method cancels a stream by QuicStreamId rather than by pointer
+  // (or by a method on QuartcStreamInterface) because QuartcSession (and not
+  // the caller) owns the streams.  Streams may finish and be deleted before the
+  // caller tries to cancel them, rendering the caller's pointers invalid.
+  virtual void CancelStream(QuicStreamId stream_id) = 0;
+
   // Send and receive packets, like a virtual UDP socket. For example, this
   // could be implemented by WebRTC's IceTransport.
   class PacketTransport {
diff --git a/net/quic/quartc/quartc_session_test.cc b/net/quic/quartc/quartc_session_test.cc
new file mode 100644
index 0000000..a4fc6232
--- /dev/null
+++ b/net/quic/quartc/quartc_session_test.cc
@@ -0,0 +1,641 @@
+// 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/quic/quartc/quartc_session.h"
+
+#include "net/quic/core/crypto/crypto_server_config_protobuf.h"
+#include "net/quic/core/quic_simple_buffer_allocator.h"
+#include "net/quic/core/quic_types.h"
+#include "net/quic/quartc/quartc_factory.h"
+#include "net/quic/quartc/quartc_factory_interface.h"
+#include "net/quic/quartc/quartc_packet_writer.h"
+#include "net/quic/quartc/quartc_stream_interface.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+
+namespace {
+
+static const char kExporterLabel[] = "label";
+static const uint8_t kExporterContext[] = "context";
+static const size_t kExporterContextLen = sizeof(kExporterContext);
+static const size_t kOutputKeyLength = 20;
+static QuartcStreamInterface::WriteParameters kDefaultWriteParam;
+static QuartcSessionInterface::OutgoingStreamParameters kDefaultStreamParam;
+static QuicByteCount kDefaultMaxPacketSize = 1200;
+
+// Single-threaded scheduled task runner based on a MockClock.
+//
+// Simulates asynchronous execution on a single thread by holding scheduled
+// tasks until Run() is called. Performs no synchronization, assumes that
+// Schedule() and Run() are called on the same thread.
+class FakeTaskRunner : public QuartcTaskRunnerInterface {
+ public:
+  explicit FakeTaskRunner(MockClock* clock)
+      : tasks_([this](const TaskType& l, const TaskType& r) {
+          // Items at a later time should run after items at an earlier time.
+          // Priority queue comparisons should return true if l appears after r.
+          return l->time() > r->time();
+        }),
+        clock_(clock) {}
+
+  ~FakeTaskRunner() override {}
+
+  // Runs all tasks scheduled in the next total_ms milliseconds.  Advances the
+  // clock by total_ms.  Runs tasks in time order.  Executes tasks scheduled at
+  // the same in an arbitrary order.
+  void Run(uint32_t total_ms) {
+    for (uint32_t i = 0; i < total_ms; ++i) {
+      while (!tasks_.empty() && tasks_.top()->time() <= clock_->Now()) {
+        tasks_.top()->Run();
+        tasks_.pop();
+      }
+      clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+    }
+  }
+
+ private:
+  class InnerTask {
+   public:
+    InnerTask(std::function<void()> task, QuicTime time)
+        : task_(std::move(task)), time_(time) {}
+
+    void Cancel() { cancelled_ = true; }
+
+    void Run() {
+      if (!cancelled_) {
+        task_();
+      }
+    }
+
+    QuicTime time() const { return time_; }
+
+   private:
+    bool cancelled_ = false;
+    std::function<void()> task_;
+    QuicTime time_;
+  };
+
+ public:
+  // Hook for cancelling a scheduled task.
+  class ScheduledTask : public QuartcTaskRunnerInterface::ScheduledTask {
+   public:
+    explicit ScheduledTask(std::shared_ptr<InnerTask> inner)
+        : inner_(std::move(inner)) {}
+
+    // Cancel if the caller deletes the ScheduledTask.  This behavior is
+    // consistent with the actual task runner Quartc uses.
+    ~ScheduledTask() override { Cancel(); }
+
+    // ScheduledTask implementation.
+    void Cancel() override { inner_->Cancel(); }
+
+   private:
+    std::shared_ptr<InnerTask> inner_;
+  };
+
+  // See QuartcTaskRunnerInterface.
+  std::unique_ptr<QuartcTaskRunnerInterface::ScheduledTask> Schedule(
+      Task* task,
+      uint64_t delay_ms) override {
+    auto inner = std::shared_ptr<InnerTask>(new InnerTask(
+        [task] { task->Run(); },
+        clock_->Now() + QuicTime::Delta::FromMilliseconds(delay_ms)));
+    tasks_.push(inner);
+    return std::unique_ptr<QuartcTaskRunnerInterface::ScheduledTask>(
+        new ScheduledTask(inner));
+  }
+
+  // Schedules a function to run immediately.
+  void Schedule(std::function<void()> task) {
+    tasks_.push(std::shared_ptr<InnerTask>(
+        new InnerTask(std::move(task), clock_->Now())));
+  }
+
+ private:
+  // InnerTasks are shared by the queue and ScheduledTask (which hooks into it
+  // to implement Cancel()).
+  using TaskType = std::shared_ptr<InnerTask>;
+  std::priority_queue<TaskType,
+                      std::vector<TaskType>,
+                      std::function<bool(const TaskType&, const TaskType&)>>
+      tasks_;
+  MockClock* clock_;
+};
+
+// QuartcClock that wraps a MockClock.
+//
+// This is silly because Quartc wraps it as a QuicClock, and MockClock is
+// already a QuicClock.  But we don't have much choice.  We need to pass a
+// QuartcClockInterface into the Quartc wrappers.
+class MockQuartcClock : public QuartcClockInterface {
+ public:
+  explicit MockQuartcClock(MockClock* clock) : clock_(clock) {}
+
+  int64_t NowMicroseconds() override {
+    return clock_->WallNow().ToUNIXMicroseconds();
+  }
+
+ private:
+  MockClock* clock_;
+};
+
+// Used by QuicCryptoServerConfig to provide server credentials, returning a
+// canned response equal to |success|.
+class FakeProofSource : public ProofSource {
+ public:
+  explicit FakeProofSource(bool success) : success_(success) {}
+
+  // ProofSource override.
+  void GetProof(const QuicSocketAddress& server_ip,
+                const string& hostname,
+                const string& server_config,
+                QuicVersion quic_version,
+                QuicStringPiece chlo_hash,
+                const QuicTagVector& connection_options,
+                std::unique_ptr<Callback> callback) override {
+    QuicReferenceCountedPointer<ProofSource::Chain> chain;
+    QuicCryptoProof proof;
+    if (success_) {
+      std::vector<string> certs;
+      certs.push_back("Required to establish handshake");
+      chain = new ProofSource::Chain(certs);
+      proof.signature = "Signature";
+      proof.leaf_cert_scts = "Time";
+    }
+    callback->Run(success_, chain, proof, nullptr /* details */);
+  }
+
+ private:
+  // Whether or not obtaining proof source succeeds.
+  bool success_;
+};
+
+// Used by QuicCryptoClientConfig to verify server credentials, returning a
+// canned response of QUIC_SUCCESS if |success| is true.
+class FakeProofVerifier : public ProofVerifier {
+ public:
+  explicit FakeProofVerifier(bool success) : success_(success) {}
+
+  // ProofVerifier override
+  QuicAsyncStatus VerifyProof(
+      const string& hostname,
+      const uint16_t port,
+      const string& server_config,
+      QuicVersion quic_version,
+      QuicStringPiece chlo_hash,
+      const std::vector<string>& certs,
+      const string& cert_sct,
+      const string& signature,
+      const ProofVerifyContext* context,
+      string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* verify_details,
+      std::unique_ptr<ProofVerifierCallback> callback) override {
+    return success_ ? QUIC_SUCCESS : QUIC_FAILURE;
+  }
+
+  QuicAsyncStatus VerifyCertChain(
+      const string& hostname,
+      const std::vector<string>& certs,
+      const ProofVerifyContext* context,
+      string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* details,
+      std::unique_ptr<ProofVerifierCallback> callback) override {
+    LOG(INFO) << "VerifyProof() ignoring credentials and returning success";
+    return success_ ? QUIC_SUCCESS : QUIC_FAILURE;
+  }
+
+ private:
+  // Whether or not proof verification succeeds.
+  bool success_;
+};
+
+// Used by the FakeTransportChannel.
+class FakeTransportChannelObserver {
+ public:
+  virtual ~FakeTransportChannelObserver() {}
+
+  // Called when the other peer is trying to send message.
+  virtual void OnTransportChannelReadPacket(const string& data) = 0;
+};
+
+// Simulate the P2P communication transport. Used by the
+// QuartcSessionInterface::Transport.
+class FakeTransportChannel {
+ public:
+  explicit FakeTransportChannel(FakeTaskRunner* task_runner, MockClock* clock)
+      : task_runner_(task_runner), clock_(clock) {}
+
+  void SetDestination(FakeTransportChannel* dest) {
+    if (!dest_) {
+      dest_ = dest;
+      dest_->SetDestination(this);
+    }
+  }
+
+  int SendPacket(const char* data, size_t len) {
+    // If the destination is not set.
+    if (!dest_) {
+      return -1;
+    }
+    // Advance the time 10us to ensure the RTT is never 0ms.
+    clock_->AdvanceTime(QuicTime::Delta::FromMicroseconds(10));
+    if (async_ && task_runner_) {
+      string packet(data, len);
+      task_runner_->Schedule([this, packet] { send(packet); });
+    } else {
+      send(string(data, len));
+    }
+    return static_cast<int>(len);
+  }
+
+  void send(const string& data) {
+    DCHECK(dest_);
+    DCHECK(dest_->observer());
+    dest_->observer()->OnTransportChannelReadPacket(data);
+  }
+
+  FakeTransportChannelObserver* observer() { return observer_; }
+
+  void SetObserver(FakeTransportChannelObserver* observer) {
+    observer_ = observer;
+  }
+
+  void SetAsync(bool async) { async_ = async; }
+
+ private:
+  // The writing destination of this channel.
+  FakeTransportChannel* dest_ = nullptr;
+  // The observer of this channel. Called when the received the data.
+  FakeTransportChannelObserver* observer_ = nullptr;
+  // If async, will send packets by running asynchronous tasks.
+  bool async_ = false;
+  // Used to send data asynchronously.
+  FakeTaskRunner* task_runner_;
+  // The test clock.  Used to ensure the RTT is not 0.
+  MockClock* clock_;
+};
+
+// Used by the QuartcPacketWriter.
+class FakeTransport : public QuartcSessionInterface::PacketTransport {
+ public:
+  explicit FakeTransport(FakeTransportChannel* channel) : channel_(channel) {}
+
+  bool CanWrite() override { return true; }
+
+  int Write(const char* buffer, size_t buf_len) override {
+    DCHECK(channel_);
+    return channel_->SendPacket(buffer, buf_len);
+  }
+
+ private:
+  FakeTransportChannel* channel_;
+};
+
+class FakeQuartcSessionDelegate : public QuartcSessionInterface::Delegate {
+ public:
+  explicit FakeQuartcSessionDelegate(
+      QuartcStreamInterface::Delegate* stream_delegate)
+      : stream_delegate_(stream_delegate) {}
+  // Called when peers have established forward-secure encryption
+  void OnCryptoHandshakeComplete() override {
+    LOG(INFO) << "Crypto handshake complete!";
+  }
+  // Called when connection closes locally, or remotely by peer.
+  void OnConnectionClosed(int error_code, bool from_remote) override {
+    connected_ = false;
+  }
+  // Called when an incoming QUIC stream is created.
+  void OnIncomingStream(QuartcStreamInterface* quartc_stream) override {
+    last_incoming_stream_ = quartc_stream;
+    last_incoming_stream_->SetDelegate(stream_delegate_);
+  }
+
+  QuartcStreamInterface* incoming_stream() { return last_incoming_stream_; }
+
+  bool connected() { return connected_; }
+
+ private:
+  QuartcStreamInterface* last_incoming_stream_;
+  bool connected_ = true;
+  QuartcStream::Delegate* stream_delegate_;
+};
+
+class FakeQuartcStreamDelegate : public QuartcStreamInterface::Delegate {
+ public:
+  void OnReceived(QuartcStreamInterface* stream,
+                  const char* data,
+                  size_t size) override {
+    last_received_data_ = string(data, size);
+  }
+
+  void OnClose(QuartcStreamInterface* stream) override {}
+
+  void OnBufferedAmountDecrease(QuartcStreamInterface* stream) override {}
+
+  string data() { return last_received_data_; }
+
+ private:
+  string last_received_data_;
+};
+
+class QuartcSessionForTest : public QuartcSession,
+                             public FakeTransportChannelObserver {
+ public:
+  QuartcSessionForTest(std::unique_ptr<QuicConnection> connection,
+                       const QuicConfig& config,
+                       const string& remote_fingerprint_value,
+                       Perspective perspective,
+                       QuicConnectionHelperInterface* helper,
+                       QuicClock* clock)
+      : QuartcSession(std::move(connection),
+                      config,
+                      remote_fingerprint_value,
+                      perspective,
+                      helper,
+                      clock) {
+    stream_delegate_.reset(new FakeQuartcStreamDelegate);
+    session_delegate_.reset(
+        new FakeQuartcSessionDelegate(stream_delegate_.get()));
+
+    SetDelegate(session_delegate_.get());
+  }
+
+  // QuartcPacketWriter override.
+  void OnTransportChannelReadPacket(const string& data) override {
+    OnTransportReceived(data.c_str(), data.length());
+  }
+
+  string data() { return stream_delegate_->data(); }
+
+  bool has_data() { return !data().empty(); }
+
+  FakeQuartcSessionDelegate* session_delegate() {
+    return session_delegate_.get();
+  }
+
+  FakeQuartcStreamDelegate* stream_delegate() { return stream_delegate_.get(); }
+
+ private:
+  std::unique_ptr<FakeQuartcStreamDelegate> stream_delegate_;
+  std::unique_ptr<FakeQuartcSessionDelegate> session_delegate_;
+};
+
+class QuartcSessionTest : public ::testing::Test,
+                          public QuicConnectionHelperInterface {
+ public:
+  ~QuartcSessionTest() override {}
+
+  void Init() {
+    // Quic crashes if packets are sent at time 0, and the clock defaults to 0.
+    clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+    client_channel_.reset(new FakeTransportChannel(&task_runner_, &clock_));
+    server_channel_.reset(new FakeTransportChannel(&task_runner_, &clock_));
+    // Make the channel asynchronous so that two peer will not keep calling each
+    // other when they exchange information.
+    client_channel_->SetAsync(true);
+    client_channel_->SetDestination(server_channel_.get());
+
+    client_transport_.reset(new FakeTransport(client_channel_.get()));
+    server_transport_.reset(new FakeTransport(server_channel_.get()));
+
+    client_writer_.reset(
+        new QuartcPacketWriter(client_transport_.get(), kDefaultMaxPacketSize));
+    server_writer_.reset(
+        new QuartcPacketWriter(server_transport_.get(), kDefaultMaxPacketSize));
+  }
+
+  // The parameters are used to control whether the handshake will success or
+  // not.
+  void CreateClientAndServerSessions(bool client_handshake_success = true,
+                                     bool server_handshake_success = true) {
+    Init();
+    client_peer_ = CreateSession(Perspective::IS_CLIENT);
+    server_peer_ = CreateSession(Perspective::IS_SERVER);
+
+    client_channel_->SetObserver(client_peer_.get());
+    server_channel_->SetObserver(server_peer_.get());
+
+    client_peer_->SetClientCryptoConfig(
+        new QuicCryptoClientConfig(std::unique_ptr<ProofVerifier>(
+            new FakeProofVerifier(client_handshake_success))));
+
+    QuicCryptoServerConfig* server_config = new QuicCryptoServerConfig(
+        "TESTING", QuicRandom::GetInstance(),
+        std::unique_ptr<FakeProofSource>(
+            new FakeProofSource(server_handshake_success)));
+    // Provide server with serialized config string to prove ownership.
+    QuicCryptoServerConfig::ConfigOptions options;
+    std::unique_ptr<QuicServerConfigProtobuf> primary_config(
+        server_config->GenerateConfig(QuicRandom::GetInstance(), &clock_,
+                                      options));
+    std::unique_ptr<CryptoHandshakeMessage> message(
+        server_config->AddConfig(std::move(primary_config), clock_.WallNow()));
+
+    server_peer_->SetServerCryptoConfig(server_config);
+  }
+
+  std::unique_ptr<QuartcSessionForTest> CreateSession(Perspective perspective) {
+    std::unique_ptr<QuicConnection> quic_connection =
+        CreateConnection(perspective);
+    string remote_fingerprint_value = "value";
+    QuicConfig config;
+    return std::unique_ptr<QuartcSessionForTest>(new QuartcSessionForTest(
+        std::move(quic_connection), config, remote_fingerprint_value,
+        perspective, this, &clock_));
+  }
+
+  std::unique_ptr<QuicConnection> CreateConnection(Perspective perspective) {
+    QuartcPacketWriter* writer = perspective == Perspective::IS_CLIENT
+                                     ? client_writer_.get()
+                                     : server_writer_.get();
+    QuicIpAddress ip;
+    ip.FromString("0.0.0.0");
+    bool owns_writer = false;
+    if (!alarm_factory_) {
+      // QuartcFactory is only used as an alarm factory.
+      QuartcFactoryConfig config;
+      config.clock = &quartc_clock_;
+      config.task_runner = &task_runner_;
+      alarm_factory_.reset(new QuartcFactory(config));
+    }
+    return std::unique_ptr<QuicConnection>(new QuicConnection(
+        0, QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/,
+        alarm_factory_.get(), writer, owns_writer, perspective,
+        AllSupportedVersions()));
+  }
+
+  // Runs all tasks scheduled in the next 200 ms.
+  void RunTasks() { task_runner_.Run(200); }
+
+  void StartHandshake() {
+    server_peer_->StartCryptoHandshake();
+    client_peer_->StartCryptoHandshake();
+    RunTasks();
+  }
+
+  // Test handshake establishment and sending/receiving of data for two
+  // directions.
+  void TestStreamConnection() {
+    ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+    ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+    ASSERT_TRUE(server_peer_->IsEncryptionEstablished());
+    ASSERT_TRUE(client_peer_->IsEncryptionEstablished());
+
+    uint8_t server_key[kOutputKeyLength];
+    uint8_t client_key[kOutputKeyLength];
+    bool use_context = true;
+    bool server_success = server_peer_->ExportKeyingMaterial(
+        kExporterLabel, kExporterContext, kExporterContextLen, use_context,
+        server_key, kOutputKeyLength);
+    ASSERT_TRUE(server_success);
+    bool client_success = client_peer_->ExportKeyingMaterial(
+        kExporterLabel, kExporterContext, kExporterContextLen, use_context,
+        client_key, kOutputKeyLength);
+    ASSERT_TRUE(client_success);
+    EXPECT_EQ(0, memcmp(server_key, client_key, sizeof(server_key)));
+
+    // Now we can establish encrypted outgoing stream.
+    QuartcStreamInterface* outgoing_stream =
+        server_peer_->CreateOutgoingStream(kDefaultStreamParam);
+    ASSERT_NE(nullptr, outgoing_stream);
+    EXPECT_TRUE(server_peer_->HasOpenDynamicStreams());
+
+    outgoing_stream->SetDelegate(server_peer_->stream_delegate());
+
+    // Send a test message from peer 1 to peer 2.
+    const char kTestMessage[] = "Hello";
+    outgoing_stream->Write(kTestMessage, strlen(kTestMessage),
+                           kDefaultWriteParam);
+    RunTasks();
+
+    // Wait for peer 2 to receive messages.
+    ASSERT_TRUE(client_peer_->has_data());
+
+    QuartcStreamInterface* incoming =
+        client_peer_->session_delegate()->incoming_stream();
+    ASSERT_TRUE(incoming);
+    EXPECT_TRUE(client_peer_->HasOpenDynamicStreams());
+
+    EXPECT_EQ(client_peer_->data(), kTestMessage);
+    // Send a test message from peer 2 to peer 1.
+    const char kTestResponse[] = "Response";
+    incoming->Write(kTestResponse, strlen(kTestResponse), kDefaultWriteParam);
+    RunTasks();
+    // Wait for peer 1 to receive messages.
+    ASSERT_TRUE(server_peer_->has_data());
+
+    EXPECT_EQ(server_peer_->data(), kTestResponse);
+  }
+
+  // Test that client and server are not connected after handshake failure.
+  void TestDisconnectAfterFailedHandshake() {
+    EXPECT_TRUE(!client_peer_->session_delegate()->connected());
+    EXPECT_TRUE(!server_peer_->session_delegate()->connected());
+
+    EXPECT_FALSE(client_peer_->IsEncryptionEstablished());
+    EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed());
+
+    EXPECT_FALSE(server_peer_->IsEncryptionEstablished());
+    EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed());
+  }
+
+  const QuicClock* GetClock() const override { return &clock_; }
+
+  QuicRandom* GetRandomGenerator() override {
+    return QuicRandom::GetInstance();
+  }
+
+  QuicBufferAllocator* GetBufferAllocator() override {
+    return &buffer_allocator_;
+  }
+
+ protected:
+  std::unique_ptr<QuicAlarmFactory> alarm_factory_;
+  SimpleBufferAllocator buffer_allocator_;
+  MockClock clock_;
+  MockQuartcClock quartc_clock_{&clock_};
+
+  std::unique_ptr<FakeTransportChannel> client_channel_;
+  std::unique_ptr<FakeTransportChannel> server_channel_;
+  std::unique_ptr<FakeTransport> client_transport_;
+  std::unique_ptr<FakeTransport> server_transport_;
+  std::unique_ptr<QuartcPacketWriter> client_writer_;
+  std::unique_ptr<QuartcPacketWriter> server_writer_;
+  std::unique_ptr<QuartcSessionForTest> client_peer_;
+  std::unique_ptr<QuartcSessionForTest> server_peer_;
+
+  FakeTaskRunner task_runner_{&clock_};
+};
+
+TEST_F(QuartcSessionTest, StreamConnection) {
+  CreateClientAndServerSessions();
+  StartHandshake();
+  TestStreamConnection();
+}
+
+TEST_F(QuartcSessionTest, ClientRejection) {
+  CreateClientAndServerSessions(false /*client_handshake_success*/,
+                                true /*server_handshake_success*/);
+  StartHandshake();
+  TestDisconnectAfterFailedHandshake();
+}
+
+TEST_F(QuartcSessionTest, ServerRejection) {
+  CreateClientAndServerSessions(true /*client_handshake_success*/,
+                                false /*server_handshake_success*/);
+  StartHandshake();
+  TestDisconnectAfterFailedHandshake();
+}
+
+// Test that data streams are not created before handshake.
+TEST_F(QuartcSessionTest, CannotCreateDataStreamBeforeHandshake) {
+  CreateClientAndServerSessions();
+  EXPECT_EQ(nullptr, server_peer_->CreateOutgoingStream(kDefaultStreamParam));
+  EXPECT_EQ(nullptr, client_peer_->CreateOutgoingStream(kDefaultStreamParam));
+}
+
+TEST_F(QuartcSessionTest, CloseQuartcStream) {
+  CreateClientAndServerSessions();
+  StartHandshake();
+  ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+  ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+  QuartcStreamInterface* stream =
+      client_peer_->CreateOutgoingStream(kDefaultStreamParam);
+  ASSERT_NE(nullptr, stream);
+
+  uint32_t id = stream->stream_id();
+  EXPECT_FALSE(client_peer_->IsClosedStream(id));
+  stream->SetDelegate(client_peer_->stream_delegate());
+  stream->Close();
+  RunTasks();
+  EXPECT_TRUE(client_peer_->IsClosedStream(id));
+}
+
+TEST_F(QuartcSessionTest, CancelQuartcStream) {
+  CreateClientAndServerSessions();
+  StartHandshake();
+  ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+  ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+  QuartcStreamInterface* stream =
+      client_peer_->CreateOutgoingStream(kDefaultStreamParam);
+  ASSERT_NE(nullptr, stream);
+
+  uint32_t id = stream->stream_id();
+  EXPECT_FALSE(client_peer_->IsClosedStream(id));
+  stream->SetDelegate(client_peer_->stream_delegate());
+  client_peer_->CancelStream(id);
+  EXPECT_EQ(stream->stream_error(),
+            QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
+  EXPECT_TRUE(client_peer_->IsClosedStream(id));
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/quic/quartc/quartc_stream.cc b/net/quic/quartc/quartc_stream.cc
index 932f14d1..2cae48d 100644
--- a/net/quic/quartc/quartc_stream.cc
+++ b/net/quic/quartc/quartc_stream.cc
@@ -31,7 +31,7 @@
 void QuartcStream::OnClose() {
   QuicStream::OnClose();
   DCHECK(delegate_);
-  delegate_->OnClose(this, connection_error());
+  delegate_->OnClose(this);
 }
 
 void QuartcStream::OnCanWrite() {
@@ -52,6 +52,14 @@
   return QuicStream::fin_sent();
 }
 
+int QuartcStream::stream_error() {
+  return QuicStream::stream_error();
+}
+
+int QuartcStream::connection_error() {
+  return QuicStream::connection_error();
+}
+
 void QuartcStream::Write(const char* data,
                          size_t size,
                          const WriteParameters& param) {
diff --git a/net/quic/quartc/quartc_stream.h b/net/quic/quartc/quartc_stream.h
index 1883451..3ee21a4 100644
--- a/net/quic/quartc/quartc_stream.h
+++ b/net/quic/quartc/quartc_stream.h
@@ -12,7 +12,8 @@
 namespace net {
 
 // Implements a QuartcStreamInterface using a QuicStream.
-class QuartcStream : public QuicStream, public QuartcStreamInterface {
+class QUIC_EXPORT_PRIVATE QuartcStream : public QuicStream,
+                                         public QuartcStreamInterface {
  public:
   QuartcStream(QuicStreamId id, QuicSession* session);
 
@@ -32,6 +33,10 @@
 
   bool fin_sent() override;
 
+  int stream_error() override;
+
+  int connection_error() override;
+
   void Write(const char* data,
              size_t size,
              const WriteParameters& param) override;
diff --git a/net/quic/quartc/quartc_stream_interface.h b/net/quic/quartc/quartc_stream_interface.h
index 25e928e..5e816a32 100644
--- a/net/quic/quartc/quartc_stream_interface.h
+++ b/net/quic/quartc/quartc_stream_interface.h
@@ -14,7 +14,7 @@
 // in-order. To send/receive data out of order, use separate streams. To
 // send/receive unreliably, close a stream after reliability is no longer
 // needed.
-class QuartcStreamInterface {
+class QUIC_EXPORT_PRIVATE QuartcStreamInterface {
  public:
   virtual ~QuartcStreamInterface() {}
 
@@ -28,6 +28,10 @@
   // determine if all the data has been sent
   virtual bool fin_sent() = 0;
 
+  virtual int stream_error() = 0;
+
+  virtual int connection_error() = 0;
+
   struct WriteParameters {
     WriteParameters() : fin(false) {}
     // |fin| is set to be true when there is no more data need to be send
@@ -61,7 +65,7 @@
     // endpoint.
     // TODO(zhihuang) Creates a map from the integer error_code to WebRTC native
     // error code.
-    virtual void OnClose(QuartcStreamInterface* stream, int error_code) = 0;
+    virtual void OnClose(QuartcStreamInterface* stream) = 0;
 
     // Called when buffered_amount() decreases.
     virtual void OnBufferedAmountDecrease(QuartcStreamInterface* stream) = 0;
diff --git a/net/quic/quartc/quartc_stream_test.cc b/net/quic/quartc/quartc_stream_test.cc
new file mode 100644
index 0000000..3302df6
--- /dev/null
+++ b/net/quic/quartc/quartc_stream_test.cc
@@ -0,0 +1,276 @@
+// 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/quic/quartc/quartc_stream.h"
+
+#include "net/quic/core/crypto/quic_random.h"
+#include "net/quic/core/quic_session.h"
+#include "net/quic/core/quic_simple_buffer_allocator.h"
+#include "net/quic/quartc/quartc_clock_interface.h"
+#include "net/quic/quartc/quartc_factory.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/spdy/core/spdy_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+static const SpdyPriority kDefaultPriority = 3;
+static const QuicStreamId kStreamId = 5;
+static const QuartcStreamInterface::WriteParameters kDefaultParam;
+
+// MockQuicSession that does not create streams and writes data from
+// QuicStream to a string.
+class MockQuicSession : public QuicSession {
+ public:
+  MockQuicSession(QuicConnection* connection,
+                  const QuicConfig& config,
+                  std::string* write_buffer)
+      : QuicSession(connection, nullptr /*visitor*/, config),
+        write_buffer_(write_buffer) {}
+
+  ~MockQuicSession() override {}
+
+  // Writes outgoing data from QuicStream to a string.
+  QuicConsumedData WritevData(
+      QuicStream* stream,
+      QuicStreamId id,
+      QuicIOVector iovector,
+      QuicStreamOffset offset,
+      StreamSendingState state,
+      QuicReferenceCountedPointer<
+          QuicAckListenerInterface> /*ack_notifier_delegate*/) override {
+    if (!writable_) {
+      return QuicConsumedData(0, false);
+    }
+
+    const char* data = reinterpret_cast<const char*>(iovector.iov->iov_base);
+    size_t len = iovector.total_length;
+    write_buffer_->append(data, len);
+    return QuicConsumedData(len, state != StreamSendingState::NO_FIN);
+  }
+
+  QuartcStream* CreateIncomingDynamicStream(QuicStreamId id) override {
+    return nullptr;
+  }
+
+  QuartcStream* CreateOutgoingDynamicStream(SpdyPriority priority) override {
+    return nullptr;
+  }
+
+  const QuicCryptoStream* GetCryptoStream() const override { return nullptr; }
+  QuicCryptoStream* GetMutableCryptoStream() override { return nullptr; }
+
+  // Called by QuicStream when they want to close stream.
+  void SendRstStream(QuicStreamId id,
+                     QuicRstStreamErrorCode error,
+                     QuicStreamOffset bytes_written) override {}
+
+  // Sets whether data is written to buffer, or else if this is write blocked.
+  void set_writable(bool writable) { writable_ = writable; }
+
+  // Tracks whether the stream is write blocked and its priority.
+  void RegisterReliableStream(QuicStreamId stream_id, SpdyPriority priority) {
+    write_blocked_streams()->RegisterStream(stream_id, priority);
+  }
+
+  // The session take ownership of the stream.
+  void ActivateReliableStream(std::unique_ptr<QuicStream> stream) {
+    ActivateStream(std::move(stream));
+  }
+
+ protected:
+  std::unique_ptr<QuicStream> CreateStream(QuicStreamId id) override {
+    return nullptr;
+  }
+
+ private:
+  // Stores written data from ReliableQuicStreamAdapter.
+  std::string* write_buffer_;
+  // Whether data is written to write_buffer_.
+  bool writable_ = true;
+};
+
+// Packet writer that does nothing. This is required for QuicConnection but
+// isn't used for writing data.
+class DummyPacketWriter : public QuicPacketWriter {
+ public:
+  DummyPacketWriter() {}
+
+  // QuicPacketWriter overrides.
+  WriteResult WritePacket(const char* buffer,
+                          size_t buf_len,
+                          const QuicIpAddress& self_address,
+                          const QuicSocketAddress& peer_address,
+                          PerPacketOptions* options) override {
+    return WriteResult(WRITE_STATUS_ERROR, 0);
+  }
+
+  bool IsWriteBlockedDataBuffered() const override { return false; }
+
+  bool IsWriteBlocked() const override { return false; };
+
+  void SetWritable() override {}
+
+  QuicByteCount GetMaxPacketSize(
+      const QuicSocketAddress& peer_address) const override {
+    return 0;
+  }
+};
+
+class MockQuartcStreamDelegate : public QuartcStreamInterface::Delegate {
+ public:
+  MockQuartcStreamDelegate(int id, std::string* read_buffer)
+      : id_(id), read_buffer_(read_buffer) {}
+
+  void OnBufferedAmountDecrease(QuartcStreamInterface* stream) override {
+    queued_bytes_amount_ = stream->buffered_amount();
+  }
+
+  void OnReceived(QuartcStreamInterface* stream,
+                  const char* data,
+                  size_t size) override {
+    EXPECT_EQ(id_, stream->stream_id());
+    read_buffer_->append(data, size);
+  }
+
+  void OnClose(QuartcStreamInterface* stream) override { closed_ = true; }
+
+  bool closed() { return closed_; }
+
+  int queued_bytes_amount() { return queued_bytes_amount_; }
+
+ protected:
+  uint32_t id_;
+  // Data read by the QuicStream.
+  std::string* read_buffer_;
+  // Whether the QuicStream is closed.
+  bool closed_ = false;
+  int queued_bytes_amount_ = -1;
+};
+
+class QuartcStreamTest : public ::testing::Test,
+                         public QuicConnectionHelperInterface {
+ public:
+  void CreateReliableQuicStream() {
+    // Arbitrary values for QuicConnection.
+    Perspective perspective = Perspective::IS_SERVER;
+    QuicIpAddress ip;
+    ip.FromString("0.0.0.0");
+    bool owns_writer = true;
+
+    // We only use QuartcFactory for its role as an alarm factory.
+    QuartcFactoryConfig config;
+    alarm_factory_.reset(new QuartcFactory(config));
+
+    connection_.reset(new QuicConnection(
+        0, QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/,
+        alarm_factory_.get(), new DummyPacketWriter(), owns_writer, perspective,
+        AllSupportedVersions()));
+
+    session_.reset(
+        new MockQuicSession(connection_.get(), QuicConfig(), &write_buffer_));
+    mock_stream_delegate_.reset(
+        new MockQuartcStreamDelegate(kStreamId, &read_buffer_));
+    stream_ = new QuartcStream(kStreamId, session_.get());
+    stream_->SetDelegate(mock_stream_delegate_.get());
+    session_->RegisterReliableStream(stream_->stream_id(), kDefaultPriority);
+    session_->ActivateReliableStream(std::unique_ptr<QuartcStream>(stream_));
+  }
+
+  ~QuartcStreamTest() override {}
+
+  const QuicClock* GetClock() const override { return &clock_; }
+
+  QuicRandom* GetRandomGenerator() override {
+    return QuicRandom::GetInstance();
+  }
+
+  QuicBufferAllocator* GetBufferAllocator() override {
+    return &buffer_allocator_;
+  }
+
+ protected:
+  // The QuicSession will take the ownership.
+  QuartcStream* stream_;
+  std::unique_ptr<MockQuartcStreamDelegate> mock_stream_delegate_;
+  std::unique_ptr<MockQuicSession> session_;
+  // Data written by the ReliableQuicStreamAdapterTest.
+  std::string write_buffer_;
+  // Data read by the ReliableQuicStreamAdapterTest.
+  std::string read_buffer_;
+  std::unique_ptr<QuicAlarmFactory> alarm_factory_;
+  std::unique_ptr<QuicConnection> connection_;
+  // Used to implement the QuicConnectionHelperInterface.
+  SimpleBufferAllocator buffer_allocator_;
+  MockClock clock_;
+};
+
+// Write an entire string.
+TEST_F(QuartcStreamTest, WriteDataWhole) {
+  CreateReliableQuicStream();
+  stream_->Write("Foo bar", 7, kDefaultParam);
+  EXPECT_EQ("Foo bar", write_buffer_);
+}
+
+// Write part of a string.
+TEST_F(QuartcStreamTest, WriteDataPartial) {
+  CreateReliableQuicStream();
+  stream_->Write("Foo bar", 5, kDefaultParam);
+  EXPECT_EQ("Foo b", write_buffer_);
+}
+
+// Test that strings are buffered correctly.
+TEST_F(QuartcStreamTest, BufferData) {
+  CreateReliableQuicStream();
+
+  session_->set_writable(false);
+  stream_->Write("Foo bar", 7, kDefaultParam);
+  // The data will be buffered.
+  EXPECT_EQ(0ul, write_buffer_.size());
+  EXPECT_TRUE(stream_->HasBufferedData());
+  EXPECT_EQ(-1, mock_stream_delegate_->queued_bytes_amount());
+  // The session is writable and the buffered data amount will change.
+  session_->set_writable(true);
+  stream_->OnCanWrite();
+  EXPECT_EQ(0, mock_stream_delegate_->queued_bytes_amount());
+  EXPECT_FALSE(stream_->HasBufferedData());
+  EXPECT_EQ("Foo bar", write_buffer_);
+
+  stream_->Write("xyzzy", 5, kDefaultParam);
+  EXPECT_EQ("Foo barxyzzy", write_buffer_);
+}
+
+// Read an entire string.
+TEST_F(QuartcStreamTest, ReadDataWhole) {
+  CreateReliableQuicStream();
+  QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
+  stream_->OnStreamFrame(frame);
+
+  EXPECT_EQ("Hello, World!", read_buffer_);
+}
+
+// Read part of a string.
+TEST_F(QuartcStreamTest, ReadDataPartial) {
+  CreateReliableQuicStream();
+  QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
+  frame.data_length = 5;
+  stream_->OnStreamFrame(frame);
+
+  EXPECT_EQ("Hello", read_buffer_);
+}
+
+// Test that closing the stream results in a callback.
+TEST_F(QuartcStreamTest, CloseStream) {
+  CreateReliableQuicStream();
+  EXPECT_FALSE(mock_stream_delegate_->closed());
+  stream_->OnClose();
+  EXPECT_TRUE(mock_stream_delegate_->closed());
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/quic/test_tools/quic_session_peer.cc b/net/quic/test_tools/quic_session_peer.cc
index b744aa3..a5ca5d62 100644
--- a/net/quic/test_tools/quic_session_peer.cc
+++ b/net/quic/test_tools/quic_session_peer.cc
@@ -71,6 +71,18 @@
 }
 
 // static
+const QuicSession::ClosedStreams& QuicSessionPeer::closed_streams(
+    QuicSession* session) {
+  return *session->closed_streams();
+}
+
+// static
+const QuicSession::ZombieStreamMap& QuicSessionPeer::zombie_streams(
+    QuicSession* session) {
+  return session->zombie_streams();
+}
+
+// static
 std::unordered_set<QuicStreamId>* QuicSessionPeer::GetDrainingStreams(
     QuicSession* session) {
   return &session->draining_streams_;
diff --git a/net/quic/test_tools/quic_session_peer.h b/net/quic/test_tools/quic_session_peer.h
index f89238b1..f0c3e00 100644
--- a/net/quic/test_tools/quic_session_peer.h
+++ b/net/quic/test_tools/quic_session_peer.h
@@ -38,6 +38,9 @@
   GetLocallyClosedStreamsHighestOffset(QuicSession* session);
   static QuicSession::StaticStreamMap& static_streams(QuicSession* session);
   static QuicSession::DynamicStreamMap& dynamic_streams(QuicSession* session);
+  static const QuicSession::ClosedStreams& closed_streams(QuicSession* session);
+  static const QuicSession::ZombieStreamMap& zombie_streams(
+      QuicSession* session);
   static std::unordered_set<QuicStreamId>* GetDrainingStreams(
       QuicSession* session);
   static void ActivateStream(QuicSession* session,
diff --git a/net/quic/test_tools/simulator/quic_endpoint_test.cc b/net/quic/test_tools/simulator/quic_endpoint_test.cc
index 1253517..fffd3638 100644
--- a/net/quic/test_tools/simulator/quic_endpoint_test.cc
+++ b/net/quic/test_tools/simulator/quic_endpoint_test.cc
@@ -147,8 +147,6 @@
 
 // Simulate three hosts trying to send data to a fourth one simultaneously.
 TEST_F(QuicEndpointTest, Competition) {
-  simulator_.set_enable_random_delays(true);
-
   auto endpoint_a = QuicMakeUnique<QuicEndpoint>(
       &simulator_, "Endpoint A", "Endpoint D (A)", Perspective::IS_CLIENT, 42);
   auto endpoint_b = QuicMakeUnique<QuicEndpoint>(
diff --git a/net/spdy/chromium/bidirectional_stream_spdy_impl.cc b/net/spdy/chromium/bidirectional_stream_spdy_impl.cc
index dbef1f0c..fa223bb4 100644
--- a/net/spdy/chromium/bidirectional_stream_spdy_impl.cc
+++ b/net/spdy/chromium/bidirectional_stream_spdy_impl.cc
@@ -110,30 +110,6 @@
   return ERR_IO_PENDING;
 }
 
-void BidirectionalStreamSpdyImpl::SendData(const scoped_refptr<IOBuffer>& data,
-                                           int length,
-                                           bool end_stream) {
-  DCHECK(length > 0 || (length == 0 && end_stream));
-  DCHECK(!write_pending_);
-
-  if (written_end_of_stream_) {
-    LOG(ERROR) << "Writing after end of stream is written.";
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError,
-                              weak_factory_.GetWeakPtr(), ERR_UNEXPECTED));
-    return;
-  }
-
-  write_pending_ = true;
-  written_end_of_stream_ = end_stream;
-  if (MaybeHandleStreamClosedInSendData())
-    return;
-
-  DCHECK(!stream_closed_);
-  stream_->SendData(data.get(), length,
-                    end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
-}
-
 void BidirectionalStreamSpdyImpl::SendvData(
     const std::vector<scoped_refptr<IOBuffer>>& buffers,
     const std::vector<int>& lengths,
@@ -160,13 +136,17 @@
     total_len += len;
   }
 
-  pending_combined_buffer_ = new net::IOBuffer(total_len);
-  int len = 0;
-  // TODO(xunjieli): Get rid of extra copy. Coalesce headers and data frames.
-  for (size_t i = 0; i < buffers.size(); ++i) {
-    memcpy(pending_combined_buffer_->data() + len, buffers[i]->data(),
-           lengths[i]);
-    len += lengths[i];
+  if (buffers.size() == 1) {
+    pending_combined_buffer_ = buffers[0];
+  } else {
+    pending_combined_buffer_ = new net::IOBuffer(total_len);
+    int len = 0;
+    // TODO(xunjieli): Get rid of extra copy. Coalesce headers and data frames.
+    for (size_t i = 0; i < buffers.size(); ++i) {
+      memcpy(pending_combined_buffer_->data() + len, buffers[i]->data(),
+             lengths[i]);
+      len += lengths[i];
+    }
   }
   stream_->SendData(pending_combined_buffer_.get(), total_len,
                     end_stream ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
diff --git a/net/spdy/chromium/bidirectional_stream_spdy_impl.h b/net/spdy/chromium/bidirectional_stream_spdy_impl.h
index b40f3c0..61e00e0 100644
--- a/net/spdy/chromium/bidirectional_stream_spdy_impl.h
+++ b/net/spdy/chromium/bidirectional_stream_spdy_impl.h
@@ -50,9 +50,6 @@
              std::unique_ptr<base::Timer> timer) override;
   void SendRequestHeaders() override;
   int ReadData(IOBuffer* buf, int buf_len) override;
-  void SendData(const scoped_refptr<IOBuffer>& data,
-                int length,
-                bool end_stream) override;
   void SendvData(const std::vector<scoped_refptr<IOBuffer>>& buffers,
                  const std::vector<int>& lengths,
                  bool end_stream) override;
diff --git a/net/spdy/chromium/bidirectional_stream_spdy_impl_unittest.cc b/net/spdy/chromium/bidirectional_stream_spdy_impl_unittest.cc
index 3b81307c..e291424b 100644
--- a/net/spdy/chromium/bidirectional_stream_spdy_impl_unittest.cc
+++ b/net/spdy/chromium/bidirectional_stream_spdy_impl_unittest.cc
@@ -139,9 +139,7 @@
   }
 
   void SendData(IOBuffer* data, int length, bool end_of_stream) {
-    not_expect_callback_ = true;
-    stream_->SendData(data, length, end_of_stream);
-    not_expect_callback_ = false;
+    SendvData({data}, {length}, end_of_stream);
   }
 
   void SendvData(const std::vector<scoped_refptr<IOBuffer>>& data,
diff --git a/net/third_party/nist-pkits/generate_tests.py b/net/third_party/nist-pkits/generate_tests.py
index 8083d62a..04afc6f8 100644
--- a/net/third_party/nist-pkits/generate_tests.py
+++ b/net/third_party/nist-pkits/generate_tests.py
@@ -688,17 +688,6 @@
              user_constrained_policy_set=[]),
   ],
 
-  '4.10.7': [ # Invalid Mapping From anyPolicy Test7 
-    # Procedure: Validate Invalid Mapping From anyPolicy Test7 EE using the
-    # default settings or open and verify Signed Test Message 6.2.2.100 using
-    # the default settings.
-    #
-    # Expected Result: The path should not validate successfully since the
-    # intermediate certificate includes a policy mapping extension in which
-    # anyPolicy appears as an issuerDomainPolicy. 
-    TestInfo(False, user_constrained_policy_set=[]),
-  ],
-
   '4.10.8': [ # Invalid Mapping To anyPolicy Test8
     # Procedure: Validate Invalid Mapping To anyPolicy Test8 EE using the
     # default settings or open and verify Signed Test Message 6.2.2.101 using
@@ -707,6 +696,8 @@
     # Expected Result: The path should not validate successfully since the
     # intermediate certificate includes a policy mapping extension in which
     # anyPolicy appears as an subjectDomainPolicy.
+    #
+    # TODO(eroman): What should user_constrained_policy_set be?
     TestInfo(False, user_constrained_policy_set=[]),
   ],
 
diff --git a/net/third_party/nist-pkits/pkits_testcases-inl.h b/net/third_party/nist-pkits/pkits_testcases-inl.h
index 8ddf7c6..d4b709d 100644
--- a/net/third_party/nist-pkits/pkits_testcases-inl.h
+++ b/net/third_party/nist-pkits/pkits_testcases-inl.h
@@ -2113,7 +2113,6 @@
                               "MappingFromanyPolicyCACRL"};
   PkitsTestInfo info;
   info.should_validate = false;
-  info.SetUserConstrainedPolicySet("");
 
   this->RunTest(certs, crls, info);
 }
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index ca66c54..59b09424 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -555,7 +555,9 @@
   void VerifyCleanConnection(bool had_packet_loss) {
     QuicConnectionStats client_stats =
         client_->client()->session()->connection()->GetStats();
-    if (!had_packet_loss) {
+    // TODO(ianswett): Determine why this becomes even more flaky with BBR
+    // enabled.  b/62141144
+    if (!had_packet_loss && !FLAGS_quic_reloadable_flag_quic_default_to_bbr) {
       EXPECT_EQ(0u, client_stats.packets_lost);
     }
     EXPECT_EQ(0u, client_stats.packets_discarded);
@@ -2250,6 +2252,27 @@
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
 }
 
+TEST_P(EndToEndTest, CanceledStreamDoesNotBecomeZombie) {
+  ASSERT_TRUE(Initialize());
+  if (!FLAGS_quic_reloadable_flag_quic_use_stream_notifier) {
+    return;
+  }
+
+  EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+  // Lose the request.
+  SetPacketLossPercentage(100);
+  client_->SendRequest("/small_response");
+  // Cancel the stream, and let the RST_STREAM go through.
+  SetPacketLossPercentage(0);
+  QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+  // Reset stream.
+  stream->Reset(QUIC_STREAM_CANCELLED);
+  QuicSession* session = client_->client()->session();
+  // Verify canceled stream does not become zombie.
+  EXPECT_TRUE(QuicSessionPeer::zombie_streams(session).empty());
+  EXPECT_EQ(1u, QuicSessionPeer::closed_streams(session).size());
+}
+
 // A test stream that gives |response_body_| as an error response body.
 class ServerStreamWithErrorResponseBody : public QuicSimpleServerStream {
  public:
diff --git a/skia/public/interfaces/BUILD.gn b/skia/public/interfaces/BUILD.gn
index 9b87555..7654731e 100644
--- a/skia/public/interfaces/BUILD.gn
+++ b/skia/public/interfaces/BUILD.gn
@@ -9,6 +9,9 @@
     "bitmap.mojom",
     "image_filter.mojom",
   ]
+
+  # TODO(crbug.com/699569): Convert to use the new JS bindings.
+  use_new_js_bindings = false
 }
 
 mojom("test_interfaces") {
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index bd322ac..4145aceb 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -4375,6 +4375,7 @@
 crbug.com/591099 external/wpt/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-paths.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-ports.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-protocols.html [ Crash ]
+crbug.com/591099 external/wpt/content-security-policy/embedded-enforcement/subsumption_algorithm-nonces.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/embedded-enforcement/subsumption_algorithm-none.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/embedded-enforcement/subsumption_algorithm-self.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/embedded-enforcement/subsumption_algorithm-strict_dynamic.html [ Crash ]
@@ -4524,7 +4525,7 @@
 crbug.com/591099 external/wpt/css/CSS2/linebox/border-padding-bleed-001.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/CSS2/linebox/border-padding-bleed-002.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/CSS2/linebox/border-padding-bleed-003.xht [ Failure Pass ]
-crbug.com/591099 external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Failure ]
+crbug.com/591099 external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/CSS2/linebox/inline-formatting-context-001.xht [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/linebox/inline-formatting-context-013.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/CSS2/linebox/inline-formatting-context-022.xht [ Failure Pass ]
@@ -4651,7 +4652,7 @@
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/inline-replaced-width-013.xht [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/inline-replaced-width-015.xht [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/inlines-013.xht [ Failure ]
-crbug.com/591099 external/wpt/css/CSS2/normal-flow/inlines-020.xht [ Failure ]
+crbug.com/591099 external/wpt/css/CSS2/normal-flow/inlines-020.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/inline-table-zorder-003.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/inline-table-zorder-005.xht [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/max-height-002.xht [ Failure ]
@@ -5109,11 +5110,16 @@
 crbug.com/591099 external/wpt/css/css-grid-1/grid-definition/fr-unit.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-grid-1/grid-definition/fr-unit-with-percentage.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-grid-1/grid-definition/grid-layout-auto-tracks.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css/css-grid-1/grid-items/grid-inline-z-axis-ordering-overlapped-items-006.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-grid-1/grid-items/grid-item-containing-block-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-grid-1/grid-items/grid-item-containing-block-002.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-grid-1/grid-items/grid-item-containing-block-003.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-grid-1/grid-items/grid-item-containing-block-004.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-grid-1/grid-items/grid-items-sizing-alignment-001.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css/css-grid-1/grid-items/grid-z-axis-ordering-overlapped-items-006.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-grid-1/grid-model/grid-inline-margins-no-collapse-001.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-grid-1/grid-model/grid-margins-no-collapse-001.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-grid-1/grid-model/grid-support-display-001.html [ Crash ]
 crbug.com/591099 external/wpt/css/css-position-3/position-sticky-left.html [ Crash Failure ]
 crbug.com/591099 external/wpt/css/css-position-3/position-sticky-table-th-bottom.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-position-3/position-sticky-table-th-left.html [ Failure ]
@@ -5582,7 +5588,7 @@
 crbug.com/591099 external/wpt/css/css-writing-modes-3/line-box-direction-vlr-013.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes-3/line-box-direction-vlr-014.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes-3/line-box-direction-vlr-018.xht [ Failure Pass ]
-crbug.com/591099 external/wpt/css/css-writing-modes-3/line-box-direction-vlr-020.xht [ Failure ]
+crbug.com/591099 external/wpt/css/css-writing-modes-3/line-box-direction-vlr-020.xht [ Crash Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes-3/line-box-direction-vrl-002.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes-3/line-box-direction-vrl-005.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes-3/line-box-direction-vrl-006.xht [ Failure ]
@@ -5899,6 +5905,7 @@
 crbug.com/591099 external/wpt/eventsource/eventsource-onmessage-realm.htm [ Crash ]
 crbug.com/591099 external/wpt/fetch/api/request/multi-globals/url-parsing.html [ Crash ]
 crbug.com/591099 external/wpt/fetch/api/response/multi-globals/url-parsing.html [ Crash ]
+crbug.com/591099 external/wpt/fetch/dangling-markup-mitigation.tentative.html [ Crash ]
 crbug.com/591099 external/wpt/FileAPI/idlharness.html [ Crash ]
 crbug.com/591099 external/wpt/FileAPI/url/blob-url-in-sandboxed-iframe.html [ Crash ]
 crbug.com/591099 external/wpt/FileAPI/url/multi-global-origin-serialization.sub.html [ Crash ]
@@ -6853,7 +6860,7 @@
 crbug.com/591099 external/wpt/service-workers/service-worker/windowclient-navigate.https.html [ Crash ]
 crbug.com/591099 external/wpt/shadow-dom/leaktests/html-collection.html [ Crash ]
 crbug.com/591099 external/wpt/shadow-dom/leaktests/window-frames.html [ Crash ]
-crbug.com/591099 external/wpt/shadow-dom/MouseEvent-prototype-offsetX-offsetY.html [ Failure ]
+crbug.com/591099 external/wpt/shadow-dom/MouseEvent-prototype-offsetX-offsetY.html [ Failure Pass ]
 crbug.com/591099 external/wpt/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html [ Crash Pass ]
 crbug.com/591099 external/wpt/shadow-dom/untriaged/events/event-retargeting/test-001.html [ Crash ]
 crbug.com/591099 external/wpt/shadow-dom/untriaged/events/test-001.html [ Crash ]
@@ -7205,6 +7212,7 @@
 crbug.com/591099 fast/block/float/vertical-move-relayout.html [ Failure ]
 crbug.com/591099 fast/block/float/width-update-after-clear.html [ Failure ]
 crbug.com/591099 fast/block/hr-border-box-sizing.html [ Failure ]
+crbug.com/591099 fast/block/hr-with-float.html [ Failure ]
 crbug.com/591099 fast/block/inline-children-root-linebox-crash.html [ Crash Failure ]
 crbug.com/591099 fast/block/line-layout/crash-in-isolate-with-positioned-child.html [ Failure Pass ]
 crbug.com/591099 fast/block/line-layout/double-line-break-obj-removal-crash.html [ Crash Pass ]
@@ -7443,7 +7451,7 @@
 crbug.com/591099 fast/borders/border-image-massive-scale.html [ Failure ]
 crbug.com/591099 fast/borders/border-image-outset.html [ Failure ]
 crbug.com/591099 fast/borders/border-image-outset-in-shorthand.html [ Failure ]
-crbug.com/591099 fast/borders/border-image-outset-split-inline.html [ Failure ]
+crbug.com/591099 fast/borders/border-image-outset-split-inline.html [ Failure Pass ]
 crbug.com/591099 fast/borders/border-image-repeat.html [ Failure ]
 crbug.com/591099 fast/borders/border-image-repeat-round.html [ Failure ]
 crbug.com/591099 fast/borders/border-image-rotate-transform.html [ Failure ]
@@ -12530,7 +12538,7 @@
 crbug.com/591099 fast/inline/inline-box-background-long-image.html [ Failure ]
 crbug.com/591099 fast/inline/inline-box-background-repeat-x.html [ Failure ]
 crbug.com/591099 fast/inline/inline-box-background-repeat-y.html [ Failure ]
-crbug.com/591099 fast/inline/inline-continuation-borders.html [ Failure ]
+crbug.com/591099 fast/inline/inline-continuation-borders.html [ Failure Pass ]
 crbug.com/591099 fast/inline/inline-destroy-dirty-lines-crash.html [ Failure ]
 crbug.com/591099 fast/inline/inline-fixed-position-boundingbox.html [ Failure ]
 crbug.com/591099 fast/inline/inline-focus-ring.html [ Failure ]
@@ -12544,7 +12552,7 @@
 crbug.com/591099 fast/inline/inline-text-quirk-bpm.html [ Failure ]
 crbug.com/591099 fast/inline/inline-width-containing-collapsed-whitespace-and-image-in-float.html [ Crash Failure ]
 crbug.com/591099 fast/inline/inline-with-empty-inline-children.html [ Failure ]
-crbug.com/591099 fast/inline/inline-wrap-with-parent-padding.html [ Failure ]
+crbug.com/591099 fast/inline/inline-wrap-with-parent-padding.html [ Failure Pass ]
 crbug.com/591099 fast/inline/justify-emphasis-inline-box.html [ Failure ]
 crbug.com/591099 fast/inline/leading-space-after-nested-empty-inlines.html [ Failure ]
 crbug.com/591099 fast/inline/left-right-center-inline-alignment-in-ltr-and-rtl-blocks.html [ Failure ]
@@ -12956,6 +12964,7 @@
 crbug.com/591099 fast/lists/li-br.html [ Crash Failure ]
 crbug.com/591099 fast/lists/li-minimum-long-value.html [ Crash Pass ]
 crbug.com/591099 fast/lists/list-item-line-height.html [ Crash Failure ]
+crbug.com/591099 fast/lists/list-marker-avoid-float.html [ Crash Failure ]
 crbug.com/591099 fast/lists/list-marker-before-content-table.html [ Failure ]
 crbug.com/591099 fast/lists/list-marker-before-float.html [ Crash Failure ]
 crbug.com/591099 fast/lists/list-marker-before-float-nested.html [ Crash Failure ]
@@ -14770,7 +14779,7 @@
 crbug.com/591099 fast/text/line-breaks-after-ideographic-comma-or-full-stop-2.html [ Failure ]
 crbug.com/591099 fast/text/line-breaks-after-ideographic-comma-or-full-stop.html [ Failure ]
 crbug.com/591099 fast/text/line-breaks-after-white-space.html [ Failure Pass ]
-crbug.com/591099 fast/text/line-breaks.html [ Failure ]
+crbug.com/591099 fast/text/line-breaks.html [ Failure Pass ]
 crbug.com/591099 fast/text/line-initial-and-final-swashes.html [ Failure ]
 crbug.com/591099 fast/text/long-word.html [ Failure Timeout ]
 crbug.com/591099 fast/text/midword-break-after-breakable-char.html [ Failure ]
@@ -15819,6 +15828,7 @@
 crbug.com/591099 http/tests/inspector/fragment.html [ Crash Failure ]
 crbug.com/591099 http/tests/inspector/indexeddb/database-data.html [ Failure ]
 crbug.com/591099 http/tests/inspector/indexeddb/database-names.html [ Failure ]
+crbug.com/591099 http/tests/inspector/indexeddb/database-refresh-view.html [ Crash ]
 crbug.com/591099 http/tests/inspector/indexeddb/database-structure.html [ Failure ]
 crbug.com/591099 http/tests/inspector/indexeddb/resources-panel.html [ Failure ]
 crbug.com/591099 http/tests/inspector/indexeddb/transaction-promise-console.html [ Failure ]
@@ -15855,7 +15865,7 @@
 crbug.com/591099 http/tests/inspector/network/network-eventsource.html [ Crash Failure ]
 crbug.com/591099 http/tests/inspector/network/network-fetch.html [ Crash Failure ]
 crbug.com/591099 http/tests/inspector/network/network-fetch-post-payload.html [ Crash Failure ]
-crbug.com/591099 http/tests/inspector/network/network-filters.html [ Failure ]
+crbug.com/591099 http/tests/inspector/network/network-filters.html [ Crash Failure ]
 crbug.com/591099 http/tests/inspector/network/network-filters-internals.html [ Failure ]
 crbug.com/591099 http/tests/inspector/network/network-iframe-load-and-delete.html [ Crash Failure ]
 crbug.com/591099 http/tests/inspector/network/network-image-404.html [ Failure ]
@@ -16167,7 +16177,7 @@
 crbug.com/591099 http/tests/mime/reload-subresource-when-type-changes.html [ Crash ]
 crbug.com/591099 http/tests/misc/acid2.html [ Crash ]
 crbug.com/591099 http/tests/misc/acid2-pixel.html [ Crash ]
-crbug.com/591099 http/tests/misc/acid3.html [ Failure ]
+crbug.com/591099 http/tests/misc/acid3.html [ Crash Failure ]
 crbug.com/591099 http/tests/misc/adopt-iframe-src-attr-after-remove.html [ Crash ]
 crbug.com/591099 http/tests/misc/async-script.html [ Failure ]
 crbug.com/591099 http/tests/misc/async-script-removed.html [ Failure ]
@@ -16201,7 +16211,7 @@
 crbug.com/591099 http/tests/misc/delete-frame-during-readystatechange.html [ Crash ]
 crbug.com/591099 http/tests/misc/delete-frame-during-readystatechange-with-gc-after-video-removal.html [ Crash ]
 crbug.com/591099 http/tests/misc/detach-during-notifyDone.html [ Crash ]
-crbug.com/591099 http/tests/misc/dns-prefetch-control.html [ Failure ]
+crbug.com/591099 http/tests/misc/dns-prefetch-control.html [ Crash Failure ]
 crbug.com/591099 http/tests/misc/DOMContentLoaded-event.html [ Crash Failure ]
 crbug.com/591099 http/tests/misc/drag-over-iframe-invalid-source-crash.html [ Crash Failure ]
 crbug.com/591099 http/tests/misc/embed-image-load-outlives-gc-without-crashing.html [ Crash Pass ]
@@ -17066,7 +17076,7 @@
 crbug.com/591099 http/tests/worklet/chromium/import-on-detached-iframe.html [ Crash ]
 crbug.com/591099 http/tests/xmlhttprequest/abort-should-destroy-responseText.html [ Failure ]
 crbug.com/591099 http/tests/xmlhttprequest/access-control-allow-lists-starting-with-comma.html [ Failure ]
-crbug.com/591099 http/tests/xmlhttprequest/access-control-and-redirects-async.html [ Failure ]
+crbug.com/591099 http/tests/xmlhttprequest/access-control-and-redirects-async.html [ Failure Timeout ]
 crbug.com/591099 http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin.html [ Failure ]
 crbug.com/591099 http/tests/xmlhttprequest/access-control-and-redirects.html [ Failure ]
 crbug.com/591099 http/tests/xmlhttprequest/access-control-basic-denied-preflight-cache.html [ Crash Failure ]
@@ -17714,7 +17724,7 @@
 crbug.com/591099 inspector/editor/text-editor-indent-autodetection.html [ Crash Failure Timeout ]
 crbug.com/591099 inspector/editor/text-editor-line-breaks.html [ Crash Failure ]
 crbug.com/591099 inspector/editor/text-editor-mark-clean.html [ Crash Failure Timeout ]
-crbug.com/591099 inspector/editor/text-editor-reveal-line.html [ Failure Timeout ]
+crbug.com/591099 inspector/editor/text-editor-reveal-line.html [ Crash Failure Timeout ]
 crbug.com/591099 inspector/editor/text-editor-search-replace.html [ Crash Failure ]
 crbug.com/591099 inspector/editor/text-editor-selection-to-search.html [ Crash Failure ]
 crbug.com/591099 inspector/editor/text-editor-smart-braces.html [ Crash Failure ]
@@ -17950,7 +17960,7 @@
 crbug.com/591099 inspector/extensions/extensions-audits-content-script.html [ Crash Failure ]
 crbug.com/591099 inspector/extensions/extensions-audits.html [ Crash Failure ]
 crbug.com/591099 inspector/extensions/extensions-eval-content-script.html [ Failure ]
-crbug.com/591099 inspector/extensions/extensions-eval.html [ Failure ]
+crbug.com/591099 inspector/extensions/extensions-eval.html [ Crash Failure ]
 crbug.com/591099 inspector/extensions/extensions-events.html [ Crash ]
 crbug.com/591099 inspector/extensions/extensions-network.html [ Crash Failure ]
 crbug.com/591099 inspector/extensions/extensions-panel.html [ Crash ]
@@ -17992,7 +18002,7 @@
 crbug.com/591099 inspector/profiler/agents-disabled-check.html [ Crash Failure ]
 crbug.com/591099 inspector/profiler/cpu-profiler-agent-crash-on-start.html [ Crash Failure ]
 crbug.com/591099 inspector/profiler/cpu-profiler-bottom-up-large-tree-search.html [ Crash Failure ]
-crbug.com/591099 inspector/profiler/cpu-profiler-bottom-up-times.html [ Failure ]
+crbug.com/591099 inspector/profiler/cpu-profiler-bottom-up-times.html [ Crash Failure ]
 crbug.com/591099 inspector/profiler/cpu-profiler-calculate-time.html [ Crash Failure ]
 crbug.com/591099 inspector/profiler/cpu-profiler-flame-chart-overview.html [ Crash Failure ]
 crbug.com/591099 inspector/profiler/cpu-profiler-native-nodes-filter.html [ Crash Failure ]
@@ -18261,7 +18271,7 @@
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-into-across-timeouts.html [ Failure ]
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-into-custom-element-callbacks.html [ Crash Failure Timeout ]
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-into-document-write.html [ Failure ]
-crbug.com/591099 inspector/sources/debugger-step/debugger-step-into-event-listener.html [ Failure ]
+crbug.com/591099 inspector/sources/debugger-step/debugger-step-into-event-listener.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-into-inlined-scripts.html [ Failure ]
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-out-across-timeouts.html [ Failure ]
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-out-custom-element-callbacks.html [ Failure ]
@@ -18273,14 +18283,14 @@
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-over.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-over-inlined-scripts.html [ Failure ]
 crbug.com/591099 inspector/sources/debugger-step/debugger-step-through-promises.html [ Failure ]
-crbug.com/591099 inspector/sources/debugger-step/step-through-event-listeners.html [ Failure ]
+crbug.com/591099 inspector/sources/debugger-step/step-through-event-listeners.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/async-call-stack-async-function.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/async-call-stack-url.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/break-on-empty-event-listener.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/break-on-set-timeout-with-syntax-error.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/callstack-placards-discarded.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/click-gutter-breakpoint.html [ Crash Failure ]
-crbug.com/591099 inspector/sources/debugger-ui/continue-to-location-markers.html [ Failure ]
+crbug.com/591099 inspector/sources/debugger-ui/continue-to-location-markers.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/copy-stack-trace.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/custom-element-lifecycle-events.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/debugger-expand-scope.html [ Crash Failure ]
@@ -18295,7 +18305,7 @@
 crbug.com/591099 inspector/sources/debugger-ui/last-execution-context.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/monitor-console-command.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/reveal-execution-line.html [ Crash Failure ]
-crbug.com/591099 inspector/sources/debugger-ui/reveal-not-skipped.html [ Failure ]
+crbug.com/591099 inspector/sources/debugger-ui/reveal-not-skipped.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/script-formatter-breakpoints-2.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/script-formatter-breakpoints-3.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/script-formatter-search.html [ Crash Failure ]
@@ -18304,7 +18314,7 @@
 crbug.com/591099 inspector/sources/debugger-ui/scripts-sorting.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/scripts-with-same-source-url.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/selected-call-frame-after-formatting-source.html [ Crash Failure ]
-crbug.com/591099 inspector/sources/debugger-ui/show-function-definition.html [ Failure ]
+crbug.com/591099 inspector/sources/debugger-ui/show-function-definition.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/show-generator-location.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/source-frame-count.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-ui/source-frame.html [ Failure ]
@@ -18460,6 +18470,7 @@
 crbug.com/591099 media/audio-delete-while-slider-thumb-clicked.html [ Crash ]
 crbug.com/591099 media/audio-only-video-intrinsic-size.html [ Failure ]
 crbug.com/591099 media/autoplay-document-move.html [ Crash ]
+crbug.com/591099 media/autoplay/document-user-activation.html [ Failure ]
 crbug.com/591099 media/auto-play-in-sandbox-with-allow-scripts.html [ Crash ]
 crbug.com/591099 media/autoplay-muted-conditions.html [ Crash ]
 crbug.com/591099 media/autoplay-muted.html [ Crash Timeout ]
@@ -18485,6 +18496,8 @@
 crbug.com/591099 media/controls/controls-cast-do-not-fade-out.html [ Crash ]
 crbug.com/591099 media/controls/controls-cast-overlay-slow-fade.html [ Crash ]
 crbug.com/591099 media/controls/controls-overlay-cast-button.html [ Crash ]
+crbug.com/591099 media/controls/controls-video-keynav.html [ Crash ]
+crbug.com/591099 media/controls/controls-video-keynav-no-controls.html [ Crash ]
 crbug.com/591099 media/controls-css-overload.html [ Crash ]
 crbug.com/591099 media/controls/download-button-displays-with-preload-none.html [ Crash ]
 crbug.com/591099 media/controls-drag-timebar.html [ Crash ]
@@ -19701,6 +19714,7 @@
 crbug.com/591099 printing/absolute-positioned.html [ Failure ]
 crbug.com/591099 printing/absolute-position-headers-and-footers.html [ Failure ]
 crbug.com/591099 printing/allowed-page-breaks.html [ Failure ]
+crbug.com/591099 printing/block-width-relayout-shrink.html [ Failure ]
 crbug.com/591099 printing/css2.1/page-break-after-000.html [ Failure ]
 crbug.com/591099 printing/css2.1/page-break-after-002.html [ Failure ]
 crbug.com/591099 printing/css2.1/page-break-after-003.html [ Failure ]
@@ -23400,7 +23414,7 @@
 crbug.com/591099 virtual/gpu-rasterization/images/zoomed-img-size.html [ Failure ]
 crbug.com/591099 virtual/gpu-rasterization/images/zoomed-offset-size.html [ Crash ]
 crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-003r.xht [ Failure Pass ]
-crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Failure ]
+crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Failure Pass ]
 crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/height-114.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/positioning/top-019.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/positioning/top-020.xht [ Failure ]
@@ -23469,6 +23483,8 @@
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/controls-cast-do-not-fade-out.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/controls-cast-overlay-slow-fade.html [ Crash Timeout ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/controls-overlay-cast-button.html [ Crash ]
+crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/controls-video-keynav.html [ Crash ]
+crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/controls-video-keynav-no-controls.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/download-button-displays-with-preload-none.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/overflow-fully-hidden.html [ Crash ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/media/controls/overlay-play-button-document-move.html [ Crash ]
@@ -24478,6 +24494,7 @@
 crbug.com/591099 virtual/threaded/printing/absolute-positioned.html [ Failure ]
 crbug.com/591099 virtual/threaded/printing/absolute-position-headers-and-footers.html [ Failure ]
 crbug.com/591099 virtual/threaded/printing/allowed-page-breaks.html [ Failure ]
+crbug.com/591099 virtual/threaded/printing/block-width-relayout-shrink.html [ Failure ]
 crbug.com/591099 virtual/threaded/printing/css2.1/page-break-after-000.html [ Failure ]
 crbug.com/591099 virtual/threaded/printing/css2.1/page-break-after-002.html [ Failure ]
 crbug.com/591099 virtual/threaded/printing/css2.1/page-break-after-003.html [ Failure ]
@@ -24625,6 +24642,7 @@
 crbug.com/591099 vr/requestPresent_resolve_webgl2.html [ Crash ]
 crbug.com/591099 vr/stageParameters_match.html [ Crash ]
 crbug.com/591099 web-animations-api/time-consistent-across-frames.html [ Crash ]
+crbug.com/591099 webaudio/BiquadFilter/tail-time-lowpass.html [ Timeout ]
 crbug.com/591099 webaudio/internals/audiocontext-lock-threading-race.html [ Failure ]
 crbug.com/591099 webaudio/internals/cycle-connection-gc.html [ Failure ]
 crbug.com/591099 webaudio/internals/mediaelementaudiosourcenode-gc.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 374817d5..3091fce 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -70,10 +70,6 @@
 crbug.com/451577 [ Linux ] inspector/extensions/extensions-reload.html [ Slow ]
 crbug.com/451577 [ Linux ] inspector/extensions/extensions-sidebar.html [ Slow ]
 
-crbug.com/578297 [ Linux ] virtual/threaded/printing/webgl-oversized-printing.html [ Slow Leak ]
-crbug.com/578297 [ Linux ] http/tests/media/media-source/mediasource-appendstream-quota-exceeded.html [ Slow ]
-crbug.com/578297 [ Linux ] virtual/mojo-loading/http/tests/media/media-source/mediasource-appendstream-quota-exceeded.html [ Slow ]
-
 # -----------------------------------------------------------------
 # Leaks in external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/*
 # -----------------------------------------------------------------
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index 0e56f9c..422fcebd 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -758,3 +758,8 @@
 # run-webkit-tests; see https://crbug.com/359838.
 http/tests/ManualTests/ [ WontFix ]
 virtual/mojo-loading/http/tests/ManualTests/ [ WontFix ]
+
+# These test produce invisible different pixels on Win7 Debug.
+[ Win7 Debug ] fast/table/backgr_border-table-column-group-collapsed-border.html [ WontFix ]
+[ Win7 Debug ] virtual/mojo-loading/fast/table/backgr_border-table-column-group-collapsed-border.html [ WontFix ]
+
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 8cfa388..9045e0d5 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2996,6 +2996,3 @@
 crbug.com/727505 [ Linux Mac Win ] virtual/threaded/fast/compositorworker/basic-plumbing-main-to-worker.html [ Pass Failure Timeout Crash ]
 crbug.com/727505 [ Linux Mac Win ] virtual/threaded/fast/compositorworker/basic-plumbing-worker-to-main.html [ Pass Failure Timeout Crash ]
 crbug.com/727505 [ Linux Mac Win ] virtual/threaded/fast/compositorworker/request-animation-frame.html [ Pass Failure Timeout Crash ]
-
-crbug.com/728895 [ Win7 Debug ] fast/table/backgr_border-table-column-group-collapsed-border.html [ Failure ]
-crbug.com/728895 [ Win7 Debug ] virtual/mojo-loading/fast/table/backgr_border-table-column-group-collapsed-border.html [ Failure ]
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-fling-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-fling-expected.txt
index 6229272..2fc4f16 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-fling-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-fling-expected.txt
@@ -6,7 +6,7 @@
 PASS successfullyParsed is true
 
 TEST COMPLETE
-PASS actualWheelEventsOccurred is >= 2
+PASS actualWheelEventsOccurred is >= 1
 PASS cumulativeScrollX is >= 300
 PASS cumulativeScrollY is >= 300
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-fling.js b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-fling.js
index 5b86bedb..1f9bdea 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-fling.js
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-fling.js
@@ -4,7 +4,7 @@
 var cumulativeScrollX = 0;
 var cumulativeScrollY = 0;
 
-var minimumWheelEventsExpected = "2";
+var minimumWheelEventsExpected = "1";
 var minimumScrollXExpected = 300;
 var minimumScrollYExpected = 300;
 
diff --git a/third_party/WebKit/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html b/third_party/WebKit/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html
index 05057cd..5248363 100644
--- a/third_party/WebKit/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html
+++ b/third_party/WebKit/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html
@@ -204,12 +204,14 @@
       assert_equals(document.scrollingElement.scrollTop, scrollingElementTop, "For scrollingElement on step " + step);
     };
 
+    assertScrollTops(0, 0, 0, 0);
+
     var frame_actions = [
       function() {
         eventSender.gestureFlingStart(10, 10, -1000000, -1000000, "touchscreen");
       },
       flingTest.step_func(function() {
-        assertScrollTops(0, 0, 0, 1);
+        assertScrollTops(100, 0, 0, 1);
       }),
       flingTest.step_func(function() {
         assertScrollTops(100, 0, 0, 2);
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-data.html b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-data.html
index 09aa702b..0badd0e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-data.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/indexeddb/database-data.html
@@ -183,7 +183,7 @@
 
     function runClearTests()
     {
-        indexedDBModel.clearObjectStore(databaseId, objectStoreName, step1);
+        indexedDBModel.clearObjectStore(databaseId, objectStoreName).then(step1);
         InspectorTest.addResult("Cleared data from objectStore");
 
         function step1() {
diff --git a/third_party/WebKit/Source/core/animation/BUILD.gn b/third_party/WebKit/Source/core/animation/BUILD.gn
index cbf02c6f..3e5d7ed 100644
--- a/third_party/WebKit/Source/core/animation/BUILD.gn
+++ b/third_party/WebKit/Source/core/animation/BUILD.gn
@@ -102,6 +102,8 @@
     "CompositorMutatorImpl.h",
     "CompositorPendingAnimations.cpp",
     "CompositorPendingAnimations.h",
+    "CompositorProxyClientImpl.cpp",
+    "CompositorProxyClientImpl.h",
     "CustomCompositorAnimationManager.cpp",
     "CustomCompositorAnimationManager.h",
     "CustomCompositorAnimations.cpp",
diff --git a/third_party/WebKit/Source/web/CompositorProxyClientImpl.cpp b/third_party/WebKit/Source/core/animation/CompositorProxyClientImpl.cpp
similarity index 92%
rename from third_party/WebKit/Source/web/CompositorProxyClientImpl.cpp
rename to third_party/WebKit/Source/core/animation/CompositorProxyClientImpl.cpp
index 7090ffa..04af0b1 100644
--- a/third_party/WebKit/Source/web/CompositorProxyClientImpl.cpp
+++ b/third_party/WebKit/Source/core/animation/CompositorProxyClientImpl.cpp
@@ -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 "web/CompositorProxyClientImpl.h"
+#include "core/animation/CompositorProxyClientImpl.h"
 
 #include "core/dom/CompositorProxy.h"
 
diff --git a/third_party/WebKit/Source/web/CompositorProxyClientImpl.h b/third_party/WebKit/Source/core/animation/CompositorProxyClientImpl.h
similarity index 93%
rename from third_party/WebKit/Source/web/CompositorProxyClientImpl.h
rename to third_party/WebKit/Source/core/animation/CompositorProxyClientImpl.h
index 0832d652..055ae93 100644
--- a/third_party/WebKit/Source/web/CompositorProxyClientImpl.h
+++ b/third_party/WebKit/Source/core/animation/CompositorProxyClientImpl.h
@@ -5,13 +5,14 @@
 #ifndef CompositorProxyClientImpl_h
 #define CompositorProxyClientImpl_h
 
+#include "core/CoreExport.h"
 #include "core/dom/CompositorProxyClient.h"
 
 namespace blink {
 
 // Registry for CompositorProxies on the control thread.
 // Owned by the control thread.
-class CompositorProxyClientImpl
+class CORE_EXPORT CompositorProxyClientImpl
     : public GarbageCollectedFinalized<CompositorProxyClientImpl>,
       public CompositorProxyClient {
   USING_GARBAGE_COLLECTED_MIXIN(CompositorProxyClientImpl);
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
index c7e7630..b939018 100644
--- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
+++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -2026,49 +2026,6 @@
 }
 
 template <>
-inline CSSIdentifierValue::CSSIdentifierValue(EImageRendering e)
-    : CSSValue(kIdentifierClass) {
-  switch (e) {
-    case EImageRendering::kAuto:
-      value_id_ = CSSValueAuto;
-      break;
-    case EImageRendering::kOptimizeSpeed:
-      value_id_ = CSSValueOptimizeSpeed;
-      break;
-    case EImageRendering::kOptimizeQuality:
-      value_id_ = CSSValueOptimizeQuality;
-      break;
-    case EImageRendering::kPixelated:
-      value_id_ = CSSValuePixelated;
-      break;
-    case EImageRendering::kOptimizeContrast:
-      value_id_ = CSSValueWebkitOptimizeContrast;
-      break;
-  }
-}
-
-template <>
-inline EImageRendering CSSIdentifierValue::ConvertTo() const {
-  switch (value_id_) {
-    case CSSValueAuto:
-      return EImageRendering::kAuto;
-    case CSSValueOptimizeSpeed:
-      return EImageRendering::kOptimizeSpeed;
-    case CSSValueOptimizeQuality:
-      return EImageRendering::kOptimizeQuality;
-    case CSSValuePixelated:
-      return EImageRendering::kPixelated;
-    case CSSValueWebkitOptimizeContrast:
-      return EImageRendering::kOptimizeContrast;
-    default:
-      break;
-  }
-
-  NOTREACHED();
-  return EImageRendering::kAuto;
-}
-
-template <>
 inline CSSIdentifierValue::CSSIdentifierValue(ETransformStyle3D e)
     : CSSValue(kIdentifierClass) {
   switch (e) {
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index 1fc15f4..3938290e 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -1284,10 +1284,9 @@
     {
       name: "image-rendering",
       inherited: true,
-      field_template: "storage_only",
-      type_name: "EImageRendering",
-      default_value: "EImageRendering::kAuto",
-      field_size: 3,
+      field_template: "keyword",
+      keywords: ["auto", "optimize-speed", "optimize-quality", "-webkit-optimize-contrast", "pixelated"],
+      default_value: "auto",
       field_group: "rare-inherited",
     },
     {
diff --git a/third_party/WebKit/Source/core/css/OWNERS b/third_party/WebKit/Source/core/css/OWNERS
index 9c6d10c2..dc8cf82 100644
--- a/third_party/WebKit/Source/core/css/OWNERS
+++ b/third_party/WebKit/Source/core/css/OWNERS
@@ -1,4 +1,5 @@
 meade@chromium.org
 suzyh@chromium.org
+nainar@chromium.org
 
 # COMPONENT: Blink>CSS
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.cpp b/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.cpp
index 256f86a..4d50580 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.cpp
+++ b/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.cpp
@@ -75,7 +75,7 @@
   return CSSPrimitiveValue::UnitTypeToString(unit_);
 }
 
-String CSSUnitValue::cssType() const {
+String CSSUnitValue::type() const {
   if (unit_ == CSSPrimitiveValue::UnitType::kNumber)
     return "number";
   if (unit_ == CSSPrimitiveValue::UnitType::kPercentage)
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.h b/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.h
index 8ec12fd0..ffd2703 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.h
+++ b/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.h
@@ -27,7 +27,7 @@
   void setUnit(const String& new_unit, ExceptionState&);
   String unit() const;
 
-  String cssType() const;
+  String type() const;
 
   StyleValueType GetType() const override { return StyleValueType::kUnitType; }
 
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.idl b/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.idl
index 02ab6d4..8d7dcf3 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.idl
+++ b/third_party/WebKit/Source/core/css/cssom/CSSUnitValue.idl
@@ -14,5 +14,5 @@
 ] interface CSSUnitValue : CSSNumericValue {
   [EnforceRange] attribute double value;
   [RaisesException=Setter] attribute DOMString unit;
-  [ImplementedAs=cssType] readonly attribute DOMString type;
+  readonly attribute DOMString type;
 };
diff --git a/third_party/WebKit/Source/core/dom/NodeListsNodeData.h b/third_party/WebKit/Source/core/dom/NodeListsNodeData.h
index 12d43d3..8480aa9 100644
--- a/third_party/WebKit/Source/core/dom/NodeListsNodeData.h
+++ b/third_party/WebKit/Source/core/dom/NodeListsNodeData.h
@@ -62,15 +62,14 @@
     return list;
   }
 
+  using NamedNodeListKey = std::pair<CollectionType, AtomicString>;
   struct NodeListAtomicCacheMapEntryHash {
     STATIC_ONLY(NodeListAtomicCacheMapEntryHash);
-    static unsigned GetHash(
-        const std::pair<unsigned char, AtomicString>& entry) {
+    static unsigned GetHash(const NamedNodeListKey& entry) {
       return DefaultHash<AtomicString>::Hash::GetHash(entry.second) +
              entry.first;
     }
-    static bool Equal(const std::pair<unsigned char, AtomicString>& a,
-                      const std::pair<unsigned char, AtomicString>& b) {
+    static bool Equal(const NamedNodeListKey& a, const NamedNodeListKey& b) {
       return a == b;
     }
     static const bool safe_to_compare_to_empty_or_deleted =
@@ -79,7 +78,7 @@
 
   // Oilpan: keep a weak reference to the collection objects.
   // Object unregistration is handled by GC's weak processing.
-  typedef HeapHashMap<std::pair<unsigned char, AtomicString>,
+  typedef HeapHashMap<NamedNodeListKey,
                       WeakMember<LiveNodeListBase>,
                       NodeListAtomicCacheMapEntryHash>
       NodeListAtomicNameCacheMap;
@@ -92,7 +91,7 @@
               const AtomicString& name) {
     DCHECK(ThreadState::Current()->IsGCForbidden());
     NodeListAtomicNameCacheMap::AddResult result = atomic_name_caches_.insert(
-        NamedNodeListKey(collection_type, name), nullptr);
+        std::make_pair(collection_type, name), nullptr);
     if (!result.is_new_entry) {
       return static_cast<T*>(result.stored_value->value.Get());
     }
@@ -181,12 +180,6 @@
  private:
   NodeListsNodeData() : child_node_list_(nullptr) {}
 
-  std::pair<unsigned char, AtomicString> NamedNodeListKey(
-      CollectionType type,
-      const AtomicString& name) {
-    return std::pair<unsigned char, AtomicString>(type, name);
-  }
-
   // Can be a ChildNodeList or an EmptyNodeList.
   WeakMember<NodeList> child_node_list_;
   NodeListAtomicNameCacheMap atomic_name_caches_;
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallbackTest.cpp b/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallbackTest.cpp
index fc6e225..f3351b8 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallbackTest.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallbackTest.cpp
@@ -16,7 +16,7 @@
 class IdleSpellCheckCallbackTest : public SpellCheckTestBase {
  protected:
   IdleSpellCheckCallback& IdleChecker() {
-    return GetFrame().GetSpellChecker().GetIdleSpellCheckCallback();
+    return GetSpellChecker().GetIdleSpellCheckCallback();
   }
 
   void TransitTo(State state) {
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckTestBase.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckTestBase.cpp
index 321d14f3..f0a4e7e6 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckTestBase.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckTestBase.cpp
@@ -4,6 +4,8 @@
 
 #include "core/editing/spellcheck/SpellCheckTestBase.h"
 
+#include "core/frame/LocalFrame.h"
+
 namespace blink {
 
 void SpellCheckTestBase::SetUp() {
@@ -14,4 +16,8 @@
   SetupPageWithClients(&page_clients);
 }
 
+SpellChecker& SpellCheckTestBase::GetSpellChecker() const {
+  return GetFrame().GetSpellChecker();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckTestBase.h b/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckTestBase.h
index de455da..b31852c 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckTestBase.h
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckTestBase.h
@@ -11,10 +11,14 @@
 
 namespace blink {
 
+class SpellChecker;
+
 class SpellCheckTestBase : public EditingTestBase {
  protected:
   void SetUp() override;
 
+  SpellChecker& GetSpellChecker() const;
+
  private:
   class DummySpellCheckerClient : public EmptySpellCheckerClient {
    public:
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckerTest.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckerTest.cpp
index 6fb4114..9af8433 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckerTest.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckerTest.cpp
@@ -36,7 +36,7 @@
   Element* input = GetDocument().QuerySelector("input");
   input->focus();
   // Do not crash in advanceToNextMisspelling.
-  GetDocument().GetFrame()->GetSpellChecker().AdvanceToNextMisspelling(false);
+  GetSpellChecker().AdvanceToNextMisspelling(false);
 }
 
 // Regression test for crbug.com/701309
@@ -52,7 +52,7 @@
   UpdateAllLifecyclePhases();
 
   // Do not crash in advanceToNextMisspelling.
-  GetDocument().GetFrame()->GetSpellChecker().AdvanceToNextMisspelling(false);
+  GetSpellChecker().AdvanceToNextMisspelling(false);
 }
 
 // Regression test for crbug.com/728801
@@ -66,8 +66,7 @@
                                .Build());
   UpdateAllLifecyclePhases();
 
-  // TODO(xiaochengh): We should have SpellCheckTestBase::GetSpellChecker().
-  GetFrame().GetSpellChecker().AdvanceToNextMisspelling(false);
+  GetSpellChecker().AdvanceToNextMisspelling(false);
 }
 
 TEST_F(SpellCheckerTest, SpellCheckDoesNotCauseUpdateLayout) {
@@ -88,10 +87,10 @@
       SelectionInDOMTree::Builder().Collapse(new_position).Build());
   ASSERT_EQ(3u, input->selectionStart());
 
-  EXPECT_TRUE(GetFrame().GetSpellChecker().IsSpellCheckingEnabled());
+  EXPECT_TRUE(GetSpellChecker().IsSpellCheckingEnabled());
   ForceLayout();
   int start_count = LayoutCount();
-  GetFrame().GetSpellChecker().RespondToChangedSelection(
+  GetSpellChecker().RespondToChangedSelection(
       old_selection.Start(),
       FrameSelection::kCloseTyping | FrameSelection::kClearTypingStyle);
   EXPECT_EQ(start_count, LayoutCount());
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameClient.h b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
index 5dd9ed4..58be94c 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameClient.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
@@ -203,7 +203,7 @@
   };
   virtual bool CanCreatePluginWithoutRenderer(
       const String& mime_type) const = 0;
-  virtual PluginView* CreatePlugin(HTMLPlugInElement*,
+  virtual PluginView* CreatePlugin(HTMLPlugInElement&,
                                    const KURL&,
                                    const Vector<String>&,
                                    const Vector<String>&,
diff --git a/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp b/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
index 1e7a734..ac7486f 100644
--- a/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
@@ -557,7 +557,8 @@
         require_layout_object ? LocalFrameClient::kFailOnDetachedPlugin
                               : LocalFrameClient::kAllowDetachedPlugin;
     PluginView* plugin = frame->Loader().Client()->CreatePlugin(
-        this, url, param_names, param_values, mime_type, load_manually, policy);
+        *this, url, param_names, param_values, mime_type, load_manually,
+        policy);
     if (!plugin) {
       if (!layout_item.IsNull() &&
           !layout_item.ShowsUnavailablePluginIndicator()) {
diff --git a/third_party/WebKit/Source/core/layout/ImageQualityController.cpp b/third_party/WebKit/Source/core/layout/ImageQualityController.cpp
index 3245bac..d0d4eab 100644
--- a/third_party/WebKit/Source/core/layout/ImageQualityController.cpp
+++ b/third_party/WebKit/Source/core/layout/ImageQualityController.cpp
@@ -179,7 +179,8 @@
   if (!layer)
     return false;
 
-  if (object.Style()->ImageRendering() == EImageRendering::kOptimizeContrast)
+  if (object.Style()->ImageRendering() ==
+      EImageRendering::kWebkitOptimizeContrast)
     return true;
 
   if (LocalFrame* frame = object.GetFrame()) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index c881d97..ed733a9 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1438,13 +1438,6 @@
       TransformState&,
       VisualRectFlags = kDefaultVisualRectFlags) const;
 
-  // Allows objects to adjust |visualEffect|, which is in the space of the
-  // paint invalidation container, for any special raster effects that might
-  // expand the rastered pixel area. Returns true if the rect is expanded.
-  virtual bool AdjustVisualRectForRasterEffects(LayoutRect& visual_rect) const {
-    return false;
-  }
-
   // Return the offset to the column in which the specified point (in
   // flow-thread coordinates) lives. This is used to convert a flow-thread point
   // to a point in the containing coordinate space.
diff --git a/third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp b/third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp
index a44ab00..9e8d2ac 100644
--- a/third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp
+++ b/third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp
@@ -468,7 +468,8 @@
   PaintLayer::MapRectInPaintInvalidationContainerToBacking(
       *paint_invalidation_container_, rect);
 
-  current_object_.AdjustVisualRectForRasterEffects(rect);
+  if (!rect.IsEmpty())
+    rect.Inflate(current_object_.VisualRectOutsetForRasterEffects());
 
   rect.Move(current_object_.ScrollAdjustmentForPaintInvalidation(
       *paint_invalidation_container_));
@@ -518,7 +519,8 @@
   PaintLayer::MapRectInPaintInvalidationContainerToBacking(
       *paint_invalidation_container_, rect);
 
-  current_object_.AdjustVisualRectForRasterEffects(rect);
+  if (!rect.IsEmpty())
+    rect.Inflate(current_object_.VisualRectOutsetForRasterEffects());
 
   rect.Move(current_object_.ScrollAdjustmentForPaintInvalidation(
       *paint_invalidation_container_));
diff --git a/third_party/WebKit/Source/core/layout/TextAutosizer.cpp b/third_party/WebKit/Source/core/layout/TextAutosizer.cpp
index 77650aa..f5212eae 100644
--- a/third_party/WebKit/Source/core/layout/TextAutosizer.cpp
+++ b/third_party/WebKit/Source/core/layout/TextAutosizer.cpp
@@ -1155,7 +1155,8 @@
       parent_deepest_block_containing_all_text));
 #endif
 
-  float content_width = cluster->root_->ContentLogicalWidth().ToFloat();
+  float content_width =
+      DeepestBlockContainingAllText(cluster)->ContentLogicalWidth().ToFloat();
   float cluster_text_width =
       parent_deepest_block_containing_all_text->ContentLogicalWidth().ToFloat();
 
diff --git a/third_party/WebKit/Source/core/layout/TextAutosizerTest.cpp b/third_party/WebKit/Source/core/layout/TextAutosizerTest.cpp
index da1c5880..b6fdb705 100644
--- a/third_party/WebKit/Source/core/layout/TextAutosizerTest.cpp
+++ b/third_party/WebKit/Source/core/layout/TextAutosizerTest.cpp
@@ -739,7 +739,7 @@
   Element* html = GetDocument().body()->parentElement();
   html->setInnerHTML(
       "<head>"
-      "  <meta name='viewport' content='800'>"
+      "  <meta name='viewport' content='width=800'>"
       "  <style>"
       "    html { font-size:16px; font-family:'Times New Roman';}"
       "  </style>"
@@ -774,4 +774,41 @@
       IntSize(360, 640));
   GetDocument().View()->UpdateAllLifecyclePhases();
 }
+
+TEST_F(TextAutosizerTest, narrowContentInsideNestedWideBlock) {
+  Element* html = GetDocument().body()->parentElement();
+  html->setInnerHTML(
+      "<head>"
+      "  <meta name='viewport' content='width=800'>"
+      "  <style>"
+      "    html { font-size:16px;}"
+      "  </style>"
+      "</head>"
+      "<body>"
+      "  <div style='width:800px'>"
+      "    <div style='width:800px'>"
+      "      <div style='width:200px' id='content'>"
+      "        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed "
+      "        do eiusmod tempor incididunt ut labore et dolore magna aliqua."
+      "        Ut enim ad minim veniam, quis nostrud exercitation ullamco "
+      "        laboris nisi ut aliquip ex ea commodo consequat. Duis aute "
+      "        irure dolor in reprehenderit in voluptate velit esse cillum "
+      "        dolore eu fugiat nulla pariatur. Excepteur sint occaecat "
+      "        cupidatat non proident, sunt in culpa qui officia deserunt "
+      "        mollit anim id est laborum."
+      "      </div>"
+      "    </div>"
+      "    Content belong to first wide block."
+      "  </div>"
+      "</body>",
+      ASSERT_NO_EXCEPTION);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  Element* content = GetDocument().getElementById("content");
+  //(content width = 200px) / (window width = 320px) < 1.0f, multiplier = 1.0,
+  // font-size = 16px;
+  EXPECT_FLOAT_EQ(16.f,
+                  content->GetLayoutObject()->Style()->ComputedFontSize());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.cpp
index bfe6efc..dec3bb7 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.cpp
@@ -45,19 +45,6 @@
 
 namespace blink {
 
-bool LayoutSVGShape::AdjustVisualRectForRasterEffects(
-    LayoutRect& visual_rect) const {
-  // Account for raster expansions due to SVG stroke hairline raster effects.
-  if (!visual_rect.IsEmpty() && StyleRef().SvgStyle().HasVisibleStroke()) {
-    LayoutUnit pad(0.5f);
-    if (StyleRef().SvgStyle().CapStyle() != kButtCap)
-      pad += 0.5f;
-    visual_rect.Inflate(pad);
-    return true;
-  }
-  return false;
-}
-
 LayoutSVGShape::LayoutSVGShape(SVGGeometryElement* node)
     : LayoutSVGModelObject(node),
       // Default is false, the cached rects are empty from the beginning.
@@ -340,4 +327,15 @@
   return *rare_data_.get();
 }
 
+LayoutUnit LayoutSVGShape::VisualRectOutsetForRasterEffects() const {
+  // Account for raster expansions due to SVG stroke hairline raster effects.
+  if (StyleRef().SvgStyle().HasVisibleStroke()) {
+    LayoutUnit outset(0.5f);
+    if (StyleRef().SvgStyle().CapStyle() != kButtCap)
+      outset += 0.5f;
+    return outset;
+  }
+  return LayoutUnit();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.h
index 1cd89611..f59a3f0 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.h
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGShape.h
@@ -106,7 +106,7 @@
   const char* GetName() const override { return "LayoutSVGShape"; }
 
  protected:
-  bool AdjustVisualRectForRasterEffects(LayoutRect&) const override;
+  LayoutUnit VisualRectOutsetForRasterEffects() const override;
 
   void ClearPath() { path_.reset(); }
   void CreatePath();
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.cpp b/third_party/WebKit/Source/core/loader/EmptyClients.cpp
index 003ff95..9871f76 100644
--- a/third_party/WebKit/Source/core/loader/EmptyClients.cpp
+++ b/third_party/WebKit/Source/core/loader/EmptyClients.cpp
@@ -169,7 +169,7 @@
   return nullptr;
 }
 
-PluginView* EmptyLocalFrameClient::CreatePlugin(HTMLPlugInElement*,
+PluginView* EmptyLocalFrameClient::CreatePlugin(HTMLPlugInElement&,
                                                 const KURL&,
                                                 const Vector<String>&,
                                                 const Vector<String>&,
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.h b/third_party/WebKit/Source/core/loader/EmptyClients.h
index f6469c8..9138d0c 100644
--- a/third_party/WebKit/Source/core/loader/EmptyClients.h
+++ b/third_party/WebKit/Source/core/loader/EmptyClients.h
@@ -335,7 +335,7 @@
   LocalFrame* CreateFrame(const FrameLoadRequest&,
                           const AtomicString&,
                           HTMLFrameOwnerElement*) override;
-  PluginView* CreatePlugin(HTMLPlugInElement*,
+  PluginView* CreatePlugin(HTMLPlugInElement&,
                            const KURL&,
                            const Vector<String>&,
                            const Vector<String>&,
diff --git a/third_party/WebKit/Source/core/paint/HTMLCanvasPainter.cpp b/third_party/WebKit/Source/core/paint/HTMLCanvasPainter.cpp
index a6411b2..0362d91b 100644
--- a/third_party/WebKit/Source/core/paint/HTMLCanvasPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/HTMLCanvasPainter.cpp
@@ -56,7 +56,7 @@
   // is set.  See bug for more details: crbug.com/353716.
   InterpolationQuality interpolation_quality =
       layout_html_canvas_.Style()->ImageRendering() ==
-              EImageRendering::kOptimizeContrast
+              EImageRendering::kWebkitOptimizeContrast
           ? kInterpolationLow
           : CanvasDefaultInterpolationQuality;
   if (layout_html_canvas_.Style()->ImageRendering() ==
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
index 058ed32..2bc0b55 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
@@ -135,7 +135,8 @@
     result.MoveBy(-context.paint_invalidation_container->PaintOffset());
   }
 
-  object.AdjustVisualRectForRasterEffects(result);
+  if (!result.IsEmpty())
+    result.Inflate(object.VisualRectOutsetForRasterEffects());
 
   PaintLayer::MapRectInPaintInvalidationContainerToBacking(
       *context.paint_invalidation_container, result);
diff --git a/third_party/WebKit/Source/core/style/BUILD.gn b/third_party/WebKit/Source/core/style/BUILD.gn
index 317da73..112d3d55 100644
--- a/third_party/WebKit/Source/core/style/BUILD.gn
+++ b/third_party/WebKit/Source/core/style/BUILD.gn
@@ -51,8 +51,10 @@
     "NinePieceImage.cpp",
     "NinePieceImage.h",
     "OutlineValue.h",
+    "PaintImages.h",
     "QuotesData.cpp",
     "QuotesData.h",
+    "ScrollSnapPoints.h",
     "ShadowData.cpp",
     "ShadowData.h",
     "ShadowList.cpp",
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index ddf32276..b073dd9 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -921,7 +921,7 @@
 void ComputedStyle::AddPaintImage(StyleImage* image) {
   if (!rare_non_inherited_data_.Access()->paint_images_) {
     rare_non_inherited_data_.Access()->paint_images_ =
-        WTF::MakeUnique<Vector<Persistent<StyleImage>>>();
+        WTF::MakeUnique<PaintImages>();
   }
   rare_non_inherited_data_.Access()->paint_images_->push_back(image);
 }
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index 5a9ad613..b772b0a 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -677,7 +677,11 @@
   // column-rule-width (aka -webkit-column-rule-width)
   static unsigned short InitialColumnRuleWidth() { return 3; }
   unsigned short ColumnRuleWidth() const {
-    return rare_non_inherited_data_->multi_col_->RuleWidth();
+    const BorderValue& rule = rare_non_inherited_data_->multi_col_->rule_;
+    if (rule.Style() == EBorderStyle::kNone ||
+        rule.Style() == EBorderStyle::kHidden)
+      return 0;
+    return rule.Width();
   }
   void SetColumnRuleWidth(unsigned short w) {
     SET_NESTED_BORDER_WIDTH(rare_non_inherited_data_, multi_col_, rule_, w);
@@ -1007,17 +1011,6 @@
     SET_VAR(rare_inherited_data_, respect_image_orientation_, v);
   }
 
-  // image-rendering
-  static EImageRendering InitialImageRendering() {
-    return EImageRendering::kAuto;
-  }
-  EImageRendering ImageRendering() const {
-    return static_cast<EImageRendering>(rare_inherited_data_->image_rendering_);
-  }
-  void SetImageRendering(EImageRendering v) {
-    SET_VAR(rare_inherited_data_, image_rendering_, static_cast<unsigned>(v));
-  }
-
   // isolation
   static EIsolation InitialIsolation() { return kIsolationAuto; }
   EIsolation Isolation() const {
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
index be6ea14..d4a285e 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
@@ -266,14 +266,6 @@
 
 enum TextOverflow { kTextOverflowClip = 0, kTextOverflowEllipsis };
 
-enum class EImageRendering {
-  kAuto,
-  kOptimizeSpeed,
-  kOptimizeQuality,
-  kOptimizeContrast,
-  kPixelated
-};
-
 static const size_t kGridAutoFlowBits = 4;
 enum InternalGridAutoFlowAlgorithm {
   kInternalAutoFlowAlgorithmSparse = 0x1,
diff --git a/third_party/WebKit/Source/core/style/OWNERS b/third_party/WebKit/Source/core/style/OWNERS
index 9c6d10c2..e501eae3 100644
--- a/third_party/WebKit/Source/core/style/OWNERS
+++ b/third_party/WebKit/Source/core/style/OWNERS
@@ -1,4 +1,6 @@
 meade@chromium.org
 suzyh@chromium.org
+nainar@chromium.org
+shend@chromium.org
 
 # COMPONENT: Blink>CSS
diff --git a/third_party/WebKit/Source/core/style/PaintImages.h b/third_party/WebKit/Source/core/style/PaintImages.h
new file mode 100644
index 0000000..27864fe
--- /dev/null
+++ b/third_party/WebKit/Source/core/style/PaintImages.h
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PaintImages_h
+#define PaintImages_h
+
+#include <memory>
+#include "core/style/StyleImage.h"
+#include "platform/heap/Persistent.h"
+#include "platform/wtf/PtrUtil.h"
+#include "platform/wtf/Vector.h"
+
+namespace blink {
+
+// Not to be deleted through a pointer to the base class.
+class PaintImages : public Vector<Persistent<StyleImage>> {
+ public:
+  std::unique_ptr<PaintImages> Clone() const {
+    return WTF::WrapUnique(new PaintImages(*this));
+  }
+};
+
+}  // namespace blink
+
+#endif  // PaintImages_h
diff --git a/third_party/WebKit/Source/core/style/ScrollSnapPoints.h b/third_party/WebKit/Source/core/style/ScrollSnapPoints.h
new file mode 100644
index 0000000..473265b6
--- /dev/null
+++ b/third_party/WebKit/Source/core/style/ScrollSnapPoints.h
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ScrollSnapPoints_h
+#define ScrollSnapPoints_h
+
+#include <memory>
+#include "platform/Length.h"
+
+namespace blink {
+
+struct ScrollSnapPoints {
+  DISALLOW_NEW();
+
+  ScrollSnapPoints()
+      : repeat_offset(100, kPercent), has_repeat(false), uses_elements(false) {}
+
+  bool operator==(const ScrollSnapPoints& other) const {
+    return repeat_offset == other.repeat_offset &&
+           has_repeat == other.has_repeat &&
+           uses_elements == other.uses_elements;
+  }
+  bool operator!=(const ScrollSnapPoints& other) const {
+    return !(*this == other);
+  }
+
+  Length repeat_offset;
+  bool has_repeat;
+  bool uses_elements;
+};
+
+}  // namespace blink
+
+#endif  // ScrollSnapPoints_h
diff --git a/third_party/WebKit/Source/core/style/StyleMultiColData.h b/third_party/WebKit/Source/core/style/StyleMultiColData.h
index bc9d257a..122465cd 100644
--- a/third_party/WebKit/Source/core/style/StyleMultiColData.h
+++ b/third_party/WebKit/Source/core/style/StyleMultiColData.h
@@ -47,13 +47,6 @@
   bool operator==(const StyleMultiColData&) const;
   bool operator!=(const StyleMultiColData& o) const { return !(*this == o); }
 
-  unsigned short RuleWidth() const {
-    if (rule_.Style() == EBorderStyle::kNone ||
-        rule_.Style() == EBorderStyle::kHidden)
-      return 0;
-    return rule_.Width();
-  }
-
   float width_;
   unsigned short count_;
   float gap_;
diff --git a/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.cpp b/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.cpp
index c5f5bec..72b1d5b 100644
--- a/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.cpp
+++ b/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.cpp
@@ -176,6 +176,9 @@
       visited_link_border_right_color_(o.visited_link_border_right_color_),
       visited_link_border_top_color_(o.visited_link_border_top_color_),
       visited_link_border_bottom_color_(o.visited_link_border_bottom_color_),
+      callback_selectors_(o.callback_selectors_),
+      paint_images_(o.paint_images_ ? new PaintImages(*o.paint_images_)
+                                    : nullptr),
       variables_(o.variables_ ? o.variables_->Clone() : nullptr),
       align_content_(o.align_content_),
       align_items_(o.align_items_),
diff --git a/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.h b/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.h
index 912ff09..0ea0316 100644
--- a/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.h
+++ b/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.h
@@ -37,6 +37,7 @@
 #include "core/style/LineClampValue.h"
 #include "core/style/NinePieceImage.h"
 #include "core/style/OutlineValue.h"
+#include "core/style/PaintImages.h"
 #include "core/style/ShapeValue.h"
 #include "core/style/StyleContentAlignmentData.h"
 #include "core/style/StyleScrollSnapData.h"
@@ -147,7 +148,7 @@
 
   Vector<String> callback_selectors_;
 
-  std::unique_ptr<Vector<Persistent<StyleImage>>> paint_images_;
+  std::unique_ptr<PaintImages> paint_images_;
 
   std::unique_ptr<StyleNonInheritedVariables> variables_;
 
diff --git a/third_party/WebKit/Source/core/style/StyleScrollSnapData.cpp b/third_party/WebKit/Source/core/style/StyleScrollSnapData.cpp
index 5acff71..b33d1b2 100644
--- a/third_party/WebKit/Source/core/style/StyleScrollSnapData.cpp
+++ b/third_party/WebKit/Source/core/style/StyleScrollSnapData.cpp
@@ -29,14 +29,6 @@
 
 namespace blink {
 
-ScrollSnapPoints::ScrollSnapPoints()
-    : repeat_offset(100, kPercent), has_repeat(false), uses_elements(false) {}
-
-bool operator==(const ScrollSnapPoints& a, const ScrollSnapPoints& b) {
-  return a.repeat_offset == b.repeat_offset && a.has_repeat == b.has_repeat &&
-         a.uses_elements == b.uses_elements;
-}
-
 StyleScrollSnapData::StyleScrollSnapData()
     : x_points_(ComputedStyle::InitialScrollSnapPointsX()),
       y_points_(ComputedStyle::InitialScrollSnapPointsY()),
diff --git a/third_party/WebKit/Source/core/style/StyleScrollSnapData.h b/third_party/WebKit/Source/core/style/StyleScrollSnapData.h
index f0ae6bcf..517f57b 100644
--- a/third_party/WebKit/Source/core/style/StyleScrollSnapData.h
+++ b/third_party/WebKit/Source/core/style/StyleScrollSnapData.h
@@ -26,6 +26,7 @@
 #ifndef StyleScrollSnapData_h
 #define StyleScrollSnapData_h
 
+#include "core/style/ScrollSnapPoints.h"
 #include "platform/LengthPoint.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/RefCounted.h"
@@ -33,19 +34,6 @@
 
 namespace blink {
 
-struct ScrollSnapPoints {
-  DISALLOW_NEW();
-  Length repeat_offset;
-  bool has_repeat;
-  bool uses_elements;
-  ScrollSnapPoints();
-};
-
-bool operator==(const ScrollSnapPoints&, const ScrollSnapPoints&);
-inline bool operator!=(const ScrollSnapPoints& a, const ScrollSnapPoints& b) {
-  return !(a == b);
-}
-
 class StyleScrollSnapData : public RefCounted<StyleScrollSnapData> {
  public:
   static PassRefPtr<StyleScrollSnapData> Create() {
diff --git a/third_party/WebKit/Source/core/svg/SVGElement.cpp b/third_party/WebKit/Source/core/svg/SVGElement.cpp
index 4ff98319..8225f3e 100644
--- a/third_party/WebKit/Source/core/svg/SVGElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGElement.cpp
@@ -743,7 +743,18 @@
   }
 }
 
-typedef HashMap<QualifiedName, AnimatedPropertyType> AttributeToPropertyTypeMap;
+// If the attribute is not present in the map, the map will return the "empty
+// value" - which is kAnimatedUnknown.
+struct AnimatedPropertyTypeHashTraits : HashTraits<AnimatedPropertyType> {
+  static const bool kEmptyValueIsZero = true;
+  static AnimatedPropertyType EmptyValue() { return kAnimatedUnknown; }
+};
+
+using AttributeToPropertyTypeMap = HashMap<QualifiedName,
+                                           AnimatedPropertyType,
+                                           DefaultHash<QualifiedName>::Hash,
+                                           HashTraits<QualifiedName>,
+                                           AnimatedPropertyTypeHashTraits>;
 AnimatedPropertyType SVGElement::AnimatedPropertyTypeForCSSAttribute(
     const QualifiedName& attribute_name) {
   DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, css_property_map, ());
@@ -812,9 +823,6 @@
     for (size_t i = 0; i < WTF_ARRAY_LENGTH(attr_to_types); i++)
       css_property_map.Set(attr_to_types[i].attr, attr_to_types[i].prop_type);
   }
-  // If the attribute is not present in the map, this will return the "empty
-  // value" per HashTraits - which is AnimatedUnknown.
-  DCHECK_EQ(HashTraits<AnimatedPropertyType>::EmptyValue(), kAnimatedUnknown);
   return css_property_map.at(attribute_name);
 }
 
diff --git a/third_party/WebKit/Source/core/workers/BUILD.gn b/third_party/WebKit/Source/core/workers/BUILD.gn
index bf0958a..ccc8e28 100644
--- a/third_party/WebKit/Source/core/workers/BUILD.gn
+++ b/third_party/WebKit/Source/core/workers/BUILD.gn
@@ -22,8 +22,6 @@
     "InProcessWorkerMessagingProxy.h",
     "InProcessWorkerObjectProxy.cpp",
     "InProcessWorkerObjectProxy.h",
-    "MainThreadWorklet.cpp",
-    "MainThreadWorklet.h",
     "MainThreadWorkletGlobalScope.cpp",
     "MainThreadWorkletGlobalScope.h",
     "ParentFrameTaskRunners.cpp",
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp b/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp
deleted file mode 100644
index 4ff8973..0000000
--- a/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "core/workers/MainThreadWorklet.h"
-
-#include "bindings/core/v8/ScriptPromiseResolver.h"
-#include "bindings/core/v8/ScriptSourceCode.h"
-#include "bindings/core/v8/V8BindingForCore.h"
-#include "core/dom/TaskRunnerHelper.h"
-#include "core/frame/LocalFrame.h"
-#include "core/workers/WorkletGlobalScopeProxy.h"
-#include "core/workers/WorkletPendingTasks.h"
-#include "platform/WebTaskRunner.h"
-#include "platform/wtf/WTF.h"
-
-namespace blink {
-
-MainThreadWorklet::MainThreadWorklet(LocalFrame* frame) : Worklet(frame) {}
-
-WorkletGlobalScopeProxy* MainThreadWorklet::FindAvailableGlobalScope() const {
-  DCHECK(IsMainThread());
-  // TODO(nhiroki): Support the case where there are multiple global scopes.
-  DCHECK_EQ(1u, GetNumberOfGlobalScopes());
-  return proxies_.begin()->get();
-}
-
-// Implementation of the second half of the "addModule(moduleURL, options)"
-// algorithm:
-// https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule
-void MainThreadWorklet::FetchAndInvokeScript(const KURL& module_url_record,
-                                             const WorkletOptions& options,
-                                             ScriptPromiseResolver* resolver) {
-  DCHECK(IsMainThread());
-  if (!GetExecutionContext())
-    return;
-
-  // Step 6: "Let credentialOptions be the credentials member of options."
-  // TODO(nhiroki): Add tests for credentialOptions (https://crbug.com/710837).
-  WebURLRequest::FetchCredentialsMode credentials_mode =
-      ParseCredentialsOption(options.credentials());
-
-  // Step 7: "Let outsideSettings be the relevant settings object of this."
-  // In the specification, outsideSettings is used for posting a task to the
-  // document's responsible event loop. In our implementation, we use the
-  // document's UnspecedLoading task runner as that is what we commonly use for
-  // module loading.
-  RefPtr<WebTaskRunner> outside_settings_task_runner =
-      TaskRunnerHelper::Get(TaskType::kUnspecedLoading, GetExecutionContext());
-
-  // Step 8: "Let moduleResponsesMap be worklet's module responses map."
-  // TODO(nhiroki): Implement moduleResponsesMap (https://crbug.com/627945).
-
-  // Step 9: "Let workletGlobalScopeType be worklet's worklet global scope
-  // type."
-  // workletGlobalScopeType is encoded into the class name (e.g., PaintWorklet).
-
-  // Step 10: "If the worklet's WorkletGlobalScopes is empty, run the following
-  // steps:"
-  //   10.1: "Create a WorkletGlobalScope given workletGlobalScopeType,
-  //          moduleResponsesMap, and outsideSettings."
-  //   10.2: "Add the WorkletGlobalScope to worklet's WorkletGlobalScopes."
-  // "Depending on the type of worklet the user agent may create additional
-  // WorkletGlobalScopes at this time."
-  while (NeedsToCreateGlobalScope())
-    proxies_.insert(CreateGlobalScope());
-  DCHECK_EQ(1u, GetNumberOfGlobalScopes());
-
-  // Step 11: "Let pendingTaskStruct be a new pending tasks struct with counter
-  // initialized to the length of worklet's WorkletGlobalScopes."
-  WorkletPendingTasks* pending_tasks =
-      new WorkletPendingTasks(GetNumberOfGlobalScopes(), resolver);
-
-  // Step 12: "For each workletGlobalScope in the worklet's
-  // WorkletGlobalScopes, queue a task on the workletGlobalScope to fetch and
-  // invoke a worklet script given workletGlobalScope, moduleURLRecord,
-  // moduleResponsesMap, credentialOptions, outsideSettings, pendingTaskStruct,
-  // and promise."
-  // TODO(nhiroki): Queue a task instead of executing this here.
-  for (const auto& proxy : proxies_) {
-    proxy->FetchAndInvokeScript(module_url_record, credentials_mode,
-                                outside_settings_task_runner, pending_tasks);
-  }
-}
-
-void MainThreadWorklet::ContextDestroyed(ExecutionContext* execution_context) {
-  DCHECK(IsMainThread());
-  for (const auto& proxy : proxies_)
-    proxy->TerminateWorkletGlobalScope();
-}
-
-DEFINE_TRACE(MainThreadWorklet) {
-  Worklet::Trace(visitor);
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorklet.h b/third_party/WebKit/Source/core/workers/MainThreadWorklet.h
deleted file mode 100644
index 72b1bc6..0000000
--- a/third_party/WebKit/Source/core/workers/MainThreadWorklet.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MainThreadWorklet_h
-#define MainThreadWorklet_h
-
-#include "core/workers/Worklet.h"
-
-#include "bindings/core/v8/ScriptPromise.h"
-#include "core/CoreExport.h"
-#include "platform/heap/Handle.h"
-
-namespace blink {
-
-class LocalFrame;
-class ScriptPromiseResolver;
-class WorkletGlobalScopeProxy;
-
-// A MainThreadWorklet is a worklet that runs only on the main thread.
-// TODO(nhiroki): This is a temporary class to support module loading for main
-// thread worklets. This and ThreadedWorklet will be merged into the base
-// Worklet class once threaded worklets are ready to use module loading.
-class CORE_EXPORT MainThreadWorklet : public Worklet {
-  USING_GARBAGE_COLLECTED_MIXIN(MainThreadWorklet);
-  WTF_MAKE_NONCOPYABLE(MainThreadWorklet);
-
- public:
-  virtual ~MainThreadWorklet() = default;
-
-  // ContextLifecycleObserver
-  void ContextDestroyed(ExecutionContext*) final;
-
-  DECLARE_VIRTUAL_TRACE();
-
- protected:
-  explicit MainThreadWorklet(LocalFrame*);
-
-  // Returns one of available global scopes.
-  WorkletGlobalScopeProxy* FindAvailableGlobalScope() const;
-
-  size_t GetNumberOfGlobalScopes() const { return proxies_.size(); }
-
- private:
-  // Worklet.
-  void FetchAndInvokeScript(const KURL& module_url_record,
-                            const WorkletOptions&,
-                            ScriptPromiseResolver*) override;
-
-  // Returns true if there are no global scopes or additional global scopes are
-  // necessary. CreateGlobalScope() will be called in that case. Each worklet
-  // can define how to pool global scopes here.
-  virtual bool NeedsToCreateGlobalScope() = 0;
-  virtual std::unique_ptr<WorkletGlobalScopeProxy> CreateGlobalScope() = 0;
-
-  // "A Worklet has a list of the worklet's WorkletGlobalScopes. Initially this
-  // list is empty; it is populated when the user agent chooses to create its
-  // WorkletGlobalScope."
-  // https://drafts.css-houdini.org/worklets/#worklet-section
-  // TODO(nhiroki): Make (Paint)WorkletGlobalScopeProxy GC-managed object to
-  // avoid that GC graphs are disjointed (https://crbug.com/719775).
-  HashSet<std::unique_ptr<WorkletGlobalScopeProxy>> proxies_;
-};
-
-}  // namespace blink
-
-#endif  // MainThreadWorklet_h
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorklet.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorklet.cpp
index 7a873ded..1d7b5f9 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorklet.cpp
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorklet.cpp
@@ -83,6 +83,16 @@
       std::move(outside_settings_task_runner), pending_tasks);
 }
 
+bool ThreadedWorklet::NeedsToCreateGlobalScope() {
+  NOTREACHED();
+  return false;
+}
+
+std::unique_ptr<WorkletGlobalScopeProxy> ThreadedWorklet::CreateGlobalScope() {
+  NOTREACHED();
+  return nullptr;
+}
+
 void ThreadedWorklet::ContextDestroyed(ExecutionContext* execution_context) {
   DCHECK(IsMainThread());
   if (IsInitialized())
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorklet.h b/third_party/WebKit/Source/core/workers/ThreadedWorklet.h
index 297d997..46c8e05 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorklet.h
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorklet.h
@@ -43,7 +43,9 @@
   // Worklet
   void FetchAndInvokeScript(const KURL& module_url_record,
                             const WorkletOptions&,
-                            ScriptPromiseResolver*) override;
+                            ScriptPromiseResolver*) final;
+  bool NeedsToCreateGlobalScope() final;
+  std::unique_ptr<WorkletGlobalScopeProxy> CreateGlobalScope() final;
 
   // Called when addModule() is called for the first time.
   virtual void Initialize() = 0;
diff --git a/third_party/WebKit/Source/core/workers/Worklet.cpp b/third_party/WebKit/Source/core/workers/Worklet.cpp
index deb56b048..5fc33e55 100644
--- a/third_party/WebKit/Source/core/workers/Worklet.cpp
+++ b/third_party/WebKit/Source/core/workers/Worklet.cpp
@@ -9,7 +9,9 @@
 #include "core/dom/Document.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/frame/LocalFrame.h"
-#include "core/workers/WorkletGlobalScopeProxy.h"
+#include "core/workers/WorkletPendingTasks.h"
+#include "platform/WebTaskRunner.h"
+#include "platform/wtf/WTF.h"
 
 namespace blink {
 
@@ -62,6 +64,12 @@
   return promise;
 }
 
+void Worklet::ContextDestroyed(ExecutionContext* execution_context) {
+  DCHECK(IsMainThread());
+  for (const auto& proxy : proxies_)
+    proxy->TerminateWorkletGlobalScope();
+}
+
 WebURLRequest::FetchCredentialsMode Worklet::ParseCredentialsOption(
     const String& credentials_option) {
   if (credentials_option == "omit")
@@ -74,6 +82,71 @@
   return WebURLRequest::kFetchCredentialsModeOmit;
 }
 
+WorkletGlobalScopeProxy* Worklet::FindAvailableGlobalScope() const {
+  DCHECK(IsMainThread());
+  // TODO(nhiroki): Support the case where there are multiple global scopes.
+  DCHECK_EQ(1u, GetNumberOfGlobalScopes());
+  return proxies_.begin()->get();
+}
+
+// Implementation of the second half of the "addModule(moduleURL, options)"
+// algorithm:
+// https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule
+void Worklet::FetchAndInvokeScript(const KURL& module_url_record,
+                                   const WorkletOptions& options,
+                                   ScriptPromiseResolver* resolver) {
+  DCHECK(IsMainThread());
+  if (!GetExecutionContext())
+    return;
+
+  // Step 6: "Let credentialOptions be the credentials member of options."
+  // TODO(nhiroki): Add tests for credentialOptions (https://crbug.com/710837).
+  WebURLRequest::FetchCredentialsMode credentials_mode =
+      ParseCredentialsOption(options.credentials());
+
+  // Step 7: "Let outsideSettings be the relevant settings object of this."
+  // In the specification, outsideSettings is used for posting a task to the
+  // document's responsible event loop. In our implementation, we use the
+  // document's UnspecedLoading task runner as that is what we commonly use for
+  // module loading.
+  RefPtr<WebTaskRunner> outside_settings_task_runner =
+      TaskRunnerHelper::Get(TaskType::kUnspecedLoading, GetExecutionContext());
+
+  // Step 8: "Let moduleResponsesMap be worklet's module responses map."
+  // TODO(nhiroki): Implement moduleResponsesMap (https://crbug.com/627945).
+
+  // Step 9: "Let workletGlobalScopeType be worklet's worklet global scope
+  // type."
+  // workletGlobalScopeType is encoded into the class name (e.g., PaintWorklet).
+
+  // Step 10: "If the worklet's WorkletGlobalScopes is empty, run the following
+  // steps:"
+  //   10.1: "Create a WorkletGlobalScope given workletGlobalScopeType,
+  //          moduleResponsesMap, and outsideSettings."
+  //   10.2: "Add the WorkletGlobalScope to worklet's WorkletGlobalScopes."
+  // "Depending on the type of worklet the user agent may create additional
+  // WorkletGlobalScopes at this time."
+  while (NeedsToCreateGlobalScope())
+    proxies_.insert(CreateGlobalScope());
+  DCHECK_EQ(1u, GetNumberOfGlobalScopes());
+
+  // Step 11: "Let pendingTaskStruct be a new pending tasks struct with counter
+  // initialized to the length of worklet's WorkletGlobalScopes."
+  WorkletPendingTasks* pending_tasks =
+      new WorkletPendingTasks(GetNumberOfGlobalScopes(), resolver);
+
+  // Step 12: "For each workletGlobalScope in the worklet's
+  // WorkletGlobalScopes, queue a task on the workletGlobalScope to fetch and
+  // invoke a worklet script given workletGlobalScope, moduleURLRecord,
+  // moduleResponsesMap, credentialOptions, outsideSettings, pendingTaskStruct,
+  // and promise."
+  // TODO(nhiroki): Queue a task instead of executing this here.
+  for (const auto& proxy : proxies_) {
+    proxy->FetchAndInvokeScript(module_url_record, credentials_mode,
+                                outside_settings_task_runner, pending_tasks);
+  }
+}
+
 DEFINE_TRACE(Worklet) {
   ContextLifecycleObserver::Trace(visitor);
 }
diff --git a/third_party/WebKit/Source/core/workers/Worklet.h b/third_party/WebKit/Source/core/workers/Worklet.h
index 690d9aa..8775818 100644
--- a/third_party/WebKit/Source/core/workers/Worklet.h
+++ b/third_party/WebKit/Source/core/workers/Worklet.h
@@ -8,6 +8,7 @@
 #include "bindings/core/v8/ScriptPromise.h"
 #include "core/CoreExport.h"
 #include "core/dom/ContextLifecycleObserver.h"
+#include "core/workers/WorkletGlobalScopeProxy.h"
 #include "core/workers/WorkletOptions.h"
 #include "platform/bindings/ScriptWrappable.h"
 #include "platform/heap/Handle.h"
@@ -34,9 +35,12 @@
 
   // Worklet.idl
   // addModule() imports ES6 module scripts.
-  virtual ScriptPromise addModule(ScriptState*,
-                                  const String& module_url,
-                                  const WorkletOptions&);
+  ScriptPromise addModule(ScriptState*,
+                          const String& module_url,
+                          const WorkletOptions&);
+
+  // ContextLifecycleObserver
+  void ContextDestroyed(ExecutionContext*) override;
 
   DECLARE_VIRTUAL_TRACE();
 
@@ -47,10 +51,29 @@
   static WebURLRequest::FetchCredentialsMode ParseCredentialsOption(
       const String& credentials_option);
 
+  // Returns one of available global scopes.
+  WorkletGlobalScopeProxy* FindAvailableGlobalScope() const;
+
+  size_t GetNumberOfGlobalScopes() const { return proxies_.size(); }
+
  private:
   virtual void FetchAndInvokeScript(const KURL& module_url_record,
                                     const WorkletOptions&,
-                                    ScriptPromiseResolver*) = 0;
+                                    ScriptPromiseResolver*);
+
+  // Returns true if there are no global scopes or additional global scopes are
+  // necessary. CreateGlobalScope() will be called in that case. Each worklet
+  // can define how to pool global scopes here.
+  virtual bool NeedsToCreateGlobalScope() = 0;
+  virtual std::unique_ptr<WorkletGlobalScopeProxy> CreateGlobalScope() = 0;
+
+  // "A Worklet has a list of the worklet's WorkletGlobalScopes. Initially this
+  // list is empty; it is populated when the user agent chooses to create its
+  // WorkletGlobalScope."
+  // https://drafts.css-houdini.org/worklets/#worklet-section
+  // TODO(nhiroki): Make (Paint)WorkletGlobalScopeProxy GC-managed object to
+  // avoid that GC graphs are disjointed (https://crbug.com/719775).
+  HashSet<std::unique_ptr<WorkletGlobalScopeProxy>> proxies_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js b/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
index 6b2d25e..6f7fdec 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
@@ -1299,14 +1299,9 @@
     contextMenu.show();
   }
 
-  _clearObjectStore() {
-    /**
-     * @this {Resources.IDBObjectStoreTreeElement}
-     */
-    function callback() {
-      this.update(this._objectStore);
-    }
-    this._model.clearObjectStore(this._databaseId, this._objectStore.name, callback.bind(this));
+  async _clearObjectStore() {
+    await this._model.clearObjectStore(this._databaseId, this._objectStore.name);
+    this.update(this._objectStore);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBModel.js b/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBModel.js
index 1418001..9b495578 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBModel.js
@@ -168,14 +168,11 @@
   /**
    * @param {!Resources.IndexedDBModel.DatabaseId} databaseId
    */
-  deleteDatabase(databaseId) {
+  async deleteDatabase(databaseId) {
     if (!this._enabled)
       return;
-    this._agent.deleteDatabase(databaseId.securityOrigin, databaseId.name, error => {
-      if (error)
-        console.error('Unable to delete ' + databaseId.name, error);
-      this._loadDatabaseNames(databaseId.securityOrigin);
-    });
+    await this._agent.deleteDatabase(databaseId.securityOrigin, databaseId.name);
+    this._loadDatabaseNames(databaseId.securityOrigin);
   }
 
   refreshDatabaseNames() {
@@ -193,10 +190,10 @@
   /**
    * @param {!Resources.IndexedDBModel.DatabaseId} databaseId
    * @param {string} objectStoreName
-   * @param {function()} callback
+   * @return {!Promise}
    */
-  clearObjectStore(databaseId, objectStoreName, callback) {
-    this._agent.clearObjectStore(databaseId.securityOrigin, databaseId.name, objectStoreName, callback);
+  clearObjectStore(databaseId, objectStoreName) {
+    return this._agent.clearObjectStore(databaseId.securityOrigin, databaseId.name, objectStoreName);
   }
 
   /**
@@ -289,65 +286,44 @@
   /**
    * @param {string} securityOrigin
    */
-  _loadDatabaseNames(securityOrigin) {
-    /**
-     * @param {?Protocol.Error} error
-     * @param {!Array.<string>} databaseNames
-     * @this {Resources.IndexedDBModel}
-     */
-    function callback(error, databaseNames) {
-      if (error) {
-        console.error('IndexedDBAgent error: ' + error);
-        return;
-      }
-
-      if (!this._databaseNamesBySecurityOrigin[securityOrigin])
-        return;
-      this._updateOriginDatabaseNames(securityOrigin, databaseNames);
-    }
-
-    this._agent.requestDatabaseNames(securityOrigin, callback.bind(this));
+  async _loadDatabaseNames(securityOrigin) {
+    var databaseNames = await this._agent.requestDatabaseNames(securityOrigin);
+    if (!databaseNames)
+      return;
+    if (!this._databaseNamesBySecurityOrigin[securityOrigin])
+      return;
+    this._updateOriginDatabaseNames(securityOrigin, databaseNames);
   }
 
   /**
    * @param {!Resources.IndexedDBModel.DatabaseId} databaseId
    */
-  _loadDatabase(databaseId) {
-    /**
-     * @param {?Protocol.Error} error
-     * @param {!Protocol.IndexedDB.DatabaseWithObjectStores} databaseWithObjectStores
-     * @this {Resources.IndexedDBModel}
-     */
-    function callback(error, databaseWithObjectStores) {
-      if (error) {
-        console.error('IndexedDBAgent error: ' + error);
-        return;
-      }
+  async _loadDatabase(databaseId) {
+    var databaseWithObjectStores = await this._agent.requestDatabase(databaseId.securityOrigin, databaseId.name);
 
-      if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
-        return;
-      var databaseModel = new Resources.IndexedDBModel.Database(databaseId, databaseWithObjectStores.version);
-      this._databases.set(databaseId, databaseModel);
-      for (var i = 0; i < databaseWithObjectStores.objectStores.length; ++i) {
-        var objectStore = databaseWithObjectStores.objectStores[i];
-        var objectStoreIDBKeyPath = Resources.IndexedDBModel.idbKeyPathFromKeyPath(objectStore.keyPath);
-        var objectStoreModel = new Resources.IndexedDBModel.ObjectStore(
-            objectStore.name, objectStoreIDBKeyPath, objectStore.autoIncrement);
-        for (var j = 0; j < objectStore.indexes.length; ++j) {
-          var index = objectStore.indexes[j];
-          var indexIDBKeyPath = Resources.IndexedDBModel.idbKeyPathFromKeyPath(index.keyPath);
-          var indexModel =
-              new Resources.IndexedDBModel.Index(index.name, indexIDBKeyPath, index.unique, index.multiEntry);
-          objectStoreModel.indexes[indexModel.name] = indexModel;
-        }
-        databaseModel.objectStores[objectStoreModel.name] = objectStoreModel;
-      }
+    if (!databaseWithObjectStores)
+      return;
+    if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
+      return;
 
-      this.dispatchEventToListeners(
-          Resources.IndexedDBModel.Events.DatabaseLoaded, {model: this, database: databaseModel});
+    var databaseModel = new Resources.IndexedDBModel.Database(databaseId, databaseWithObjectStores.version);
+    this._databases.set(databaseId, databaseModel);
+    for (var objectStore of databaseWithObjectStores.objectStores) {
+      var objectStoreIDBKeyPath = Resources.IndexedDBModel.idbKeyPathFromKeyPath(objectStore.keyPath);
+      var objectStoreModel =
+          new Resources.IndexedDBModel.ObjectStore(objectStore.name, objectStoreIDBKeyPath, objectStore.autoIncrement);
+      for (var j = 0; j < objectStore.indexes.length; ++j) {
+        var index = objectStore.indexes[j];
+        var indexIDBKeyPath = Resources.IndexedDBModel.idbKeyPathFromKeyPath(index.keyPath);
+        var indexModel =
+            new Resources.IndexedDBModel.Index(index.name, indexIDBKeyPath, index.unique, index.multiEntry);
+        objectStoreModel.indexes[indexModel.name] = indexModel;
+      }
+      databaseModel.objectStores[objectStoreModel.name] = objectStoreModel;
     }
 
-    this._agent.requestDatabase(databaseId.securityOrigin, databaseId.name, callback.bind(this));
+    this.dispatchEventToListeners(
+        Resources.IndexedDBModel.Events.DatabaseLoaded, {model: this, database: databaseModel});
   }
 
   /**
@@ -386,36 +362,36 @@
    * @param {number} pageSize
    * @param {function(!Array.<!Resources.IndexedDBModel.Entry>, boolean)} callback
    */
-  _requestData(databaseId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback) {
-    /**
-     * @param {?Protocol.Error} error
-     * @param {!Array.<!Protocol.IndexedDB.DataEntry>} dataEntries
-     * @param {boolean} hasMore
-     * @this {Resources.IndexedDBModel}
-     */
-    function innerCallback(error, dataEntries, hasMore) {
-      if (error) {
-        console.error('IndexedDBAgent error: ' + error);
-        return;
-      }
+  async _requestData(databaseId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback) {
+    var keyRange = Resources.IndexedDBModel.keyRangeFromIDBKeyRange(idbKeyRange) || undefined;
 
-      var runtimeModel = this.target().model(SDK.RuntimeModel);
-      if (!runtimeModel || !this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
-        return;
-      var entries = [];
-      for (var i = 0; i < dataEntries.length; ++i) {
-        var key = runtimeModel.createRemoteObject(dataEntries[i].key);
-        var primaryKey = runtimeModel.createRemoteObject(dataEntries[i].primaryKey);
-        var value = runtimeModel.createRemoteObject(dataEntries[i].value);
-        entries.push(new Resources.IndexedDBModel.Entry(key, primaryKey, value));
-      }
-      callback(entries, hasMore);
+    var response = await this._agent.invoke_requestData({
+      securityOrigin: databaseId.securityOrigin,
+      databaseName,
+      objectStoreName,
+      indexName,
+      skipCount,
+      pageSize,
+      keyRange
+    });
+
+    if (response[Protocol.Error]) {
+      console.error('IndexedDBAgent error: ' + response[Protocol.Error]);
+      return;
     }
 
-    var keyRange = Resources.IndexedDBModel.keyRangeFromIDBKeyRange(idbKeyRange);
-    this._agent.requestData(
-        databaseId.securityOrigin, databaseName, objectStoreName, indexName, skipCount, pageSize,
-        keyRange ? keyRange : undefined, innerCallback.bind(this));
+    var runtimeModel = this.target().model(SDK.RuntimeModel);
+    if (!runtimeModel || !this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
+      return;
+    var dataEntries = response.objectStoreDataEntries;
+    var entries = [];
+    for (var dataEntry of dataEntries) {
+      var key = runtimeModel.createRemoteObject(dataEntry.key);
+      var primaryKey = runtimeModel.createRemoteObject(dataEntry.primaryKey);
+      var value = runtimeModel.createRemoteObject(dataEntry.value);
+      entries.push(new Resources.IndexedDBModel.Entry(key, primaryKey, value));
+    }
+    callback(entries, response.hasMore);
   }
 };
 
@@ -434,7 +410,6 @@
   ArrayType: 'array'
 };
 
-
 /** @enum {symbol} */
 Resources.IndexedDBModel.Events = {
   DatabaseAdded: Symbol('DatabaseAdded'),
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBViews.js b/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBViews.js
index a421227..efeface 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBViews.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/IndexedDBViews.js
@@ -331,16 +331,11 @@
   /**
    * @param {!Common.Event} event
    */
-  _clearButtonClicked(event) {
-    /**
-     * @this {Resources.IDBDataView}
-     */
-    function cleared() {
-      this._clearButton.setEnabled(true);
-      this._updateData(true);
-    }
+  async _clearButtonClicked(event) {
     this._clearButton.setEnabled(false);
-    this._model.clearObjectStore(this._databaseId, this._objectStore.name, cleared.bind(this));
+    await this._model.clearObjectStore(this._databaseId, this._objectStore.name);
+    this._clearButton.setEnabled(true);
+    this._updateData(true);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
index 47a5fde..ddeccd4 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
@@ -157,7 +157,7 @@
     this._nextExtensionIndex = 0;
     this._appendExtensionData();
 
-    this._updateSearchHighlight(false, true);
+    this._updateSearchResults(false, false);
     this._refresh();
   }
 
@@ -264,18 +264,13 @@
     this._countersView.setWindowTimes(startTime, endTime);
     this._windowStartTime = startTime;
     this._windowEndTime = endTime;
+    this._updateSearchResults(false, false);
   }
 
   /**
-   * @param {?SDK.TracingModel.Event} event
-   * @param {string=} regex
-   * @param {boolean=} select
+   * @param {!SDK.TracingModel.Event} event
    */
-  _highlightSearchResult(event, regex, select) {
-    if (!event) {
-      this._delegate.select(null);
-      return;
-    }
+  _highlightSearchResult(event) {
     var timelineSelection = this._mainDataProvider.selectionForEvent(event);
     if (timelineSelection)
       this._delegate.select(timelineSelection);
@@ -336,7 +331,7 @@
     if (!this._searchResults || !this._searchResults.length)
       return;
     var index = this._selectedSearchResult ? this._searchResults.indexOf(this._selectedSearchResult) : -1;
-    this._jumpToSearchResult(index + 1);
+    this._selectSearchResult(mod(index + 1, this._searchResults.length));
   }
 
   /**
@@ -346,7 +341,7 @@
     if (!this._searchResults || !this._searchResults.length)
       return;
     var index = this._selectedSearchResult ? this._searchResults.indexOf(this._selectedSearchResult) : 0;
-    this._jumpToSearchResult(index - 1);
+    this._selectSearchResult(mod(index - 1, this._searchResults.length));
   }
 
   /**
@@ -368,36 +363,10 @@
   /**
    * @param {number} index
    */
-  _jumpToSearchResult(index) {
-    this._selectSearchResult(mod(index, this._searchResults.length));
-    this._highlightSearchResult(this._selectedSearchResult, this._searchRegex, true);
-  }
-
-  /**
-   * @param {number} index
-   */
   _selectSearchResult(index) {
     this._selectedSearchResult = this._searchResults[index];
     this._searchableView.updateCurrentMatchIndex(index);
-  }
-
-  _clearHighlight() {
-    this._highlightSearchResult(null);
-  }
-
-  /**
-   * @param {boolean} revealRecord
-   * @param {boolean} shouldJump
-   * @param {boolean=} jumpBackwards
-   */
-  _updateSearchHighlight(revealRecord, shouldJump, jumpBackwards) {
-    if (!this._searchRegex) {
-      this._clearHighlight();
-      return;
-    }
-    if (!this._searchResults)
-      this._updateSearchResults(shouldJump, jumpBackwards);
-    this._highlightSearchResult(this._selectedSearchResult, this._searchRegex, revealRecord);
+    this._highlightSearchResult(this._selectedSearchResult);
   }
 
   /**
@@ -405,6 +374,9 @@
    * @param {boolean=} jumpBackwards
    */
   _updateSearchResults(shouldJump, jumpBackwards) {
+    var oldSelectedSearchResult = this._selectedSearchResult;
+    delete this._selectedSearchResult;
+    this._searchResults = [];
     if (!this._searchRegex)
       return;
 
@@ -421,26 +393,22 @@
         matches.push(event);
     }
 
-    var matchesCount = matches.length;
-    if (matchesCount) {
-      this._searchResults = matches;
-      this._searchableView.updateSearchMatchesCount(matchesCount);
-
-      var selectedIndex = matches.indexOf(this._selectedSearchResult);
-      if (shouldJump && selectedIndex === -1)
-        selectedIndex = jumpBackwards ? this._searchResults.length - 1 : 0;
-      this._selectSearchResult(selectedIndex);
-    } else {
-      this._searchableView.updateSearchMatchesCount(0);
-      delete this._selectedSearchResult;
-    }
+    this._searchableView.updateSearchMatchesCount(matches.length);
+    this._searchResults = matches;
+    if (!shouldJump || !matches.length)
+      return;
+    var selectedIndex = matches.indexOf(oldSelectedSearchResult);
+    if (selectedIndex === -1)
+      selectedIndex = jumpBackwards ? this._searchResults.length - 1 : 0;
+    this._selectSearchResult(selectedIndex);
   }
 
   /**
    * @override
    */
   searchCanceled() {
-    this._clearHighlight();
+    if (this._selectedSearchResult)
+      this._delegate.select(null);
     delete this._searchResults;
     delete this._selectedSearchResult;
     delete this._searchRegex;
@@ -454,8 +422,7 @@
    */
   performSearch(searchConfig, shouldJump, jumpBackwards) {
     this._searchRegex = searchConfig.toSearchRegex();
-    delete this._searchResults;
-    this._updateSearchHighlight(true, shouldJump, jumpBackwards);
+    this._updateSearchResults(shouldJump, jumpBackwards);
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/scripts/build/generate_protocol_externs.py b/third_party/WebKit/Source/devtools/scripts/build/generate_protocol_externs.py
index c6b4a9c..a3c865c1 100755
--- a/third_party/WebKit/Source/devtools/scripts/build/generate_protocol_externs.py
+++ b/third_party/WebKit/Source/devtools/scripts/build/generate_protocol_externs.py
@@ -49,7 +49,6 @@
 NON_PROMISIFIED_DOMAINS = frozenset([
     "CSS",
     "DOMDebugger",
-    "IndexedDB",
 ])
 
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp
index e7b38a2..7ff8a43 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp
@@ -56,7 +56,19 @@
 using namespace HTMLNames;
 
 namespace {
-typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
+
+struct AccessibilityRoleHashTraits : HashTraits<AccessibilityRole> {
+  static const bool kEmptyValueIsZero = true;
+  static AccessibilityRole EmptyValue() {
+    return AccessibilityRole::kUnknownRole;
+  }
+};
+
+using ARIARoleMap = HashMap<String,
+                            AccessibilityRole,
+                            CaseFoldingHash,
+                            HashTraits<String>,
+                            AccessibilityRoleHashTraits>;
 
 struct RoleEntry {
   const char* aria_role;
diff --git a/third_party/WebKit/Source/web/AnimationWorkletProxyClientImpl.cpp b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletProxyClientImpl.cpp
similarity index 92%
rename from third_party/WebKit/Source/web/AnimationWorkletProxyClientImpl.cpp
rename to third_party/WebKit/Source/modules/compositorworker/AnimationWorkletProxyClientImpl.cpp
index 41ae51d..b077724 100644
--- a/third_party/WebKit/Source/web/AnimationWorkletProxyClientImpl.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletProxyClientImpl.cpp
@@ -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 "web/AnimationWorkletProxyClientImpl.h"
+#include "modules/compositorworker/AnimationWorkletProxyClientImpl.h"
 
 #include "core/animation/CompositorMutatorImpl.h"
 #include "core/dom/CompositorProxy.h"
diff --git a/third_party/WebKit/Source/web/AnimationWorkletProxyClientImpl.h b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletProxyClientImpl.h
similarity index 91%
rename from third_party/WebKit/Source/web/AnimationWorkletProxyClientImpl.h
rename to third_party/WebKit/Source/modules/compositorworker/AnimationWorkletProxyClientImpl.h
index 94de72d2..2778e623 100644
--- a/third_party/WebKit/Source/web/AnimationWorkletProxyClientImpl.h
+++ b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletProxyClientImpl.h
@@ -6,10 +6,11 @@
 #define AnimationWorkletProxyClientImpl_h
 
 #include "core/animation/CompositorAnimator.h"
+#include "core/animation/CompositorProxyClientImpl.h"
 #include "core/dom/AnimationWorkletProxyClient.h"
+#include "modules/ModulesExport.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Noncopyable.h"
-#include "web/CompositorProxyClientImpl.h"
 
 namespace blink {
 
@@ -21,7 +22,7 @@
 //
 // This is constructed on the main thread but it is used in the worklet backing
 // thread i.e., compositor thread.
-class AnimationWorkletProxyClientImpl final
+class MODULES_EXPORT AnimationWorkletProxyClientImpl final
     : public GarbageCollectedFinalized<AnimationWorkletProxyClientImpl>,
       public AnimationWorkletProxyClient,
       public CompositorAnimator {
diff --git a/third_party/WebKit/Source/modules/compositorworker/BUILD.gn b/third_party/WebKit/Source/modules/compositorworker/BUILD.gn
index df26dd2..c6f2574 100644
--- a/third_party/WebKit/Source/modules/compositorworker/BUILD.gn
+++ b/third_party/WebKit/Source/modules/compositorworker/BUILD.gn
@@ -14,6 +14,8 @@
     "AnimationWorkletGlobalScope.h",
     "AnimationWorkletMessagingProxy.cpp",
     "AnimationWorkletMessagingProxy.h",
+    "AnimationWorkletProxyClientImpl.cpp",
+    "AnimationWorkletProxyClientImpl.h",
     "AnimationWorkletThread.cpp",
     "AnimationWorkletThread.h",
     "Animator.cpp",
@@ -26,6 +28,8 @@
     "CompositorWorkerGlobalScope.h",
     "CompositorWorkerMessagingProxy.cpp",
     "CompositorWorkerMessagingProxy.h",
+    "CompositorWorkerProxyClientImpl.cpp",
+    "CompositorWorkerProxyClientImpl.h",
     "CompositorWorkerThread.cpp",
     "CompositorWorkerThread.h",
     "WindowAnimationWorklet.cpp",
diff --git a/third_party/WebKit/Source/web/CompositorWorkerProxyClientImpl.cpp b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerProxyClientImpl.cpp
similarity index 98%
rename from third_party/WebKit/Source/web/CompositorWorkerProxyClientImpl.cpp
rename to third_party/WebKit/Source/modules/compositorworker/CompositorWorkerProxyClientImpl.cpp
index 9d12da3..2e873ca 100644
--- a/third_party/WebKit/Source/web/CompositorWorkerProxyClientImpl.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerProxyClientImpl.cpp
@@ -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 "web/CompositorWorkerProxyClientImpl.h"
+#include "modules/compositorworker/CompositorWorkerProxyClientImpl.h"
 
 #include "core/animation/CompositorMutatorImpl.h"
 #include "core/dom/CompositorProxy.h"
diff --git a/third_party/WebKit/Source/web/CompositorWorkerProxyClientImpl.h b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerProxyClientImpl.h
similarity index 93%
rename from third_party/WebKit/Source/web/CompositorWorkerProxyClientImpl.h
rename to third_party/WebKit/Source/modules/compositorworker/CompositorWorkerProxyClientImpl.h
index 10cce10e..07ff568c 100644
--- a/third_party/WebKit/Source/web/CompositorWorkerProxyClientImpl.h
+++ b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerProxyClientImpl.h
@@ -6,10 +6,11 @@
 #define CompositorWorkerProxyClientImpl_h
 
 #include "core/animation/CompositorAnimator.h"
+#include "core/animation/CompositorProxyClientImpl.h"
 #include "core/dom/CompositorWorkerProxyClient.h"
+#include "modules/ModulesExport.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Noncopyable.h"
-#include "web/CompositorProxyClientImpl.h"
 
 namespace blink {
 
@@ -25,7 +26,7 @@
 //
 // Owned by the main thread.
 // Should be accessed only on the compositor thread.
-class CompositorWorkerProxyClientImpl final
+class MODULES_EXPORT CompositorWorkerProxyClientImpl final
     : public GarbageCollectedFinalized<CompositorWorkerProxyClientImpl>,
       public CompositorWorkerProxyClient,
       public CompositorAnimator {
diff --git a/third_party/WebKit/Source/modules/csspaint/PaintWorklet.cpp b/third_party/WebKit/Source/modules/csspaint/PaintWorklet.cpp
index 063effb..bef58fc 100644
--- a/third_party/WebKit/Source/modules/csspaint/PaintWorklet.cpp
+++ b/third_party/WebKit/Source/modules/csspaint/PaintWorklet.cpp
@@ -17,7 +17,7 @@
 }
 
 PaintWorklet::PaintWorklet(LocalFrame* frame)
-    : MainThreadWorklet(frame),
+    : Worklet(frame),
       pending_generator_registry_(new PaintWorkletPendingGeneratorRegistry) {}
 
 PaintWorklet::~PaintWorklet() = default;
@@ -38,7 +38,7 @@
 
 DEFINE_TRACE(PaintWorklet) {
   visitor->Trace(pending_generator_registry_);
-  MainThreadWorklet::Trace(visitor);
+  Worklet::Trace(visitor);
 }
 
 bool PaintWorklet::NeedsToCreateGlobalScope() {
diff --git a/third_party/WebKit/Source/modules/csspaint/PaintWorklet.h b/third_party/WebKit/Source/modules/csspaint/PaintWorklet.h
index 35848ae4..ec7cf2f 100644
--- a/third_party/WebKit/Source/modules/csspaint/PaintWorklet.h
+++ b/third_party/WebKit/Source/modules/csspaint/PaintWorklet.h
@@ -5,7 +5,7 @@
 #ifndef PaintWorklet_h
 #define PaintWorklet_h
 
-#include "core/workers/MainThreadWorklet.h"
+#include "core/workers/Worklet.h"
 #include "modules/ModulesExport.h"
 #include "modules/csspaint/PaintWorkletGlobalScopeProxy.h"
 #include "modules/csspaint/PaintWorkletPendingGeneratorRegistry.h"
@@ -18,7 +18,7 @@
 
 // Manages a paint worklet:
 // https://drafts.css-houdini.org/css-paint-api/#dom-css-paintworklet
-class MODULES_EXPORT PaintWorklet final : public MainThreadWorklet {
+class MODULES_EXPORT PaintWorklet final : public Worklet {
   WTF_MAKE_NONCOPYABLE(PaintWorklet);
 
  public:
@@ -35,7 +35,7 @@
 
   explicit PaintWorklet(LocalFrame*);
 
-  // Implements MainThreadWorklet.
+  // Implements Worklet.
   bool NeedsToCreateGlobalScope() final;
   std::unique_ptr<WorkletGlobalScopeProxy> CreateGlobalScope() final;
 
diff --git a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.cpp b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.cpp
index a52fdab..e7871108 100644
--- a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.cpp
+++ b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.cpp
@@ -353,21 +353,25 @@
 
 void SourceBuffer::appendBuffer(DOMArrayBuffer* data,
                                 ExceptionState& exception_state) {
-  BLINK_SBLOG << __func__ << " this=" << this << " size=" << data->ByteLength();
+  double media_time = GetMediaTime();
+  BLINK_SBLOG << __func__ << " this=" << this << " media_time=" << media_time
+              << " size=" << data->ByteLength();
   // Section 3.2 appendBuffer()
   // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
-  AppendBufferInternal(static_cast<const unsigned char*>(data->Data()),
+  AppendBufferInternal(media_time,
+                       static_cast<const unsigned char*>(data->Data()),
                        data->ByteLength(), exception_state);
 }
 
 void SourceBuffer::appendBuffer(NotShared<DOMArrayBufferView> data,
                                 ExceptionState& exception_state) {
-  BLINK_SBLOG << __func__ << " this=" << this
+  double media_time = GetMediaTime();
+  BLINK_SBLOG << __func__ << " this=" << this << " media_time=" << media_time
               << " size=" << data.View()->byteLength();
   // Section 3.2 appendBuffer()
   // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
   AppendBufferInternal(
-      static_cast<const unsigned char*>(data.View()->BaseAddress()),
+      media_time, static_cast<const unsigned char*>(data.View()->BaseAddress()),
       data.View()->byteLength(), exception_state);
 }
 
@@ -682,6 +686,13 @@
   // 7-8. TODO(servolk): Remove text tracks once SourceBuffer has text tracks.
 }
 
+double SourceBuffer::GetMediaTime() {
+  double media_time = std::numeric_limits<float>::quiet_NaN();
+  if (source_ && source_->MediaElement())
+    media_time = source_->MediaElement()->currentTime();
+  return media_time;
+}
+
 template <class T>
 T* FindExistingTrackById(const TrackListBase<T>& track_list, const String& id) {
   // According to MSE specification
@@ -1070,7 +1081,8 @@
   async_event_queue_->EnqueueEvent(event);
 }
 
-bool SourceBuffer::PrepareAppend(size_t new_data_size,
+bool SourceBuffer::PrepareAppend(double media_time,
+                                 size_t new_data_size,
                                  ExceptionState& exception_state) {
   TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::prepareAppend", this);
   // http://w3c.github.io/media-source/#sourcebuffer-prepare-append
@@ -1106,7 +1118,7 @@
   source_->OpenIfInEndedState();
 
   // 5. Run the coded frame eviction algorithm.
-  if (!EvictCodedFrames(new_data_size)) {
+  if (!EvictCodedFrames(media_time, new_data_size)) {
     // 6. If the buffer full flag equals true, then throw a QUOTA_EXCEEDED_ERR
     //    exception and abort these steps.
     BLINK_SBLOG << __func__ << " this=" << this
@@ -1123,7 +1135,7 @@
   return true;
 }
 
-bool SourceBuffer::EvictCodedFrames(size_t new_data_size) {
+bool SourceBuffer::EvictCodedFrames(double media_time, size_t new_data_size) {
   DCHECK(source_);
   DCHECK(source_->MediaElement());
 
@@ -1133,19 +1145,18 @@
     return true;
   }
 
-  double current_time = source_->MediaElement()->currentTime();
-  bool result =
-      web_source_buffer_->EvictCodedFrames(current_time, new_data_size);
+  bool result = web_source_buffer_->EvictCodedFrames(media_time, new_data_size);
   if (!result) {
     BLINK_SBLOG << __func__ << " this=" << this
                 << " failed. newDataSize=" << new_data_size
-                << " currentTime=" << current_time << " buffered="
+                << " media_time=" << media_time << " buffered="
                 << WebTimeRangesToString(web_source_buffer_->Buffered());
   }
   return result;
 }
 
-void SourceBuffer::AppendBufferInternal(const unsigned char* data,
+void SourceBuffer::AppendBufferInternal(double media_time,
+                                        const unsigned char* data,
                                         unsigned size,
                                         ExceptionState& exception_state) {
   TRACE_EVENT_ASYNC_BEGIN1("media", "SourceBuffer::appendBuffer", this, "size",
@@ -1154,7 +1165,7 @@
   // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
 
   // 1. Run the prepare append algorithm.
-  if (!PrepareAppend(size, exception_state)) {
+  if (!PrepareAppend(media_time, size, exception_state)) {
     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendBuffer", this);
     return;
   }
@@ -1246,7 +1257,9 @@
   }
 
   TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendBuffer", this);
-  BLINK_SBLOG << __func__ << " done. this=" << this << " buffered="
+  double media_time = GetMediaTime();
+  BLINK_SBLOG << __func__ << " done. this=" << this
+              << " media_time=" << media_time << " buffered="
               << WebTimeRangesToString(web_source_buffer_->Buffered());
 }
 
diff --git a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.h b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.h
index 7045ad03..a5f1726 100644
--- a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.h
+++ b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.h
@@ -126,9 +126,12 @@
   bool IsRemoved() const;
   void ScheduleEvent(const AtomicString& event_name);
 
-  bool PrepareAppend(size_t new_data_size, ExceptionState&);
-  bool EvictCodedFrames(size_t new_data_size);
-  void AppendBufferInternal(const unsigned char*, unsigned, ExceptionState&);
+  bool PrepareAppend(double media_time, size_t new_data_size, ExceptionState&);
+  bool EvictCodedFrames(double media_time, size_t new_data_size);
+  void AppendBufferInternal(double media_time,
+                            const unsigned char*,
+                            unsigned,
+                            ExceptionState&);
   void AppendBufferAsyncPart();
   void AppendError();
 
@@ -139,6 +142,10 @@
 
   void RemoveMediaTracks();
 
+  // Returns MediaElement playback position (i.e. MediaElement.currentTime() )
+  // in seconds, or NaN if media element is not available.
+  double GetMediaTime();
+
   const TrackDefault* GetTrackDefault(
       const AtomicString& track_type,
       const AtomicString& byte_stream_track_id) const;
diff --git a/third_party/WebKit/Source/platform/exported/WebActiveGestureAnimation.cpp b/third_party/WebKit/Source/platform/exported/WebActiveGestureAnimation.cpp
index a1490260..aa3070dc 100644
--- a/third_party/WebKit/Source/platform/exported/WebActiveGestureAnimation.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebActiveGestureAnimation.cpp
@@ -33,20 +33,12 @@
 namespace blink {
 
 std::unique_ptr<WebActiveGestureAnimation>
-WebActiveGestureAnimation::CreateAtAnimationStart(
-    std::unique_ptr<WebGestureCurve> curve,
-    WebGestureCurveTarget* target) {
-  return WTF::WrapUnique(
-      new WebActiveGestureAnimation(std::move(curve), target, 0, true));
-}
-
-std::unique_ptr<WebActiveGestureAnimation>
 WebActiveGestureAnimation::CreateWithTimeOffset(
     std::unique_ptr<WebGestureCurve> curve,
     WebGestureCurveTarget* target,
     double start_time) {
-  return WTF::WrapUnique(new WebActiveGestureAnimation(std::move(curve), target,
-                                                       start_time, false));
+  return WTF::WrapUnique(
+      new WebActiveGestureAnimation(std::move(curve), target, start_time));
 }
 
 WebActiveGestureAnimation::~WebActiveGestureAnimation() {}
@@ -54,18 +46,10 @@
 WebActiveGestureAnimation::WebActiveGestureAnimation(
     std::unique_ptr<WebGestureCurve> curve,
     WebGestureCurveTarget* target,
-    double start_time,
-    bool waiting_for_first_tick)
-    : start_time_(start_time),
-      waiting_for_first_tick_(waiting_for_first_tick),
-      curve_(std::move(curve)),
-      target_(target) {}
+    double start_time)
+    : start_time_(start_time), curve_(std::move(curve)), target_(target) {}
 
 bool WebActiveGestureAnimation::Animate(double time) {
-  if (waiting_for_first_tick_) {
-    start_time_ = time;
-    waiting_for_first_tick_ = false;
-  }
   // All WebGestureCurves assume zero-based time, so we subtract
   // the animation start time before passing to the curve.
   return curve_->Apply(time - start_time_, target_);
diff --git a/third_party/WebKit/Source/platform/exported/WebActiveGestureAnimation.h b/third_party/WebKit/Source/platform/exported/WebActiveGestureAnimation.h
index 45b29d4f..e1adc588 100644
--- a/third_party/WebKit/Source/platform/exported/WebActiveGestureAnimation.h
+++ b/third_party/WebKit/Source/platform/exported/WebActiveGestureAnimation.h
@@ -46,9 +46,6 @@
   WTF_MAKE_NONCOPYABLE(WebActiveGestureAnimation);
 
  public:
-  static std::unique_ptr<WebActiveGestureAnimation> CreateAtAnimationStart(
-      std::unique_ptr<WebGestureCurve>,
-      WebGestureCurveTarget*);
   static std::unique_ptr<WebActiveGestureAnimation> CreateWithTimeOffset(
       std::unique_ptr<WebGestureCurve>,
       WebGestureCurveTarget*,
@@ -61,11 +58,9 @@
   // Assumes a valid WebGestureCurveTarget that outlives the animation.
   WebActiveGestureAnimation(std::unique_ptr<WebGestureCurve>,
                             WebGestureCurveTarget*,
-                            double start_time,
-                            bool waiting_for_first_tick);
+                            double start_time);
 
   double start_time_;
-  bool waiting_for_first_tick_;
   std::unique_ptr<WebGestureCurve> curve_;
   WebGestureCurveTarget* target_;
 };
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
index b95765c..0e9ce5c 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
@@ -257,6 +257,7 @@
   // Now rect is in the space of the containing transform node of pending_layer,
   // so need to subtract off the layer offset.
   rect.Rect().Move(-layer_offset.x(), -layer_offset.y());
+  rect.Rect().Inflate(paint_chunk.outset_for_raster_effects);
   return EnclosingIntRect(rect.Rect());
 }
 
@@ -330,9 +331,6 @@
       auto info = raster_tracking->invalidations[i];
       info.rect = rect;
       cc_tracking.invalidations.push_back(info);
-      // TODO(crbug.com/496260): Some antialiasing effects overflow the paint
-      // invalidation rect.
-      rect.Inflate(1);
       cc_tracking.invalidation_region_since_last_paint.Unite(rect);
     }
   }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
index 5686ef19..8f6b369 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
@@ -10,6 +10,7 @@
   virtual ~SameSizeAsDisplayItem() {}  // Allocate vtable pointer.
   void* pointer;
   LayoutRect rect;
+  LayoutUnit outset;
   int i;
 #ifndef NDEBUG
   WTF::String debug_string_;
@@ -247,7 +248,12 @@
   }
   string_builder.Append("\", visualRect: \"");
   string_builder.Append(VisualRect().ToString());
-  string_builder.Append("\", type: \"");
+  string_builder.Append("\", ");
+  if (OutsetForRasterEffects()) {
+    string_builder.Append(
+        String::Format("outset: %f, ", OutsetForRasterEffects().ToFloat()));
+  }
+  string_builder.Append("type: \"");
   string_builder.Append(TypeAsDebugString(GetType()));
   string_builder.Append('"');
   if (skipped_cache_)
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
index a3608a3..0b0c013 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
@@ -200,6 +200,7 @@
   DisplayItem(const DisplayItemClient& client, Type type, size_t derived_size)
       : client_(&client),
         visual_rect_(client.VisualRect()),
+        outset_for_raster_effects_(client.VisualRectOutsetForRasterEffects()),
         type_(type),
         derived_size_(derived_size),
         skipped_cache_(false)
@@ -239,6 +240,9 @@
   // not invalidated. Otherwise it saves the previous visual rect of the client.
   // See DisplayItemClient::VisualRect() about its coordinate space.
   const LayoutRect& VisualRect() const { return visual_rect_; }
+  LayoutUnit OutsetForRasterEffects() const {
+    return outset_for_raster_effects_;
+  }
 
   // Visual rect can change without needing invalidation of the client, e.g.
   // when ancestor clip changes. This is called from PaintController::
@@ -376,6 +380,7 @@
 
   const DisplayItemClient* client_;
   LayoutRect visual_rect_;
+  LayoutUnit outset_for_raster_effects_;
 
   static_assert(kTypeLast < (1 << 16),
                 "DisplayItem::Type should fit in 16 bits");
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
index 4b10024..a743328 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
@@ -51,11 +51,19 @@
 
   virtual String DebugName() const = 0;
 
-  // The visual rect of this DisplayItemClient, in the object space of the
-  // object that owns the GraphicsLayer, i.e. offset by
+  // The visual rect of this DisplayItemClient. For SPv1, it's in the object
+  // space of the object that owns the GraphicsLayer, i.e. offset by
   // GraphicsLayer::OffsetFromLayoutObjectWithSubpixelAccumulation().
+  // For SPv2, it's in the space of the parent transform node.
   virtual LayoutRect VisualRect() const = 0;
 
+  // The outset will be used to inflate visual rect after the visual rect is
+  // mapped into the space of the composited layer, for any special raster
+  // effects that might expand the rastered pixel area.
+  virtual LayoutUnit VisualRectOutsetForRasterEffects() const {
+    return LayoutUnit();
+  }
+
   // This is declared here instead of in LayoutObject for verifying the
   // condition in DrawingRecorder.
   // Returns true if the object itself will not generate any effective painted
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintChunk.h b/third_party/WebKit/Source/platform/graphics/paint/PaintChunk.h
index 74c0d28..41e1be6 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintChunk.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintChunk.h
@@ -23,7 +23,11 @@
 // This is a Slimming Paint v2 class.
 struct PaintChunk {
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
-  PaintChunk() : begin_index(0), end_index(0), known_to_be_opaque(false) {}
+  PaintChunk()
+      : begin_index(0),
+        end_index(0),
+        outset_for_raster_effects(0),
+        known_to_be_opaque(false) {}
   PaintChunk(size_t begin,
              size_t end,
              const DisplayItem::Id* chunk_id,
@@ -31,6 +35,7 @@
       : begin_index(begin),
         end_index(end),
         properties(props),
+        outset_for_raster_effects(0),
         known_to_be_opaque(false) {
     if (chunk_id)
       id.emplace(*chunk_id);
@@ -83,6 +88,11 @@
   // the containing transform node.
   FloatRect bounds;
 
+  // Some raster effects can exceed |bounds| in the rasterization space. This
+  // is the maximum DisplayItemClient::VisualRectOutsetForRasterEffects() of
+  // all clients of items in this chunk.
+  float outset_for_raster_effects;
+
   // True if the bounds are filled entirely with opaque contents.
   bool known_to_be_opaque;
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
index 00159eab..085a75c 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
@@ -271,6 +271,10 @@
             new_paint_chunks_.PaintChunkAt(last_chunk_index));
       }
     }
+
+    new_paint_chunks_.LastChunk().outset_for_raster_effects =
+        std::max(new_paint_chunks_.LastChunk().outset_for_raster_effects,
+                 display_item.OutsetForRasterEffects().ToFloat());
   }
 
 #if DCHECK_IS_ON()
diff --git a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp
index d8e60eea1..6cf1cc3 100644
--- a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp
@@ -36,6 +36,13 @@
 
 namespace {
 
+struct PolicyAreasHashTraits : HashTraits<SchemeRegistry::PolicyAreas> {
+  static const bool kEmptyValueIsZero = true;
+  static SchemeRegistry::PolicyAreas EmptyValue() {
+    return SchemeRegistry::kPolicyAreaNone;
+  }
+};
+
 class URLSchemesRegistry final {
  public:
   URLSchemesRegistry()
@@ -73,7 +80,7 @@
   URLSchemesSet service_worker_schemes;
   URLSchemesSet fetch_api_schemes;
   URLSchemesSet first_party_when_top_level_schemes;
-  URLSchemesMap<SchemeRegistry::PolicyAreas>
+  URLSchemesMap<SchemeRegistry::PolicyAreas, PolicyAreasHashTraits>
       content_security_policy_bypassing_schemes;
   URLSchemesSet secure_context_bypassing_schemes;
   URLSchemesSet allowed_in_referrer_schemes;
diff --git a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h
index e58eea3..7a62412 100644
--- a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h
+++ b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h
@@ -38,8 +38,12 @@
 
 using URLSchemesSet = HashSet<String>;
 
-template <typename T>
-using URLSchemesMap = HashMap<String, T>;
+template <typename Mapped, typename MappedTraits>
+using URLSchemesMap = HashMap<String,
+                              Mapped,
+                              DefaultHash<String>::Hash,
+                              HashTraits<String>,
+                              MappedTraits>;
 
 class PLATFORM_EXPORT SchemeRegistry {
   STATIC_ONLY(SchemeRegistry);
diff --git a/third_party/WebKit/Source/platform/wtf/HashFunctions.h b/third_party/WebKit/Source/platform/wtf/HashFunctions.h
index 37f1956..2d0decb 100644
--- a/third_party/WebKit/Source/platform/wtf/HashFunctions.h
+++ b/third_party/WebKit/Source/platform/wtf/HashFunctions.h
@@ -214,7 +214,9 @@
 
 // Canonical implementation of DefaultHash.
 template <typename T>
-struct DefaultHash : DefaultHashImpl<T, std::is_integral<T>::value> {};
+struct DefaultHash
+    : DefaultHashImpl<T, std::is_integral<T>::value || std::is_enum<T>::value> {
+};
 
 // Specializations of DefaultHash follow.
 template <>
diff --git a/third_party/WebKit/Source/platform/wtf/HashSetTest.cpp b/third_party/WebKit/Source/platform/wtf/HashSetTest.cpp
index e123ff0..e596af9 100644
--- a/third_party/WebKit/Source/platform/wtf/HashSetTest.cpp
+++ b/third_party/WebKit/Source/platform/wtf/HashSetTest.cpp
@@ -502,6 +502,21 @@
   EXPECT_TRUE(IsOneTwoThree(ReturnOneTwoThree()));
 }
 
+enum TestEnum {
+  kItem0,
+};
+
+enum class TestEnumClass : unsigned char {
+  kItem0,
+};
+
+TEST(HashSetTest, HasTraitsForEnum) {
+  // Ensure that enum hash keys are buildable.
+  HashSet<TestEnum> set1;
+  HashSet<TestEnumClass> set2;
+  HashSet<std::pair<TestEnum, TestEnumClass>> set3;
+}
+
 }  // anonymous namespace
 
 }  // namespace WTF
diff --git a/third_party/WebKit/Source/platform/wtf/HashTraits.h b/third_party/WebKit/Source/platform/wtf/HashTraits.h
index 1e6122a..0623ed7 100644
--- a/third_party/WebKit/Source/platform/wtf/HashTraits.h
+++ b/third_party/WebKit/Source/platform/wtf/HashTraits.h
@@ -37,6 +37,8 @@
 
 template <bool isInteger, typename T>
 struct GenericHashTraitsBase;
+template <bool is_enum, typename T>
+struct EnumOrGenericHashTraits;
 template <typename T>
 struct HashTraits;
 
@@ -138,7 +140,22 @@
 };
 
 template <typename T>
-struct HashTraits : GenericHashTraits<T> {};
+struct EnumOrGenericHashTraits<false, T> : GenericHashTraits<T> {};
+
+// Default traits for an enum type.  0 is very popular, and -1 is also popular.
+// So we use -128 and -127.
+template <typename T>
+struct EnumOrGenericHashTraits<true, T> : GenericHashTraits<T> {
+  static const bool kEmptyValueIsZero = false;
+  static T EmptyValue() { return static_cast<T>(-128); }
+  static void ConstructDeletedValue(T& slot, bool) {
+    slot = static_cast<T>(-127);
+  }
+  static bool IsDeletedValue(T value) { return value == static_cast<T>(-127); }
+};
+
+template <typename T>
+struct HashTraits : EnumOrGenericHashTraits<std::is_enum<T>::value, T> {};
 
 template <typename T>
 struct FloatHashTraits : GenericHashTraits<T> {
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn
index 6b92b6a..3d10b41 100644
--- a/third_party/WebKit/Source/web/BUILD.gn
+++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -36,8 +36,6 @@
   defines = [ "BLINK_WEB_IMPLEMENTATION=1" ]
 
   sources = [
-    "AnimationWorkletProxyClientImpl.cpp",
-    "AnimationWorkletProxyClientImpl.h",
     "AssertMatchingEnums.cpp",
     "AudioOutputDeviceClientImpl.cpp",
     "AudioOutputDeviceClientImpl.h",
@@ -47,10 +45,6 @@
     "ColorChooserPopupUIController.h",
     "ColorChooserUIController.cpp",
     "ColorChooserUIController.h",
-    "CompositorProxyClientImpl.cpp",
-    "CompositorProxyClientImpl.h",
-    "CompositorWorkerProxyClientImpl.cpp",
-    "CompositorWorkerProxyClientImpl.h",
     "ContextMenuClientImpl.cpp",
     "ContextMenuClientImpl.h",
     "DedicatedWorkerMessagingProxyProviderImpl.cpp",
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
index 56b9734..78a1ecfc 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
@@ -748,7 +748,7 @@
 }
 
 PluginView* LocalFrameClientImpl::CreatePlugin(
-    HTMLPlugInElement* element,
+    HTMLPlugInElement& element,
     const KURL& url,
     const Vector<String>& param_names,
     const Vector<String>& param_values,
@@ -776,7 +776,7 @@
   if (!web_plugin->Initialize(container))
     return nullptr;
 
-  if (policy != kAllowDetachedPlugin && !element->GetLayoutObject())
+  if (policy != kAllowDetachedPlugin && !element.GetLayoutObject())
     return nullptr;
 
   return container;
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.h b/third_party/WebKit/Source/web/LocalFrameClientImpl.h
index 9cffed0..76554d2b 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.h
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.h
@@ -149,7 +149,7 @@
                           const WTF::AtomicString& name,
                           HTMLFrameOwnerElement*) override;
   virtual bool CanCreatePluginWithoutRenderer(const String& mime_type) const;
-  PluginView* CreatePlugin(HTMLPlugInElement*,
+  PluginView* CreatePlugin(HTMLPlugInElement&,
                            const KURL&,
                            const Vector<WTF::String>&,
                            const Vector<WTF::String>&,
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index f77db328..a26f281 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -59,6 +59,8 @@
 #include "core/page/FocusController.h"
 #include "core/page/Page.h"
 #include "core/page/PointerLockController.h"
+#include "modules/compositorworker/AnimationWorkletProxyClientImpl.h"
+#include "modules/compositorworker/CompositorWorkerProxyClientImpl.h"
 #include "platform/KeyboardCodes.h"
 #include "platform/WebFrameScheduler.h"
 #include "platform/animation/CompositorAnimationHost.h"
@@ -70,8 +72,6 @@
 #include "public/web/WebPlugin.h"
 #include "public/web/WebRange.h"
 #include "public/web/WebWidgetClient.h"
-#include "web/AnimationWorkletProxyClientImpl.h"
-#include "web/CompositorWorkerProxyClientImpl.h"
 #include "web/WebDevToolsAgentImpl.h"
 #include "web/WebPagePopupImpl.h"
 #include "web/WebRemoteFrameImpl.h"
diff --git a/third_party/WebKit/Source/web/WebHelperPluginImpl.cpp b/third_party/WebKit/Source/web/WebHelperPluginImpl.cpp
index db365906..48856a7 100644
--- a/third_party/WebKit/Source/web/WebHelperPluginImpl.cpp
+++ b/third_party/WebKit/Source/web/WebHelperPluginImpl.cpp
@@ -66,7 +66,7 @@
   DCHECK(frame->GetFrame()->GetDocument()->Url().IsValid());
   plugin_container_ = ToWebPluginContainerBase(
       frame->GetFrame()->Loader().Client()->CreatePlugin(
-          object_element_.Get(), frame->GetFrame()->GetDocument()->Url(),
+          *object_element_, frame->GetFrame()->GetDocument()->Url(),
           attribute_names, attribute_values, plugin_type, false,
           LocalFrameClient::kAllowDetachedPlugin));
 
diff --git a/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp b/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
index f39385d..0df0ec45 100644
--- a/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
@@ -682,9 +682,9 @@
 
 // Private methods -------------------------------------------------------------
 
-WebPluginContainerImpl::WebPluginContainerImpl(HTMLPlugInElement* element,
+WebPluginContainerImpl::WebPluginContainerImpl(HTMLPlugInElement& element,
                                                WebPlugin* web_plugin)
-    : WebPluginContainerBase(element->GetDocument().GetFrame()),
+    : WebPluginContainerBase(element.GetDocument().GetFrame()),
       is_attached_(false),
       element_(element),
       web_plugin_(web_plugin),
diff --git a/third_party/WebKit/Source/web/WebPluginContainerImpl.h b/third_party/WebKit/Source/web/WebPluginContainerImpl.h
index f03ffdb..16f5d69 100644
--- a/third_party/WebKit/Source/web/WebPluginContainerImpl.h
+++ b/third_party/WebKit/Source/web/WebPluginContainerImpl.h
@@ -65,7 +65,7 @@
   USING_PRE_FINALIZER(WebPluginContainerImpl, PreFinalize);
 
  public:
-  static WebPluginContainerImpl* Create(HTMLPlugInElement* element,
+  static WebPluginContainerImpl* Create(HTMLPlugInElement& element,
                                         WebPlugin* web_plugin) {
     return new WebPluginContainerImpl(element, web_plugin);
   }
@@ -199,7 +199,7 @@
       IntRect& clipped_local_rect,
       IntRect& unclipped_int_local_rect) const;
 
-  WebPluginContainerImpl(HTMLPlugInElement*, WebPlugin*);
+  WebPluginContainerImpl(HTMLPlugInElement&, WebPlugin*);
 
   WebTouchEvent TransformTouchEvent(const WebInputEvent&);
   WebCoalescedInputEvent TransformCoalescedTouchEvent(
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index bf4ec99..c710189a 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -97,6 +97,8 @@
 #include "core/timing/DOMWindowPerformance.h"
 #include "core/timing/Performance.h"
 #include "modules/accessibility/AXObjectCacheImpl.h"
+#include "modules/compositorworker/AnimationWorkletProxyClientImpl.h"
+#include "modules/compositorworker/CompositorWorkerProxyClientImpl.h"
 #include "modules/credentialmanager/CredentialManagerClient.h"
 #include "modules/encryptedmedia/MediaKeysController.h"
 #include "modules/speech/SpeechRecognitionClientProxy.h"
@@ -164,8 +166,6 @@
 #include "public/web/WebSelection.h"
 #include "public/web/WebViewClient.h"
 #include "public/web/WebWindowFeatures.h"
-#include "web/AnimationWorkletProxyClientImpl.h"
-#include "web/CompositorWorkerProxyClientImpl.h"
 #include "web/DedicatedWorkerMessagingProxyProviderImpl.h"
 #include "web/FullscreenController.h"
 #include "web/LinkHighlightImpl.h"
@@ -698,8 +698,8 @@
                             event.data.fling_start.velocity_y),
               WebSize());
       DCHECK(fling_curve);
-      gesture_animation_ = WebActiveGestureAnimation::CreateAtAnimationStart(
-          std::move(fling_curve), this);
+      gesture_animation_ = WebActiveGestureAnimation::CreateWithTimeOffset(
+          std::move(fling_curve), this, event.TimeStampSeconds());
       MainFrameImpl()->FrameWidget()->ScheduleAnimation();
       event_result = WebInputEventResult::kHandledSystem;
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1bf8fe4..cc60142 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -24365,9 +24365,38 @@
   <int value="2" label="LowStorage"/>
 </enum>
 
+<enum name="MigrationUIMigrationResult" type="int">
+  <int value="0" label="Success in new migration"/>
+  <int value="1" label="Success in resumed migration"/>
+  <int value="2" label="General failure in new migration"/>
+  <int value="3" label="General failure in resumed migration"/>
+  <int value="4" label="Request failure in new migration"/>
+  <int value="5" label="Request failure in resumed migration"/>
+  <int value="6" label="Mount failure in new migration"/>
+  <int value="7" label="Mount failure in resumed migration"/>
+</enum>
+
+<enum name="MigrationUIRemoveCryptohomeResult" type="int">
+  <int value="0" label="Success in new migration"/>
+  <int value="1" label="Success in resumed migration"/>
+  <int value="2" label="Failure in new migration"/>
+  <int value="3" label="Failure in resumed migration"/>
+</enum>
+
+<enum name="MigrationUIScreen" type="int">
+  <int value="0" label="Initial"/>
+  <int value="1" label="Ready"/>
+  <int value="2" label="Migrating"/>
+  <int value="3" label="Migration failed"/>
+  <int value="4" label="Not enough storage"/>
+</enum>
+
 <enum name="MigrationUIUserChoice" type="int">
   <int value="0" label="Update"/>
   <int value="1" label="Skip"/>
+  <int value="2" label="Restart on migration failure"/>
+  <int value="3" label="Restart on low storage"/>
+  <int value="4" label="Report an issue"/>
 </enum>
 
 <enum name="MissingStartType" type="int">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 9402456..f84a4d36 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -9871,6 +9871,27 @@
   </summary>
 </histogram>
 
+<histogram name="Cryptohome.MigrationUI.MigrationResult"
+    enum="MigrationUIMigrationResult">
+  <owner>fukino@chromium.org</owner>
+  <summary>
+    The result of encryption migration from eCryptfs to Ext4 dircrypto. The
+    recorded result is what the migration UI in Chrome side is notified from
+    cryptohomed.
+  </summary>
+</histogram>
+
+<histogram name="Cryptohome.MigrationUI.RemoveCryptohomeResult"
+    enum="MigrationUIRemoveCryptohomeResult">
+  <owner>fukino@chromium.org</owner>
+  <summary>
+    The result of the removal of user's cryptohome. When the migration UI is
+    notified that the migration failed, the UI tries to remove the user's
+    cryptohome to make sure that the user can create clean crytohome directory
+    in the next sign-in.
+  </summary>
+</histogram>
+
 <histogram name="Cryptohome.MigrationUI.UserChoice"
     enum="MigrationUIUserChoice">
   <owner>fukino@chromium.org</owner>
@@ -9881,6 +9902,15 @@
   </summary>
 </histogram>
 
+<histogram name="Cryptohome.MigrationUI.VisibleScreen" enum="MigrationUIScreen">
+  <owner>fukino@chromium.org</owner>
+  <summary>
+    How many times each screen in migration UI is shown to the user. A screen is
+    recorded as a visible screen when the screen is kept displayed at least for
+    a second.
+  </summary>
+</histogram>
+
 <histogram name="Cryptohome.TimeToCompleteDircryptoMigration" units="ms">
   <owner>dspaid@chromium.org</owner>
   <summary>
diff --git a/ui/arc/notification/arc_notification_view.cc b/ui/arc/notification/arc_notification_view.cc
index c5f0115..dcf1887 100644
--- a/ui/arc/notification/arc_notification_view.cc
+++ b/ui/arc/notification/arc_notification_view.cc
@@ -86,6 +86,11 @@
     contents_view_delegate_->UpdateControlButtonsVisibility();
 }
 
+void ArcNotificationView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  // This data is never used since this view is never focused when the content
+  // view is focusable.
+}
+
 void ArcNotificationView::OnSlideChanged() {
   if (contents_view_delegate_)
     contents_view_delegate_->OnSlideChanged();
diff --git a/ui/arc/notification/arc_notification_view.h b/ui/arc/notification/arc_notification_view.h
index 8384327..a940766 100644
--- a/ui/arc/notification/arc_notification_view.h
+++ b/ui/arc/notification/arc_notification_view.h
@@ -40,6 +40,7 @@
   bool IsCloseButtonFocused() const override;
   void RequestFocusOnCloseButton() override;
   void UpdateControlButtonsVisibility() override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
   // views::SlideOutController::Delegate:
   void OnSlideChanged() override;