diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
index 9cb3f51..62404a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -35,7 +35,6 @@
     private final Delegate mDelegate;
     private final SuggestionsCategoryInfo mCategoryInfo;
     private final OfflinePageBridge mOfflinePageBridge;
-    private final SuggestionsRanker mSuggestionsRanker;
 
     // Children
     private final SectionHeader mHeader;
@@ -67,10 +66,9 @@
         mDelegate = delegate;
         mCategoryInfo = info;
         mOfflinePageBridge = offlinePageBridge;
-        mSuggestionsRanker = ranker;
 
         mHeader = new SectionHeader(info.getTitle());
-        mSuggestionsList = new SuggestionsList(manager, info);
+        mSuggestionsList = new SuggestionsList(manager, ranker, info);
         mStatus = StatusItem.createNoSuggestionsItem(info);
         mMoreButton = new ActionItem(this, ranker);
         mProgressIndicator = new ProgressItem();
@@ -83,11 +81,13 @@
     private static class SuggestionsList extends ChildNode implements Iterable<SnippetArticle> {
         private final List<SnippetArticle> mSuggestions = new ArrayList<>();
         private final NewTabPageManager mNewTabPageManager;
+        private final SuggestionsRanker mSuggestionsRanker;
         private final SuggestionsCategoryInfo mCategoryInfo;
 
         public SuggestionsList(NewTabPageManager newTabPageManager,
-                SuggestionsCategoryInfo categoryInfo) {
+                SuggestionsRanker ranker, SuggestionsCategoryInfo categoryInfo) {
             mNewTabPageManager = newTabPageManager;
+            mSuggestionsRanker = ranker;
             mCategoryInfo = categoryInfo;
         }
 
@@ -108,8 +108,10 @@
                 NewTabPageViewHolder holder, int position, List<Object> payloads) {
             checkIndex(position);
             assert holder instanceof SnippetArticleViewHolder;
+            SnippetArticle suggestion = getSuggestionAt(position);
+            mSuggestionsRanker.rankSuggestion(suggestion);
             ((SnippetArticleViewHolder) holder)
-                    .onBindViewHolder(getSuggestionAt(position), mCategoryInfo, payloads);
+                    .onBindViewHolder(suggestion, mCategoryInfo, payloads);
         }
 
         @Override
@@ -356,7 +358,6 @@
         mSuggestionsList.addAll(suggestions);
 
         for (SnippetArticle article : suggestions) {
-            mSuggestionsRanker.rankSuggestion(article);
             if (!article.requiresExactOfflinePage()) {
                 updateSnippetOfflineAvailability(article);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java
index 49b546e..2652d51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java
@@ -37,6 +37,8 @@
      * @see SnippetArticle#getGlobalRank()
      */
     public void rankSuggestion(SnippetArticle suggestion) {
+        if (suggestion.getPerSectionRank() != -1) return; // Suggestion was already ranked.
+
         int globalRank = mTotalAddedSuggestions++;
         int perSectionRank = mSuggestionsAddedPerSection.get(suggestion.mCategory);
         mSuggestionsAddedPerSection.put(suggestion.mCategory, perSectionRank + 1);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java
index 452238f..a477c2c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java
@@ -85,12 +85,51 @@
 
         SectionList sectionList = new SectionList(mNtpManager, mOfflinePageBridge);
 
+        bindViewHolders(sectionList);
+
         assertThat(articles.get(0).getGlobalRank(), equalTo(0));
         assertThat(articles.get(0).getPerSectionRank(), equalTo(0));
         assertThat(articles.get(2).getGlobalRank(), equalTo(2));
         assertThat(articles.get(2).getPerSectionRank(), equalTo(2));
         assertThat(bookmarks.get(1).getGlobalRank(), equalTo(4));
         assertThat(bookmarks.get(1).getPerSectionRank(), equalTo(1));
+    }
+
+    @Test
+    @Feature({"Ntp"})
+    public void testGetSuggestionRankWithChanges() {
+        // Setup the section list the following way:
+        //
+        //  Item type | local rank | global rank
+        // -----------+------------+-------------
+        // HEADER     |            |
+        // ARTICLE    | 0          | 0
+        // ARTICLE    | 1          | 1
+        // ARTICLE    | 2          | 2
+        // HEADER     |            |
+        // STATUS     |            |
+        // ACTION     | 0          |
+        // BOOKMARK   | 0          | 3
+        // BOOKMARK   | 1          | 4
+        // BOOKMARK   | 2          | 5
+        // BOOKMARK   | 3          | 6
+        List<SnippetArticle> articles =
+                registerCategory(mSuggestionSource, KnownCategories.ARTICLES, 3);
+        registerCategory(mSuggestionSource, KnownCategories.DOWNLOADS, 0);
+        List<SnippetArticle> bookmarks =
+                registerCategory(mSuggestionSource, KnownCategories.BOOKMARKS, 4);
+
+        SectionList sectionList = new SectionList(mNtpManager, mOfflinePageBridge);
+
+        bindViewHolders(sectionList, 0, 5); // Bind until after the third article.
+
+        assertThat(articles.get(0).getGlobalRank(), equalTo(0));
+        assertThat(articles.get(0).getPerSectionRank(), equalTo(0));
+        assertThat(articles.get(2).getGlobalRank(), equalTo(2));
+        assertThat(articles.get(2).getPerSectionRank(), equalTo(2));
+        assertThat(bookmarks.get(1).getGlobalRank(), equalTo(-1));  // Not bound nor ranked yet.
+        assertThat(bookmarks.get(1).getPerSectionRank(), equalTo(-1));
+
 
         // Test ranks after changes: remove then add some items.
         @SuppressWarnings("unchecked")
@@ -103,9 +142,11 @@
         List<SnippetArticle> newBookmarks =
                 createDummySuggestions(6, KnownCategories.BOOKMARKS).subList(4, 6);
 
-        sectionList.onMoreSuggestions(KnownCategories.ARTICLES, newArticles);
+        sectionList.onMoreSuggestions(KnownCategories.ARTICLES, newArticles.subList(0, 1));
         sectionList.onMoreSuggestions(KnownCategories.BOOKMARKS, newBookmarks);
 
+        bindViewHolders(sectionList, 3, sectionList.getItemCount());
+
         // After the changes we should have:
         //  Item type | local rank | global rank
         // -----------+------------+-------------
@@ -113,31 +154,62 @@
         // ARTICLE    | 0          | 0
         // ARTICLE    | 1          | 1
         //            | -          | -  (deleted)
-        // ARTICLE    | 3          | 7  (new)
-        // ARTICLE    | 4          | 8  (new)
+        // ARTICLE    | 3          | 3  (new)
         // HEADER     |            |
         // STATUS     |            |
         // ACTION     | 0          |
-        // BOOKMARK   | 0          | 3
-        // BOOKMARK   | 1          | 4
-        // BOOKMARK   | 2          | 5
-        // BOOKMARK   | 3          | 6
-        // BOOKMARK   | 4          | 9  (new)
-        // BOOKMARK   | 5          | 10 (new)
+        // BOOKMARK   | 0          | 4 (old but not seen until now)
+        // BOOKMARK   | 1          | 5 (old but not seen until now)
+        // BOOKMARK   | 2          | 6 (old but not seen until now)
+        // BOOKMARK   | 3          | 7 (old but not seen until now)
+        // BOOKMARK   | 4          | 8 (new)
+        // BOOKMARK   | 5          | 9 (new)
 
-        // The old ranks should not change.
+
+        // The new article is seen first and gets the next global rank
+        assertThat(newArticles.get(0).getGlobalRank(), equalTo(3));
+        assertThat(newArticles.get(0).getPerSectionRank(), equalTo(3));
+
+        // Bookmarks old and new are seen after the new article and have higher global ranks
+        assertThat(bookmarks.get(1).getGlobalRank(), equalTo(5));
+        assertThat(bookmarks.get(1).getPerSectionRank(), equalTo(1));
+        assertThat(newBookmarks.get(1).getGlobalRank(), equalTo(9));
+        assertThat(newBookmarks.get(1).getPerSectionRank(), equalTo(5));
+
+        // Add one more article
+        sectionList.onMoreSuggestions(KnownCategories.ARTICLES, newArticles.subList(1, 2));
+        bindViewHolders(sectionList);
+
+        // After the changes we should have:
+        //  Item type | local rank | global rank
+        // -----------+------------+-------------
+        // HEADER     |            |
+        // ARTICLE    | 0          | 0
+        // ARTICLE    | 1          | 1
+        //            | -          | -  (deleted)
+        // ARTICLE    | 3          | 3
+        // ARTICLE    | 4          | 10 (new)
+        // HEADER     |            |
+        // STATUS     |            |
+        // ACTION     | 0          |
+        // BOOKMARK   | 0          | 4
+        // BOOKMARK   | 1          | 5
+        // BOOKMARK   | 2          | 6
+        // BOOKMARK   | 3          | 7
+        // BOOKMARK   | 4          | 8
+        // BOOKMARK   | 5          | 9
+
+        // Old suggestions' ranks should not change.
         assertThat(articles.get(0).getGlobalRank(), equalTo(0));
         assertThat(articles.get(0).getPerSectionRank(), equalTo(0));
         assertThat(articles.get(2).getGlobalRank(), equalTo(2));
         assertThat(articles.get(2).getPerSectionRank(), equalTo(2));
-        assertThat(bookmarks.get(1).getGlobalRank(), equalTo(4));
+        assertThat(bookmarks.get(1).getGlobalRank(), equalTo(5));
         assertThat(bookmarks.get(1).getPerSectionRank(), equalTo(1));
 
-        // New ranks take into account the previously existing items.
-        assertThat(newArticles.get(1).getGlobalRank(), equalTo(8));
+        // The new article should get the last global rank
+        assertThat(newArticles.get(1).getGlobalRank(), equalTo(10));
         assertThat(newArticles.get(1).getPerSectionRank(), equalTo(4));
-        assertThat(newBookmarks.get(1).getGlobalRank(), equalTo(10));
-        assertThat(newBookmarks.get(1).getPerSectionRank(), equalTo(5));
     }
 
     @Test
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index ed74ad41..6601930 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -15196,6 +15196,12 @@
       <message name="IDS_FLAGS_ENABLE_NTP_STANDALONE_SUGGESTIONS_UI_DESCRIPTION" desc="Description for the flag to enable a standalone content suggestions UI." translateable="false">
         Show a menu item to open a standalone content suggestions UI for testing.
       </message>
+      <message name="IDS_FLAGS_ENABLE_NTP_SUGGESTIONS_NOTIFICATIONS_NAME" desc="Name for the flag to enable notifications for content suggestions on the New Tab page." translateable="false">
+        Notify about new content suggestions available at the New Tab page
+      </message>
+      <message name="IDS_FLAGS_ENABLE_NTP_SUGGESTIONS_NOTIFICATIONS_DESCRIPTION" desc="Description for the flag to enable notifications for content suggestions on the New Tab page." translateable="false">
+        If enabled, notifications will inform about new content suggestions on the New Tab page (see #enable-ntp-snippets).
+      </message>
     </if>
 
     <if expr="is_android">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 2a3aecd..1bbe0a8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -26,6 +26,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "cc/base/switches.h"
+#include "chrome/browser/ntp_snippets/ntp_snippets_features.h"
 #include "chrome/browser/prerender/prerender_field_trial.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_content_client.h"
@@ -535,6 +536,19 @@
 #endif  // OS_ANDROID
 
 #if defined(OS_ANDROID)
+const FeatureEntry::FeatureParam
+    kContentSuggestionsNotificationsFeatureVariationAlways[] = {
+        {kContentSuggestionsNotificationsAlwaysNotifyParam, "true"}};
+
+const FeatureEntry::FeatureVariation
+    kContentSuggestionsNotificationsFeatureVariations[] = {
+        {"(notify always)",
+         kContentSuggestionsNotificationsFeatureVariationAlways,
+         arraysize(kContentSuggestionsNotificationsFeatureVariationAlways),
+         nullptr}};
+#endif  // OS_ANDROID
+
+#if defined(OS_ANDROID)
 const FeatureEntry::FeatureParam kNTPSnippetsFeatureVariationChromeReader[] = {
     {"content_suggestions_backend", ntp_snippets::kChromeReaderServer}};
 
@@ -734,8 +748,7 @@
     {"show-overdraw-feedback", IDS_FLAGS_SHOW_OVERDRAW_FEEDBACK,
      IDS_FLAGS_SHOW_OVERDRAW_FEEDBACK_DESCRIPTION, kOsAll,
      SINGLE_VALUE_TYPE(cc::switches::kShowOverdrawFeedback)},
-    {"ui-disable-partial-swap",
-     IDS_FLAGS_UI_PARTIAL_SWAP_NAME,
+    {"ui-disable-partial-swap", IDS_FLAGS_UI_PARTIAL_SWAP_NAME,
      IDS_FLAGS_UI_PARTIAL_SWAP_DESCRIPTION, kOsAll,
      SINGLE_DISABLE_VALUE_TYPE(switches::kUIDisablePartialSwap)},
 #if BUILDFLAG(ENABLE_WEBRTC)
@@ -1050,8 +1063,7 @@
      IDS_FLAGS_MATERIAL_DESIGN_INK_DROP_ANIMATION_SPEED_NAME,
      IDS_FLAGS_MATERIAL_DESIGN_INK_DROP_ANIMATION_SPEED_DESCRIPTION, kOsCrOS,
      MULTI_VALUE_TYPE(kAshMaterialDesignInkDropAnimationSpeed)},
-    {"ui-slow-animations",
-     IDS_FLAGS_UI_SLOW_ANIMATIONS_NAME,
+    {"ui-slow-animations", IDS_FLAGS_UI_SLOW_ANIMATIONS_NAME,
      IDS_FLAGS_UI_SLOW_ANIMATIONS_DESCRIPTION, kOsCrOS,
      SINGLE_VALUE_TYPE(switches::kUISlowAnimations)},
     {"disable-cloud-import", IDS_FLAGS_CLOUD_IMPORT,
@@ -1914,6 +1926,13 @@
      IDS_FLAGS_ENABLE_NTP_STANDALONE_SUGGESTIONS_UI_NAME,
      IDS_FLAGS_ENABLE_NTP_STANDALONE_SUGGESTIONS_UI_DESCRIPTION, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kNTPSuggestionsStandaloneUIFeature)},
+    {"enable-ntp-suggestions-notifications",
+     IDS_FLAGS_ENABLE_NTP_SUGGESTIONS_NOTIFICATIONS_NAME,
+     IDS_FLAGS_ENABLE_NTP_SUGGESTIONS_NOTIFICATIONS_DESCRIPTION, kOsAndroid,
+     FEATURE_WITH_VARIATIONS_VALUE_TYPE(
+         kContentSuggestionsNotificationsFeature,
+         kContentSuggestionsNotificationsFeatureVariations,
+         ntp_snippets::kStudyName)},
 #endif  // OS_ANDROID
 #if BUILDFLAG(ENABLE_WEBRTC) && BUILDFLAG(RTC_USE_H264) && \
     !defined(MEDIA_DISABLE_FFMPEG)
@@ -2186,7 +2205,6 @@
      FEATURE_VALUE_TYPE(features::kLsdPermissionPrompt)},
 #endif
 
-
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms.xml. See note in
     // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test.
diff --git a/chrome/browser/prerender/prerender_final_status.cc b/chrome/browser/prerender/prerender_final_status.cc
index 609531b..4bbea01 100644
--- a/chrome/browser/prerender/prerender_final_status.cc
+++ b/chrome/browser/prerender/prerender_final_status.cc
@@ -69,6 +69,7 @@
     "Block Third Party Cookies",
     "Credential Manager API",
     "NoStatePrefetch Finished",
+    "Low-End Device",
     "Max",
 };
 static_assert(arraysize(kFinalStatusNames) == FINAL_STATUS_MAX + 1,
diff --git a/chrome/browser/prerender/prerender_final_status.h b/chrome/browser/prerender/prerender_final_status.h
index 718eeac..48b2904 100644
--- a/chrome/browser/prerender/prerender_final_status.h
+++ b/chrome/browser/prerender/prerender_final_status.h
@@ -69,6 +69,7 @@
   FINAL_STATUS_BLOCK_THIRD_PARTY_COOKIES = 54,
   FINAL_STATUS_CREDENTIAL_MANAGER_API = 55,
   FINAL_STATUS_NOSTATE_PREFETCH_FINISHED = 56,
+  FINAL_STATUS_LOW_END_DEVICE = 57,
   FINAL_STATUS_MAX,
 };
 
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index fe53187d..36a6dba 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -903,8 +903,11 @@
 
   // Allow only Requests for offlining on low end devices, the lifetime of
   // those prerenders is managed by the offliner.
-  if (IsLowEndDevice() && origin != ORIGIN_OFFLINE)
+  if (IsLowEndDevice() && origin != ORIGIN_OFFLINE) {
+    RecordFinalStatusWithoutCreatingPrerenderContents(
+        url_arg, origin, FINAL_STATUS_LOW_END_DEVICE);
     return nullptr;
+  }
 
   if ((origin == ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN ||
        origin == ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN) &&
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index b79a260..45df72b 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/simple_test_tick_clock.h"
@@ -400,6 +401,8 @@
         chrome_browser_net::NETWORK_PREDICTION_ALWAYS);
   }
 
+  const base::HistogramTester& histogram_tester() { return histogram_tester_; }
+
  private:
   // Needed to pass PrerenderManager's DCHECKs.
   base::MessageLoop message_loop_;
@@ -410,6 +413,7 @@
   std::unique_ptr<PrerenderLinkManager> prerender_link_manager_;
   int last_prerender_id_;
   base::FieldTrialList field_trial_list_;
+  base::HistogramTester histogram_tester_;
 };
 
 TEST_F(PrerenderTest, PrerenderRespectsDisableFlag) {
@@ -471,6 +475,8 @@
 
   profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
   EXPECT_FALSE(AddSimplePrerender(url));
+  histogram_tester().ExpectUniqueSample(
+      "Prerender.FinalStatus", FINAL_STATUS_BLOCK_THIRD_PARTY_COOKIES, 1);
 }
 
 TEST_F(PrerenderTest, OfflinePrerenderIgnoresThirdPartyCookiesPref) {
@@ -570,6 +576,8 @@
   ASSERT_TRUE(PrerenderManager::IsPrerenderingPossible());
   prerender_manager()->SetIsLowEndDevice(true);
   EXPECT_FALSE(AddSimplePrerender(url));
+  histogram_tester().ExpectUniqueSample("Prerender.FinalStatus",
+                                        FINAL_STATUS_LOW_END_DEVICE, 1);
 }
 
 TEST_F(PrerenderTest, OfflinePrerenderPossibleOnLowEndDevice) {
@@ -838,6 +846,8 @@
   tick_clock->Advance(base::TimeDelta::FromSeconds(1));
   EXPECT_FALSE(
       prerender_manager()->AddPrerenderFromOmnibox(kUrl, nullptr, gfx::Size()));
+  histogram_tester().ExpectBucketCount("Prerender.FinalStatus",
+                                       FINAL_STATUS_DUPLICATE, 1);
 
   // Prefetching after time_to_live succeeds.
   tick_clock->Advance(
@@ -1244,6 +1254,8 @@
   DisablePrerender();
   EXPECT_FALSE(prerender_manager()->AddPrerenderFromOmnibox(
       GURL("http://www.example.com"), nullptr, gfx::Size()));
+  histogram_tester().ExpectUniqueSample("Prerender.FinalStatus",
+                                        FINAL_STATUS_PRERENDERING_DISABLED, 1);
 }
 
 TEST_F(PrerenderTest, LinkRelStillAllowedWhenDisabled) {
@@ -1293,6 +1305,8 @@
           url, content::Referrer(), nullptr, gfx::Rect(kSize)));
   EXPECT_FALSE(prerender_handle);
   EXPECT_FALSE(prerender_contents->prerendering_has_started());
+  histogram_tester().ExpectUniqueSample("Prerender.FinalStatus",
+                                        FINAL_STATUS_CELLULAR_NETWORK, 1);
 }
 
 // Checks that the "PrerenderSilence" experiment does not disable offline
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 7f6fa03..3974f16 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -2389,11 +2389,11 @@
       base::FilePath(FILE_PATH_LITERAL("href.html")));
   }
 
-  base::string16 getFirstPageTitle() {
+  base::string16 GetFirstPageTitle() {
     return ASCIIToUTF16(kFirstPageTitle);
   }
 
-  base::string16 getSecondPageTitle() {
+  base::string16 GetSecondPageTitle() {
     return ASCIIToUTF16(kSecondPageTitle);
   }
 
@@ -2421,15 +2421,14 @@
       same_tab_observer.Wait();
       EXPECT_EQ(1u, chrome::GetBrowserCount(browser->profile()));
       EXPECT_EQ(1, browser->tab_strip_model()->count());
-      EXPECT_EQ(getSecondPageTitle(), web_contents->GetTitle());
+      EXPECT_EQ(GetSecondPageTitle(), web_contents->GetTitle());
       return;
     }
 
-    content::WindowedNotificationObserver observer(
-        chrome::NOTIFICATION_TAB_ADDED,
-        content::NotificationService::AllSources());
+    content::TestNavigationObserver new_tab_observer(nullptr);
+    new_tab_observer.StartWatchingNewWebContents();
     SimulateMouseClick(web_contents, modifiers, button);
-    observer.Wait();
+    new_tab_observer.Wait();
 
     if (disposition == WindowOpenDisposition::NEW_WINDOW) {
       EXPECT_EQ(2u, chrome::GetBrowserCount(browser->profile()));
@@ -2439,12 +2438,11 @@
     EXPECT_EQ(1u, chrome::GetBrowserCount(browser->profile()));
     EXPECT_EQ(2, browser->tab_strip_model()->count());
     web_contents = browser->tab_strip_model()->GetActiveWebContents();
-    WaitForLoadStop(web_contents);
     if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) {
-      EXPECT_EQ(getSecondPageTitle(), web_contents->GetTitle());
+      EXPECT_EQ(GetSecondPageTitle(), web_contents->GetTitle());
     } else {
       ASSERT_EQ(WindowOpenDisposition::NEW_BACKGROUND_TAB, disposition);
-      EXPECT_EQ(getFirstPageTitle(), web_contents->GetTitle());
+      EXPECT_EQ(GetFirstPageTitle(), web_contents->GetTitle());
     }
   }
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index c8e77bc4..164fb43 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9194.0.0
\ No newline at end of file
+9196.0.0
\ No newline at end of file
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 82ec036..586ba07 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -1086,8 +1086,15 @@
     std::string regexp = "^(" + scheme + ":\\/\\/)([\\w-]+\\.)*" +
                          registered_domain + "(:" + port + ")?\\/$";
     s.BindString(placeholder++, regexp);
-  }
-  if (should_federated_apply) {
+
+    if (should_federated_apply) {
+      // This regex matches any subdomain of |registered_domain|, in particular
+      // it matches the empty subdomain. Hence exact domain matches are also
+      // retrieved.
+      s.BindString(placeholder++,
+                   "^federation://([\\w-]+\\.)*" + registered_domain + "/.+$");
+    }
+  } else if (should_federated_apply) {
     std::string expression =
         base::StringPrintf("federation://%s/%%", form.origin.host().c_str());
     s.BindString(placeholder++, expression);
@@ -1221,6 +1228,11 @@
       } else if (!new_form->federation_origin.unique() &&
                  IsFederatedMatch(new_form->signon_realm,
                                   matched_form->origin)) {
+      } else if (!new_form->federation_origin.unique() &&
+                 IsFederatedPSLMatch(new_form->signon_realm,
+                                     matched_form->origin)) {
+        psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND_FEDERATED;
+        new_form->is_public_suffix_match = true;
       } else {
         continue;
       }
@@ -1277,13 +1289,15 @@
   std::string psl_statement = "OR signon_realm REGEXP ? ";
   std::string federated_statement =
       "OR (signon_realm LIKE ? AND password_type == 2) ";
+  std::string psl_federated_statement =
+      "OR (signon_realm REGEXP ? AND password_type == 2) ";
   DCHECK(get_statement_psl_.empty());
   get_statement_psl_ = get_statement_ + psl_statement;
   DCHECK(get_statement_federated_.empty());
   get_statement_federated_ = get_statement_ + federated_statement;
   DCHECK(get_statement_psl_federated_.empty());
   get_statement_psl_federated_ =
-      get_statement_ + psl_statement + federated_statement;
+      get_statement_ + psl_statement + psl_federated_statement;
   DCHECK(created_statement_.empty());
   created_statement_ =
       "SELECT " + all_column_names +
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index 248f70c..e93ce3f 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -413,13 +413,18 @@
   PasswordStore::FormDigest form_request = {
       PasswordForm::SCHEME_HTML, "https://foo.com/", GURL("https://foo.com/")};
   EXPECT_TRUE(db().GetLogins(form_request, &result));
-  EXPECT_THAT(result, testing::ElementsAre(Pointee(form)));
+  // Both forms are matched, only form2 is a PSL match.
+  form.is_public_suffix_match = false;
+  form2.is_public_suffix_match = true;
+  EXPECT_THAT(result, UnorderedElementsAre(Pointee(form), Pointee(form2)));
 
   // Match against the mobile site.
   form_request.origin = GURL("https://mobile.foo.com/");
   form_request.signon_realm = "https://mobile.foo.com/";
   EXPECT_TRUE(db().GetLogins(form_request, &result));
+  // Both forms are matched, only form is a PSL match.
   form.is_public_suffix_match = true;
+  form2.is_public_suffix_match = false;
   EXPECT_THAT(result, UnorderedElementsAre(Pointee(form), Pointee(form2)));
 }
 
@@ -534,6 +539,28 @@
   EXPECT_THAT(result, testing::ElementsAre(Pointee(form2)));
 }
 
+TEST_F(LoginDatabaseTest, TestFederatedPSLMatching) {
+  // Save a federated credential for the PSL matched site.
+  PasswordForm form;
+  form.origin = GURL("https://psl.example.com/");
+  form.action = GURL("https://psl.example.com/login");
+  form.signon_realm = "federation://psl.example.com/accounts.google.com";
+  form.username_value = ASCIIToUTF16("test1@gmail.com");
+  form.type = PasswordForm::TYPE_API;
+  form.federation_origin = url::Origin(GURL("https://accounts.google.com/"));
+  form.scheme = PasswordForm::SCHEME_HTML;
+  EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
+
+  // Match against.
+  PasswordStore::FormDigest form_request = {PasswordForm::SCHEME_HTML,
+                                            "https://example.com/",
+                                            GURL("https://example.com/login")};
+  std::vector<std::unique_ptr<PasswordForm>> result;
+  EXPECT_TRUE(db().GetLogins(form_request, &result));
+  form.is_public_suffix_match = true;
+  EXPECT_THAT(result, testing::ElementsAre(Pointee(form)));
+}
+
 // This test fails if the implementation of GetLogins uses GetCachedStatement
 // instead of GetUniqueStatement, since REGEXP is in use. See
 // http://crbug.com/248608.
diff --git a/components/password_manager/core/browser/psl_matching_helper.cc b/components/password_manager/core/browser/psl_matching_helper.cc
index ff01008..8790e8a 100644
--- a/components/password_manager/core/browser/psl_matching_helper.cc
+++ b/components/password_manager/core/browser/psl_matching_helper.cc
@@ -11,6 +11,7 @@
 #include "components/autofill/core/common/password_form.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "url/gurl.h"
+#include "url/url_constants.h"
 
 using autofill::PasswordForm;
 
@@ -57,4 +58,26 @@
                           base::CompareCase::INSENSITIVE_ASCII);
 }
 
+bool IsFederatedPSLMatch(const std::string& signon_realm, const GURL& origin) {
+  // The format should be "federation://origin.host/federation.host;
+  // Check for presence of "federation://" prefix.
+  static constexpr char federation_prefix[] = "federation://";
+  if (!base::StartsWith(signon_realm, federation_prefix,
+                        base::CompareCase::INSENSITIVE_ASCII))
+    return false;
+
+  // Replace federation scheme with HTTPS. This results in correct parsing of
+  // host and path, and forces origin to have a HTTPS scheme in order to return
+  // true.
+  GURL::Replacements replacements;
+  replacements.SetSchemeStr(url::kHttpsScheme);
+  GURL https_signon_realm = GURL(signon_realm).ReplaceComponents(replacements);
+
+  // Check for non-empty federation.host.
+  if (!https_signon_realm.has_path() || https_signon_realm.path_piece() == "/")
+    return false;
+
+  return IsPublicSuffixDomainMatch(https_signon_realm.GetOrigin().spec(),
+                                   origin.GetOrigin().spec());
+}
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/psl_matching_helper.h b/components/password_manager/core/browser/psl_matching_helper.h
index 22071b60..56b2ac4 100644
--- a/components/password_manager/core/browser/psl_matching_helper.h
+++ b/components/password_manager/core/browser/psl_matching_helper.h
@@ -19,6 +19,7 @@
   PSL_DOMAIN_MATCH_NOT_USED = 0,
   PSL_DOMAIN_MATCH_NONE,
   PSL_DOMAIN_MATCH_FOUND,
+  PSL_DOMAIN_MATCH_FOUND_FEDERATED,
   PSL_DOMAIN_MATCH_COUNT
 };
 
@@ -48,6 +49,9 @@
 // |origin|.
 bool IsFederatedMatch(const std::string& signon_realm, const GURL& origin);
 
+// Returns true iff |signon_realm| designates a federated PSL matching
+// credential for the |origin|.
+bool IsFederatedPSLMatch(const std::string& signon_realm, const GURL& origin);
 }  // namespace password_manager
 
 #endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PSL_MATCHING_HELPER_H_
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index f49b6a13..072d500 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -267,10 +267,6 @@
 }
 
 static bool ShouldCreateGpuCompositorFrameSink(ui::Compositor* compositor) {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kHeadless))
-    return false;
-
 #if defined(OS_CHROMEOS)
   // Software fallback does not happen on Chrome OS.
   return true;
diff --git a/content/browser/compositor/offscreen_browser_compositor_output_surface.cc b/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
index 8e4827a..8c7581c 100644
--- a/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
@@ -14,6 +14,7 @@
 #include "components/display_compositor/compositor_overlay_candidate_validator.h"
 #include "content/browser/compositor/reflector_impl.h"
 #include "content/browser/compositor/reflector_texture.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
@@ -157,7 +158,7 @@
       sync_token,
       base::Bind(
           &OffscreenBrowserCompositorOutputSurface::OnSwapBuffersComplete,
-          weak_ptr_factory_.GetWeakPtr()));
+          weak_ptr_factory_.GetWeakPtr(), frame.latency_info));
 }
 
 bool OffscreenBrowserCompositorOutputSurface::IsDisplayedAsOverlayPlane()
@@ -186,7 +187,9 @@
   }
 }
 
-void OffscreenBrowserCompositorOutputSurface::OnSwapBuffersComplete() {
+void OffscreenBrowserCompositorOutputSurface::OnSwapBuffersComplete(
+    const std::vector<ui::LatencyInfo>& latency_info) {
+  RenderWidgetHostImpl::CompositorFrameDrawn(latency_info);
   client_->DidReceiveSwapBuffersAck();
 }
 
diff --git a/content/browser/compositor/offscreen_browser_compositor_output_surface.h b/content/browser/compositor/offscreen_browser_compositor_output_surface.h
index 01b9a18..917b78b3 100644
--- a/content/browser/compositor/offscreen_browser_compositor_output_surface.h
+++ b/content/browser/compositor/offscreen_browser_compositor_output_surface.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "content/browser/compositor/browser_compositor_output_surface.h"
+#include "ui/events/latency_info.h"
 
 namespace ui {
 class ContextProviderCommandBuffer;
@@ -56,7 +57,7 @@
   void SetSurfaceSuspendedForRecycle(bool suspended) override {};
 #endif
 
-  void OnSwapBuffersComplete();
+  void OnSwapBuffersComplete(const std::vector<ui::LatencyInfo>& latency_info);
 
   cc::OutputSurfaceClient* client_ = nullptr;
   gfx::Size reshape_size_;
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index cccd523..65c2a71 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -5,12 +5,14 @@
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 
 #include <map>
+#include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/atomic_sequence_num.h"
 #include "base/bind.h"
-#include "base/memory/scoped_vector.h"
+#include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "content/browser/message_port_message_filter.h"
@@ -45,14 +47,14 @@
                        base::Unretained(&next_routing_id_))) {}
 
   bool Send(IPC::Message* message) override {
-    message_queue_.push_back(message);
+    message_queue_.push_back(base::WrapUnique(message));
     return true;
   }
 
  private:
   ~MockMessagePortMessageFilter() override {}
   base::AtomicSequenceNumber next_routing_id_;
-  ScopedVector<IPC::Message> message_queue_;
+  std::vector<std::unique_ptr<IPC::Message>> message_queue_;
 };
 
 }  // namespace
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 1c423d0..f44f8637 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -1871,66 +1871,6 @@
   EXPECT_EQ(1, GetRequestCount(kPageUrl));
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest,
-                       AbortPreloadRequest) {
-  const char kPageUrl[] = "/service_worker/navigation_preload.html";
-  const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
-  const char kPage[] = "<title>ERROR</title>Hello world.";
-  // In this script, event.preloadResponse is not guarded by event.waitUntil.
-  // So the preload request should be canceled, when the fetch event handler
-  // has been executed.
-  const std::string kScript =
-      kEnableNavigationPreloadScript +
-      "var preload_resolve;\n"
-      "var preload_promise = new Promise(r => { preload_resolve = r; });\n"
-      "self.addEventListener('fetch', event => {\n"
-      "    event.preloadResponse.then(\n"
-      "        _ => { preload_resolve({result: 'RESOLVED',\n"
-      "                                info: 'Preload resolved.'}); },\n"
-      "        e => { preload_resolve({result: 'REJECTED',\n"
-      "                                info: e.toString()}); });\n"
-      "    event.respondWith(\n"
-      "        new Response(\n"
-      "            '<title>WAITING</title><script>\\n' +\n"
-      "            'navigator.serviceWorker.onmessage = e => {\\n' +\n"
-      "            '    var div = document.createElement(\\'div\\');\\n' +\n"
-      "            '    div.appendChild(' +\n"
-      "            '        document.createTextNode(e.data.info));\\n' +\n"
-      "            '    document.body.appendChild(div);\\n' +\n"
-      "            '    document.title = e.data.result;\\n' +\n"
-      "            '  };\\n' +\n"
-      "            'navigator.serviceWorker.controller.postMessage(\\n' +\n"
-      "            '    null);\\n' +\n"
-      "            '</script>',"
-      "            {headers: [['content-type', 'text/html']]}));\n"
-      "  });\n"
-      "self.addEventListener('message', event => {\n"
-      "    event.waitUntil(\n"
-      "        preload_promise.then(\n"
-      "            result => event.source.postMessage(result)));\n"
-      "  });";
-  const GURL page_url = embedded_test_server()->GetURL(kPageUrl);
-  const GURL worker_url = embedded_test_server()->GetURL(kWorkerUrl);
-  RegisterStaticFile(kPageUrl, kPage, "text/html");
-  RegisterStaticFile(kWorkerUrl, kScript, "text/javascript");
-
-  RegisterMonitorRequestHandler();
-  StartServerAndNavigateToSetup();
-  SetupForNavigationPreloadTest(page_url, worker_url);
-
-  const base::string16 title = base::ASCIIToUTF16("REJECTED");
-  TitleWatcher title_watcher(shell()->web_contents(), title);
-  title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16("RESOLVED"));
-  title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16("ERROR"));
-  NavigateToURL(shell(), page_url);
-  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
-
-  EXPECT_EQ(
-      "AbortError: Service Worker navigation preload aborted. Need to guard "
-      "with respondWith or waitUntil.",
-      GetTextContent());
-}
-
 IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest, NetworkError) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index ded2b79..85cf9d7db3 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -75,7 +75,14 @@
  public:
   explicit DelegatingURLLoaderClient(mojom::URLLoaderClientPtr client)
       : binding_(this), client_(std::move(client)) {}
-  ~DelegatingURLLoaderClient() override {}
+  ~DelegatingURLLoaderClient() override {
+    if (!completed_) {
+      // Let the service worker know that the request has been canceled.
+      ResourceRequestCompletionStatus status;
+      status.error_code = net::ERR_ABORTED;
+      client_->OnComplete(status);
+    }
+  }
 
   void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override {
     client_->OnDataDownloaded(data_length, encoded_length);
@@ -102,6 +109,7 @@
   void OnComplete(
       const ResourceRequestCompletionStatus& completion_status) override {
     client_->OnComplete(completion_status);
+    completed_ = true;
   }
 
   void Bind(mojom::URLLoaderClientAssociatedPtrInfo* ptr_info,
@@ -112,6 +120,7 @@
  private:
   mojo::AssociatedBinding<mojom::URLLoaderClient> binding_;
   mojom::URLLoaderClientPtr client_;
+  bool completed_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(DelegatingURLLoaderClient);
 };
@@ -167,18 +176,6 @@
   return ServiceWorkerMetrics::EventType::FETCH_WAITUNTIL;
 }
 
-void OnFetchEventFinished(
-    ServiceWorkerVersion* version,
-    int event_finish_id,
-    mojom::URLLoaderFactoryPtr url_loader_factory,
-    std::unique_ptr<mojom::URLLoader> url_loader,
-    std::unique_ptr<mojom::URLLoaderClient> url_loader_client,
-    ServiceWorkerStatusCode status,
-    base::Time dispatch_event_time) {
-  version->FinishRequest(event_finish_id, status != SERVICE_WORKER_ERROR_ABORT,
-                         dispatch_event_time);
-}
-
 }  // namespace
 
 // Helper to receive the fetch event response even if
@@ -211,6 +208,29 @@
   DISALLOW_COPY_AND_ASSIGN(ResponseCallback);
 };
 
+// This class keeps the URL loader related assets alive while the FetchEvent is
+// ongoing in the service worker.
+class ServiceWorkerFetchDispatcher::URLLoaderAssets
+    : public base::RefCounted<ServiceWorkerFetchDispatcher::URLLoaderAssets> {
+ public:
+  URLLoaderAssets(mojom::URLLoaderFactoryPtr url_loader_factory,
+                  std::unique_ptr<mojom::URLLoader> url_loader,
+                  std::unique_ptr<mojom::URLLoaderClient> url_loader_client)
+      : url_loader_factory_(std::move(url_loader_factory)),
+        url_loader_(std::move(url_loader)),
+        url_loader_client_(std::move(url_loader_client)) {}
+
+ private:
+  friend class base::RefCounted<URLLoaderAssets>;
+  virtual ~URLLoaderAssets() {}
+
+  mojom::URLLoaderFactoryPtr url_loader_factory_;
+  std::unique_ptr<mojom::URLLoader> url_loader_;
+  std::unique_ptr<mojom::URLLoaderClient> url_loader_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLLoaderAssets);
+};
+
 ServiceWorkerFetchDispatcher::ServiceWorkerFetchDispatcher(
     std::unique_ptr<ServiceWorkerFetchRequest> request,
     ServiceWorkerVersion* version,
@@ -335,15 +355,13 @@
 
   // |event_dispatcher| is owned by |version_|. So it is safe to pass the
   // unretained raw pointer of |version_| to OnFetchEventFinished callback.
-  // Pass |url_loader_factory_|, |url_Loader_| and |url_loader_client_| to the
-  // callback to keep them alive while the FetchEvent is onging in the service
-  // worker.
+  // Pass |url_loader_assets_| to the callback to keep the URL loader related
+  // assets alive while the FetchEvent is ongoing in the service worker.
   version_->event_dispatcher()->DispatchFetchEvent(
       fetch_event_id, *request_, std::move(preload_handle_),
-      base::Bind(&OnFetchEventFinished, base::Unretained(version_.get()),
-                 event_finish_id, base::Passed(std::move(url_loader_factory_)),
-                 base::Passed(std::move(url_loader_)),
-                 base::Passed(std::move(url_loader_client_))));
+      base::Bind(&ServiceWorkerFetchDispatcher::OnFetchEventFinished,
+                 base::Unretained(version_.get()), event_finish_id,
+                 url_loader_assets_));
 }
 
 void ServiceWorkerFetchDispatcher::DidFailToDispatch(
@@ -413,11 +431,12 @@
       return false;
   }
 
-  DCHECK(!url_loader_factory_);
-  mojom::URLLoaderFactoryPtr factory;
+  DCHECK(!url_loader_assets_);
+
+  mojom::URLLoaderFactoryPtr url_loader_factory;
   URLLoaderFactoryImpl::Create(
       ResourceRequesterInfo::CreateForNavigationPreload(requester_info),
-      mojo::MakeRequest(&url_loader_factory_));
+      mojo::MakeRequest(&url_loader_factory));
 
   preload_handle_ = mojom::FetchEventPreloadHandle::New();
 
@@ -460,20 +479,22 @@
       std::move(url_loader_client_ptr));
   mojom::URLLoaderClientAssociatedPtrInfo url_loader_client_associated_ptr_info;
   url_loader_client->Bind(&url_loader_client_associated_ptr_info,
-                          url_loader_factory_.associated_group());
+                          url_loader_factory.associated_group());
   mojom::URLLoaderAssociatedPtr url_loader_associated_ptr;
 
-  url_loader_factory_->CreateLoaderAndStart(
+  url_loader_factory->CreateLoaderAndStart(
       mojo::MakeRequest(&url_loader_associated_ptr,
-                        url_loader_factory_.associated_group()),
+                        url_loader_factory.associated_group()),
       original_info->GetRouteID(), request_id, request,
       std::move(url_loader_client_associated_ptr_info));
 
   std::unique_ptr<DelegatingURLLoader> url_loader(
-      new DelegatingURLLoader(std::move(url_loader_associated_ptr)));
+      base::MakeUnique<DelegatingURLLoader>(
+          std::move(url_loader_associated_ptr)));
   preload_handle_->url_loader = url_loader->CreateInterfacePtrAndBind();
-  url_loader_ = std::move(url_loader);
-  url_loader_client_ = std::move(url_loader_client);
+  url_loader_assets_ =
+      new URLLoaderAssets(std::move(url_loader_factory), std::move(url_loader),
+                          std::move(url_loader_client));
   return true;
 }
 
@@ -484,4 +505,15 @@
   return ResourceTypeToEventType(resource_type_);
 }
 
+// static
+void ServiceWorkerFetchDispatcher::OnFetchEventFinished(
+    ServiceWorkerVersion* version,
+    int event_finish_id,
+    scoped_refptr<URLLoaderAssets> url_loader_assets,
+    ServiceWorkerStatusCode status,
+    base::Time dispatch_event_time) {
+  version->FinishRequest(event_finish_id, status != SERVICE_WORKER_ERROR_ABORT,
+                         dispatch_event_time);
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h
index 7f745ce3..3064d7d 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.h
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/time/time.h"
@@ -61,6 +62,7 @@
 
  private:
   class ResponseCallback;
+  class URLLoaderAssets;
 
   void DidWaitForActivation();
   void StartWorker();
@@ -76,6 +78,13 @@
                 ServiceWorkerFetchEventResult fetch_result,
                 const ServiceWorkerResponse& response);
 
+  static void OnFetchEventFinished(
+      ServiceWorkerVersion* version,
+      int event_finish_id,
+      scoped_refptr<URLLoaderAssets> url_loader_assets,
+      ServiceWorkerStatusCode status,
+      base::Time dispatch_event_time);
+
   ServiceWorkerMetrics::EventType GetEventType() const;
 
   scoped_refptr<ServiceWorkerVersion> version_;
@@ -86,9 +95,9 @@
   ResourceType resource_type_;
   base::Optional<base::TimeDelta> timeout_;
   bool did_complete_;
-  mojom::URLLoaderFactoryPtr url_loader_factory_;
-  std::unique_ptr<mojom::URLLoader> url_loader_;
-  std::unique_ptr<mojom::URLLoaderClient> url_loader_client_;
+
+  scoped_refptr<URLLoaderAssets> url_loader_assets_;
+
   mojom::FetchEventPreloadHandlePtr preload_handle_;
 
   base::WeakPtrFactory<ServiceWorkerFetchDispatcher> weak_factory_;
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
index 5ec763d..32f1495 100644
--- a/content/browser/service_worker/service_worker_internals_ui.cc
+++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -6,12 +6,13 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "base/bind.h"
-#include "base/memory/scoped_vector.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "content/browser/devtools/devtools_agent_host_impl.h"
@@ -93,6 +94,15 @@
   (*version.get().*method)(callback);
 }
 
+std::vector<const Value*> ConvertToRawPtrVector(
+    const std::vector<std::unique_ptr<const Value>>& args) {
+  std::vector<const Value*> args_rawptrs(args.size());
+  std::transform(
+      args.begin(), args.end(), args_rawptrs.begin(),
+      [](const std::unique_ptr<const Value>& arg) { return arg.get(); });
+  return args_rawptrs;
+}
+
 base::ProcessId GetRealProcessId(int process_host_id) {
   if (process_host_id == ChildProcessHost::kInvalidUniqueID)
     return base::kNullProcessId;
@@ -171,15 +181,15 @@
   info->SetInteger("devtools_agent_route_id", version.devtools_agent_route_id);
 }
 
-ListValue* GetRegistrationListValue(
+std::unique_ptr<ListValue> GetRegistrationListValue(
     const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
-  ListValue* result = new ListValue();
+  auto result = base::MakeUnique<ListValue>();
   for (std::vector<ServiceWorkerRegistrationInfo>::const_iterator it =
            registrations.begin();
        it != registrations.end();
        ++it) {
     const ServiceWorkerRegistrationInfo& registration = *it;
-    std::unique_ptr<DictionaryValue> registration_info(new DictionaryValue());
+    auto registration_info = base::MakeUnique<DictionaryValue>();
     registration_info->SetString("scope", registration.pattern.spec());
     registration_info->SetString(
         "registration_id", base::Int64ToString(registration.registration_id));
@@ -203,14 +213,14 @@
   return result;
 }
 
-ListValue* GetVersionListValue(
+std::unique_ptr<ListValue> GetVersionListValue(
     const std::vector<ServiceWorkerVersionInfo>& versions) {
-  ListValue* result = new ListValue();
+  auto result = base::MakeUnique<ListValue>();
   for (std::vector<ServiceWorkerVersionInfo>::const_iterator it =
            versions.begin();
        it != versions.end();
        ++it) {
-    std::unique_ptr<DictionaryValue> info(new DictionaryValue());
+    auto info = base::MakeUnique<DictionaryValue>();
     UpdateVersionInfo(*it, info.get());
     result->Append(std::move(info));
   }
@@ -248,14 +258,14 @@
   if (!internals)
     return;
 
-  ScopedVector<const Value> args;
+  std::vector<std::unique_ptr<const Value>> args;
   args.push_back(GetRegistrationListValue(live_registrations));
   args.push_back(GetVersionListValue(live_versions));
   args.push_back(GetRegistrationListValue(stored_registrations));
-  args.push_back(new FundamentalValue(partition_id));
-  args.push_back(new StringValue(context_path.value()));
+  args.push_back(base::MakeUnique<FundamentalValue>(partition_id));
+  args.push_back(base::MakeUnique<StringValue>(context_path.value()));
   internals->web_ui()->CallJavascriptFunctionUnsafe(
-      "serviceworker.onPartitionData", args.get());
+      "serviceworker.onPartitionData", ConvertToRawPtrVector(args));
 }
 
 }  // namespace
@@ -286,39 +296,41 @@
                        int thread_id,
                        const ErrorInfo& info) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    ScopedVector<const Value> args;
-    args.push_back(new FundamentalValue(partition_id_));
-    args.push_back(new StringValue(base::Int64ToString(version_id)));
-    args.push_back(new FundamentalValue(process_id));
-    args.push_back(new FundamentalValue(thread_id));
-    std::unique_ptr<DictionaryValue> value(new DictionaryValue());
+    std::vector<std::unique_ptr<const Value>> args;
+    args.push_back(base::MakeUnique<FundamentalValue>(partition_id_));
+    args.push_back(
+        base::MakeUnique<StringValue>(base::Int64ToString(version_id)));
+    args.push_back(base::MakeUnique<FundamentalValue>(process_id));
+    args.push_back(base::MakeUnique<FundamentalValue>(thread_id));
+    auto value = base::MakeUnique<DictionaryValue>();
     value->SetString("message", info.error_message);
     value->SetInteger("lineNumber", info.line_number);
     value->SetInteger("columnNumber", info.column_number);
     value->SetString("sourceURL", info.source_url.spec());
-    args.push_back(value.release());
+    args.push_back(std::move(value));
     web_ui_->CallJavascriptFunctionUnsafe("serviceworker.onErrorReported",
-                                          args.get());
+                                          ConvertToRawPtrVector(args));
   }
   void OnReportConsoleMessage(int64_t version_id,
                               int process_id,
                               int thread_id,
                               const ConsoleMessage& message) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    ScopedVector<const Value> args;
-    args.push_back(new FundamentalValue(partition_id_));
-    args.push_back(new StringValue(base::Int64ToString(version_id)));
-    args.push_back(new FundamentalValue(process_id));
-    args.push_back(new FundamentalValue(thread_id));
-    std::unique_ptr<DictionaryValue> value(new DictionaryValue());
+    std::vector<std::unique_ptr<const Value>> args;
+    args.push_back(base::MakeUnique<FundamentalValue>(partition_id_));
+    args.push_back(
+        base::MakeUnique<StringValue>(base::Int64ToString(version_id)));
+    args.push_back(base::MakeUnique<FundamentalValue>(process_id));
+    args.push_back(base::MakeUnique<FundamentalValue>(thread_id));
+    auto value = base::MakeUnique<DictionaryValue>();
     value->SetInteger("sourceIdentifier", message.source_identifier);
     value->SetInteger("message_level", message.message_level);
     value->SetString("message", message.message);
     value->SetInteger("lineNumber", message.line_number);
     value->SetString("sourceURL", message.source_url.spec());
-    args.push_back(value.release());
+    args.push_back(std::move(value));
     web_ui_->CallJavascriptFunctionUnsafe(
-        "serviceworker.onConsoleMessageReported", args.get());
+        "serviceworker.onConsoleMessageReported", ConvertToRawPtrVector(args));
   }
   void OnRegistrationStored(int64_t registration_id,
                             const GURL& pattern) override {
@@ -438,8 +450,8 @@
     partition_id = it->second->partition_id();
   } else {
     partition_id = next_partition_id_++;
-    std::unique_ptr<PartitionObserver> new_observer(
-        new PartitionObserver(partition_id, web_ui()));
+    auto new_observer =
+        base::MakeUnique<PartitionObserver>(partition_id, web_ui());
     context->AddObserver(new_observer.get());
     observers_[reinterpret_cast<uintptr_t>(partition)] =
         std::move(new_observer);
diff --git a/content/browser/service_worker/service_worker_url_request_job.cc b/content/browser/service_worker/service_worker_url_request_job.cc
index 62325d45a..f69a0ac 100644
--- a/content/browser/service_worker/service_worker_url_request_job.cc
+++ b/content/browser/service_worker/service_worker_url_request_job.cc
@@ -565,7 +565,13 @@
     ServiceWorkerFetchEventResult fetch_result,
     const ServiceWorkerResponse& response,
     const scoped_refptr<ServiceWorkerVersion>& version) {
-  fetch_dispatcher_.reset();
+  // Do not clear |fetch_dispatcher_| if it has dispatched a navigation preload
+  // request to keep the mojom::URLLoader related objects in it, because the
+  // preload response might still need to be streamed even after calling
+  // respondWith().
+  if (!did_navigation_preload_) {
+    fetch_dispatcher_.reset();
+  }
   ServiceWorkerMetrics::RecordFetchEventStatus(IsMainResourceLoad(), status);
 
   ServiceWorkerMetrics::URLRequestJobResult result =
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 2333cceee1..cdfde347 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -341,7 +341,14 @@
         TracingController::StartTracingDoneCallback());
   }
 
-  RunTestOnMainThreadLoop();
+  {
+    // This can be called from a posted task. Allow nested tasks here, because
+    // otherwise the test body will have to do it in order to use RunLoop for
+    // waiting.
+    base::MessageLoop::ScopedNestableTaskAllower allow(
+        base::MessageLoop::current());
+    RunTestOnMainThreadLoop();
+  }
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableTracing)) {
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 06394e0..921e8bf 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/process/kill.h"
 #include "base/rand_util.h"
+#include "base/stl_util.h"
 #include "base/strings/pattern.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
@@ -1205,9 +1206,7 @@
 
 TitleWatcher::TitleWatcher(WebContents* web_contents,
                            const base::string16& expected_title)
-    : WebContentsObserver(web_contents),
-      message_loop_runner_(new MessageLoopRunner) {
-  EXPECT_TRUE(web_contents != NULL);
+    : WebContentsObserver(web_contents) {
   expected_titles_.push_back(expected_title);
 }
 
@@ -1220,7 +1219,7 @@
 
 const base::string16& TitleWatcher::WaitAndGetTitle() {
   TestTitle();
-  message_loop_runner_->Run();
+  run_loop_.Run();
   return observed_title_;
 }
 
@@ -1237,15 +1236,11 @@
 }
 
 void TitleWatcher::TestTitle() {
-  std::vector<base::string16>::const_iterator it =
-      std::find(expected_titles_.begin(),
-                expected_titles_.end(),
-                web_contents()->GetTitle());
-  if (it == expected_titles_.end())
-    return;
-
-  observed_title_ = *it;
-  message_loop_runner_->Quit();
+  const base::string16& current_title = web_contents()->GetTitle();
+  if (base::ContainsValue(expected_titles_, current_title)) {
+    observed_title_ = current_title;
+    run_loop_.Quit();
+  }
 }
 
 RenderProcessHostWatcher::RenderProcessHostWatcher(
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index b490899..684d1c94 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/process/process.h"
+#include "base/run_loop.h"
 #include "base/strings/string16.h"
 #include "build/build_config.h"
 #include "cc/output/compositor_frame.h"
@@ -374,7 +375,7 @@
   void TestTitle();
 
   std::vector<base::string16> expected_titles_;
-  scoped_refptr<MessageLoopRunner> message_loop_runner_;
+  base::RunLoop run_loop_;
 
   // The most recently observed expected title, if any.
   base::string16 observed_title_;
diff --git a/content/public/test/test_frame_navigation_observer.cc b/content/public/test/test_frame_navigation_observer.cc
index 3f20309..46f1906 100644
--- a/content/public/test/test_frame_navigation_observer.cc
+++ b/content/public/test/test_frame_navigation_observer.cc
@@ -32,9 +32,7 @@
       frame_tree_node_id_(ToRenderFrameHostImpl(adapter)->GetFrameTreeNodeId()),
       navigation_started_(false),
       has_committed_(false),
-      wait_for_commit_(false),
-      message_loop_runner_(
-          new MessageLoopRunner(MessageLoopRunner::QuitMode::IMMEDIATE)) {
+      wait_for_commit_(false) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
@@ -43,7 +41,7 @@
 void TestFrameNavigationObserver::Wait() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   wait_for_commit_ = false;
-  message_loop_runner_->Run();
+  run_loop_.Run();
 }
 
 void TestFrameNavigationObserver::WaitForCommit() {
@@ -53,7 +51,7 @@
     return;
 
   wait_for_commit_ = true;
-  message_loop_runner_->Run();
+  run_loop_.Run();
 }
 
 void TestFrameNavigationObserver::DidStartProvisionalLoadForFrame(
@@ -82,7 +80,7 @@
 
   has_committed_ = true;
   if (wait_for_commit_)
-    message_loop_runner_->Quit();
+    run_loop_.Quit();
 }
 
 void TestFrameNavigationObserver::DidStopLoading() {
@@ -90,7 +88,7 @@
     return;
 
   navigation_started_ = false;
-  message_loop_runner_->Quit();
+  run_loop_.Quit();
 }
 
 }  // namespace content
diff --git a/content/public/test/test_frame_navigation_observer.h b/content/public/test/test_frame_navigation_observer.h
index 9717daf..aadc9d7 100644
--- a/content/public/test/test_frame_navigation_observer.h
+++ b/content/public/test/test_frame_navigation_observer.h
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/run_loop.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
@@ -64,8 +65,8 @@
   // of the document.
   bool wait_for_commit_;
 
-  // The MessageLoopRunner used to spin the message loop.
-  scoped_refptr<MessageLoopRunner> message_loop_runner_;
+  // The RunLoop used to spin the message loop.
+  base::RunLoop run_loop_;
 
   DISALLOW_COPY_AND_ASSIGN(TestFrameNavigationObserver);
 };
diff --git a/content/public/test/test_utils.cc b/content/public/test/test_utils.cc
index 3673a95..f0aa1317 100644
--- a/content/public/test/test_utils.cc
+++ b/content/public/test/test_utils.cc
@@ -323,8 +323,8 @@
   if (child_thread_count_) {
     DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI));
     DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
-    runner_ = new MessageLoopRunner;
-    runner_->Run();
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
   }
   BrowserChildProcessObserver::Remove(this);
   RenderProcessHost::SetRunRendererInProcess(false);
@@ -340,8 +340,8 @@
   if (--child_thread_count_)
     return;
 
-  if (runner_.get())
-    runner_->Quit();
+  if (run_loop_)
+    run_loop_->Quit();
 }
 
 RenderFrameDeletedObserver::RenderFrameDeletedObserver(RenderFrameHost* rfh)
@@ -378,19 +378,19 @@
 
 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
     WebContents* web_contents)
-    : WebContentsObserver(web_contents),
-      message_loop_runner_(new MessageLoopRunner) {
+    : WebContentsObserver(web_contents) {
   EXPECT_TRUE(web_contents != NULL);
 }
 
-WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {}
+WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
+}
 
 void WebContentsDestroyedWatcher::Wait() {
-  message_loop_runner_->Run();
+  run_loop_.Run();
 }
 
 void WebContentsDestroyedWatcher::WebContentsDestroyed() {
-  message_loop_runner_->Quit();
+  run_loop_.Quit();
 }
 
 }  // namespace content
diff --git a/content/public/test/test_utils.h b/content/public/test/test_utils.h
index 70c1553..6a3ab90 100644
--- a/content/public/test/test_utils.h
+++ b/content/public/test/test_utils.h
@@ -262,7 +262,7 @@
       const ChildProcessData& data) override;
 
   int child_thread_count_;
-  scoped_refptr<MessageLoopRunner> runner_;
+  std::unique_ptr<base::RunLoop> run_loop_;
   std::unique_ptr<TestServiceManagerContext> shell_context_;
 
   DISALLOW_COPY_AND_ASSIGN(InProcessUtilityThreadHelper);
@@ -303,7 +303,7 @@
   // Overridden WebContentsObserver methods.
   void WebContentsDestroyed() override;
 
-  scoped_refptr<MessageLoopRunner> message_loop_runner_;
+  base::RunLoop run_loop_;
 
   DISALLOW_COPY_AND_ASSIGN(WebContentsDestroyedWatcher);
 };
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 533230c..3a0baf0 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -267,19 +267,6 @@
         binding_(this, std::move(preload_handle->url_loader_client_request)) {}
 
   ~NavigationPreloadRequest() override {
-    if (result_reported_)
-      return;
-    ServiceWorkerContextClient* client =
-        ServiceWorkerContextClient::ThreadSpecificInstance();
-    if (!client)
-      return;
-    client->OnNavigationPreloadError(
-        fetch_event_id_,
-        base::MakeUnique<blink::WebServiceWorkerError>(
-            blink::WebServiceWorkerError::ErrorTypeAbort,
-            blink::WebString::fromASCII(
-                "Service Worker navigation preload aborted. Need to guard with "
-                "respondWith or waitUntil.")));
   }
 
   void OnReceiveResponse(
@@ -297,8 +284,7 @@
 
   void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
                          const ResourceResponseHead& response_head) override {
-    // Cancel the request.
-    url_loader_ = nullptr;
+    // This will delete |this|.
     ReportErrorToClient(
         "Service Worker navigation preload doesn't suport redirect.");
   }
@@ -322,17 +308,22 @@
   }
 
   void OnComplete(const ResourceRequestCompletionStatus& status) override {
-    // We don't report to |client| if OnStartLoadingResponseBody() has already
-    // called OnNavigationPreloadResponse().
-    if (result_reported_)
+    if (status.error_code != net::OK) {
+      // This will delete |this|.
+      ReportErrorToClient("Service Worker navigation preload network error.");
       return;
-    DCHECK_NE(0, status.error_code);
-    ReportErrorToClient("Service Worker navigation preload network error.");
+    }
+
+    ServiceWorkerContextClient* client =
+        ServiceWorkerContextClient::ThreadSpecificInstance();
+    if (!client)
+      return;
+    // This will delete |this|.
+    client->OnNavigationPreloadComplete(fetch_event_id_);
   }
 
  private:
   void MaybeReportResponseToClient() {
-    DCHECK(!result_reported_);
     if (!response_ || !body_.is_valid())
       return;
     ServiceWorkerContextClient* client =
@@ -343,7 +334,6 @@
     client->OnNavigationPreloadResponse(
         fetch_event_id_, std::move(response_),
         base::MakeUnique<WebDataConsumerHandleImpl>(std::move(body_)));
-    result_reported_ = true;
   }
 
   void ReportErrorToClient(const char* error_message) {
@@ -351,11 +341,11 @@
         ServiceWorkerContextClient::ThreadSpecificInstance();
     if (!client)
       return;
+    // This will delete |this|.
     client->OnNavigationPreloadError(
         fetch_event_id_, base::MakeUnique<blink::WebServiceWorkerError>(
                              blink::WebServiceWorkerError::ErrorTypeNetwork,
                              blink::WebString::fromUTF8(error_message)));
-    result_reported_ = true;
   }
 
   const int fetch_event_id_;
@@ -365,7 +355,6 @@
 
   std::unique_ptr<blink::WebURLResponse> response_;
   mojo::ScopedDataPipeConsumerHandle body_;
-  bool result_reported_ = false;
 };
 
 ServiceWorkerContextClient*
@@ -709,12 +698,6 @@
     int fetch_event_id,
     blink::WebServiceWorkerEventResult result,
     double event_dispatch_time) {
-  if (context_->preload_requests.Lookup(fetch_event_id)) {
-    // Deletes NavigationPreloadRequest. If the network request is ongoing, it
-    // will be canceled by deleting the mojom::URLLoaderPtr in the
-    // NavigationPreloadRequest.
-    context_->preload_requests.Remove(fetch_event_id);
-  }
   const FetchCallback* callback =
       context_->fetch_event_callbacks.Lookup(fetch_event_id);
   DCHECK(callback);
@@ -1258,6 +1241,12 @@
     int fetch_event_id,
     std::unique_ptr<blink::WebServiceWorkerError> error) {
   proxy_->onNavigationPreloadError(fetch_event_id, std::move(error));
+  context_->preload_requests.Remove(fetch_event_id);
+}
+
+void ServiceWorkerContextClient::OnNavigationPreloadComplete(
+    int fetch_event_id) {
+  context_->preload_requests.Remove(fetch_event_id);
 }
 
 base::WeakPtr<ServiceWorkerContextClient>
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index c77c5ec5..69b4065 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -257,10 +257,17 @@
                            const base::string16& message);
   void OnPing();
 
+  // Called to resolve the FetchEvent.preloadResponse promise.
   void OnNavigationPreloadResponse(
       int fetch_event_id,
       std::unique_ptr<blink::WebURLResponse> response,
       std::unique_ptr<blink::WebDataConsumerHandle> data_consumer_handle);
+  // Called when the navigation preload request completed. Either
+  // OnNavigationPreloadComplete() or OnNavigationPreloadError() must be called
+  // to release the preload related resources.
+  void OnNavigationPreloadComplete(int fetch_event_id);
+  // Called when an error occurred while receiving the response of the
+  // navigation preload request.
   void OnNavigationPreloadError(
       int fetch_event_id,
       std::unique_ptr<blink::WebServiceWorkerError> error);
diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc
index f129a9d..1f0ac7bb 100644
--- a/content/test/content_browser_test_utils_internal.cc
+++ b/content/test/content_browser_test_utils_internal.cc
@@ -379,15 +379,13 @@
                                        ->delegate()
                                        ->GetAsWebContents()),
       frame_tree_node_id_(frame_tree_node->frame_tree_node_id()),
-      url_(url),
-      message_loop_runner_(
-          new MessageLoopRunner(MessageLoopRunner::QuitMode::IMMEDIATE)) {
+      url_(url) {
 }
 
 UrlCommitObserver::~UrlCommitObserver() {}
 
 void UrlCommitObserver::Wait() {
-  message_loop_runner_->Run();
+  run_loop_.Run();
 }
 
 void UrlCommitObserver::DidFinishNavigation(
@@ -396,7 +394,7 @@
       !navigation_handle->IsErrorPage() &&
       navigation_handle->GetURL() == url_ &&
       navigation_handle->GetFrameTreeNodeId() == frame_tree_node_id_) {
-    message_loop_runner_->Quit();
+    run_loop_.Quit();
   }
 }
 
diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h
index 4575586..bdcbb8d 100644
--- a/content/test/content_browser_test_utils_internal.h
+++ b/content/test/content_browser_test_utils_internal.h
@@ -17,6 +17,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
 #include "cc/surfaces/surface_id.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -31,7 +32,6 @@
 namespace content {
 
 class FrameTreeNode;
-class MessageLoopRunner;
 class RenderFrameHost;
 class RenderWidgetHostViewChildFrame;
 class Shell;
@@ -199,8 +199,8 @@
   // The URL this observer is expecting to be committed.
   GURL url_;
 
-  // The MessageLoopRunner used to spin the message loop.
-  scoped_refptr<MessageLoopRunner> message_loop_runner_;
+  // The RunLoop used to spin the message loop.
+  base::RunLoop run_loop_;
 
   DISALLOW_COPY_AND_ASSIGN(UrlCommitObserver);
 };
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 4804aa3..d6cd106 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -336,6 +336,7 @@
     "//net/tools/testserver/",
     "//third_party/pyftpdlib/",
     "//third_party/pywebsocket/",
+    "//third_party/skia/",
     "//third_party/tlslite/",
     "test/data/",
   ]
diff --git a/headless/lib/DEPS b/headless/lib/DEPS
new file mode 100644
index 0000000..54e541c3
--- /dev/null
+++ b/headless/lib/DEPS
@@ -0,0 +1,6 @@
+specific_include_rules = {
+  "headless_web_contents_browsertest.cc": [
+    "+third_party/skia/include",
+  ]
+}
+
diff --git a/headless/lib/headless_web_contents_browsertest.cc b/headless/lib/headless_web_contents_browsertest.cc
index 18b4295..03097ca1 100644
--- a/headless/lib/headless_web_contents_browsertest.cc
+++ b/headless/lib/headless_web_contents_browsertest.cc
@@ -6,8 +6,10 @@
 #include <string>
 #include <vector>
 
+#include "base/base64.h"
 #include "content/public/test/browser_test.h"
 #include "headless/public/devtools/domains/page.h"
+#include "headless/public/devtools/domains/runtime.h"
 #include "headless/public/devtools/domains/security.h"
 #include "headless/public/headless_browser.h"
 #include "headless/public/headless_devtools_client.h"
@@ -15,6 +17,9 @@
 #include "headless/test/headless_browser_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
 
@@ -56,10 +61,41 @@
             browser_context->GetAllWebContents().size());
 }
 
+namespace {
+bool DecodePNG(std::string base64_data, SkBitmap* bitmap) {
+  std::string png_data;
+  if (!base::Base64Decode(base64_data, &png_data))
+    return false;
+  return gfx::PNGCodec::Decode(
+      reinterpret_cast<unsigned const char*>(png_data.data()), png_data.size(),
+      bitmap);
+}
+}  // namespace
+
+// Parameter specifies whether --disable-gpu should be used.
 class HeadlessWebContentsScreenshotTest
-    : public HeadlessAsyncDevTooledBrowserTest {
+    : public HeadlessAsyncDevTooledBrowserTest,
+      public ::testing::WithParamInterface<bool> {
  public:
+  void SetUp() override {
+    EnablePixelOutput();
+    if (GetParam())
+      UseSoftwareCompositing();
+    HeadlessAsyncDevTooledBrowserTest::SetUp();
+  }
+
   void RunDevTooledTest() override {
+    std::unique_ptr<runtime::EvaluateParams> params =
+        runtime::EvaluateParams::Builder()
+            .SetExpression("document.body.style.background = '#0000ff'")
+            .Build();
+    devtools_client_->GetRuntime()->Evaluate(
+        std::move(params),
+        base::Bind(&HeadlessWebContentsScreenshotTest::OnPageSetupCompleted,
+                   base::Unretained(this)));
+  }
+
+  void OnPageSetupCompleted(std::unique_ptr<runtime::EvaluateResult> result) {
     devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot(
         page::CaptureScreenshotParams::Builder().Build(),
         base::Bind(&HeadlessWebContentsScreenshotTest::OnScreenshotCaptured,
@@ -68,12 +104,26 @@
 
   void OnScreenshotCaptured(
       std::unique_ptr<page::CaptureScreenshotResult> result) {
-    EXPECT_LT(0U, result->GetData().length());
+    std::string base64 = result->GetData();
+    EXPECT_LT(0U, base64.length());
+    SkBitmap result_bitmap;
+    EXPECT_TRUE(DecodePNG(base64, &result_bitmap));
+
+    EXPECT_EQ(800, result_bitmap.width());
+    EXPECT_EQ(600, result_bitmap.height());
+    SkColor actual_color = result_bitmap.getColor(400, 300);
+    SkColor expected_color = SkColorSetRGB(0x00, 0x00, 0xff);
+    EXPECT_EQ(expected_color, actual_color);
     FinishAsynchronousTest();
   }
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsScreenshotTest);
+HEADLESS_ASYNC_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotTest);
+
+// Instantiate test case for both software and gpu compositing modes.
+INSTANTIATE_TEST_CASE_P(HeadlessWebContentsScreenshotTests,
+                        HeadlessWebContentsScreenshotTest,
+                        ::testing::Bool());
 
 class HeadlessWebContentsSecurityTest
     : public HeadlessAsyncDevTooledBrowserTest,
diff --git a/headless/test/headless_browser_test.h b/headless/test/headless_browser_test.h
index 3db973b..1a258ce 100644
--- a/headless/test/headless_browser_test.h
+++ b/headless/test/headless_browser_test.h
@@ -94,6 +94,10 @@
   IN_PROC_BROWSER_TEST_F(TEST_FIXTURE_NAME, RunAsyncTest) { RunTest(); } \
   class AsyncHeadlessBrowserTestNeedsSemicolon##TEST_FIXTURE_NAME {}
 
+#define HEADLESS_ASYNC_DEVTOOLED_TEST_P(TEST_FIXTURE_NAME)               \
+  IN_PROC_BROWSER_TEST_P(TEST_FIXTURE_NAME, RunAsyncTest) { RunTest(); } \
+  class AsyncHeadlessBrowserTestNeedsSemicolon##TEST_FIXTURE_NAME {}
+
 // Base class for tests that require access to a DevToolsClient. Subclasses
 // should override the RunDevTooledTest() method, which is called asynchronously
 // when the DevToolsClient is ready.
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
index 42ebcf2..f4ca8cb 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
@@ -374,10 +374,10 @@
   UIView* emptyCollectionBackground = [[UIView alloc] initWithFrame:CGRectZero];
 
   NSString* rawText = nil;
-  if (IsIPadIdiom())
-    rawText = l10n_util::GetNSString(IDS_IOS_READING_LIST_EMPTY_MESSAGE_IPAD);
-  else
+  if (IsCompact())
     rawText = l10n_util::GetNSString(IDS_IOS_READING_LIST_EMPTY_MESSAGE_IPHONE);
+  else
+    rawText = l10n_util::GetNSString(IDS_IOS_READING_LIST_EMPTY_MESSAGE_IPAD);
 
   rawText = [[rawText stringByAppendingString:@"\n\n"]
       stringByAppendingString:l10n_util::GetNSString(
diff --git a/ios/chrome/browser/ui/tools_menu/tools_popup_controller.mm b/ios/chrome/browser/ui/tools_menu/tools_popup_controller.mm
index 837a52b..9a5a274 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_popup_controller.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_popup_controller.mm
@@ -222,7 +222,7 @@
       // Do nothing when tapping the tools menu a second time.
       break;
     case IDC_SHOW_READING_LIST:
-      // TODO(crbug.com/582957): Add metric here.
+      base::RecordAction(UserMetricsAction("MobileMenuReadingList"));
       break;
     default:
       NOTREACHED();
diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc
index 04636f3..8a969b1 100644
--- a/media/audio/audio_input_controller.cc
+++ b/media/audio/audio_input_controller.cc
@@ -334,10 +334,8 @@
       user_input_monitor, false));
 
   if (!controller->task_runner_->PostTask(
-          FROM_HERE,
-          base::Bind(&AudioInputController::DoCreateForStream,
-                     controller,
-                     stream))) {
+          FROM_HERE, base::Bind(&AudioInputController::DoCreateForStream,
+                                controller, stream, false))) {
     controller = nullptr;
   }
 
@@ -382,8 +380,10 @@
 
   // MakeAudioInputStream might fail and return nullptr. If so,
   // DoCreateForStream will handle and report it.
-  DoCreateForStream(audio_manager->MakeAudioInputStream(
-      params, device_id, base::Bind(&AudioInputController::LogMessage, this)));
+  auto* stream = audio_manager->MakeAudioInputStream(
+      params, device_id, base::Bind(&AudioInputController::LogMessage, this));
+  DoCreateForStream(stream,
+                    params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY);
 }
 
 void AudioInputController::DoCreateForLowLatency(AudioManager* audio_manager,
@@ -398,24 +398,30 @@
     log_silence_state_ = true;
 #endif
 
+  // Set the low latency timer to non-null. It'll be reset when capture starts.
   low_latency_create_time_ = base::TimeTicks::Now();
   DoCreate(audio_manager, params, device_id);
 }
 
 void AudioInputController::DoCreateForStream(
-    AudioInputStream* stream_to_control) {
+    AudioInputStream* stream_to_control,
+    bool low_latency) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(!stream_);
 
   if (!stream_to_control) {
-    LogCaptureStartupResult(CAPTURE_STARTUP_CREATE_STREAM_FAILED);
+    LogCaptureStartupResult(
+        low_latency ? CAPTURE_STARTUP_CREATE_LOW_LATENCY_STREAM_FAILED
+                    : CAPTURE_STARTUP_CREATE_STREAM_FAILED);
     handler_->OnError(this, STREAM_CREATE_ERROR);
     return;
   }
 
   if (!stream_to_control->Open()) {
     stream_to_control->Close();
-    LogCaptureStartupResult(CAPTURE_STARTUP_OPEN_STREAM_FAILED);
+    LogCaptureStartupResult(low_latency
+                                ? CAPTURE_STARTUP_OPEN_LOW_LATENCY_STREAM_FAILED
+                                : CAPTURE_STARTUP_OPEN_STREAM_FAILED);
     handler_->OnError(this, STREAM_OPEN_ERROR);
     return;
   }
@@ -453,6 +459,9 @@
     prev_key_down_count_ = user_input_monitor_->GetKeyPressCount();
   }
 
+  if (!low_latency_create_time_.is_null())
+    low_latency_create_time_ = base::TimeTicks::Now();
+
   audio_callback_.reset(new AudioCallback(this));
   stream_->Start(audio_callback_.get());
 }
@@ -469,15 +478,22 @@
     stream_->Stop();
 
     if (!low_latency_create_time_.is_null()) {
+      // Since the usage pattern of getUserMedia commonly is that a stream
+      // (and accompanied audio track) is created and immediately closed or
+      // discarded, we collect stats separately for short lived audio streams
+      // (500ms or less) that we did not receive a callback for, as
+      // 'stopped early' and 'never got data'.
+      const base::TimeDelta duration =
+          base::TimeTicks::Now() - low_latency_create_time_;
       LogCaptureStartupResult(audio_callback_->received_callback()
                                   ? CAPTURE_STARTUP_OK
-                                  : CAPTURE_STARTUP_NEVER_GOT_DATA);
+                                  : (duration.InMilliseconds() < 500
+                                         ? CAPTURE_STARTUP_STOPPED_EARLY
+                                         : CAPTURE_STARTUP_NEVER_GOT_DATA));
       UMA_HISTOGRAM_BOOLEAN("Media.Audio.Capture.CallbackError",
                             audio_callback_->error_during_callback());
       if (audio_callback_->received_callback()) {
         // Log the total duration (since DoCreate) and update the histogram.
-        const base::TimeDelta duration =
-            base::TimeTicks::Now() - low_latency_create_time_;
         UMA_HISTOGRAM_LONG_TIMES("Media.InputStreamDuration", duration);
         const std::string log_string = base::StringPrintf(
             "AIC::DoClose: stream duration=%" PRId64 " seconds",
@@ -508,7 +524,6 @@
     debug_writer_->Stop();
 
   max_volume_ = 0.0;
-  low_latency_create_time_ = base::TimeTicks();  // Reset to null.
   weak_ptr_factory_.InvalidateWeakPtrs();
 }
 
diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h
index cfd6592..d6af909e 100644
--- a/media/audio/audio_input_controller.h
+++ b/media/audio/audio_input_controller.h
@@ -235,7 +235,11 @@
     CAPTURE_STARTUP_CREATE_STREAM_FAILED = 2,
     CAPTURE_STARTUP_OPEN_STREAM_FAILED = 3,
     CAPTURE_STARTUP_NEVER_GOT_DATA = 4,
-    CAPTURE_STARTUP_RESULT_MAX = CAPTURE_STARTUP_NEVER_GOT_DATA
+    CAPTURE_STARTUP_STOPPED_EARLY = 5,
+    CAPTURE_STARTUP_CREATE_LOW_LATENCY_STREAM_FAILED = 6,
+    CAPTURE_STARTUP_OPEN_LOW_LATENCY_STREAM_FAILED = 7,
+    CAPTURE_STARTUP_RESULT_MAX =
+        CAPTURE_STARTUP_CREATE_LOW_LATENCY_STREAM_FAILED
   };
 
 #if defined(AUDIO_POWER_MONITORING)
@@ -281,7 +285,7 @@
   void DoCreateForLowLatency(AudioManager* audio_manager,
                              const AudioParameters& params,
                              const std::string& device_id);
-  void DoCreateForStream(AudioInputStream* stream_to_control);
+  void DoCreateForStream(AudioInputStream* stream_to_control, bool low_latency);
   void DoRecord();
   void DoClose();
   void DoReportError();
diff --git a/media/gpu/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2_video_decode_accelerator.cc
index 0df948c..50203b23 100644
--- a/media/gpu/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2_video_decode_accelerator.cc
@@ -927,6 +927,7 @@
       if (result == H264Parser::kEOStream) {
         // We've reached the end of the buffer before finding a frame boundary.
         decoder_partial_frame_pending_ = true;
+        *endpos = size;
         return true;
       }
       switch (nalu.nal_unit_type) {
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index aaf4cf1a..1262825b 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -386,8 +386,6 @@
 
 # These tests were timing out on the MSAN builder.
 crbug.com/671158 [ Linux ] external/wpt/encoding/textdecoder-labels.html [ Slow ]
-crbug.com/671158 [ Linux ] http/tests/streams/writable-streams/write.https.html [ Slow ]
-crbug.com/671158 [ Linux ] virtual/mojo-loading/http/tests/streams/writable-streams/write.https.html [ Slow ]
 
 # Usually plenty fast, but sometimes very slow on windows.
 crbug.com/678498 [ Win ] http/tests/serviceworker/foreign-fetch-cors.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations
index 7d321a9..30235da 100644
--- a/third_party/WebKit/LayoutTests/W3CImportExpectations
+++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -308,7 +308,8 @@
 external/wpt/resources/webidl2/test [ Skip ]
 external/wpt/screen-orientation [ Skip ]
 external/wpt/secure-contexts [ Skip ]
-external/wpt/selection [ Skip ]
+## Owners: dom-dev@chromium.org
+# external/wpt/selection [ Pass ]
 external/wpt/selectors [ Skip ]
 external/wpt/selectors-api [ Skip ]
 external/wpt/serve [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/fast/html/summary-invisible-click.html b/third_party/WebKit/LayoutTests/fast/html/summary-invisible-click.html
new file mode 100644
index 0000000..3889288
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/html/summary-invisible-click.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+
+<details style="display:none">
+<summary>Click me</summary>
+Blah, blah, ...
+</detail>
+
+<script>
+test(() => {
+  const summary = document.querySelector('summary');
+  const details = document.querySelector('details');
+  assert_false(details.open);
+  summary.click();
+  assert_true(details.open);
+  summary.click();
+  assert_false(details.open);
+}, 'click() on hidden SUMMARY should toggle DETAILS.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture-failed.html b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture-failed.html
new file mode 100644
index 0000000..65317e0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture-failed.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+The top navigation from this iframe should be blocked. This text should appear.
+<script>
+window.onload = function()
+{
+    try {
+        top.location = "http://localhost:8000/security/frameNavigation/resources/navigation-changed-iframe.html";
+        top.postMessage("FAIL", "*");
+    } catch(e) {
+        top.postMessage("PASS", "*");
+    }
+}
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-ALLOWED-top-navigation-with-user-gesture-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-ALLOWED-top-navigation-with-user-gesture-expected.txt
new file mode 100644
index 0000000..9968bd11
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-ALLOWED-top-navigation-with-user-gesture-expected.txt
@@ -0,0 +1,3 @@
+localhost
+
+PASSED: Navigation succeeded.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-ALLOWED-top-navigation-with-user-gesture.html b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-ALLOWED-top-navigation-with-user-gesture.html
new file mode 100644
index 0000000..375c979
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-ALLOWED-top-navigation-with-user-gesture.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+    <style>
+        iframe { width: 400px; height: 200px;}
+    </style>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        function loaded()
+        {
+            document.getElementsByTagName('h4')[0].innerHTML = document.domain;
+            var iframe = document.getElementById("i");
+            // The iframe uses eventSender to emulate a user navigatation, which requires absolute coordinates.
+            // Because the iframe is cross-origin, it can't get the offsets itself, so leak them.
+            frames[0].postMessage({x: iframe.offsetLeft, y: iframe.offsetTop}, "*");
+        }
+    </script>
+</head>
+<body onload="loaded();">
+    <p>This tests that an iframe in sandbox with 'allow-top-navigation-with-user-activation'
+    can navigate the top level page, if it is trigged by a user gesture.</p>
+    <h4>DOMAIN</h4>
+    <iframe id="i" sandbox="allow-scripts allow-top-navigation-with-user-activation" src="http://localhost:8000/security/frameNavigation/resources/iframe-that-performs-parent-navigation.html"></iframe>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-DENIED-top-navigation-without-user-gesture-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-DENIED-top-navigation-without-user-gesture-expected.txt
new file mode 100644
index 0000000..530cfaad
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-DENIED-top-navigation-without-user-gesture-expected.txt
@@ -0,0 +1,10 @@
+CONSOLE ERROR: line 8: Unsafe JavaScript attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/frameNavigation/sandbox-DENIED-top-navigation-without-user-gesture.html' from frame with URL 'http://localhost:8000/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture-failed.html'. The frame attempting navigation of the top-level window is sandboxed with the 'allow-top-navigation-with-user-activation' flag, but has no user activation (aka gesture). See https://www.chromestatus.com/feature/5629582019395584.
+
+This tests that an iframe in sandbox with 'allow-top-navigation-with-user-activation' cannot navigate its top level page, if it is not trigged by a user gesture.
+
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+The top navigation from this iframe should be blocked. This text should appear.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-DENIED-top-navigation-without-user-gesture.html b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-DENIED-top-navigation-without-user-gesture.html
new file mode 100644
index 0000000..071fdc71
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/frameNavigation/sandbox-DENIED-top-navigation-without-user-gesture.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+    testRunner.setDumpConsoleMessages(true);
+    testRunner.waitUntilDone();
+}
+
+window.addEventListener("message", e => {
+  if (e.data == "PASS")
+    testRunner.notifyDone();
+  else
+    testRunner.testFailed("'top.location' didn't throw.");
+});
+</script>
+</head>
+<body>
+    <p>This tests that an iframe in sandbox with 'allow-top-navigation-with-user-activation'
+    cannot navigate its top level page, if it is not trigged by a user gesture.</p>
+    <iframe sandbox='allow-top-navigation-with-user-activation allow-scripts' src="http://localhost:8000/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture-failed.html"></iframe>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/broken-chunked-encoding.html b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/broken-chunked-encoding.html
new file mode 100644
index 0000000..126a15f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/broken-chunked-encoding.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload with chunked encoding</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.js"></script>
+<script>
+promise_test(t => {
+    var script = 'resources/broken-chunked-encoding-worker.js';
+    var scope = 'resources/broken-chunked-encoding-scope.php';
+    return service_worker_unregister_and_register(t, script, scope)
+      .then(registration => {
+          add_completion_callback(_ => registration.unregister());
+          var worker = registration.installing;
+          return wait_for_state(t, worker, 'activated');
+        })
+      .then(_ => with_iframe(scope))
+      .then(frame => {
+          assert_equals(
+            frame.contentDocument.body.textContent,
+            'Done');
+        });
+  }, 'Navigation Preload with broken chunked encoding must fail.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/chunked-encoding.html b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/chunked-encoding.html
new file mode 100644
index 0000000..e071a0d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/chunked-encoding.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload with chunked encoding</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.js"></script>
+<script>
+promise_test(t => {
+    var script = 'resources/chunked-encoding-worker.js';
+    var scope = 'resources/chunked-encoding-scope.php';
+    return service_worker_unregister_and_register(t, script, scope)
+      .then(registration => {
+          add_completion_callback(_ => registration.unregister());
+          var worker = registration.installing;
+          return wait_for_state(t, worker, 'activated');
+        })
+      .then(_ => with_iframe(scope))
+      .then(frame => {
+          assert_equals(
+            frame.contentDocument.body.textContent,
+            '0123456789');
+        });
+  }, 'Navigation Preload must work with chunked encoding.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/broken-chunked-encoding-scope.php b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/broken-chunked-encoding-scope.php
new file mode 100644
index 0000000..c22c38aa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/broken-chunked-encoding-scope.php
@@ -0,0 +1,5 @@
+<?php
+  header("Content-type: text/html; charset=UTF-8");
+  header("Transfer-encoding: chunked");
+  echo "hello\nworld\n";
+?>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/broken-chunked-encoding-worker.js b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/broken-chunked-encoding-worker.js
new file mode 100644
index 0000000..27268e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/broken-chunked-encoding-worker.js
@@ -0,0 +1,11 @@
+self.addEventListener('activate', event => {
+    event.waitUntil(
+        self.registration.navigationPreload.enable());
+  });
+
+self.addEventListener('fetch', event => {
+    event.respondWith(event.preloadResponse
+      .then(
+        _ => new Response('Fail: got a response'),
+        _ => new Response('Done')));
+  });
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/chunked-encoding-scope.php b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/chunked-encoding-scope.php
new file mode 100644
index 0000000..fae4a8d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/chunked-encoding-scope.php
@@ -0,0 +1,17 @@
+<?php
+  function put_chunk($txt) {
+    echo sprintf("%x\r\n", strlen($txt));
+    echo "$txt\r\n";
+  }
+  header("Content-type: text/html; charset=UTF-8");
+  header("Transfer-encoding: chunked");
+  for ($i = 0; $i < 10; $i++) {
+    // Call ob_flush(), flush() and usleep() to send the response as multiple
+    // chunks.
+    ob_flush();
+    flush();
+    usleep(1000);
+    put_chunk("$i");
+  }
+  echo "0\r\n\r\n";
+?>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/chunked-encoding-worker.js b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/chunked-encoding-worker.js
new file mode 100644
index 0000000..f30e5ed2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/navigation-preload/resources/chunked-encoding-worker.js
@@ -0,0 +1,8 @@
+self.addEventListener('activate', event => {
+    event.waitUntil(
+        self.registration.navigationPreload.enable());
+  });
+
+self.addEventListener('fetch', event => {
+    event.respondWith(event.preloadResponse);
+  });
diff --git a/third_party/WebKit/LayoutTests/http/tests/streams/writable-streams/write.js b/third_party/WebKit/LayoutTests/http/tests/streams/writable-streams/write.js
index f341e84..a754b51f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/streams/writable-streams/write.js
+++ b/third_party/WebKit/LayoutTests/http/tests/streams/writable-streams/write.js
@@ -165,7 +165,7 @@
 }, 'when sink\'s write throws an error, the stream should become errored and the promise should reject');
 
 promise_test(() => {
-  const numberOfWrites = 10000;
+  const numberOfWrites = 1000;
 
   let resolveFirstWritePromise;
   let writeCount = 0;
diff --git a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md
index a1de374..9d5dd81 100644
--- a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md
+++ b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md
@@ -788,20 +788,6 @@
 generate bindings for the callback function. The binding layer uses a
 `ScriptValue` instead.
 
-### [CreateEventEnabled] _(i, m, a, c)_
-
-Summary: Like `[RuntimeEnabled]`, it controls at runtime whether bindings are exposed, but only affects createEvent support. See crbug.com/392584#c22.
-
-Usage: `[CreateEventEnabled=FeatureName]`. FeatureName must be included in [RuntimeEnabledFeatures.in](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in) like RuntimeEnabled.
-
-```webidl
-[
-    CreateEventEnabled=TouchEventFeatureDetection
-] interface TouchEvent : UIEvent { ... };
-```
-
-The feature is enabled at runtime and the createEvent operation will throw exception on the web if disabled. It's worth noting the event constructor (modern way of creating events) still works, so this is used only for very special case legacy compat issues (probably nothing ever other than TouchEvent).
-
 #### [Custom=PropertyQuery|PropertyEnumerator] _(s)_
 
 Summary: `[Custom=PropertyEnumerator]` allows you to write custom bindings for the case where properties of a given interface are enumerated; a custom named enumerator. There is currently only one use, and in that case it is used with `[Custom=PropertyQuery]`, since the query is also custom.
diff --git a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt
index 0e46e6f..faae80f 100644
--- a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt
+++ b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt
@@ -42,7 +42,6 @@
 # FIXME: remove [ConstructorCallWith=Document], as can instead use
 # [ConstructorCallWith=ExecutionContext] + toDocument(executionContext)
 ConstructorCallWith=ExecutionContext|ScriptState|Document
-CreateEventEnabled=*
 CrossOrigin=|Getter|Setter
 Custom=|Getter|Setter|LegacyCallAsFunction|VisitDOMWrapper|PropertyGetter|PropertyEnumerator|PropertyQuery|CallPrologue|CallEpilogue
 CustomConstructor
diff --git a/third_party/WebKit/Source/build/scripts/make_event_factory.py b/third_party/WebKit/Source/build/scripts/make_event_factory.py
index 5d78cf3..370452a 100755
--- a/third_party/WebKit/Source/build/scripts/make_event_factory.py
+++ b/third_party/WebKit/Source/build/scripts/make_event_factory.py
@@ -104,7 +104,6 @@
 
 class EventFactoryWriter(in_generator.Writer):
     defaults = {
-        'CreateEventEnabled': None,
         'ImplementedAs': None,
         'RuntimeEnabled': None,
     }
@@ -126,14 +125,25 @@
         super(EventFactoryWriter, self).__init__(in_file_path)
         self.namespace = self.in_file.parameters['namespace'].strip('"')
         self.suffix = self.in_file.parameters['suffix'].strip('"')
-        # Set CreateEventEnabled if feature is RuntimeEnabled (see crbug.com/332588).
-        for entry in self.in_file.name_dictionaries:
-            if 'RuntimeEnabled' in entry and 'CreateEventEnabled' not in entry:
-                entry['CreateEventEnabled'] = entry['RuntimeEnabled']
+        self._validate_entries()
         self._outputs = {(self.namespace + self.suffix + "Headers.h"): self.generate_headers_header,
                          (self.namespace + self.suffix + ".cpp"): self.generate_implementation,
                         }
 
+    def _validate_entries(self):
+        # If there is more than one entry with the same script name, only the first one will ever
+        # be hit in practice, and so we'll silently ignore any properties requested for the second
+        # (like RuntimeEnabled - see crbug.com/332588).
+        entries_by_script_name = dict()
+        for entry in self.in_file.name_dictionaries:
+            script_name = name_utilities.script_name(entry)
+            if script_name in entries_by_script_name:
+                self._fatal('Multiple entries with script_name=%(script_name)s: %(name1)s %(name2)s' % {
+                    'script_name': script_name,
+                    'name1': entry['name'],
+                    'name2': entries_by_script_name[script_name]['name']})
+            entries_by_script_name[script_name] = entry
+
     def _fatal(self, message):
         print 'FATAL ERROR: ' + message
         exit(1)
diff --git a/third_party/WebKit/Source/build/scripts/templates/EventFactory.cpp.tmpl b/third_party/WebKit/Source/build/scripts/templates/EventFactory.cpp.tmpl
index 27606444..64d27433 100644
--- a/third_party/WebKit/Source/build/scripts/templates/EventFactory.cpp.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/EventFactory.cpp.tmpl
@@ -16,9 +16,9 @@
 {{namespace}}* {{namespace}}{{suffix}}Factory::create(ExecutionContext* executionContext, const String& type) {
   {% for event in events if event|script_name|create_event_whitelist or event|script_name|create_event_legacy_whitelist %}
   {% if event|script_name|create_event_whitelist %}
-  if (equalIgnoringCase(type, "{{event|script_name}}"){% if event.CreateEventEnabled %} && RuntimeEnabledFeatures::{{event.CreateEventEnabled|lower_first}}(){% endif %}) {
+  if (equalIgnoringCase(type, "{{event|script_name}}"){% if event.RuntimeEnabled %} && RuntimeEnabledFeatures::{{event.RuntimeEnabled|lower_first}}(){% endif %}) {
   {% else %}
-  if (type == "{{event|script_name}}"{% if event.CreateEventEnabled %} && RuntimeEnabledFeatures::{{event.CreateEventEnabled|lower_first}}(){% endif %}) {
+  if (type == "{{event|script_name}}"{% if event.RuntimeEnabled %} && RuntimeEnabledFeatures::{{event.RuntimeEnabled|lower_first}}(){% endif %}) {
   {% endif %}
     {% if not event|script_name|create_event_whitelist %}
     UseCounter::count(executionContext, UseCounter::{{event|script_name|measure_name}});
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index b2f3959..f646882 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1065,6 +1065,7 @@
     "dom/ElementVisibilityObserverTest.cpp",
     "dom/ExecutionContextTaskTest.cpp",
     "dom/IdleDeadlineTest.cpp",
+    "dom/ModulatorTest.cpp",
     "dom/MutationObserverTest.cpp",
     "dom/NodeTest.cpp",
     "dom/NthIndexCacheTest.cpp",
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index 9b6ccc4..eeeb3df 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -349,10 +349,12 @@
     "properties/CSSPropertyAPIClip.cpp",
     "properties/CSSPropertyAPIColumnGap.cpp",
     "properties/CSSPropertyAPIContain.cpp",
+    "properties/CSSPropertyAPICursor.cpp",
     "properties/CSSPropertyAPIFlexBasis.cpp",
     "properties/CSSPropertyAPIFontSizeAdjust.cpp",
     "properties/CSSPropertyAPIFontVariantLigatures.cpp",
     "properties/CSSPropertyAPIFontVariationSettings.cpp",
+    "properties/CSSPropertyAPILetterAndWordSpacing.cpp",
     "properties/CSSPropertyAPIOffsetPosition.cpp",
     "properties/CSSPropertyAPIOutlineColor.cpp",
     "properties/CSSPropertyAPIOutlineOffset.cpp",
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.in b/third_party/WebKit/Source/core/css/CSSProperties.in
index bf897af..6f45c528 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.in
+++ b/third_party/WebKit/Source/core/css/CSSProperties.in
@@ -250,7 +250,7 @@
 content custom_all, typedom_types=[Image], repeated
 counter-increment custom_all
 counter-reset custom_all
-cursor inherited, custom_all
+cursor inherited, custom_all, api_class
 cx interpolable, svg, converter=convertLength
 cy interpolable, svg, converter=convertLength
 d interpolable, svg, converter=convertPathOrNone
@@ -290,7 +290,7 @@
 justify-items runtime_flag=CSSGridLayout, initial=initialSelfAlignment, converter=convertSelfOrDefaultAlignmentData
 justify-self runtime_flag=CSSGridLayout, initial=initialSelfAlignment, converter=convertSelfOrDefaultAlignmentData
 left typedom_types=[Length], keywords=[auto], supports_percentage, interpolable, initial=initialOffset, converter=convertLengthOrAuto
-letter-spacing interpolable, inherited, initial=initialLetterWordSpacing, converter=convertSpacing
+letter-spacing interpolable, inherited, initial=initialLetterWordSpacing, converter=convertSpacing, api_class=CSSPropertyAPILetterAndWordSpacing
 lighting-color interpolable, svg, converter=convertColor
 line-height interpolable, inherited, getter=specifiedLineHeight, converter=convertLineHeight
 list-style-image interpolable, inherited, custom_value, typedom_types=[Image]
@@ -475,7 +475,7 @@
 width interpolable, initial=initialSize, converter=convertLengthSizing, typedom_types=[Length], keywords=[auto], supports_percentage
 will-change custom_all, api_class
 word-break inherited
-word-spacing interpolable, inherited, initial=initialLetterWordSpacing, converter=convertSpacing
+word-spacing interpolable, inherited, initial=initialLetterWordSpacing, converter=convertSpacing, api_class=CSSPropertyAPILetterAndWordSpacing
 // UAs must treat 'word-wrap' as an alternate name for the 'overflow-wrap' property. So using the same handlers.
 word-wrap inherited, name_for_methods=OverflowWrap
 z-index interpolable, type_name=int, custom_all, api_class
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index 175d52f..a0887600 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -516,15 +516,6 @@
   return list;
 }
 
-static CSSValue* consumeSpacing(CSSParserTokenRange& range,
-                                CSSParserMode cssParserMode) {
-  if (range.peek().id() == CSSValueNormal)
-    return consumeIdent(range);
-  // TODO(timloh): allow <percentage>s in word-spacing.
-  return consumeLength(range, cssParserMode, ValueRangeAll,
-                       UnitlessQuirk::Allow);
-}
-
 static CSSValue* consumeFontSize(
     CSSParserTokenRange& range,
     CSSParserMode cssParserMode,
@@ -1484,58 +1475,6 @@
                                 UnitlessQuirk::Forbid);
 }
 
-static CSSValue* consumeCursor(CSSParserTokenRange& range,
-                               const CSSParserContext* context,
-                               bool inQuirksMode) {
-  CSSValueList* list = nullptr;
-  while (CSSValue* image = consumeImage(range, context,
-                                        ConsumeGeneratedImagePolicy::Forbid)) {
-    double num;
-    IntPoint hotSpot(-1, -1);
-    bool hotSpotSpecified = false;
-    if (consumeNumberRaw(range, num)) {
-      hotSpot.setX(int(num));
-      if (!consumeNumberRaw(range, num))
-        return nullptr;
-      hotSpot.setY(int(num));
-      hotSpotSpecified = true;
-    }
-
-    if (!list)
-      list = CSSValueList::createCommaSeparated();
-
-    list->append(
-        *CSSCursorImageValue::create(*image, hotSpotSpecified, hotSpot));
-    if (!consumeCommaIncludingWhitespace(range))
-      return nullptr;
-  }
-
-  CSSValueID id = range.peek().id();
-  if (!range.atEnd() && context->isUseCounterRecordingEnabled()) {
-    if (id == CSSValueWebkitZoomIn)
-      context->useCounter()->count(UseCounter::PrefixedCursorZoomIn);
-    else if (id == CSSValueWebkitZoomOut)
-      context->useCounter()->count(UseCounter::PrefixedCursorZoomOut);
-  }
-  CSSValue* cursorType = nullptr;
-  if (id == CSSValueHand) {
-    if (!inQuirksMode)  // Non-standard behavior
-      return nullptr;
-    cursorType = CSSIdentifierValue::create(CSSValuePointer);
-    range.consumeIncludingWhitespace();
-  } else if ((id >= CSSValueAuto && id <= CSSValueWebkitZoomOut) ||
-             id == CSSValueCopy || id == CSSValueNone) {
-    cursorType = consumeIdent(range);
-  } else {
-    return nullptr;
-  }
-
-  if (!list)
-    return cursorType;
-  list->append(*cursorType);
-  return list;
-}
-
 static CSSValue* consumeAttr(CSSParserTokenRange args,
                              const CSSParserContext* context) {
   if (args.peek().type() != IdentToken)
@@ -2804,9 +2743,6 @@
       return consumeFontFamily(m_range);
     case CSSPropertyFontWeight:
       return consumeFontWeight(m_range);
-    case CSSPropertyLetterSpacing:
-    case CSSPropertyWordSpacing:
-      return consumeSpacing(m_range, m_context->mode());
     case CSSPropertyFontSize:
       return consumeFontSize(m_range, m_context->mode(), UnitlessQuirk::Allow);
     case CSSPropertyLineHeight:
@@ -3027,8 +2963,6 @@
     case CSSPropertyRx:
     case CSSPropertyRy:
       return consumeRxOrRy(m_range);
-    case CSSPropertyCursor:
-      return consumeCursor(m_range, m_context, inQuirksMode());
     case CSSPropertyContent:
       return consumeContent(m_range, m_context);
     case CSSPropertyListStyleImage:
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPICursor.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPICursor.cpp
new file mode 100644
index 0000000..918f758
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPICursor.cpp
@@ -0,0 +1,70 @@
+// 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/css/properties/CSSPropertyAPICursor.h"
+
+#include "core/css/CSSCursorImageValue.h"
+#include "core/css/CSSValueList.h"
+#include "core/css/parser/CSSParserContext.h"
+#include "core/css/parser/CSSParserMode.h"
+#include "core/css/parser/CSSPropertyParserHelpers.h"
+#include "core/frame/UseCounter.h"
+
+namespace blink {
+
+const CSSValue* CSSPropertyAPICursor::parseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext* context) {
+  bool inQuirksMode = isQuirksModeBehavior(context->mode());
+  CSSValueList* list = nullptr;
+  while (CSSValue* image = CSSPropertyParserHelpers::consumeImage(
+             range, context,
+             CSSPropertyParserHelpers::ConsumeGeneratedImagePolicy::Forbid)) {
+    double num;
+    IntPoint hotSpot(-1, -1);
+    bool hotSpotSpecified = false;
+    if (CSSPropertyParserHelpers::consumeNumberRaw(range, num)) {
+      hotSpot.setX(int(num));
+      if (!CSSPropertyParserHelpers::consumeNumberRaw(range, num))
+        return nullptr;
+      hotSpot.setY(int(num));
+      hotSpotSpecified = true;
+    }
+
+    if (!list)
+      list = CSSValueList::createCommaSeparated();
+
+    list->append(
+        *CSSCursorImageValue::create(*image, hotSpotSpecified, hotSpot));
+    if (!CSSPropertyParserHelpers::consumeCommaIncludingWhitespace(range))
+      return nullptr;
+  }
+
+  CSSValueID id = range.peek().id();
+  if (!range.atEnd() && context->isUseCounterRecordingEnabled()) {
+    if (id == CSSValueWebkitZoomIn)
+      context->useCounter()->count(UseCounter::PrefixedCursorZoomIn);
+    else if (id == CSSValueWebkitZoomOut)
+      context->useCounter()->count(UseCounter::PrefixedCursorZoomOut);
+  }
+  CSSValue* cursorType = nullptr;
+  if (id == CSSValueHand) {
+    if (!inQuirksMode)  // Non-standard behavior
+      return nullptr;
+    cursorType = CSSIdentifierValue::create(CSSValuePointer);
+    range.consumeIncludingWhitespace();
+  } else if ((id >= CSSValueAuto && id <= CSSValueWebkitZoomOut) ||
+             id == CSSValueCopy || id == CSSValueNone) {
+    cursorType = CSSPropertyParserHelpers::consumeIdent(range);
+  } else {
+    return nullptr;
+  }
+
+  if (!list)
+    return cursorType;
+  list->append(*cursorType);
+  return list;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPILetterAndWordSpacing.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPILetterAndWordSpacing.cpp
new file mode 100644
index 0000000..e70a171f
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPILetterAndWordSpacing.cpp
@@ -0,0 +1,23 @@
+// 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/css/properties/CSSPropertyAPILetterAndWordSpacing.h"
+
+#include "core/css/parser/CSSParserContext.h"
+#include "core/css/parser/CSSPropertyParserHelpers.h"
+
+namespace blink {
+
+const CSSValue* CSSPropertyAPILetterAndWordSpacing::parseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext* context) {
+  if (range.peek().id() == CSSValueNormal)
+    return CSSPropertyParserHelpers::consumeIdent(range);
+  // TODO(timloh): allow <percentage>s in word-spacing.
+  return CSSPropertyParserHelpers::consumeLength(
+      range, context->mode(), ValueRangeAll,
+      CSSPropertyParserHelpers::UnitlessQuirk::Allow);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/Modulator.cpp b/third_party/WebKit/Source/core/dom/Modulator.cpp
index c523d83..58dc787 100644
--- a/third_party/WebKit/Source/core/dom/Modulator.cpp
+++ b/third_party/WebKit/Source/core/dom/Modulator.cpp
@@ -16,4 +16,30 @@
   return scriptState->perContextData()->modulator();
 }
 
+KURL Modulator::resolveModuleSpecifier(const String& moduleRequest,
+                                       const KURL& baseURL) {
+  // Step 1. Apply the URL parser to specifier. If the result is not failure,
+  // return the result.
+  KURL url(KURL(), moduleRequest);
+  if (url.isValid())
+    return url;
+
+  // Step 2. If specifier does not start with the character U+002F SOLIDUS (/),
+  // the two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), or the
+  // three-character sequence U+002E FULL STOP, U+002E FULL STOP, U+002F SOLIDUS
+  // (../), return failure and abort these steps.
+  if (!moduleRequest.startsWith("/") && !moduleRequest.startsWith("./") &&
+      !moduleRequest.startsWith("../"))
+    return KURL();
+
+  // Step 3. Return the result of applying the URL parser to specifier with
+  // script's base URL as the base URL.
+  DCHECK(baseURL.isValid());
+  KURL absoluteURL(baseURL, moduleRequest);
+  if (absoluteURL.isValid())
+    return absoluteURL;
+
+  return KURL();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/Modulator.h b/third_party/WebKit/Source/core/dom/Modulator.h
index 1abe03e..6f923196 100644
--- a/third_party/WebKit/Source/core/dom/Modulator.h
+++ b/third_party/WebKit/Source/core/dom/Modulator.h
@@ -7,6 +7,7 @@
 
 #include "core/CoreExport.h"
 #include "platform/heap/Handle.h"
+#include "platform/weborigin/KURL.h"
 
 namespace blink {
 
@@ -20,6 +21,10 @@
 class CORE_EXPORT Modulator : public GarbageCollectedMixin {
  public:
   static Modulator* from(LocalFrame*);
+
+  // https://html.spec.whatwg.org/#resolve-a-module-specifier
+  static KURL resolveModuleSpecifier(const String& moduleRequest,
+                                     const KURL& baseURL);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/ModulatorTest.cpp b/third_party/WebKit/Source/core/dom/ModulatorTest.cpp
new file mode 100644
index 0000000..698b201
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/ModulatorTest.cpp
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/dom/Modulator.h"
+
+#include "platform/testing/TestingPlatformSupport.h"
+#include "public/platform/Platform.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(ModulatorTest, resolveModuleSpecifier) {
+  // Taken from examples listed in
+  // https://html.spec.whatwg.org/#resolve-a-module-specifier
+
+  // "The following are valid module specifiers according to the above
+  // algorithm:"
+  EXPECT_TRUE(
+      Modulator::resolveModuleSpecifier("https://example.com/apples.js", KURL())
+          .isValid());
+
+  KURL resolved =
+      Modulator::resolveModuleSpecifier("http:example.com\\pears.mjs", KURL());
+  EXPECT_TRUE(resolved.isValid());
+  EXPECT_EQ("http://example.com/pears.mjs", resolved.getString());
+
+  KURL baseURL(KURL(), "https://example.com");
+  EXPECT_TRUE(
+      Modulator::resolveModuleSpecifier("//example.com/", baseURL).isValid());
+  EXPECT_TRUE(
+      Modulator::resolveModuleSpecifier("./strawberries.js.cgi", baseURL)
+          .isValid());
+  EXPECT_TRUE(
+      Modulator::resolveModuleSpecifier("../lychees", baseURL).isValid());
+  EXPECT_TRUE(
+      Modulator::resolveModuleSpecifier("/limes.jsx", baseURL).isValid());
+  EXPECT_TRUE(Modulator::resolveModuleSpecifier(
+                  "data:text/javascript,export default 'grapes';", KURL())
+                  .isValid());
+  EXPECT_TRUE(
+      Modulator::resolveModuleSpecifier(
+          "blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f",
+          KURL())
+          .isValid());
+
+  // "The following are valid module specifiers according to the above
+  // algorithm, but will invariably cause failures when they are fetched:"
+  EXPECT_TRUE(Modulator::resolveModuleSpecifier(
+                  "javascript:export default 'artichokes';", KURL())
+                  .isValid());
+  EXPECT_TRUE(Modulator::resolveModuleSpecifier(
+                  "data:text/plain,export default 'kale';", KURL())
+                  .isValid());
+  EXPECT_TRUE(
+      Modulator::resolveModuleSpecifier("about:legumes", KURL()).isValid());
+  EXPECT_TRUE(
+      Modulator::resolveModuleSpecifier("wss://example.com/celery", KURL())
+          .isValid());
+
+  // "The following are not valid module specifiers according to the above
+  // algorithm:"
+  EXPECT_FALSE(
+      Modulator::resolveModuleSpecifier("https://f:b/c", KURL()).isValid());
+  EXPECT_FALSE(
+      Modulator::resolveModuleSpecifier("pumpkins.js", KURL()).isValid());
+
+  // Unprefixed module specifiers should still fail w/ a valid baseURL.
+  EXPECT_FALSE(
+      Modulator::resolveModuleSpecifier("avocados.js", baseURL).isValid());
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/SandboxFlags.cpp b/third_party/WebKit/Source/core/dom/SandboxFlags.cpp
index c4b7c8b..4b92e66 100644
--- a/third_party/WebKit/Source/core/dom/SandboxFlags.cpp
+++ b/third_party/WebKit/Source/core/dom/SandboxFlags.cpp
@@ -27,6 +27,7 @@
 #include "core/dom/SandboxFlags.h"
 
 #include "core/html/parser/HTMLParserIdioms.h"
+#include "platform/RuntimeEnabledFeatures.h"
 #include "wtf/text/StringBuilder.h"
 
 namespace blink {
@@ -65,6 +66,11 @@
       flags &= ~SandboxModals;
     } else if (equalIgnoringCase(sandboxToken, "allow-presentation")) {
       flags &= ~SandboxPresentation;
+    } else if (equalIgnoringCase(sandboxToken,
+                                 "allow-top-navigation-with-user-activation") &&
+               RuntimeEnabledFeatures::
+                   topNavWithUserActivationInSandboxEnabled()) {
+      flags &= ~SandboxTopNavigationWithUserActivation;
     } else {
       if (numberOfTokenErrors)
         tokenErrors.append(", '");
diff --git a/third_party/WebKit/Source/core/dom/SandboxFlags.h b/third_party/WebKit/Source/core/dom/SandboxFlags.h
index c5ba93b8..538bd3e 100644
--- a/third_party/WebKit/Source/core/dom/SandboxFlags.h
+++ b/third_party/WebKit/Source/core/dom/SandboxFlags.h
@@ -55,6 +55,8 @@
   // See
   // https://w3c.github.io/presentation-api/#sandboxing-and-the-allow-presentation-keyword
   SandboxPresentation = 1 << 13,
+  // See https://github.com/WICG/interventions/issues/42.
+  SandboxTopNavigationWithUserActivation = 1 << 14,
   SandboxAll = -1  // Mask with all bits set to 1.
 };
 
diff --git a/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp b/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp
index 2ae71ef..36b17fac 100644
--- a/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp
@@ -43,41 +43,34 @@
       : m_document(Document::create()),
         m_element(m_document->createElement("foo")) {}
 
-  void TearDown() override { m_scriptRunner.release(); }
-
- protected:
-  void initialize() {
+  void SetUp() override {
     // We have to create ScriptRunner after initializing platform, because we
     // need Platform::current()->currentThread()->scheduler()->
     // loadingTaskRunner() to be initialized before creating ScriptRunner to
     // save it in constructor.
     m_scriptRunner = ScriptRunner::create(m_document.get());
   }
+  void TearDown() override { m_scriptRunner.release(); }
 
+ protected:
   Persistent<Document> m_document;
   Persistent<Element> m_element;
   Persistent<ScriptRunner> m_scriptRunner;
   WTF::Vector<int> m_order;
+  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+      m_platform;
 };
 
 TEST_F(ScriptRunnerTest, QueueSingleScript_Async) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader = MockScriptLoader::create(m_element.get());
   m_scriptRunner->queueScriptForExecution(scriptLoader, ScriptRunner::Async);
   m_scriptRunner->notifyScriptReady(scriptLoader, ScriptRunner::Async);
 
   EXPECT_CALL(*scriptLoader, execute());
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 }
 
 TEST_F(ScriptRunnerTest, QueueSingleScript_InOrder) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader = MockScriptLoader::create(m_element.get());
   m_scriptRunner->queueScriptForExecution(scriptLoader, ScriptRunner::InOrder);
 
@@ -86,14 +79,10 @@
 
   m_scriptRunner->notifyScriptReady(scriptLoader, ScriptRunner::InOrder);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 }
 
 TEST_F(ScriptRunnerTest, QueueMultipleScripts_InOrder) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader1 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader2 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader3 = MockScriptLoader::create(m_element.get());
@@ -125,7 +114,7 @@
   for (int i = 2; i >= 0; i--) {
     isReady[i] = true;
     m_scriptRunner->notifyScriptReady(scriptLoaders[i], ScriptRunner::InOrder);
-    platform->runUntilIdle();
+    m_platform->runUntilIdle();
   }
 
   // But ensure the scripts were run in the expected order.
@@ -133,10 +122,6 @@
 }
 
 TEST_F(ScriptRunnerTest, QueueMixedScripts) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader1 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader2 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader3 = MockScriptLoader::create(m_element.get());
@@ -179,17 +164,13 @@
     m_order.push_back(5);
   }));
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   // Async tasks are expected to run first.
   EXPECT_THAT(m_order, ElementsAre(4, 5, 1, 2, 3));
 }
 
 TEST_F(ScriptRunnerTest, QueueReentrantScript_Async) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader1 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader2 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader3 = MockScriptLoader::create(m_element.get());
@@ -217,21 +198,17 @@
 
   // Make sure that re-entrant calls to notifyScriptReady don't cause
   // ScriptRunner::execute to do more work than expected.
-  platform->runSingleTask();
+  m_platform->runSingleTask();
   EXPECT_THAT(m_order, ElementsAre(1));
 
-  platform->runSingleTask();
+  m_platform->runSingleTask();
   EXPECT_THAT(m_order, ElementsAre(1, 2));
 
-  platform->runSingleTask();
+  m_platform->runSingleTask();
   EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
 }
 
 TEST_F(ScriptRunnerTest, QueueReentrantScript_InOrder) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader1 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader2 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader3 = MockScriptLoader::create(m_element.get());
@@ -267,21 +244,17 @@
 
   // Make sure that re-entrant calls to queueScriptForExecution don't cause
   // ScriptRunner::execute to do more work than expected.
-  platform->runSingleTask();
+  m_platform->runSingleTask();
   EXPECT_THAT(m_order, ElementsAre(1));
 
-  platform->runSingleTask();
+  m_platform->runSingleTask();
   EXPECT_THAT(m_order, ElementsAre(1, 2));
 
-  platform->runSingleTask();
+  m_platform->runSingleTask();
   EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
 }
 
 TEST_F(ScriptRunnerTest, QueueReentrantScript_ManyAsyncScripts) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoaders[20];
   for (int i = 0; i < 20; i++)
     scriptLoaders[i] = nullptr;
@@ -311,7 +284,7 @@
         m_order.push_back(0);
       }));
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   int expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
                     10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
@@ -320,10 +293,6 @@
 }
 
 TEST_F(ScriptRunnerTest, ResumeAndSuspend_InOrder) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader1 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader2 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader3 = MockScriptLoader::create(m_element.get());
@@ -356,20 +325,16 @@
   EXPECT_CALL(*scriptLoader3, isReady()).WillRepeatedly(Return(true));
   m_scriptRunner->notifyScriptReady(scriptLoader3, ScriptRunner::InOrder);
 
-  platform->runSingleTask();
+  m_platform->runSingleTask();
   m_scriptRunner->suspend();
   m_scriptRunner->resume();
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   // Make sure elements are correct and in right order.
   EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
 }
 
 TEST_F(ScriptRunnerTest, ResumeAndSuspend_Async) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader1 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader2 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader3 = MockScriptLoader::create(m_element.get());
@@ -392,20 +357,16 @@
     m_order.push_back(3);
   }));
 
-  platform->runSingleTask();
+  m_platform->runSingleTask();
   m_scriptRunner->suspend();
   m_scriptRunner->resume();
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   // Make sure elements are correct.
   EXPECT_THAT(m_order, WhenSorted(ElementsAre(1, 2, 3)));
 }
 
 TEST_F(ScriptRunnerTest, LateNotifications) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   MockScriptLoader* scriptLoader1 = MockScriptLoader::create(m_element.get());
   MockScriptLoader* scriptLoader2 = MockScriptLoader::create(m_element.get());
 
@@ -423,21 +384,17 @@
   }));
 
   m_scriptRunner->notifyScriptReady(scriptLoader1, ScriptRunner::InOrder);
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   // At this moment all tasks can be already executed. Make sure that we do not
   // crash here.
   m_scriptRunner->notifyScriptReady(scriptLoader2, ScriptRunner::InOrder);
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   EXPECT_THAT(m_order, ElementsAre(1, 2));
 }
 
 TEST_F(ScriptRunnerTest, TasksWithDeadScriptRunner) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize();
-
   Persistent<MockScriptLoader> scriptLoader1 =
       MockScriptLoader::create(m_element.get());
   Persistent<MockScriptLoader> scriptLoader2 =
@@ -461,7 +418,7 @@
   EXPECT_CALL(*scriptLoader1, execute()).Times(0);
   EXPECT_CALL(*scriptLoader2, execute()).Times(0);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/TaskRunnerHelper.h b/third_party/WebKit/Source/core/dom/TaskRunnerHelper.h
index 0ca15474..37710d0 100644
--- a/third_party/WebKit/Source/core/dom/TaskRunnerHelper.h
+++ b/third_party/WebKit/Source/core/dom/TaskRunnerHelper.h
@@ -20,22 +20,79 @@
 enum class TaskType : unsigned {
   // Speced tasks and related internal tasks should be posted to one of
   // the following task runners. These task runners may be throttled.
+
+  // https://html.spec.whatwg.org/multipage/webappapis.html#generic-task-sources
+  //
+  // This task source is used for features that react to DOM manipulations, such
+  // as things that happen in a non-blocking fashion when an element is inserted
+  // into the document.
   DOMManipulation,
+  // This task source is used for features that react to user interaction, for
+  // example keyboard or mouse input. Events sent in response to user input
+  // (e.g. click events) must be fired using tasks queued with the user
+  // interaction task source.
   UserInteraction,
+  // This task source is used for features that trigger in response to network
+  // activity.
   Networking,
+  // This task source is used to queue calls to history.back() and similar APIs.
   HistoryTraversal,
+
+  // https://html.spec.whatwg.org/multipage/embedded-content.html#the-embed-element
+  // This task source is used for the embed element setup steps.
   Embed,
+
+  // https://html.spec.whatwg.org/multipage/embedded-content.html#media-elements
+  // This task source is used for all tasks queued in the [Media elements]
+  // section and subsections of the spec unless explicitly specified otherwise.
   MediaElementEvent,
+
+  // https://html.spec.whatwg.org/multipage/scripting.html#the-canvas-element
+  // This task source is used to invoke the result callback of
+  // HTMLCanvasElement.toBlob().
   CanvasBlobSerialization,
+
+  // https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
+  // This task source is used when an algorithm requires a microtask to be
+  // queued.
   Microtask,
+
+  // https://html.spec.whatwg.org/multipage/webappapis.html#timers
+  // This task source is used to queue tasks queued by setInterval() and similar
+  // APIs.
   Timer,
+
+  // https://html.spec.whatwg.org/multipage/comms.html#sse-processing-model
+  // This task source is used for any tasks that are queued by EventSource
+  // objects.
   RemoteEvent,
+
+  // https://html.spec.whatwg.org/multipage/comms.html#feedback-from-the-protocol
+  // The task source for all tasks queued in the [WebSocket] section of the
+  // spec.
   WebSocket,
+
+  // https://html.spec.whatwg.org/multipage/comms.html#web-messaging
+  // This task source is used for the tasks in cross-document messaging.
   PostedMessage,
+
+  // https://html.spec.whatwg.org/multipage/comms.html#message-ports
   UnshippedPortMessage,
+
+  // https://www.w3.org/TR/FileAPI/#blobreader-task-source
+  // This task source is used for all tasks queued in the FileAPI spec to read
+  // byte sequences associated with Blob and File objects.
   FileReading,
+
+  // https://www.w3.org/TR/IndexedDB/#request-api
   DatabaseAccess,
+
+  // https://w3c.github.io/presentation-api/#common-idioms
+  // This task source is used for all tasks in the Presentation API spec.
   Presentation,
+
+  // https://www.w3.org/TR/2016/WD-generic-sensor-20160830/#sensor-task-source
+  // This task source is used for all tasks in the Sensor API spec.
   Sensor,
 
   // Use MiscPlatformAPI for a task that is defined in the spec but is not yet
diff --git a/third_party/WebKit/Source/core/events/EventAliases.in b/third_party/WebKit/Source/core/events/EventAliases.in
index 37ce52a..f6e3087 100644
--- a/third_party/WebKit/Source/core/events/EventAliases.in
+++ b/third_party/WebKit/Source/core/events/EventAliases.in
@@ -8,3 +8,5 @@
 SVGEvents ImplementedAs=Event
 UIEvents ImplementedAs=UIEvent
 WebKitTransitionEvent ImplementedAs=TransitionEvent
+
+core/events/TouchEvent RuntimeEnabled=touchEventFeatureDetectionEnabled
diff --git a/third_party/WebKit/Source/core/events/TouchEvent.idl b/third_party/WebKit/Source/core/events/TouchEvent.idl
index f7875d8..539fc2a6 100644
--- a/third_party/WebKit/Source/core/events/TouchEvent.idl
+++ b/third_party/WebKit/Source/core/events/TouchEvent.idl
@@ -27,7 +27,6 @@
 
 [
     Constructor(DOMString type, optional TouchEventInit eventInitDict),
-    CreateEventEnabled=TouchEventFeatureDetection,
 ] interface TouchEvent : UIEvent {
     readonly attribute TouchList touches;
     readonly attribute TouchList targetTouches;
diff --git a/third_party/WebKit/Source/core/frame/Frame.cpp b/third_party/WebKit/Source/core/frame/Frame.cpp
index d259c5d..b9731fff 100644
--- a/third_party/WebKit/Source/core/frame/Frame.cpp
+++ b/third_party/WebKit/Source/core/frame/Frame.cpp
@@ -48,6 +48,7 @@
 #include "core/page/Page.h"
 #include "platform/Histogram.h"
 #include "platform/InstanceCounters.h"
+#include "platform/UserGestureIndicator.h"
 #include "platform/feature_policy/FeaturePolicy.h"
 #include "platform/network/ResourceError.h"
 
@@ -284,15 +285,32 @@
       return false;
     }
 
-    // Top navigation is forbidden unless opted-in. allow-top-navigation
-    // will also skips origin checks.
+    // Top navigation is forbidden unless opted-in. allow-top-navigation or
+    // allow-top-navigation-with-user-activation will also skips origin checks.
     if (targetFrame == tree().top()) {
-      if (securityContext()->isSandboxed(SandboxTopNavigation)) {
+      if (securityContext()->isSandboxed(SandboxTopNavigation) &&
+          securityContext()->isSandboxed(
+              SandboxTopNavigationWithUserActivation)) {
+        // TODO(binlu): To add "or 'allow-top-navigation-with-user-activation'"
+        // to the reason below, once the new flag is shipped.
         reason =
             "The frame attempting navigation of the top-level window is "
             "sandboxed, but the 'allow-top-navigation' flag is not set.";
         return false;
       }
+      if (securityContext()->isSandboxed(SandboxTopNavigation) &&
+          !securityContext()->isSandboxed(
+              SandboxTopNavigationWithUserActivation) &&
+          !UserGestureIndicator::processingUserGesture()) {
+        // With only 'allow-top-navigation-with-user-activation' (but not
+        // 'allow-top-navigation'), top navigation requires a user gesture.
+        reason =
+            "The frame attempting navigation of the top-level window is "
+            "sandboxed with the 'allow-top-navigation-with-user-activation' "
+            "flag, but has no user activation (aka gesture). See "
+            "https://www.chromestatus.com/feature/5629582019395584.";
+        return false;
+      }
       return true;
     }
   }
diff --git a/third_party/WebKit/Source/core/html/HTMLSummaryElement.cpp b/third_party/WebKit/Source/core/html/HTMLSummaryElement.cpp
index d5c5792c..f789441 100644
--- a/third_party/WebKit/Source/core/html/HTMLSummaryElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLSummaryElement.cpp
@@ -98,7 +98,7 @@
 }
 
 void HTMLSummaryElement::defaultEventHandler(Event* event) {
-  if (isMainSummary() && layoutObject()) {
+  if (isMainSummary()) {
     if (event->type() == EventTypeNames::DOMActivate &&
         !isClickableControl(event->target()->toNode())) {
       if (HTMLDetailsElement* details = detailsElement())
@@ -140,10 +140,7 @@
 }
 
 bool HTMLSummaryElement::willRespondToMouseClickEvents() {
-  if (isMainSummary() && layoutObject())
-    return true;
-
-  return HTMLElement::willRespondToMouseClickEvents();
+  return isMainSummary() || HTMLElement::willRespondToMouseClickEvents();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLTrackElement.cpp b/third_party/WebKit/Source/core/html/HTMLTrackElement.cpp
index ad63ae1..ca2d9c6 100644
--- a/third_party/WebKit/Source/core/html/HTMLTrackElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLTrackElement.cpp
@@ -27,6 +27,7 @@
 
 #include "core/HTMLNames.h"
 #include "core/dom/Document.h"
+#include "core/dom/TaskRunnerHelper.h"
 #include "core/events/Event.h"
 #include "core/frame/csp/ContentSecurityPolicy.h"
 #include "core/html/CrossOriginAttribute.h"
@@ -49,7 +50,9 @@
 
 inline HTMLTrackElement::HTMLTrackElement(Document& document)
     : HTMLElement(trackTag, document),
-      m_loadTimer(this, &HTMLTrackElement::loadTimerFired) {
+      m_loadTimer(TaskRunnerHelper::get(TaskType::Networking, &document),
+                  this,
+                  &HTMLTrackElement::loadTimerFired) {
   DVLOG(TRACK_LOG_LEVEL) << "HTMLTrackElement - " << (void*)this;
 }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLTrackElement.h b/third_party/WebKit/Source/core/html/HTMLTrackElement.h
index 7e395bf..d6d5c7e 100644
--- a/third_party/WebKit/Source/core/html/HTMLTrackElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLTrackElement.h
@@ -86,7 +86,7 @@
 
   Member<LoadableTextTrack> m_track;
   Member<TextTrackLoader> m_loader;
-  Timer<HTMLTrackElement> m_loadTimer;
+  TaskRunnerTimer<HTMLTrackElement> m_loadTimer;
   KURL m_url;
 };
 
diff --git a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
index 84060c8..ef20ba40 100644
--- a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
+++ b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
@@ -5,6 +5,7 @@
 #include "core/paint/FirstMeaningfulPaintDetector.h"
 
 #include "core/css/FontFaceSet.h"
+#include "core/dom/TaskRunnerHelper.h"
 #include "core/fetch/ResourceFetcher.h"
 #include "core/paint/PaintTiming.h"
 #include "platform/instrumentation/tracing/TraceEvent.h"
@@ -29,9 +30,11 @@
 }
 
 FirstMeaningfulPaintDetector::FirstMeaningfulPaintDetector(
-    PaintTiming* paintTiming)
+    PaintTiming* paintTiming,
+    Document& document)
     : m_paintTiming(paintTiming),
       m_networkStableTimer(
+          TaskRunnerHelper::get(TaskType::UnspecedTimer, &document),
           this,
           &FirstMeaningfulPaintDetector::networkStableTimerFired) {}
 
diff --git a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.h b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.h
index 5d63d4a..6053c29 100644
--- a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.h
+++ b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.h
@@ -38,7 +38,7 @@
 
   static FirstMeaningfulPaintDetector& from(Document&);
 
-  FirstMeaningfulPaintDetector(PaintTiming*);
+  FirstMeaningfulPaintDetector(PaintTiming*, Document&);
   virtual ~FirstMeaningfulPaintDetector() {}
 
   void markNextPaintAsMeaningfulIfNeeded(const LayoutObjectCounter&,
@@ -65,7 +65,7 @@
   double m_accumulatedSignificanceWhileHavingBlankText = 0.0;
   unsigned m_prevLayoutObjectCount = 0;
   bool m_seenFirstMeaningfulPaintCandidate = false;
-  Timer<FirstMeaningfulPaintDetector> m_networkStableTimer;
+  TaskRunnerTimer<FirstMeaningfulPaintDetector> m_networkStableTimer;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.h b/third_party/WebKit/Source/core/paint/PaintInvalidator.h
index 9a8e2a6..0c9ee0c 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.h
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.h
@@ -94,12 +94,15 @@
   void processPendingDelayedPaintInvalidations();
 
  private:
-  LayoutRect computeVisualRectInBacking(const LayoutObject&,
-                                        const PaintInvalidatorContext&);
-  LayoutPoint computeLocationInBacking(const LayoutObject&,
-                                       const PaintInvalidatorContext&);
-  void updatePaintingLayer(const LayoutObject&, PaintInvalidatorContext&);
-  void updateContext(const LayoutObject&, PaintInvalidatorContext&);
+  ALWAYS_INLINE LayoutRect
+  computeVisualRectInBacking(const LayoutObject&,
+                             const PaintInvalidatorContext&);
+  ALWAYS_INLINE LayoutPoint
+  computeLocationInBacking(const LayoutObject&, const PaintInvalidatorContext&);
+  ALWAYS_INLINE void updatePaintingLayer(const LayoutObject&,
+                                         PaintInvalidatorContext&);
+  ALWAYS_INLINE void updateContext(const LayoutObject&,
+                                   PaintInvalidatorContext&);
 
   Vector<const LayoutObject*> m_pendingDelayedPaintInvalidations;
   GeometryMapper m_geometryMapper;
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index d7c0c39..0b176f6 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -38,10 +38,11 @@
 }
 
 // True if a new property was created, false if an existing one was updated.
-bool updatePreTranslation(FrameView& frameView,
-                          PassRefPtr<const TransformPaintPropertyNode> parent,
-                          const TransformationMatrix& matrix,
-                          const FloatPoint3D& origin) {
+static bool updatePreTranslation(
+    FrameView& frameView,
+    PassRefPtr<const TransformPaintPropertyNode> parent,
+    const TransformationMatrix& matrix,
+    const FloatPoint3D& origin) {
   DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled());
   if (auto* existingPreTranslation = frameView.preTranslation()) {
     existingPreTranslation->update(std::move(parent), matrix, origin);
@@ -53,7 +54,7 @@
 }
 
 // True if a new property was created, false if an existing one was updated.
-bool updateContentClip(
+static bool updateContentClip(
     FrameView& frameView,
     PassRefPtr<const ClipPaintPropertyNode> parent,
     PassRefPtr<const TransformPaintPropertyNode> localTransformSpace,
@@ -70,7 +71,7 @@
 }
 
 // True if a new property was created, false if an existing one was updated.
-bool updateScrollTranslation(
+static bool updateScrollTranslation(
     FrameView& frameView,
     PassRefPtr<const TransformPaintPropertyNode> parent,
     const TransformationMatrix& matrix,
@@ -87,14 +88,15 @@
 
 // True if a new property was created or a main thread scrolling reason changed
 // (which can affect descendants), false if an existing one was updated.
-bool updateScroll(FrameView& frameView,
-                  PassRefPtr<const ScrollPaintPropertyNode> parent,
-                  PassRefPtr<const TransformPaintPropertyNode> scrollOffset,
-                  const IntSize& clip,
-                  const IntSize& bounds,
-                  bool userScrollableHorizontal,
-                  bool userScrollableVertical,
-                  MainThreadScrollingReasons mainThreadScrollingReasons) {
+static bool updateScroll(
+    FrameView& frameView,
+    PassRefPtr<const ScrollPaintPropertyNode> parent,
+    PassRefPtr<const TransformPaintPropertyNode> scrollOffset,
+    const IntSize& clip,
+    const IntSize& bounds,
+    bool userScrollableHorizontal,
+    bool userScrollableVertical,
+    MainThreadScrollingReasons mainThreadScrollingReasons) {
   DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled());
   if (auto* existingScroll = frameView.scroll()) {
     auto existingReasons = existingScroll->mainThreadScrollingReasons();
@@ -110,7 +112,7 @@
   return true;
 }
 
-MainThreadScrollingReasons mainThreadScrollingReasons(
+static MainThreadScrollingReasons mainThreadScrollingReasons(
     const FrameView& frameView,
     MainThreadScrollingReasons ancestorReasons) {
   auto reasons = ancestorReasons;
@@ -347,16 +349,12 @@
       style.transformOriginZ());
 }
 
-namespace {
-
-CompositorElementId createDomNodeBasedCompositorElementId(
+static CompositorElementId createDomNodeBasedCompositorElementId(
     const LayoutObject& object) {
   return createCompositorElementId(DOMNodeIds::idForNode(object.node()),
                                    CompositorSubElementId::Primary);
 }
 
-}  // namespace
-
 void PaintPropertyTreeBuilder::updateTransform(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
@@ -744,7 +742,7 @@
   context.current.paintOffset = LayoutPoint();
 }
 
-MainThreadScrollingReasons mainThreadScrollingReasons(
+static MainThreadScrollingReasons mainThreadScrollingReasons(
     const LayoutObject& object,
     MainThreadScrollingReasons ancestorReasons) {
   // The current main thread scrolling reasons implementation only changes
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
index 8ef1afb..c4386dc 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
@@ -116,32 +116,38 @@
                                    PaintPropertyTreeBuilderContext&);
 
  private:
-  static void updatePaintOffsetTranslation(const LayoutObject&,
-                                           PaintPropertyTreeBuilderContext&);
-  static void updateTransform(const LayoutObject&,
-                              PaintPropertyTreeBuilderContext&);
-  static void updateTransformForNonRootSVG(const LayoutObject&,
-                                           PaintPropertyTreeBuilderContext&);
-  static void updateEffect(const LayoutObject&,
-                           PaintPropertyTreeBuilderContext&);
-  static void updateCssClip(const LayoutObject&,
-                            PaintPropertyTreeBuilderContext&);
-  static void updateLocalBorderBoxContext(const LayoutObject&,
-                                          PaintPropertyTreeBuilderContext&);
-  static void updateScrollbarPaintOffset(const LayoutObject&,
+  ALWAYS_INLINE static void updatePaintOffsetTranslation(
+      const LayoutObject&,
+      PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updateTransform(const LayoutObject&,
+                                            PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updateTransformForNonRootSVG(
+      const LayoutObject&,
+      PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updateEffect(const LayoutObject&,
                                          PaintPropertyTreeBuilderContext&);
-  static void updateOverflowClip(const LayoutObject&,
-                                 PaintPropertyTreeBuilderContext&);
-  static void updatePerspective(const LayoutObject&,
-                                PaintPropertyTreeBuilderContext&);
-  static void updateSvgLocalToBorderBoxTransform(
+  ALWAYS_INLINE static void updateCssClip(const LayoutObject&,
+                                          PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updateLocalBorderBoxContext(
       const LayoutObject&,
       PaintPropertyTreeBuilderContext&);
-  static void updateScrollAndScrollTranslation(
+  ALWAYS_INLINE static void updateScrollbarPaintOffset(
       const LayoutObject&,
       PaintPropertyTreeBuilderContext&);
-  static void updateOutOfFlowContext(const LayoutObject&,
-                                     PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updateOverflowClip(
+      const LayoutObject&,
+      PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updatePerspective(const LayoutObject&,
+                                              PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updateSvgLocalToBorderBoxTransform(
+      const LayoutObject&,
+      PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updateScrollAndScrollTranslation(
+      const LayoutObject&,
+      PaintPropertyTreeBuilderContext&);
+  ALWAYS_INLINE static void updateOutOfFlowContext(
+      const LayoutObject&,
+      PaintPropertyTreeBuilderContext&);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintTiming.cpp b/third_party/WebKit/Source/core/paint/PaintTiming.cpp
index 7edb7876..60923e0c 100644
--- a/third_party/WebKit/Source/core/paint/PaintTiming.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintTiming.cpp
@@ -120,7 +120,7 @@
 
 PaintTiming::PaintTiming(Document& document)
     : Supplement<Document>(document),
-      m_fmpDetector(new FirstMeaningfulPaintDetector(this)) {}
+      m_fmpDetector(new FirstMeaningfulPaintDetector(this, document)) {}
 
 LocalFrame* PaintTiming::frame() const {
   return supplementable()->frame();
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.cpp
index 64fb9cb..52991b5 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.cpp
@@ -53,15 +53,6 @@
   return thread()->isCurrentThread();
 }
 
-void ThreadedWorkletGlobalScope::postTask(
-    TaskType,
-    const WebTraceLocation& location,
-    std::unique_ptr<ExecutionContextTask> task,
-    const String& taskNameForInstrumentation) {
-  thread()->postTask(location, std::move(task),
-                     !taskNameForInstrumentation.isEmpty());
-}
-
 void ThreadedWorkletGlobalScope::addConsoleMessage(
     ConsoleMessage* consoleMessage) {
   DCHECK(isContextThread());
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.h
index 6ba296ffd..2836af76 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletGlobalScope.h
@@ -22,10 +22,6 @@
   // ExecutionContext
   bool isThreadedWorkletGlobalScope() const final { return true; }
   bool isContextThread() const final;
-  void postTask(TaskType,
-                const WebTraceLocation&,
-                std::unique_ptr<ExecutionContextTask>,
-                const String& taskNameForInstrumentation) final;
   void addConsoleMessage(ConsoleMessage*) final;
   void exceptionThrown(ErrorEvent*) final;
 
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
index c7a2b074..961478a99 100644
--- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
@@ -262,14 +262,6 @@
   m_scriptController->disableEval(errorMessage);
 }
 
-void WorkerGlobalScope::postTask(TaskType,
-                                 const WebTraceLocation& location,
-                                 std::unique_ptr<ExecutionContextTask> task,
-                                 const String& taskNameForInstrumentation) {
-  thread()->postTask(location, std::move(task),
-                     !taskNameForInstrumentation.isEmpty());
-}
-
 void WorkerGlobalScope::addConsoleMessage(ConsoleMessage* consoleMessage) {
   DCHECK(isContextThread());
   thread()->workerReportingProxy().reportConsoleMessage(
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
index 09c5127..2c5486b81 100644
--- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
@@ -126,11 +126,6 @@
   void disableEval(const String& errorMessage) final;
   String userAgent() const final { return m_userAgent; }
 
-  void postTask(TaskType,
-                const WebTraceLocation&,
-                std::unique_ptr<ExecutionContextTask>,
-                const String& taskNameForInstrumentation = emptyString()) final;
-
   DOMTimerCoordinator* timers() final { return &m_timers; }
   SecurityContext& securityContext() final { return *this; }
   void addConsoleMessage(ConsoleMessage*) final;
diff --git a/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.cpp
index d5b5447..1a79822 100644
--- a/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.cpp
@@ -6,6 +6,7 @@
 
 #include "core/frame/Deprecation.h"
 #include "core/inspector/ConsoleMessage.h"
+#include "core/inspector/InspectorInstrumentation.h"
 #include "core/workers/WorkerReportingProxy.h"
 #include "core/workers/WorkerThread.h"
 
@@ -32,4 +33,34 @@
                              Deprecation::deprecationMessage(feature)));
 }
 
+void WorkerOrWorkletGlobalScope::postTask(
+    TaskType,
+    const WebTraceLocation& location,
+    std::unique_ptr<ExecutionContextTask> task,
+    const String& taskNameForInstrumentation) {
+  if (!thread())
+    return;
+
+  bool isInstrumented = !taskNameForInstrumentation.isEmpty();
+  if (isInstrumented) {
+    InspectorInstrumentation::asyncTaskScheduled(this, "Worker task",
+                                                 task.get());
+  }
+
+  std::unique_ptr<ExecutionContextTask> wrappedTask = createCrossThreadTask(
+      &WorkerOrWorkletGlobalScope::runTask, wrapCrossThreadWeakPersistent(this),
+      WTF::passed(std::move(task)), isInstrumented);
+  thread()->postTask(location, std::move(wrappedTask));
+}
+
+void WorkerOrWorkletGlobalScope::runTask(
+    std::unique_ptr<ExecutionContextTask> task,
+    bool isInstrumented,
+    ExecutionContext*) {
+  DCHECK(thread()->isCurrentThread());
+  InspectorInstrumentation::AsyncTask asyncTask(this, task.get(),
+                                                isInstrumented);
+  task->performTask(this);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.h
index 6a91b68..3a406f4 100644
--- a/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/WorkerOrWorkletGlobalScope.h
@@ -21,6 +21,10 @@
 
   // ExecutionContext
   bool isWorkerOrWorkletGlobalScope() const final { return true; }
+  void postTask(TaskType,
+                const WebTraceLocation&,
+                std::unique_ptr<ExecutionContextTask>,
+                const String& taskNameForInstrumentation = emptyString()) final;
 
   virtual ScriptWrappable* getScriptWrappable() const = 0;
   virtual WorkerOrWorkletScriptController* scriptController() = 0;
@@ -51,6 +55,10 @@
   void addDeprecationMessage(UseCounter::Feature);
 
  private:
+  void runTask(std::unique_ptr<ExecutionContextTask>,
+               bool isInstrumented,
+               ExecutionContext*);
+
   BitVector m_deprecationWarningBits;
 };
 
diff --git a/third_party/WebKit/Source/core/workers/WorkerThread.cpp b/third_party/WebKit/Source/core/workers/WorkerThread.cpp
index eb572eff..6b9fdb8 100644
--- a/third_party/WebKit/Source/core/workers/WorkerThread.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerThread.cpp
@@ -190,19 +190,13 @@
 }
 
 void WorkerThread::postTask(const WebTraceLocation& location,
-                            std::unique_ptr<ExecutionContextTask> task,
-                            bool isInstrumented) {
+                            std::unique_ptr<ExecutionContextTask> task) {
   if (isInShutdown())
     return;
-  if (isInstrumented) {
-    DCHECK(isCurrentThread());
-    InspectorInstrumentation::asyncTaskScheduled(globalScope(), "Worker task",
-                                                 task.get());
-  }
   workerBackingThread().backingThread().postTask(
       location, crossThreadBind(&WorkerThread::performTaskOnWorkerThread,
                                 crossThreadUnretained(this),
-                                WTF::passed(std::move(task)), isInstrumented));
+                                WTF::passed(std::move(task))));
 }
 
 void WorkerThread::appendDebuggerTask(
@@ -564,14 +558,11 @@
 }
 
 void WorkerThread::performTaskOnWorkerThread(
-    std::unique_ptr<ExecutionContextTask> task,
-    bool isInstrumented) {
+    std::unique_ptr<ExecutionContextTask> task) {
   DCHECK(isCurrentThread());
   if (m_threadState != ThreadState::Running)
     return;
 
-  InspectorInstrumentation::AsyncTask asyncTask(globalScope(), task.get(),
-                                                isInstrumented);
   {
     DEFINE_THREAD_SAFE_STATIC_LOCAL(
         CustomCountHistogram, scopedUsCounter,
diff --git a/third_party/WebKit/Source/core/workers/WorkerThread.h b/third_party/WebKit/Source/core/workers/WorkerThread.h
index cafb78d0..89702de 100644
--- a/third_party/WebKit/Source/core/workers/WorkerThread.h
+++ b/third_party/WebKit/Source/core/workers/WorkerThread.h
@@ -136,9 +136,7 @@
     return m_workerReportingProxy;
   }
 
-  void postTask(const WebTraceLocation&,
-                std::unique_ptr<ExecutionContextTask>,
-                bool isInstrumented = false);
+  void postTask(const WebTraceLocation&, std::unique_ptr<ExecutionContextTask>);
   void appendDebuggerTask(std::unique_ptr<CrossThreadClosure>);
 
   // Runs only debugger tasks while paused in debugger.
@@ -247,8 +245,7 @@
   void initializeOnWorkerThread(std::unique_ptr<WorkerThreadStartupData>);
   void prepareForShutdownOnWorkerThread();
   void performShutdownOnWorkerThread();
-  void performTaskOnWorkerThread(std::unique_ptr<ExecutionContextTask>,
-                                 bool isInstrumented);
+  void performTaskOnWorkerThread(std::unique_ptr<ExecutionContextTask>);
   void performDebuggerTaskOnWorkerThread(std::unique_ptr<CrossThreadClosure>);
   void performDebuggerTaskDontWaitOnWorkerThread();
 
diff --git a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h
index d527e3c..16a3b36 100644
--- a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h
@@ -74,13 +74,6 @@
     NOTREACHED();
     return nullptr;
   }  // WorkletGlobalScopes don't have timers.
-  void postTask(TaskType,
-                const WebTraceLocation&,
-                std::unique_ptr<ExecutionContextTask>,
-                const String&) override {
-    // TODO(ikilpatrick): implement.
-    NOTREACHED();
-  }
 
   DECLARE_VIRTUAL_TRACE();
 
diff --git a/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThreadTest.cpp b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThreadTest.cpp
index f815691f..9f946e4 100644
--- a/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThreadTest.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThreadTest.cpp
@@ -128,6 +128,7 @@
 
   RefPtr<SecurityOrigin> m_securityOrigin;
   std::unique_ptr<WorkerReportingProxy> m_reportingProxy;
+  ScopedTestingPlatformSupport<AnimationWorkletTestPlatform> m_platform;
 };
 
 TEST_F(AnimationWorkletThreadTest, Basic) {
@@ -142,8 +143,6 @@
 // Tests that the same WebThread is used for new worklets if the WebThread is
 // still alive.
 TEST_F(AnimationWorkletThreadTest, CreateSecondAndTerminateFirst) {
-  ScopedTestingPlatformSupport<AnimationWorkletTestPlatform> platform;
-
   // Create the first worklet and wait until it is initialized.
   std::unique_ptr<AnimationWorkletThread> firstWorklet =
       createAnimationWorkletThread();
@@ -179,8 +178,6 @@
 // Tests that a new WebThread is created if all existing worklets are
 // terminated before a new worklet is created.
 TEST_F(AnimationWorkletThreadTest, TerminateFirstAndCreateSecond) {
-  ScopedTestingPlatformSupport<AnimationWorkletTestPlatform> platform;
-
   // Create the first worklet, wait until it is initialized, and terminate it.
   std::unique_ptr<AnimationWorkletThread> worklet =
       createAnimationWorkletThread();
@@ -205,8 +202,6 @@
 // Tests that v8::Isolate and WebThread are correctly set-up if a worklet is
 // created while another is terminating.
 TEST_F(AnimationWorkletThreadTest, CreatingSecondDuringTerminationOfFirst) {
-  ScopedTestingPlatformSupport<AnimationWorkletTestPlatform> platform;
-
   std::unique_ptr<AnimationWorkletThread> firstWorklet =
       createAnimationWorkletThread();
   checkWorkletCanExecuteScript(firstWorklet.get());
diff --git a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerThreadTest.cpp b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerThreadTest.cpp
index 8481494..c58f4a80 100644
--- a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerThreadTest.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerThreadTest.cpp
@@ -143,11 +143,10 @@
 
   RefPtr<SecurityOrigin> m_securityOrigin;
   std::unique_ptr<InProcessWorkerObjectProxy> m_objectProxy;
+  ScopedTestingPlatformSupport<CompositorWorkerTestPlatform> m_platform;
 };
 
 TEST_F(CompositorWorkerThreadTest, Basic) {
-  ScopedTestingPlatformSupport<CompositorWorkerTestPlatform> platform;
-
   std::unique_ptr<CompositorWorkerThread> compositorWorker =
       createCompositorWorker();
   checkWorkerCanExecuteScript(compositorWorker.get());
@@ -157,8 +156,6 @@
 // Tests that the same WebThread is used for new workers if the WebThread is
 // still alive.
 TEST_F(CompositorWorkerThreadTest, CreateSecondAndTerminateFirst) {
-  ScopedTestingPlatformSupport<CompositorWorkerTestPlatform> platform;
-
   // Create the first worker and wait until it is initialized.
   std::unique_ptr<CompositorWorkerThread> firstWorker =
       createCompositorWorker();
@@ -194,8 +191,6 @@
 // Tests that a new WebThread is created if all existing workers are terminated
 // before a new worker is created.
 TEST_F(CompositorWorkerThreadTest, TerminateFirstAndCreateSecond) {
-  ScopedTestingPlatformSupport<CompositorWorkerTestPlatform> platform;
-
   // Create the first worker, wait until it is initialized, and terminate it.
   std::unique_ptr<CompositorWorkerThread> compositorWorker =
       createCompositorWorker();
@@ -220,8 +215,6 @@
 // Tests that v8::Isolate and WebThread are correctly set-up if a worker is
 // created while another is terminating.
 TEST_F(CompositorWorkerThreadTest, CreatingSecondDuringTerminationOfFirst) {
-  ScopedTestingPlatformSupport<CompositorWorkerTestPlatform> platform;
-
   std::unique_ptr<CompositorWorkerThread> firstWorker =
       createCompositorWorker();
   checkWorkerCanExecuteScript(firstWorker.get());
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
index 835600d..1eecc8a 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
@@ -121,12 +121,11 @@
  private:
   std::unique_ptr<DummyPageHolder> m_dummyPageHolder;
   Persistent<DummyFrameOwner> m_dummyFrameOwner;
-
   Persistent<LocalFrame> m_childFrame;
+  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> m_platform;
 };
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_NoRestriction) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
 
   BaseAudioContext* audioContext =
@@ -137,7 +136,6 @@
 }
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_CreateNoGesture) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
@@ -152,7 +150,6 @@
 }
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_CallResumeNoGesture) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
@@ -171,7 +168,6 @@
 }
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_CreateGesture) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
@@ -189,7 +185,6 @@
 }
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_CallResumeGesture) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
@@ -212,7 +207,6 @@
 }
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_NodeStartNoGesture) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
@@ -228,7 +222,6 @@
 }
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_NodeStartGesture) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
@@ -247,7 +240,6 @@
 }
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_NodeStartNoGestureThenSuccess) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
@@ -270,7 +262,6 @@
 }
 
 TEST_F(BaseAudioContextTest, AutoplayMetrics_NodeStartGestureThenSucces) {
-  ScopedTestingPlatformSupport<BaseAudioContextTestPlatform> platform;
   HistogramTester histogramTester;
   createChildFrame();
   childDocument().settings()->setMediaPlaybackRequiresUserGesture(true);
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index bdb3c56..fbdd9688 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -241,6 +241,7 @@
 Suborigins status=experimental
 TimerThrottlingForBackgroundTabs status=stable
 TimerThrottlingForHiddenFrames status=stable
+TopNavWithUserActivationInSandbox status=experimental
 // Many websites disable mouse support when touch APIs are available.  We'd
 // like to enable this always but can't until more websites fix this bug.
 // Chromium sets this conditionally (eg. based on the presence of a
diff --git a/third_party/WebKit/Source/platform/TimerTest.cpp b/third_party/WebKit/Source/platform/TimerTest.cpp
index 57b99b5c..80b9736 100644
--- a/third_party/WebKit/Source/platform/TimerTest.cpp
+++ b/third_party/WebKit/Source/platform/TimerTest.cpp
@@ -27,11 +27,9 @@
 
 class TimerTest : public testing::Test {
  public:
-  void initialize(
-      ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>&
-          platform) {
+  void SetUp() override {
     m_runTimes.clear();
-    platform->advanceClockSeconds(10.0);
+    m_platform->advanceClockSeconds(10.0);
     m_startTime = monotonicallyIncreasingTime();
   }
 
@@ -44,29 +42,24 @@
                               timer->nextFireInterval());
   }
 
-  void runUntilDeadline(ScopedTestingPlatformSupport<
-                            TestingPlatformSupportWithMockScheduler>& platform,
-                        double deadline) {
+  void runUntilDeadline(double deadline) {
     double period = deadline - monotonicallyIncreasingTime();
     EXPECT_GE(period, 0.0);
-    platform->runForPeriodSeconds(period);
+    m_platform->runForPeriodSeconds(period);
   }
 
   // Returns false if there are no pending delayed tasks, otherwise sets |time|
   // to the delay in seconds till the next pending delayed task is scheduled to
   // fire.
-  bool timeTillNextDelayedTask(
-      ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>&
-          platform,
-      double* time) const {
+  bool timeTillNextDelayedTask(double* time) const {
     base::TimeTicks nextRunTime;
-    if (!platform->rendererScheduler()
+    if (!m_platform->rendererScheduler()
              ->TimerTaskRunner()
              ->GetTimeDomain()
              ->NextScheduledRunTime(&nextRunTime))
       return false;
     *time = (nextRunTime -
-             platform->rendererScheduler()
+             m_platform->rendererScheduler()
                  ->TimerTaskRunner()
                  ->GetTimeDomain()
                  ->Now())
@@ -78,6 +71,8 @@
   double m_startTime;
   WTF::Vector<double> m_runTimes;
   WTF::Vector<double> m_nextFireTimes;
+  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+      m_platform;
 };
 
 class OnHeapTimerOwner final
@@ -130,289 +125,225 @@
 };
 
 TEST_F(TimerTest, StartOneShot_Zero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_FALSE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_FALSE(timeTillNextDelayedTask(&runTime));
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime));
 }
 
 TEST_F(TimerTest, StartOneShot_ZeroAndCancel) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_FALSE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_FALSE(timeTillNextDelayedTask(&runTime));
 
   timer.stop();
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_FALSE(m_runTimes.size());
 }
 
 TEST_F(TimerTest, StartOneShot_ZeroAndCancelThenRepost) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_FALSE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_FALSE(timeTillNextDelayedTask(&runTime));
 
   timer.stop();
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_FALSE(m_runTimes.size());
 
   timer.startOneShot(0, BLINK_FROM_HERE);
 
-  EXPECT_FALSE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_FALSE(timeTillNextDelayedTask(&runTime));
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime));
 }
 
 TEST_F(TimerTest, StartOneShot_Zero_RepostingAfterRunning) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_FALSE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_FALSE(timeTillNextDelayedTask(&runTime));
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime));
 
   timer.startOneShot(0, BLINK_FROM_HERE);
 
-  EXPECT_FALSE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_FALSE(timeTillNextDelayedTask(&runTime));
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime, m_startTime));
 }
 
 TEST_F(TimerTest, StartOneShot_NonZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10.0, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(10.0, runTime);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0));
 }
 
 TEST_F(TimerTest, StartOneShot_NonZeroAndCancel) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(10.0, runTime);
 
   timer.stop();
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_FALSE(m_runTimes.size());
 }
 
 TEST_F(TimerTest, StartOneShot_NonZeroAndCancelThenRepost) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(10.0, runTime);
 
   timer.stop();
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_FALSE(m_runTimes.size());
 
   double secondPostTime = monotonicallyIncreasingTime();
   timer.startOneShot(10, BLINK_FROM_HERE);
 
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(10.0, runTime);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(secondPostTime + 10.0));
 }
 
 TEST_F(TimerTest, StartOneShot_NonZero_RepostingAfterRunning) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(10.0, runTime);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0));
 
   timer.startOneShot(20, BLINK_FROM_HERE);
 
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(20.0, runTime);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0, m_startTime + 30.0));
 }
 
 TEST_F(TimerTest, PostingTimerTwiceWithSameRunTimeDoesNothing) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(10.0, runTime);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0));
 }
 
 TEST_F(TimerTest, PostingTimerTwiceWithNewerRunTimeCancelsOriginalTask) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 0.0));
 }
 
 TEST_F(TimerTest, PostingTimerTwiceWithLaterRunTimeCancelsOriginalTask) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0));
 }
 
 TEST_F(TimerTest, StartRepeatingTask) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(1.0, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(1.0, runTime);
 
-  runUntilDeadline(platform, m_startTime + 5.5);
+  runUntilDeadline(m_startTime + 5.5);
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0,
                                       m_startTime + 3.0, m_startTime + 4.0,
                                       m_startTime + 5.0));
 }
 
 TEST_F(TimerTest, StartRepeatingTask_ThenCancel) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(1.0, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(1.0, runTime);
 
-  runUntilDeadline(platform, m_startTime + 2.5);
+  runUntilDeadline(m_startTime + 2.5);
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0));
 
   timer.stop();
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0));
 }
 
 TEST_F(TimerTest, StartRepeatingTask_ThenPostOneShot) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(1.0, BLINK_FROM_HERE);
 
   double runTime;
-  EXPECT_TRUE(timeTillNextDelayedTask(platform, &runTime));
+  EXPECT_TRUE(timeTillNextDelayedTask(&runTime));
   EXPECT_FLOAT_EQ(1.0, runTime);
 
-  runUntilDeadline(platform, m_startTime + 2.5);
+  runUntilDeadline(m_startTime + 2.5);
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0));
 
   timer.startOneShot(0, BLINK_FROM_HERE);
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0,
                                       m_startTime + 2.5));
 }
 
 TEST_F(TimerTest, IsActive_NeverPosted) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
 
   EXPECT_FALSE(timer.isActive());
 }
 
 TEST_F(TimerTest, IsActive_AfterPosting_OneShotZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
@@ -420,10 +351,6 @@
 }
 
 TEST_F(TimerTest, IsActive_AfterPosting_OneShotNonZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
@@ -431,10 +358,6 @@
 }
 
 TEST_F(TimerTest, IsActive_AfterPosting_Repeating) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(1.0, BLINK_FROM_HERE);
 
@@ -442,46 +365,30 @@
 }
 
 TEST_F(TimerTest, IsActive_AfterRunning_OneShotZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_FALSE(timer.isActive());
 }
 
 TEST_F(TimerTest, IsActive_AfterRunning_OneShotNonZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_FALSE(timer.isActive());
 }
 
 TEST_F(TimerTest, IsActive_AfterRunning_Repeating) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(1.0, BLINK_FROM_HERE);
 
-  runUntilDeadline(platform, m_startTime + 10);
+  runUntilDeadline(m_startTime + 10);
   EXPECT_TRUE(timer.isActive());  // It should run until cancelled.
 }
 
 TEST_F(TimerTest, NextFireInterval_OneShotZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
@@ -489,10 +396,6 @@
 }
 
 TEST_F(TimerTest, NextFireInterval_OneShotNonZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
@@ -500,24 +403,16 @@
 }
 
 TEST_F(TimerTest, NextFireInterval_OneShotNonZero_AfterAFewSeconds) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
-  platform->setAutoAdvanceNowToPendingTasks(false);
+  m_platform->setAutoAdvanceNowToPendingTasks(false);
 
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
-  platform->advanceClockSeconds(2.0);
+  m_platform->advanceClockSeconds(2.0);
   EXPECT_FLOAT_EQ(8.0, timer.nextFireInterval());
 }
 
 TEST_F(TimerTest, NextFireInterval_Repeating) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(20, BLINK_FROM_HERE);
 
@@ -525,20 +420,12 @@
 }
 
 TEST_F(TimerTest, RepeatInterval_NeverStarted) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
 
   EXPECT_FLOAT_EQ(0.0, timer.repeatInterval());
 }
 
 TEST_F(TimerTest, RepeatInterval_OneShotZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(0, BLINK_FROM_HERE);
 
@@ -546,10 +433,6 @@
 }
 
 TEST_F(TimerTest, RepeatInterval_OneShotNonZero) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startOneShot(10, BLINK_FROM_HERE);
 
@@ -557,10 +440,6 @@
 }
 
 TEST_F(TimerTest, RepeatInterval_Repeating) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(20, BLINK_FROM_HERE);
 
@@ -568,16 +447,12 @@
 }
 
 TEST_F(TimerTest, AugmentRepeatInterval) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(10, BLINK_FROM_HERE);
   EXPECT_FLOAT_EQ(10.0, timer.repeatInterval());
   EXPECT_FLOAT_EQ(10.0, timer.nextFireInterval());
 
-  platform->advanceClockSeconds(2.0);
+  m_platform->advanceClockSeconds(2.0);
   timer.augmentRepeatInterval(10);
 
   EXPECT_FLOAT_EQ(20.0, timer.repeatInterval());
@@ -587,23 +462,19 @@
   // cc::OrderedSimpleTaskRunner) results in somewhat strange behavior of the
   // test clock which breaks this test.  Specifically the test clock advancing
   // logic ignores newly posted delayed tasks and advances too far.
-  runUntilDeadline(platform, m_startTime + 50.0);
+  runUntilDeadline(m_startTime + 50.0);
   EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 20.0, m_startTime + 40.0));
 }
 
 TEST_F(TimerTest, AugmentRepeatInterval_TimerFireDelayed) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
-  platform->setAutoAdvanceNowToPendingTasks(false);
+  m_platform->setAutoAdvanceNowToPendingTasks(false);
 
   Timer<TimerTest> timer(this, &TimerTest::countingTask);
   timer.startRepeating(10, BLINK_FROM_HERE);
   EXPECT_FLOAT_EQ(10.0, timer.repeatInterval());
   EXPECT_FLOAT_EQ(10.0, timer.nextFireInterval());
 
-  platform->advanceClockSeconds(123.0);  // Make the timer long overdue.
+  m_platform->advanceClockSeconds(123.0);  // Make the timer long overdue.
   timer.augmentRepeatInterval(10);
 
   EXPECT_FLOAT_EQ(20.0, timer.repeatInterval());
@@ -612,11 +483,7 @@
 }
 
 TEST_F(TimerTest, RepeatingTimerDoesNotDrift) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
-  platform->setAutoAdvanceNowToPendingTasks(false);
+  m_platform->setAutoAdvanceNowToPendingTasks(false);
 
   Timer<TimerTest> timer(this, &TimerTest::recordNextFireTimeTask);
   timer.startRepeating(2.0, BLINK_FROM_HERE);
@@ -626,24 +493,24 @@
 
   // Simulate timer firing early. Next scheduled task to run at
   // m_startTime + 4.0
-  platform->advanceClockSeconds(1.9);
-  runUntilDeadline(platform, monotonicallyIncreasingTime() + 0.2);
+  m_platform->advanceClockSeconds(1.9);
+  runUntilDeadline(monotonicallyIncreasingTime() + 0.2);
 
   // Next scheduled task to run at m_startTime + 6.0
-  platform->runForPeriodSeconds(2.0);
+  m_platform->runForPeriodSeconds(2.0);
   // Next scheduled task to run at m_startTime + 8.0
-  platform->runForPeriodSeconds(2.1);
+  m_platform->runForPeriodSeconds(2.1);
   // Next scheduled task to run at m_startTime + 10.0
-  platform->runForPeriodSeconds(2.9);
+  m_platform->runForPeriodSeconds(2.9);
   // Next scheduled task to run at m_startTime + 14.0 (skips a beat)
-  platform->advanceClockSeconds(3.1);
-  platform->runUntilIdle();
+  m_platform->advanceClockSeconds(3.1);
+  m_platform->runUntilIdle();
   // Next scheduled task to run at m_startTime + 18.0 (skips a beat)
-  platform->advanceClockSeconds(4.0);
-  platform->runUntilIdle();
+  m_platform->advanceClockSeconds(4.0);
+  m_platform->runUntilIdle();
   // Next scheduled task to run at m_startTime + 28.0 (skips 5 beats)
-  platform->advanceClockSeconds(10.0);
-  platform->runUntilIdle();
+  m_platform->advanceClockSeconds(10.0);
+  m_platform->runUntilIdle();
 
   EXPECT_THAT(
       m_nextFireTimes,
@@ -669,12 +536,8 @@
 };
 
 TEST_F(TimerTest, UserSuppliedWebTaskRunner) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   scoped_refptr<scheduler::TaskQueue> taskRunner(
-      platform->rendererScheduler()->NewTimerTaskRunner(
+      m_platform->rendererScheduler()->NewTimerTaskRunner(
           scheduler::TaskQueue::QueueType::TEST));
   RefPtr<scheduler::WebTaskRunnerImpl> webTaskRunner =
       scheduler::WebTaskRunnerImpl::create(taskRunner);
@@ -686,25 +549,17 @@
 }
 
 TEST_F(TimerTest, RunOnHeapTimer) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   RefPtr<OnHeapTimerOwner::Record> record = OnHeapTimerOwner::Record::create();
   Persistent<OnHeapTimerOwner> owner = new OnHeapTimerOwner(record);
 
   owner->startOneShot(0, BLINK_FROM_HERE);
 
   EXPECT_FALSE(record->timerHasFired());
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_TRUE(record->timerHasFired());
 }
 
 TEST_F(TimerTest, DestructOnHeapTimer) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   RefPtr<OnHeapTimerOwner::Record> record = OnHeapTimerOwner::Record::create();
   Persistent<OnHeapTimerOwner> owner = new OnHeapTimerOwner(record);
 
@@ -717,15 +572,11 @@
   EXPECT_TRUE(record->ownerIsDestructed());
 
   EXPECT_FALSE(record->timerHasFired());
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_FALSE(record->timerHasFired());
 }
 
 TEST_F(TimerTest, MarkOnHeapTimerAsUnreachable) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   RefPtr<OnHeapTimerOwner::Record> record = OnHeapTimerOwner::Record::create();
   Persistent<OnHeapTimerOwner> owner = new OnHeapTimerOwner(record);
 
@@ -741,7 +592,7 @@
   {
     GCForbiddenScope scope;
     EXPECT_FALSE(record->timerHasFired());
-    platform->runUntilIdle();
+    m_platform->runUntilIdle();
     EXPECT_FALSE(record->timerHasFired());
     EXPECT_FALSE(record->ownerIsDestructed());
   }
@@ -769,14 +620,10 @@
 }  // namespace
 
 TEST_F(TimerTest, MoveToNewTaskRunnerOneShot) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   std::vector<RefPtr<WebTaskRunner>> runOrder;
 
   scoped_refptr<scheduler::TaskQueue> taskRunner1(
-      platform->rendererScheduler()->NewTimerTaskRunner(
+      m_platform->rendererScheduler()->NewTimerTaskRunner(
           scheduler::TaskQueue::QueueType::TEST));
   RefPtr<scheduler::WebTaskRunnerImpl> webTaskRunner1 =
       scheduler::WebTaskRunnerImpl::create(taskRunner1);
@@ -784,7 +631,7 @@
   taskRunner1->AddTaskObserver(&taskObserver1);
 
   scoped_refptr<scheduler::TaskQueue> taskRunner2(
-      platform->rendererScheduler()->NewTimerTaskRunner(
+      m_platform->rendererScheduler()->NewTimerTaskRunner(
           scheduler::TaskQueue::QueueType::TEST));
   RefPtr<scheduler::WebTaskRunnerImpl> webTaskRunner2 =
       scheduler::WebTaskRunnerImpl::create(taskRunner2);
@@ -797,11 +644,11 @@
 
   timer.startOneShot(1, BLINK_FROM_HERE);
 
-  platform->runForPeriodSeconds(0.5);
+  m_platform->runForPeriodSeconds(0.5);
 
   timer.moveToNewTaskRunner(webTaskRunner2);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
 
   EXPECT_THAT(m_runTimes, ElementsAre(startTime + 1.0));
 
@@ -812,14 +659,10 @@
 }
 
 TEST_F(TimerTest, MoveToNewTaskRunnerRepeating) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   std::vector<RefPtr<WebTaskRunner>> runOrder;
 
   scoped_refptr<scheduler::TaskQueue> taskRunner1(
-      platform->rendererScheduler()->NewTimerTaskRunner(
+      m_platform->rendererScheduler()->NewTimerTaskRunner(
           scheduler::TaskQueue::QueueType::TEST));
   RefPtr<scheduler::WebTaskRunnerImpl> webTaskRunner1 =
       scheduler::WebTaskRunnerImpl::create(taskRunner1);
@@ -827,7 +670,7 @@
   taskRunner1->AddTaskObserver(&taskObserver1);
 
   scoped_refptr<scheduler::TaskQueue> taskRunner2(
-      platform->rendererScheduler()->NewTimerTaskRunner(
+      m_platform->rendererScheduler()->NewTimerTaskRunner(
           scheduler::TaskQueue::QueueType::TEST));
   RefPtr<scheduler::WebTaskRunnerImpl> webTaskRunner2 =
       scheduler::WebTaskRunnerImpl::create(taskRunner2);
@@ -840,11 +683,11 @@
 
   timer.startRepeating(1, BLINK_FROM_HERE);
 
-  platform->runForPeriodSeconds(2.5);
+  m_platform->runForPeriodSeconds(2.5);
 
   timer.moveToNewTaskRunner(webTaskRunner2);
 
-  platform->runForPeriodSeconds(2);
+  m_platform->runForPeriodSeconds(2);
 
   EXPECT_THAT(m_runTimes, ElementsAre(startTime + 1.0, startTime + 2.0,
                                       startTime + 3.0, startTime + 4.0));
@@ -859,25 +702,21 @@
 // This test checks that when inactive timer is moved to a different task
 // runner it isn't activated.
 TEST_F(TimerTest, MoveToNewTaskRunnerWithoutTasks) {
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform;
-  initialize(platform);
-
   scoped_refptr<scheduler::TaskQueue> taskRunner1(
-      platform->rendererScheduler()->NewTimerTaskRunner(
+      m_platform->rendererScheduler()->NewTimerTaskRunner(
           scheduler::TaskQueue::QueueType::TEST));
   RefPtr<scheduler::WebTaskRunnerImpl> webTaskRunner1 =
       scheduler::WebTaskRunnerImpl::create(taskRunner1);
 
   scoped_refptr<scheduler::TaskQueue> taskRunner2(
-      platform->rendererScheduler()->NewTimerTaskRunner(
+      m_platform->rendererScheduler()->NewTimerTaskRunner(
           scheduler::TaskQueue::QueueType::TEST));
   RefPtr<scheduler::WebTaskRunnerImpl> webTaskRunner2 =
       scheduler::WebTaskRunnerImpl::create(taskRunner2);
 
   TimerForTest<TimerTest> timer(webTaskRunner1, this, &TimerTest::countingTask);
 
-  platform->runUntilIdle();
+  m_platform->runUntilIdle();
   EXPECT_TRUE(!m_runTimes.size());
   EXPECT_TRUE(taskRunner1->IsEmpty());
   EXPECT_TRUE(taskRunner2->IsEmpty());
diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
index 1f816639..2122f02 100644
--- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
+++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
@@ -194,7 +194,7 @@
 // }
 template <class T, typename... Args>
 class ScopedTestingPlatformSupport final {
-  STACK_ALLOCATED();
+  WTF_MAKE_NONCOPYABLE(ScopedTestingPlatformSupport);
 
  public:
   explicit ScopedTestingPlatformSupport(Args&&... args) {
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
index bba0ca8..0189baf8 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
+++ b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
@@ -212,7 +212,9 @@
     int fetchEventID,
     std::unique_ptr<WebServiceWorkerError> error) {
   FetchEvent* fetchEvent = m_pendingPreloadFetchEvents.take(fetchEventID);
-  DCHECK(fetchEvent);
+  // This method may be called after onNavigationPreloadResponse() was called.
+  if (!fetchEvent)
+    return;
   fetchEvent->onNavigationPreloadError(
       workerGlobalScope()->scriptController()->getScriptState(),
       std::move(error));
diff --git a/third_party/WebKit/Source/wtf/HashMap.h b/third_party/WebKit/Source/wtf/HashMap.h
index 3ac16620a..f81c66f4 100644
--- a/third_party/WebKit/Source/wtf/HashMap.h
+++ b/third_party/WebKit/Source/wtf/HashMap.h
@@ -23,6 +23,7 @@
 
 #include "wtf/HashTable.h"
 #include "wtf/allocator/PartitionAllocator.h"
+#include <initializer_list>
 
 namespace WTF {
 
@@ -88,6 +89,15 @@
                   "Cannot put raw pointers to garbage-collected classes into "
                   "an off-heap HashMap.  Use HeapHashMap<> instead.");
   }
+  HashMap(const HashMap&) = default;
+  HashMap& operator=(const HashMap&) = default;
+  HashMap(HashMap&&) = default;
+  HashMap& operator=(HashMap&&) = default;
+
+  // For example, HashMap<int, int>({{1, 11}, {2, 22}, {3, 33}}) will give you
+  // a HashMap containing a mapping {1 -> 11, 2 -> 22, 3 -> 33}.
+  HashMap(std::initializer_list<ValueType> elements);
+  HashMap& operator=(std::initializer_list<ValueType> elements);
 
   typedef HashTableIteratorAdapter<HashTableType, ValueType> iterator;
   typedef HashTableConstIteratorAdapter<HashTableType, ValueType>
@@ -336,6 +346,31 @@
           typename W,
           typename X,
           typename Y>
+HashMap<T, U, V, W, X, Y>::HashMap(std::initializer_list<ValueType> elements) {
+  if (elements.size())
+    m_impl.reserveCapacityForSize(elements.size());
+  for (const ValueType& element : elements)
+    add(element.key, element.value);
+}
+
+template <typename T,
+          typename U,
+          typename V,
+          typename W,
+          typename X,
+          typename Y>
+auto HashMap<T, U, V, W, X, Y>::operator=(
+    std::initializer_list<ValueType> elements) -> HashMap& {
+  *this = HashMap(std::move(elements));
+  return *this;
+}
+
+template <typename T,
+          typename U,
+          typename V,
+          typename W,
+          typename X,
+          typename Y>
 inline unsigned HashMap<T, U, V, W, X, Y>::size() const {
   return m_impl.size();
 }
diff --git a/third_party/WebKit/Source/wtf/HashMapTest.cpp b/third_party/WebKit/Source/wtf/HashMapTest.cpp
index c3dbb39..622124f1 100644
--- a/third_party/WebKit/Source/wtf/HashMapTest.cpp
+++ b/third_party/WebKit/Source/wtf/HashMapTest.cpp
@@ -653,6 +653,62 @@
   map.clear();
 }
 
+bool isOneTwoThree(const HashMap<int, int>& map) {
+  return map.size() == 3 && map.contains(1) && map.contains(2) &&
+         map.contains(3) && map.get(1) == 11 && map.get(2) == 22 &&
+         map.get(3) == 33;
+};
+
+HashMap<int, int> returnOneTwoThree() {
+  return {{1, 11}, {2, 22}, {3, 33}};
+};
+
+TEST(HashMapTest, InitializerList) {
+  HashMap<int, int> empty({});
+  EXPECT_TRUE(empty.isEmpty());
+
+  HashMap<int, int> one({{1, 11}});
+  EXPECT_EQ(one.size(), 1u);
+  EXPECT_TRUE(one.contains(1));
+  EXPECT_EQ(one.get(1), 11);
+
+  HashMap<int, int> oneTwoThree({{1, 11}, {2, 22}, {3, 33}});
+  EXPECT_EQ(oneTwoThree.size(), 3u);
+  EXPECT_TRUE(oneTwoThree.contains(1));
+  EXPECT_TRUE(oneTwoThree.contains(2));
+  EXPECT_TRUE(oneTwoThree.contains(3));
+  EXPECT_EQ(oneTwoThree.get(1), 11);
+  EXPECT_EQ(oneTwoThree.get(2), 22);
+  EXPECT_EQ(oneTwoThree.get(3), 33);
+
+  // Put some jank so we can check if the assignments can clear them later.
+  empty.add(9999, 99999);
+  one.add(9999, 99999);
+  oneTwoThree.add(9999, 99999);
+
+  empty = {};
+  EXPECT_TRUE(empty.isEmpty());
+
+  one = {{1, 11}};
+  EXPECT_EQ(one.size(), 1u);
+  EXPECT_TRUE(one.contains(1));
+  EXPECT_EQ(one.get(1), 11);
+
+  oneTwoThree = {{1, 11}, {2, 22}, {3, 33}};
+  EXPECT_EQ(oneTwoThree.size(), 3u);
+  EXPECT_TRUE(oneTwoThree.contains(1));
+  EXPECT_TRUE(oneTwoThree.contains(2));
+  EXPECT_TRUE(oneTwoThree.contains(3));
+  EXPECT_EQ(oneTwoThree.get(1), 11);
+  EXPECT_EQ(oneTwoThree.get(2), 22);
+  EXPECT_EQ(oneTwoThree.get(3), 33);
+
+  // Other ways of construction: as a function parameter and in a return
+  // statement.
+  EXPECT_TRUE(isOneTwoThree({{1, 11}, {2, 22}, {3, 33}}));
+  EXPECT_TRUE(isOneTwoThree(returnOneTwoThree()));
+}
+
 }  // anonymous namespace
 
 }  // namespace WTF
diff --git a/third_party/libxslt/README.chromium b/third_party/libxslt/README.chromium
index 7d02fb6..4515315 100644
--- a/third_party/libxslt/README.chromium
+++ b/third_party/libxslt/README.chromium
@@ -1,6 +1,6 @@
 Name: libxslt
 URL: http://xmlsoft.org/XSLT
-Version: 8345634c5482ca04293ae1862d52fa9dd764aeca
+Version: 96c9c644f30ed762735802a27784cc522cff1643
 Security Critical: yes
 License: MIT
 License File: Copyright
@@ -16,9 +16,6 @@
 - Apply patch contributed here:
   https://bugs.chromium.org/p/chromium/issues/detail?id=583171#c17
 
-- Apply patch contributed here:
-  https://crbug.com/619006
-
 To import a new version:
 
 On Linux, get the latest tar via libxml.org and extract and replace
@@ -28,7 +25,8 @@
 mkdir linux && cd linux
 ../configure --without-debug --without-mem-debug --without-debugger --without-plugins --with-libxml-src=../../libxml/linux/ 
 
-Patch to not define HAVE_CLOCK_GETTIME.
+Patch to not define HAVE_CLOCK_GETTIME, HAVE_ASCTIME, HAVE_LOCALTIME
+or HAVE_MKTIME.
 
 Disable both branches of locale support in libxslt/xsltconfig.h. This
 file is shared between all platforms and there is no common locale
@@ -49,3 +47,6 @@
   python/
   tests/
   vms/
+
+This roll was done using a script:
+<https://github.com/dominiccooney/blink-tools/blob/bb28df6/roll.py>
diff --git a/third_party/libxslt/libxslt/attributes.c b/third_party/libxslt/libxslt/attributes.c
index 9165ab1..5958ef3 100644
--- a/third_party/libxslt/libxslt/attributes.c
+++ b/third_party/libxslt/libxslt/attributes.c
@@ -742,9 +742,9 @@
 /**
  * xsltAttribute:
  * @ctxt:  a XSLT process context
- * @node:  the current node in the source tree
+ * @contextNode:  the current node in the source tree
  * @inst:  the xsl:attribute element
- * @comp:  precomputed information
+ * @castedComp:  precomputed information
  *
  * Process the xslt attribute node on the source node
  */
diff --git a/third_party/libxslt/libxslt/functions.c b/third_party/libxslt/libxslt/functions.c
index a5e70210..ec22203 100644
--- a/third_party/libxslt/libxslt/functions.c
+++ b/third_party/libxslt/libxslt/functions.c
@@ -835,7 +835,7 @@
     }
     obj = valuePop(ctxt);
     tctxt = xsltXPathGetTransformContext(ctxt);
-    if (tctxt == NULL) {
+    if ((tctxt == NULL) || (tctxt->inst == NULL)) {
 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
 		"element-available() : internal error tctxt == NULL\n");
 	xmlXPathFreeObject(obj);
@@ -850,7 +850,7 @@
 
 	name = xmlStrdup(obj->stringval);
 	ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
-	if (ns != NULL) nsURI = xmlStrdup(ns->href);
+	if (ns != NULL) nsURI = ns->href;
     } else {
 	nsURI = xmlXPathNsLookup(ctxt->context, prefix);
 	if (nsURI == NULL) {
diff --git a/third_party/libxslt/libxslt/libxslt.syms b/third_party/libxslt/libxslt/libxslt.syms
index 3d9b5c6..a86cf42 100644
--- a/third_party/libxslt/libxslt/libxslt.syms
+++ b/third_party/libxslt/libxslt/libxslt.syms
@@ -107,7 +107,7 @@
   xsltFreeCompMatchList;
   xsltFreeTemplateHashes;
   xsltGetTemplate;
-  xsltMatchPattern;
+# xsltMatchPattern; removed in 1.0.12
   xsltTestCompMatchList;
 
 # preproc
@@ -210,6 +210,9 @@
 
 # xslt
   xsltCleanupGlobals;
+
+  local:
+    *;
 } ;
 
 LIBXML2_1.0.12 {
@@ -307,7 +310,6 @@
   xsltLibxmlVersion; # variable
   xsltLibxsltVersion; # variable
   xsltMaxDepth; # variable
-  xsltMaxVars; # variable
 
 # xsltInternals
   xsltParseStylesheetImportedDoc;
@@ -407,7 +409,7 @@
     global:
 
 # xsltInternals
-  xsltConstNamespaceNameXSLT; # variable
+# xsltConstNamespaceNameXSLT; requires switch REFACTORED
   xsltExtensionInstructionResultFinalize;
   xsltExtensionInstructionResultRegister;
   xsltInitCtxtKey;
@@ -416,24 +418,24 @@
   xsltInit;
 
 # xsltInternals
-  xsltParseAnyXSLTElem;
-  xsltParseSequenceConstructor;
-  xsltPointerListAddSize;
-  xsltPointerListClear;
-  xsltPointerListCreate;
-  xsltPointerListFree;
+# xsltParseAnyXSLTElem; requires switch REFACTORED
+# xsltParseSequenceConstructor; requires switch REFACTORED
+# xsltPointerListAddSize; requires switch REFACTORED
+# xsltPointerListClear; requires switch REFACTORED
+# xsltPointerListCreate; requires switch REFACTORED
+# xsltPointerListFree; requires switch REFACTORED
   xsltRegisterLocalRVT;
   xsltReleaseRVT;
-  xsltRestoreDocumentNamespaces;
+# xsltRestoreDocumentNamespaces; requires switch REFACTORED
 
 # extensions
-  xsltStyleStylesheetLevelGetExtData;
+# xsltStyleStylesheetLevelGetExtData; requires switch REFACTORED
 
 # xsltInternals
 # xsltTransStorageAdd; removed in 1.1.28
 # xsltTransStorageRemove; removed in 1.1.28
   xsltUninit;
-  xsltXSLTAttrMarker; # variable
+# xsltXSLTAttrMarker; requires switch REFACTORED
 } LIBXML2_1.1.9;
 
 LIBXML2_1.1.20 {
@@ -484,7 +486,17 @@
 # xsltlocale
   xsltFreeLocales;
 
+# xslt
+  xsltMaxVars; # variable
+
 # xsltutils
   xsltXPathCompileFlags;
 } LIBXML2_1.1.26;
 
+LIBXML2_1.1.30 {
+    global:
+
+# xsltInternals
+  xsltFlagRVTs;
+} LIBXML2_1.1.27;
+
diff --git a/third_party/libxslt/libxslt/pattern.c b/third_party/libxslt/libxslt/pattern.c
index e211a01..8359e4b 100644
--- a/third_party/libxslt/libxslt/pattern.c
+++ b/third_party/libxslt/libxslt/pattern.c
@@ -104,6 +104,7 @@
     const xmlChar *mode;         /* the mode */
     const xmlChar *modeURI;      /* the mode URI */
     xsltTemplatePtr template;    /* the associated template */
+    xmlNodePtr node;             /* the containing element */
 
     int direct;
     /* TODO fix the statically allocated size steps[] */
@@ -914,7 +915,9 @@
 	          xmlNodePtr matchNode, const xmlChar *mode,
 		  const xmlChar *modeURI) {
     int i;
+    int found = 0;
     xmlNodePtr node = matchNode;
+    xmlNodePtr oldInst;
     xsltStepOpPtr step, sel = NULL;
     xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
 
@@ -948,6 +951,10 @@
 	    return(0);
     }
 
+    /* Some XPath functions rely on inst being set correctly. */
+    oldInst = ctxt->inst;
+    ctxt->inst = comp->node;
+
     i = 0;
 restart:
     for (;i < comp->nbStep;i++) {
@@ -1140,12 +1147,9 @@
 		 * as possible this costly computation.
 		 */
 		if (comp->direct) {
-		    if (states.states != NULL) {
-			/* Free the rollback states */
-			xmlFree(states.states);
-		    }
-		    return(xsltTestCompMatchDirect(ctxt, comp, matchNode,
-						   comp->nsList, comp->nsNr));
+		    found = xsltTestCompMatchDirect(ctxt, comp, matchNode,
+						    comp->nsList, comp->nsNr);
+                    goto exit;
 		}
 
 		if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel))
@@ -1185,18 +1189,19 @@
 	}
     }
 found:
+    found = 1;
+exit:
+    ctxt->inst = oldInst;
     if (states.states != NULL) {
         /* Free the rollback states */
 	xmlFree(states.states);
     }
-    return(1);
+    return found;
 rollback:
     /* got an error try to rollback */
-    if (states.states == NULL)
-	return(0);
-    if (states.nbstates <= 0) {
-	xmlFree(states.states);
-	return(0);
+    if (states.states == NULL || states.nbstates <= 0) {
+        found = 0;
+	goto exit;
     }
     states.nbstates--;
     i = states.states[states.nbstates].step;
@@ -1944,6 +1949,7 @@
 	    goto error;
 	ctxt->cur = &(ctxt->base)[current - start];
 	element->pattern = ctxt->base;
+        element->node = node;
 	element->nsList = xmlGetNsList(doc, node);
 	j = 0;
 	if (element->nsList != NULL) {
diff --git a/third_party/libxslt/libxslt/templates.c b/third_party/libxslt/libxslt/templates.c
index 02193f7..ad83dce 100644
--- a/third_party/libxslt/libxslt/templates.c
+++ b/third_party/libxslt/libxslt/templates.c
@@ -63,6 +63,12 @@
     xmlNodePtr oldInst;
     int oldProximityPosition, oldContextSize;
 
+    if ((ctxt == NULL) || (ctxt->inst == NULL)) {
+        xsltTransformError(ctxt, NULL, NULL,
+            "xsltEvalXPathPredicate: No context or instruction\n");
+        return(0);
+    }
+
     oldContextSize = ctxt->xpathCtxt->contextSize;
     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
     oldNsNr = ctxt->xpathCtxt->nsNr;
@@ -124,6 +130,12 @@
     int oldNsNr;
     xmlNsPtr *oldNamespaces;
 
+    if ((ctxt == NULL) || (ctxt->inst == NULL)) {
+        xsltTransformError(ctxt, NULL, NULL,
+            "xsltEvalXPathStringNs: No context or instruction\n");
+        return(0);
+    }
+
     oldInst = ctxt->inst;
     oldNode = ctxt->node;
     oldPos = ctxt->xpathCtxt->proximityPosition;
diff --git a/third_party/libxslt/libxslt/transform.c b/third_party/libxslt/libxslt/transform.c
index b3fce80..519133f 100644
--- a/third_party/libxslt/libxslt/transform.c
+++ b/third_party/libxslt/libxslt/transform.c
@@ -89,10 +89,9 @@
 xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur);
 
 static xmlNodePtr
-xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
-		     xmlNodePtr invocNode,
-		     xmlNodePtr node,
-		     xmlNodePtr insert, int isLRE, int topElemVisited);
+xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
+	     xmlNodePtr node, xmlNodePtr insert, int isLRE,
+	     int topElemVisited);
 
 static void
 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
@@ -468,7 +467,7 @@
     return(xsltDoXIncludeDefault);
 }
 
-unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;
+static unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;
 
 /**
  * xsltDebugSetDefaultTrace:
@@ -767,9 +766,6 @@
  *									*
  ************************************************************************/
 
-xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt,
-                        xmlNodePtr node, xmlNodePtr insert, int literal);
-
 /**
  * xsltAddChild:
  * @parent:  the parent node
@@ -1124,7 +1120,7 @@
  *
  * Do a copy of an attribute.
  * Called by:
- *  - xsltCopyTreeInternal()
+ *  - xsltCopyTree()
  *  - xsltCopyOf()
  *  - xsltCopy()
  *
@@ -1225,7 +1221,7 @@
  * @target element node.
  *
  * Called by:
- *  - xsltCopyTreeInternal()
+ *  - xsltCopyTree()
  *
  * Returns 0 on success and -1 on errors and internal errors.
  */
@@ -1347,7 +1343,7 @@
 	    *  copy over all namespace nodes in scope.
 	    *  The damn thing about this is, that we would need to
 	    *  use the xmlGetNsList(), for every single node; this is
-	    *  also done in xsltCopyTreeInternal(), but only for the top node.
+	    *  also done in xsltCopyTree(), but only for the top node.
 	    */
 	    if (node->ns != NULL) {
 		if (isLRE) {
@@ -1405,7 +1401,7 @@
     xmlNodePtr copy, ret = NULL;
 
     while (list != NULL) {
-	copy = xsltCopyTreeInternal(ctxt, invocNode,
+	copy = xsltCopyTree(ctxt, invocNode,
 	    list, insert, isLRE, topElemVisited);
 	if (copy != NULL) {
 	    if (ret == NULL) {
@@ -1425,7 +1421,7 @@
  * Do a copy of a namespace list. If @node is non-NULL the
  * new namespaces are added automatically.
  * Called by:
- *   xsltCopyTreeInternal()
+ *   xsltCopyTree()
  *
  * QUESTION: What is the exact difference between this function
  *  and xsltCopyNamespaceList() in "namespaces.c"?
@@ -1583,7 +1579,7 @@
 }
 
 /**
- * xsltCopyTreeInternal:
+ * xsltCopyTree:
  * @ctxt:  the XSLT transformation context
  * @invocNode: responsible node in the stylesheet; used for error reports
  * @node:  the element node in the source tree
@@ -1602,10 +1598,9 @@
  * Returns a pointer to the new tree, or NULL in case of error
  */
 static xmlNodePtr
-xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
-		     xmlNodePtr invocNode,
-		     xmlNodePtr node,
-		     xmlNodePtr insert, int isLRE, int topElemVisited)
+xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
+	     xmlNodePtr node, xmlNodePtr insert, int isLRE,
+	     int topElemVisited)
 {
     xmlNodePtr copy;
 
@@ -1661,7 +1656,7 @@
 	copy = xsltAddChild(insert, copy);
         if (copy == NULL) {
             xsltTransformError(ctxt, NULL, invocNode,
-            "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
+            "xsltCopyTree: Copying of '%s' failed.\n", node->name);
             return (copy);
         }
 	/*
@@ -1792,34 +1787,11 @@
 	}
     } else {
 	xsltTransformError(ctxt, NULL, invocNode,
-	    "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
+	    "xsltCopyTree: Copying of '%s' failed.\n", node->name);
     }
     return(copy);
 }
 
-/**
- * xsltCopyTree:
- * @ctxt:  the XSLT transformation context
- * @node:  the element node in the source tree
- * @insert:  the parent in the result tree
- * @literal:  indicates if @node is a Literal Result Element
- *
- * Make a copy of the full tree under the element node @node
- * and insert it as last child of @insert
- * For literal result element, some of the namespaces may not be copied
- * over according to section 7.1.
- * TODO: Why is this a public function?
- *
- * Returns a pointer to the new tree, or NULL in case of error
- */
-xmlNodePtr
-xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
-	     xmlNodePtr insert, int literal)
-{
-    return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0));
-
-}
-
 /************************************************************************
  *									*
  *		Error/fallback processing				*
@@ -4428,8 +4400,7 @@
 			xsltShallowCopyAttr(ctxt, inst,
 			    ctxt->insert, (xmlAttrPtr) cur);
 		    } else {
-			xsltCopyTreeInternal(ctxt, inst,
-			    cur, ctxt->insert, 0, 0);
+			xsltCopyTree(ctxt, inst, cur, ctxt->insert, 0, 0);
 		    }
 		}
 	    }
diff --git a/third_party/libxslt/libxslt/variables.c b/third_party/libxslt/libxslt/variables.c
index e1a80ee..60b46eaf 100644
--- a/third_party/libxslt/libxslt/variables.c
+++ b/third_party/libxslt/libxslt/variables.c
@@ -40,7 +40,7 @@
 const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt";
 #endif
 
-const xmlChar *xsltComputingGlobalVarMarker =
+static const xmlChar *xsltComputingGlobalVarMarker =
  (const xmlChar *) " var/param being computed";
 
 #define XSLT_VAR_GLOBAL (1<<0)
diff --git a/third_party/libxslt/libxslt/variables.h b/third_party/libxslt/libxslt/variables.h
index f80eeab..24acf8d 100644
--- a/third_party/libxslt/libxslt/variables.h
+++ b/third_party/libxslt/libxslt/variables.h
@@ -38,9 +38,35 @@
  * Flags for memory management of RVTs
  */
 
+/**
+ * XSLT_RVT_LOCAL:
+ *
+ * RVT is destroyed after the current instructions ends.
+ */
 #define XSLT_RVT_LOCAL       ((void *)1)
+
+/**
+ * XSLT_RVT_VARIABLE:
+ *
+ * RVT is part of a local variable and destroyed after the variable goes out
+ * of scope.
+ */
 #define XSLT_RVT_VARIABLE    ((void *)2)
+
+/**
+ * XSLT_RVT_FUNC_RESULT:
+ *
+ * RVT is part of results returned with func:result. The RVT won't be
+ * destroyed after exiting a template and will be reset to XSLT_RVT_LOCAL or
+ * XSLT_RVT_VARIABLE in the template that receives the return value.
+ */
 #define XSLT_RVT_FUNC_RESULT ((void *)3)
+
+/**
+ * XSLT_RVT_GLOBAL:
+ *
+ * RVT is part of a global variable.
+ */
 #define XSLT_RVT_GLOBAL      ((void *)4)
 
 /*
diff --git a/third_party/libxslt/linux/Makefile b/third_party/libxslt/linux/Makefile
index bf20ed0..d3a6087 100644
--- a/third_party/libxslt/linux/Makefile
+++ b/third_party/libxslt/linux/Makefile
@@ -323,7 +323,7 @@
 PYTHON_SUBDIR = python
 PYTHON_VERSION = 2.7
 RANLIB = ranlib
-RELDATE = Wed Oct 12 2016
+RELDATE = Fri Jan 13 2017
 RM = /bin/rm
 SED = /bin/sed
 SET_MAKE = 
diff --git a/third_party/libxslt/linux/config.h b/third_party/libxslt/linux/config.h
index b0f908e..4760716 100644
--- a/third_party/libxslt/linux/config.h
+++ b/third_party/libxslt/linux/config.h
@@ -5,7 +5,7 @@
 /* #undef HAVE_ANSIDECL_H */
 
 /* Define to 1 if you have the `asctime' function. */
-#define HAVE_ASCTIME 1
+
 
 /* Define to 1 if you have the `clock_gettime' function. */
 
@@ -59,7 +59,7 @@
 #define HAVE_LOCALE_H 1
 
 /* Define to 1 if you have the `localtime' function. */
-#define HAVE_LOCALTIME 1
+
 
 /* Define to 1 if you have the `localtime_r' function. */
 #define HAVE_LOCALTIME_R 1
@@ -71,7 +71,7 @@
 #define HAVE_MEMORY_H 1
 
 /* Define to 1 if you have the `mktime' function. */
-#define HAVE_MKTIME 1
+
 
 /* Define to 1 if you have the <nan.h> header file. */
 /* #undef HAVE_NAN_H */
diff --git a/third_party/libxslt/linux/config.log b/third_party/libxslt/linux/config.log
index f0c2668..fe5b7a3 100644
--- a/third_party/libxslt/linux/config.log
+++ b/third_party/libxslt/linux/config.log
@@ -12,9 +12,9 @@
 
 hostname = REDACTED
 uname -m = x86_64
-uname -r = 3.13.0-96-generic
+uname -r = 3.13.0-105-generic
 uname -s = Linux
-uname -v = #143-Ubuntu SMP Mon Aug 29 20:15:20 UTC 2016
+uname -v = #152-Ubuntu SMP Fri Dec 2 15:37:11 UTC 2016
 
 /usr/bin/uname -p = unknown
 /bin/uname -X     = unknown
@@ -27,16 +27,6 @@
 /usr/bin/oslevel       = unknown
 /bin/universe          = unknown
 
-PATH: /usr/local/google/home/dominicc/depot_tools
-PATH: /usr/local/google/home/dominicc/depot_tools
-PATH: /usr/lib/google-golang/bin
-PATH: /usr/local/buildtools/java/jdk/bin
-PATH: /usr/local/sbin
-PATH: /usr/local/bin
-PATH: /usr/sbin
-PATH: /usr/bin
-PATH: /sbin
-PATH: /bin
 
 
 ## ----------- ##
@@ -1188,7 +1178,7 @@
 configure:13319: result: yes
 configure:13319: checking for _stat
 configure:13319: gcc -o conftest -g -O2   conftest.c  >&5
-/tmp/ccMuFRfj.o: In function `main':
+/tmp/ccHZIQtg.o: In function `main':
 /usr/local/google/work/ca/src/third_party/libxslt/linux/conftest.c:81: undefined reference to `_stat'
 collect2: error: ld returned 1 exit status
 configure:13319: $? = 1
@@ -1283,7 +1273,7 @@
 conftest.c:70:6: warning: conflicting types for built-in function 'pow' [enabled by default]
  char pow ();
       ^
-/tmp/ccCmsAqo.o: In function `main':
+/tmp/ccxi5htm.o: In function `main':
 /usr/local/google/work/ca/src/third_party/libxslt/linux/conftest.c:81: undefined reference to `pow'
 collect2: error: ld returned 1 exit status
 configure:13328: $? = 1
@@ -1385,7 +1375,7 @@
 conftest.c:71:6: warning: conflicting types for built-in function 'floor' [enabled by default]
  char floor ();
       ^
-/tmp/ccmX0exx.o: In function `main':
+/tmp/ccX5XrEw.o: In function `main':
 /usr/local/google/work/ca/src/third_party/libxslt/linux/conftest.c:82: undefined reference to `floor'
 collect2: error: ld returned 1 exit status
 configure:13377: $? = 1
@@ -1488,7 +1478,7 @@
 conftest.c:72:6: warning: conflicting types for built-in function 'fabs' [enabled by default]
  char fabs ();
       ^
-/tmp/cc40eoAH.o: In function `main':
+/tmp/cc99S55D.o: In function `main':
 /usr/local/google/work/ca/src/third_party/libxslt/linux/conftest.c:83: undefined reference to `fabs'
 collect2: error: ld returned 1 exit status
 configure:13426: $? = 1
@@ -2007,7 +1997,7 @@
 PYTHON_SUBDIR='python'
 PYTHON_VERSION='2.7'
 RANLIB='ranlib'
-RELDATE='Wed Oct 12 2016'
+RELDATE='Fri Jan 13 2017'
 RM='/bin/rm'
 SED='/bin/sed'
 SET_MAKE=''
diff --git a/third_party/libxslt/linux/libexslt/Makefile b/third_party/libxslt/linux/libexslt/Makefile
index f2b2d30..95868e02 100644
--- a/third_party/libxslt/linux/libexslt/Makefile
+++ b/third_party/libxslt/linux/libexslt/Makefile
@@ -296,7 +296,7 @@
 PYTHON_SUBDIR = python
 PYTHON_VERSION = 2.7
 RANLIB = ranlib
-RELDATE = Wed Oct 12 2016
+RELDATE = Fri Jan 13 2017
 RM = /bin/rm
 SED = /bin/sed
 SET_MAKE = 
diff --git a/third_party/libxslt/linux/libxslt.spec b/third_party/libxslt/linux/libxslt.spec
index 71e1aee..b2ddb57 100644
--- a/third_party/libxslt/linux/libxslt.spec
+++ b/third_party/libxslt/linux/libxslt.spec
@@ -129,5 +129,5 @@
 %doc python/tests/*.xsl
 
 %changelog
-* Wed Oct 12 2016 Daniel Veillard <veillard@redhat.com>
+* Fri Jan 13 2017 Daniel Veillard <veillard@redhat.com>
 - upstream release 1.1.29 see http://xmlsoft.org/XSLT/news.html
diff --git a/third_party/libxslt/linux/libxslt/Makefile b/third_party/libxslt/linux/libxslt/Makefile
index 10b6cad..53354ff3 100644
--- a/third_party/libxslt/linux/libxslt/Makefile
+++ b/third_party/libxslt/linux/libxslt/Makefile
@@ -299,7 +299,7 @@
 PYTHON_SUBDIR = python
 PYTHON_VERSION = 2.7
 RANLIB = ranlib
-RELDATE = Wed Oct 12 2016
+RELDATE = Fri Jan 13 2017
 RM = /bin/rm
 SED = /bin/sed
 SET_MAKE = 
diff --git a/third_party/libxslt/mac/config.h b/third_party/libxslt/mac/config.h
index c775e508..e6295c4 100644
--- a/third_party/libxslt/mac/config.h
+++ b/third_party/libxslt/mac/config.h
@@ -8,7 +8,7 @@
 #define HAVE_ASCTIME 1
 
 /* Define to 1 if you have the `clock_gettime' function. */
-/* #undef HAVE_CLOCK_GETTIME */
+
 
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #define HAVE_DLFCN_H 1
diff --git a/third_party/libxslt/win32/Makefile.mingw b/third_party/libxslt/win32/Makefile.mingw
index 5b102b38..4fc42fe 100644
--- a/third_party/libxslt/win32/Makefile.mingw
+++ b/third_party/libxslt/win32/Makefile.mingw
@@ -155,37 +155,37 @@
 utils : $(UTILS)
 
 clean :
-	cmd.exe /C if exist $(XSLT_INTDIR) rmdir /S /Q $(XSLT_INTDIR)
-	cmd.exe /C if exist $(XSLT_INTDIR_A) rmdir /S /Q $(XSLT_INTDIR_A)
-	cmd.exe /C if exist $(EXSLT_INTDIR) rmdir /S /Q $(EXSLT_INTDIR)
-	cmd.exe /C if exist $(EXSLT_INTDIR_A) rmdir /S /Q $(EXSLT_INTDIR_A)
-	cmd.exe /C if exist $(UTILS_INTDIR) rmdir /S /Q $(UTILS_INTDIR)
-	cmd.exe /C if exist $(BINDIR) rmdir /S /Q $(BINDIR)
-	cmd.exe /C if exist depends.mingw del depends.mingw
+	cmd.exe /C "if exist $(XSLT_INTDIR) rmdir /S /Q $(XSLT_INTDIR)"
+	cmd.exe /C "if exist $(XSLT_INTDIR_A) rmdir /S /Q $(XSLT_INTDIR_A)"
+	cmd.exe /C "if exist $(EXSLT_INTDIR) rmdir /S /Q $(EXSLT_INTDIR)"
+	cmd.exe /C "if exist $(EXSLT_INTDIR_A) rmdir /S /Q $(EXSLT_INTDIR_A)"
+	cmd.exe /C "if exist $(UTILS_INTDIR) rmdir /S /Q $(UTILS_INTDIR)"
+	cmd.exe /C "if exist $(BINDIR) rmdir /S /Q $(BINDIR)"
+	cmd.exe /C "if exist depends.mingw del depends.mingw"
 
 rebuild : clean all
 
 distclean : clean
-	cmd.exe /C if exist config.* del config.*
-	cmd.exe /C if exist depends.* del depends.*
-	cmd.exe /C if exist Makefile del Makefile
+	cmd.exe /C "if exist config.* del config.*"
+	cmd.exe /C "if exist depends.* del depends.*"
+	cmd.exe /C "if exist Makefile del Makefile"
 
 install-libs : all
-	cmd.exe /C if not exist $(INCPREFIX)\$(XSLT_BASENAME) mkdir $(INCPREFIX)\$(XSLT_BASENAME)
-	cmd.exe /C if not exist $(INCPREFIX)\$(EXSLT_BASENAME) mkdir $(INCPREFIX)\$(EXSLT_BASENAME)
-	cmd.exe /C if not exist $(BINPREFIX) mkdir $(BINPREFIX)
-	cmd.exe /C if not exist $(LIBPREFIX) mkdir $(LIBPREFIX)
-	cmd.exe /C copy $(XSLT_SRCDIR)\*.h $(INCPREFIX)\$(XSLT_BASENAME)
-	cmd.exe /C copy $(EXSLT_SRCDIR)\*.h $(INCPREFIX)\$(EXSLT_BASENAME)
-	cmd.exe /C copy $(BINDIR)\$(XSLT_SO) $(SOPREFIX)
-	cmd.exe /C copy $(BINDIR)\$(XSLT_A) $(LIBPREFIX)
-	cmd.exe /C copy $(BINDIR)\$(XSLT_IMP) $(LIBPREFIX)
-	cmd.exe /C copy $(BINDIR)\$(EXSLT_SO) $(SOPREFIX)
-	cmd.exe /C copy $(BINDIR)\$(EXSLT_A) $(LIBPREFIX)
-	cmd.exe /C copy $(BINDIR)\$(EXSLT_IMP) $(LIBPREFIX)
+	cmd.exe /C "if not exist $(INCPREFIX)\$(XSLT_BASENAME) mkdir $(INCPREFIX)\$(XSLT_BASENAME)"
+	cmd.exe /C "if not exist $(INCPREFIX)\$(EXSLT_BASENAME) mkdir $(INCPREFIX)\$(EXSLT_BASENAME)"
+	cmd.exe /C "if not exist $(BINPREFIX) mkdir $(BINPREFIX)"
+	cmd.exe /C "if not exist $(LIBPREFIX) mkdir $(LIBPREFIX)"
+	cmd.exe /C "copy $(XSLT_SRCDIR)\*.h $(INCPREFIX)\$(XSLT_BASENAME)"
+	cmd.exe /C "copy $(EXSLT_SRCDIR)\*.h $(INCPREFIX)\$(EXSLT_BASENAME)"
+	cmd.exe /C "copy $(BINDIR)\$(XSLT_SO) $(SOPREFIX)"
+	cmd.exe /C "copy $(BINDIR)\$(XSLT_A) $(LIBPREFIX)"
+	cmd.exe /C "copy $(BINDIR)\$(XSLT_IMP) $(LIBPREFIX)"
+	cmd.exe /C "copy $(BINDIR)\$(EXSLT_SO) $(SOPREFIX)"
+	cmd.exe /C "copy $(BINDIR)\$(EXSLT_A) $(LIBPREFIX)"
+	cmd.exe /C "copy $(BINDIR)\$(EXSLT_IMP) $(LIBPREFIX)"
 
 install : install-libs
-	cmd.exe /C copy $(BINDIR)\*.exe $(BINPREFIX)
+	cmd.exe /C "copy $(BINDIR)\*.exe $(BINPREFIX)"
 
 install-dist : install
 
@@ -205,16 +205,16 @@
 
 # Makes the compiler output directory.
 $(BINDIR) :
-	cmd.exe /C if not exist $(BINDIR) mkdir $(BINDIR)
+	cmd.exe /C "if not exist $(BINDIR) mkdir $(BINDIR)"
 
 
 # Makes the libxslt intermediate directory.
 $(XSLT_INTDIR) :
-	cmd.exe /C if not exist $(XSLT_INTDIR) mkdir $(XSLT_INTDIR)
+	cmd.exe /C "if not exist $(XSLT_INTDIR) mkdir $(XSLT_INTDIR)"
 
 # Makes the static libxslt intermediate directory.
 $(XSLT_INTDIR_A) :
-	cmd.exe /C if not exist $(XSLT_INTDIR_A) mkdir $(XSLT_INTDIR_A)
+	cmd.exe /C "if not exist $(XSLT_INTDIR_A) mkdir $(XSLT_INTDIR_A)"
 
 # An implicit rule for libxslt compilation.
 $(XSLT_INTDIR)/%.o : $(XSLT_SRCDIR)/%.c
@@ -244,11 +244,11 @@
 
 # Creates the libexslt intermediate directory.
 $(EXSLT_INTDIR) :
-	cmd.exe /C if not exist $(EXSLT_INTDIR) mkdir $(EXSLT_INTDIR)
+	cmd.exe /C "if not exist $(EXSLT_INTDIR) mkdir $(EXSLT_INTDIR)"
 
 # Creates the static libexslt intermediate directory.
 $(EXSLT_INTDIR_A) :
-	cmd.exe /C if not exist $(EXSLT_INTDIR_A) mkdir $(EXSLT_INTDIR_A)
+	cmd.exe /C "if not exist $(EXSLT_INTDIR_A) mkdir $(EXSLT_INTDIR_A)"
 
 # An implicit rule for libexslt compilation.
 $(EXSLT_INTDIR)/%.o : $(EXSLT_SRCDIR)/%.c
@@ -279,7 +279,7 @@
 
 # Creates the utils intermediate directory.
 $(UTILS_INTDIR) :
-	cmd.exe /C if not exist $(UTILS_INTDIR) mkdir $(UTILS_INTDIR)
+	cmd.exe /C "if not exist $(UTILS_INTDIR) mkdir $(UTILS_INTDIR)"
 
 # An implicit rule for xsltproc and friends.
 APPLIBS = $(LIBS)
diff --git a/third_party/libxslt/win32/configure.js b/third_party/libxslt/win32/configure.js
index 7f2f854..3974591 100644
--- a/third_party/libxslt/win32/configure.js
+++ b/third_party/libxslt/win32/configure.js
@@ -424,7 +424,7 @@
 if (buildLibPrefix == "")
 	buildLibPrefix = "$(PREFIX)" + dirSep + "lib";
 if (buildSoPrefix == "")
-	buildSoPrefix = "$(PREFIX)" + dirSep + "lib";
+	buildSoPrefix = "$(PREFIX)" + dirSep + "bin";
 
 // Discover the version.
 discoverVersion();
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index ad35641..14c9048a 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -9717,6 +9717,11 @@
   <obsolete>This menu item was never added to the app menu.</obsolete>
 </action>
 
+<action name="MobileMenuReadingList">
+  <owner>gambard@chromium.org</owner>
+  <description>User pressed 'Reading List' in the app menu.</description>
+</action>
+
 <action name="MobileMenuRecentTabs">
   <owner>justincohen@chromium.org</owner>
   <description>User pressed 'Recent tabs' in the app menu.</description>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 54248af..aae008f2 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -39036,10 +39036,10 @@
   <summary>
     Android: The position of a suggestion card on the NTP that is dismissed,
     typically by swiping it away. The suggestion had a URL that was NOT visited
-    before. We track the position the suggestion had in the list when the NTP
-    was loaded. This tracked position can be different from the position
+    before. We track the position the card had in the list when it was first
+    seen by the user. This tracked position can be different from the position
     observed by the user, e.g. when the user dismissed some suggestions from the
-    list before.
+    list or requested more that got inserted in the middle of the feed.
   </summary>
 </histogram>
 
@@ -39048,10 +39048,10 @@
   <summary>
     Android: The position of a suggestion card on the NTP that is dismissed,
     typically by swiping it away. The suggestion had a URL that was visited
-    before. We track the position the suggestion had in the list when the NTP
-    was loaded. This tracked position can be different from the position
+    before. We track the position the card had in the list when it was first
+    seen by the user. This tracked position can be different from the position
     observed by the user, e.g. when the user dismissed some suggestions from the
-    list before.
+    list or requested more that got inserted in the middle of the feed.
   </summary>
 </histogram>
 
@@ -39103,9 +39103,10 @@
     Android: The position of a &quot;More&quot; suggestion card that was clicked
     on the NTP. A card is considered shown when at least 1/3 of its height is
     visible on the screen. We track the position the card had in the list when
-    the NTP was loaded. This tracked position can be different from the position
-    observed by the user, e.g. when the user dismissed some suggestions from the
-    list.
+    it was first seen by the user. This tracked position can be different from
+    the position observed by the user, e.g. when the user dismissed some
+    suggestions from the list or requested more that got inserted in the middle
+    of the feed.
   </summary>
 </histogram>
 
@@ -39115,10 +39116,10 @@
     Android: The position of a &quot;More&quot; suggestion card that was shown
     on the NTP. A card is considered shown when at least 1/3 of its height is
     visible on the screen. For each card, at most one impression is recorded per
-    NTP instance. We track the position the card had in the list when the NTP
-    was loaded. This tracked position can be different from the position
-    observed by the user, e.g. when the user dismissed some suggestions from the
-    list.
+    NTP instance. We track the position the card had in the list when it was
+    first seen by the user. This tracked position can be different from the
+    position observed by the user, e.g. when the user dismissed some suggestions
+    from the list or requested more that got inserted in the middle of the feed.
   </summary>
 </histogram>
 
@@ -39164,11 +39165,12 @@
 <histogram name="NewTabPage.ContentSuggestions.Opened" units="index">
   <owner>treib@chromium.org</owner>
   <summary>
-    Android: The position of a suggestion card on the NTP that is clicked
-    through to the host website of the content. We track the position the
-    suggestion had in the list when the NTP was loaded. This tracked position
+    Android: The position of the suggestion card on the NTP, that is clicked
+    through to the host website of the content. We track the position the card
+    had in the list when it was first seen by the user. This tracked position
     can be different from the position observed by the user, e.g. when the user
-    dismissed some suggestions from the list.
+    dismissed some suggestions from the list or requested more that got inserted
+    in the middle of the feed.
   </summary>
 </histogram>
 
@@ -39188,8 +39190,8 @@
     Android: The index of a category on the NTP, whose suggestion card is
     clicked through to the host website of the content. This tracked index can
     be different from the position observed by the user, e.g. for the user a
-    category may be at the top of the NTP, but with index 1, because there is an
-    empty category above (with index 0).
+    category may be at the top of the NTP, but with index 1, because they
+    dismissed the one that was previously at the top.
   </summary>
 </histogram>
 
@@ -39225,9 +39227,10 @@
     Android: The position of a suggestion card that was shown on the NTP. A card
     is considered shown when at least 1/3 of its height is visible on the
     screen. For each card, at most one impression is recorded per NTP instance.
-    We track the position the suggestion had in the list when the NTP was
-    loaded. This tracked position can be different from the position observed by
-    the user, e.g. when the user dismissed some suggestions from the list.
+    We track the position the card had in the list when it was first seen by the
+    user. This tracked position can be different from the position observed by
+    the user, e.g. when the user dismissed some suggestions from the list or
+    requested more that got inserted in the middle of the feed.
   </summary>
 </histogram>
 
@@ -44799,8 +44802,11 @@
   <owner>yfriedman@chromium.org</owner>
   <summary>
     Breakdown on trigger rate of providing a password form autofill entry based
-    on matching stored information using the public suffix list for possible
-    matches.
+    on matching stored information using the public suffix list (PSL) for
+    possible matches. In addition, this metric also counts cases where a
+    existing federated entry was successfully matched via PSL. For example, this
+    includes cases where an existing federated credential for
+    https://example.com was used for https://subdomain.example.com.
   </summary>
 </histogram>
 
@@ -79704,9 +79710,12 @@
 <enum name="CaptureStartupResult" type="int">
   <int value="0" label="No data callback"/>
   <int value="1" label="OK"/>
-  <int value="2" label="Failed to create stream"/>
-  <int value="3" label="Failed to open stream"/>
+  <int value="2" label="Failed to create high latency stream"/>
+  <int value="3" label="Failed to open high latency stream"/>
   <int value="4" label="Never received any data"/>
+  <int value="5" label="No data received and capture stopped within 500ms"/>
+  <int value="6" label="Failed to create low latency stream"/>
+  <int value="7" label="Failed to open low latency stream"/>
 </enum>
 
 <enum name="CastCertificateStatus" type="int">
@@ -95069,6 +95078,7 @@
   <int value="-763759697" label="enable-audio-support-for-desktop-share"/>
   <int value="-759830869" label="enable-tab-discarding"/>
   <int value="-749048160" label="enable-panels"/>
+  <int value="-747463111" label="ContentSuggestionsNotifications:disabled"/>
   <int value="-746328467" label="ExpensiveBackgroundTimerThrottling:disabled"/>
   <int value="-744159181" label="disable-spdy-proxy-dev-auth-origin"/>
   <int value="-743103250" label="enable-linkable-ephemeral-apps"/>
@@ -95524,6 +95534,7 @@
   <int value="1352447982" label="enable-lcd-text"/>
   <int value="1359972809" label="enable-gesture-deletion"/>
   <int value="1361047396" label="disable-click-delay"/>
+  <int value="1361073386" label="ContentSuggestionsNotifications:enabled"/>
   <int value="1367529437" label="NTPAssetDownloadSuggestions:enabled"/>
   <int value="1367671275" label="enable-proximity-auth-proximity-detection"/>
   <int value="1371092708" label="disable-desktop-capture-picker-old-ui"/>
@@ -100154,6 +100165,7 @@
   <int value="0" label="Matching not used"/>
   <int value="1" label="No match"/>
   <int value="2" label="Match"/>
+  <int value="3" label="Federated match"/>
 </enum>
 
 <enum name="PasswordManagerShowEmptyUsername" type="int">
@@ -101072,6 +101084,7 @@
   <int value="54" label="BLOCK_THIRD_PARTY_COOKIES"/>
   <int value="55" label="CREDENTIAL_MANAGER_API"/>
   <int value="56" label="NOSTATE_PREFETCH_FINISHED"/>
+  <int value="57" label="LOW_END_DEVICE"/>
 </enum>
 
 <enum name="PrerenderHoverEvent" type="int">