diff --git a/DEPS b/DEPS
index 0ead00e..93a093a 100644
--- a/DEPS
+++ b/DEPS
@@ -403,7 +403,7 @@
 
     # For Linux and Chromium OS.
     'src/third_party/cros_system_api':
-      Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '0fed00b89ef09f5cafa1d4ed1f6c816b9710053e',
+      Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + 'e79b0c771217d6b84b85c692ae5740df8e8a2b36',
 
     # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
     'src/third_party/chromite':
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 0bf51f9..2e817ae 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -481,6 +481,8 @@
   # TODO(pcc): Make this conditional on is_official_build rather than on gn
   # flags for specific features.
   if (!is_debug && (allow_posix_link_time_opt || is_cfi) && !is_nacl) {
+    assert(use_lld || is_chromeos, "gold plugin only supported with ChromeOS")
+
     if (use_thin_lto) {
       cflags += [ "-flto=thin" ]
       ldflags += [ "-flto=thin" ]
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 83369d9..73f3bf0 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -120,6 +120,14 @@
 }
 
 declare_args() {
+  # Set to true to use lld, the LLVM linker. This flag may be used on Windows,
+  # Linux or Fuchsia.
+  use_lld = (is_win && host_os != "win") || is_fuchsia ||
+            ((allow_posix_link_time_opt || is_cfi) && target_os == "linux" &&
+             !is_chromeos && target_cpu == "x64")
+}
+
+declare_args() {
   # Whether to use the gold linker from binutils instead of lld or bfd.
   use_gold =
       (!use_lld && !(is_chromecast && is_linux &&
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni
index 70e0fa9..79f8ff8 100644
--- a/build/toolchain/toolchain.gni
+++ b/build/toolchain/toolchain.gni
@@ -60,12 +60,6 @@
     # Clang compiler version. Clang files are placed at version-dependent paths.
     clang_version = "5.0.0"
   }
-
-  # Set to true to use lld, the LLVM linker. This flag may be used on Windows
-  # or Linux.
-  use_lld = (is_win && host_os != "win") || is_fuchsia ||
-            (allow_posix_link_time_opt && target_os == "linux" &&
-             !is_chromeos && target_cpu == "x64")
 }
 
 # Check target_os here instead of is_ios as this file is loaded for secondary
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index 3938185..1417d93 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/browser/media/media_browsertest.h"
@@ -107,6 +108,9 @@
 // The type of video src used to load media.
 enum class SrcType { SRC, MSE };
 
+// How the CDM is hosted, using pepper or mojo.
+enum class CdmHostType { kPepper, kMojo };
+
 // Must be in sync with CONFIG_CHANGE_TYPE in eme_player_js/global.js
 enum class ConfigChangeType {
   CLEAR_TO_CLEAR = 0,
@@ -278,6 +282,7 @@
 #endif  // BUILDFLAG(ENABLE_PEPPER_CDMS)
 
   void SetUpCommandLineForKeySystem(const std::string& key_system,
+                                    CdmHostType cdm_host_type,
                                     base::CommandLine* command_line) {
     if (GetServerConfig(key_system))
       // Since the web and license servers listen on different ports, we need to
@@ -291,20 +296,28 @@
                         media::kClearKeyCdmAdapterFileName,
                         media::kClearKeyCdmDisplayName,
                         media::kClearKeyCdmPepperMimeType);
-      command_line->AppendSwitchASCII(switches::kEnableFeatures,
-                                      media::kExternalClearKeyForTesting.name);
+      if (cdm_host_type == CdmHostType::kMojo) {
+        scoped_feature_list_.InitWithFeatures(
+            {media::kExternalClearKeyForTesting, media::kMojoCdm}, {});
+      } else {
+        scoped_feature_list_.InitWithFeatures(
+            {media::kExternalClearKeyForTesting}, {});
+      }
     }
 #endif  // BUILDFLAG(ENABLE_PEPPER_CDMS)
   }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 #if BUILDFLAG(ENABLE_PEPPER_CDMS)
 // Tests encrypted media playback using ExternalClearKey key system in
 // decrypt-and-decode mode.
-class ECKEncryptedMediaTest : public EncryptedMediaTestBase {
+class ECKEncryptedMediaTest : public EncryptedMediaTestBase,
+                              public testing::WithParamInterface<CdmHostType> {
  public:
-  // We use special |key_system| names to do non-playback related tests, e.g.
-  // kExternalClearKeyFileIOTestKeySystem is used to test file IO.
+  // We use special |key_system| names to do non-playback related tests,
+  // e.g. kExternalClearKeyFileIOTestKeySystem is used to test file IO.
   void TestNonPlaybackCases(const std::string& key_system,
                             const std::string& expected_title) {
     // Since we do not test playback, arbitrarily choose a test file and source
@@ -319,15 +332,18 @@
                         const std::string& session_to_load,
                         const std::string& expected_title) {
     RunEncryptedMediaTest(kDefaultEmePlayer, "bear-320x240-v_enc-v.webm",
-                          kWebMVP8VideoOnly, key_system, SrcType::SRC,
+                          kWebMVP8VideoOnly, key_system, SrcType::MSE,
                           session_to_load, false, PlayCount::ONCE,
                           expected_title);
   }
 
+  bool IsUsingMojoCdm() const { return GetParam() == CdmHostType::kMojo; }
+
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EncryptedMediaTestBase::SetUpCommandLine(command_line);
-    SetUpCommandLineForKeySystem(kExternalClearKeyKeySystem, command_line);
+    SetUpCommandLineForKeySystem(kExternalClearKeyKeySystem, GetParam(),
+                                 command_line);
   }
 };
 
@@ -337,7 +353,8 @@
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EncryptedMediaTestBase::SetUpCommandLine(command_line);
-    SetUpCommandLineForKeySystem(kWidevineKeySystem, command_line);
+    SetUpCommandLineForKeySystem(kWidevineKeySystem, CdmHostType::kPepper,
+                                 command_line);
   }
 };
 
@@ -351,9 +368,10 @@
 // Note: Only parameterized (*_P) tests can be used. Non-parameterized (*_F)
 // tests will crash at GetParam(). To add non-parameterized tests, use
 // EncryptedMediaTestBase or one of its subclasses (e.g. WVEncryptedMediaTest).
-class EncryptedMediaTest : public EncryptedMediaTestBase,
-                           public testing::WithParamInterface<
-                               std::tr1::tuple<const char*, SrcType>> {
+class EncryptedMediaTest
+    : public EncryptedMediaTestBase,
+      public testing::WithParamInterface<
+          std::tr1::tuple<const char*, SrcType, CdmHostType>> {
  public:
   std::string CurrentKeySystem() {
     return std::tr1::get<0>(GetParam());
@@ -363,6 +381,8 @@
     return std::tr1::get<1>(GetParam());
   }
 
+  CdmHostType CurrentCdmHostType() { return std::tr1::get<2>(GetParam()); }
+
   void TestSimplePlayback(const std::string& encrypted_media,
                           const std::string& media_type) {
     RunSimpleEncryptedMediaTest(encrypted_media, media_type, CurrentKeySystem(),
@@ -449,7 +469,8 @@
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EncryptedMediaTestBase::SetUpCommandLine(command_line);
-    SetUpCommandLineForKeySystem(CurrentKeySystem(), command_line);
+    SetUpCommandLineForKeySystem(CurrentKeySystem(), CurrentCdmHostType(),
+                                 command_line);
   }
 };
 
@@ -459,26 +480,40 @@
 INSTANTIATE_TEST_CASE_P(MSE_ClearKey,
                         EncryptedMediaTest,
                         Combine(Values(kClearKeyKeySystem),
-                                Values(SrcType::MSE)));
+                                Values(SrcType::MSE),
+                                Values(CdmHostType::kPepper)));
 
 // External Clear Key is currently only used on platforms that use Pepper CDMs.
 #if BUILDFLAG(ENABLE_PEPPER_CDMS)
 INSTANTIATE_TEST_CASE_P(SRC_ExternalClearKey,
                         EncryptedMediaTest,
                         Combine(Values(kExternalClearKeyKeySystem),
-                                Values(SrcType::SRC)));
+                                Values(SrcType::SRC),
+                                Values(CdmHostType::kPepper)));
 
 INSTANTIATE_TEST_CASE_P(MSE_ExternalClearKey,
                         EncryptedMediaTest,
                         Combine(Values(kExternalClearKeyKeySystem),
-                                Values(SrcType::MSE)));
-#else
+                                Values(SrcType::MSE),
+                                Values(CdmHostType::kPepper)));
+
+// External Clear Key does not work with mojo CDM on Mac yet.
+// See http://crbug.com/736106
+#if !defined(OS_MACOSX)
+INSTANTIATE_TEST_CASE_P(MSE_ExternalClearKey_Mojo,
+                        EncryptedMediaTest,
+                        Combine(Values(kExternalClearKeyKeySystem),
+                                Values(SrcType::MSE),
+                                Values(CdmHostType::kMojo)));
+#endif  // !defined(OS_MACOSX)
+#else   // BUILDFLAG(ENABLE_PEPPER_CDMS)
 // To reduce test time, only run ClearKey SRC tests when we are not running
 // ExternalClearKey SRC tests.
 INSTANTIATE_TEST_CASE_P(SRC_ClearKey,
                         EncryptedMediaTest,
                         Combine(Values(kClearKeyKeySystem),
-                                Values(SrcType::SRC)));
+                                Values(SrcType::SRC),
+                                Values(CdmHostType::kPepper)));
 #endif  // BUILDFLAG(ENABLE_PEPPER_CDMS)
 
 #if defined(WIDEVINE_CDM_AVAILABLE)
@@ -486,7 +521,14 @@
 INSTANTIATE_TEST_CASE_P(MSE_Widevine,
                         EncryptedMediaTest,
                         Combine(Values(kWidevineKeySystem),
-                                Values(SrcType::MSE)));
+                                Values(SrcType::MSE),
+                                Values(CdmHostType::kPepper)));
+
+INSTANTIATE_TEST_CASE_P(MSE_Widevine_Mojo,
+                        EncryptedMediaTest,
+                        Combine(Values(kWidevineKeySystem),
+                                Values(SrcType::MSE),
+                                Values(CdmHostType::kMojo)));
 #endif  // !defined(OS_CHROMEOS)
 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
 
@@ -648,53 +690,98 @@
 }
 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
 
+INSTANTIATE_TEST_CASE_P(Pepper,
+                        ECKEncryptedMediaTest,
+                        Values(CdmHostType::kPepper));
+
+// External Clear Key does not work with mojo CDM on Mac yet.
+// See http://crbug.com/736106
+#if !defined(OS_MACOSX)
+INSTANTIATE_TEST_CASE_P(Mojo,
+                        ECKEncryptedMediaTest,
+                        Values(CdmHostType::kMojo));
+#endif  // !defined(OS_MACOSX)
+
 #if BUILDFLAG(ENABLE_PEPPER_CDMS)
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, InitializeCDMFail) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, InitializeCDMFail) {
   TestNonPlaybackCases(kExternalClearKeyInitializeFailKeySystem,
                        kEmeNotSupportedError);
 }
 
 // When CDM crashes, we should still get a decode error and all sessions should
 // be closed.
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, CDMCrashDuringDecode) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, CDMCrashDuringDecode) {
+  // TODO(xhwang): Handle mojo CDM crash correctly. See http://crbug.com/730766
+  if (IsUsingMojoCdm()) {
+    DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
+    return;
+  }
+
   IgnorePluginCrash();
   TestNonPlaybackCases(kExternalClearKeyCrashKeySystem,
                        kEmeSessionClosedAndError);
 }
 
 // Testing that the media browser test does fail on plugin crash.
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, CDMExpectedCrash) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, CDMExpectedCrash) {
+  // TODO(xhwang): Handle mojo CDM crash correctly. See http://crbug.com/730766
+  if (IsUsingMojoCdm()) {
+    DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
+    return;
+  }
+
   // Plugin crash is not ignored by default, the test is expected to fail.
   EXPECT_NONFATAL_FAILURE(TestNonPlaybackCases(kExternalClearKeyCrashKeySystem,
                                                kEmeSessionClosedAndError),
                           "Failing test due to plugin crash.");
 }
 
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, FileIOTest) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, FileIOTest) {
+  // TODO(jrummell): Support file IO in mojo CDM. See http://crbug.com/479923
+  if (IsUsingMojoCdm()) {
+    DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
+    return;
+  }
+
   TestNonPlaybackCases(kExternalClearKeyFileIOTestKeySystem, kUnitTestSuccess);
 }
 
 // TODO(xhwang): Investigate how to fake capturing activities to test the
 // network link detection logic in OutputProtectionProxy.
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, OutputProtectionTest) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, OutputProtectionTest) {
+  // TODO(xhwang): Support output protection in mojo CDM. See
+  // http://crbug.com/479843
+  if (IsUsingMojoCdm()) {
+    DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
+    return;
+  }
+
   TestNonPlaybackCases(kExternalClearKeyOutputProtectionTestKeySystem,
                        kUnitTestSuccess);
 }
 
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, PlatformVerificationTest) {
+// TODO(xhwang): Update this test to cover mojo PlatformVerification service
+// on ChromeOS. See http://crbug.com/479836
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, PlatformVerificationTest) {
   TestNonPlaybackCases(kExternalClearKeyPlatformVerificationTestKeySystem,
                        kUnitTestSuccess);
 }
 
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, Renewal) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, Renewal) {
+  // TODO(xhwang): Fix renewal time. See http://crbug.com/730762
+  if (IsUsingMojoCdm()) {
+    DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
+    return;
+  }
+
   TestPlaybackCase(kExternalClearKeyRenewalKeySystem, kNoSessionToLoad, kEnded);
 }
 
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, LoadLoadableSession) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, LoadLoadableSession) {
   TestPlaybackCase(kExternalClearKeyKeySystem, kLoadableSession, kEnded);
 }
 
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, LoadUnknownSession) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, LoadUnknownSession) {
   TestPlaybackCase(kExternalClearKeyKeySystem, kUnknownSession,
                    kEmeSessionNotFound);
 }
@@ -702,14 +789,14 @@
 const char kExternalClearKeyDecryptOnlyKeySystem[] =
     "org.chromium.externalclearkey.decryptonly";
 
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, DecryptOnly_VideoAudio_WebM) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, DecryptOnly_VideoAudio_WebM) {
   RunSimpleEncryptedMediaTest(
       "bear-320x240-av_enc-av.webm", kWebMVorbisAudioVP8Video,
       kExternalClearKeyDecryptOnlyKeySystem, SrcType::MSE);
 }
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, DecryptOnly_VideoOnly_MP4_VP9) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, DecryptOnly_VideoOnly_MP4_VP9) {
   RunSimpleEncryptedMediaTest(
       "bear-320x240-v_frag-vp9-cenc.mp4", kMP4VideoVp9Only,
       kExternalClearKeyDecryptOnlyKeySystem, SrcType::MSE);
@@ -718,7 +805,14 @@
 #endif  // BUILDFLAG(ENABLE_PEPPER_CDMS)
 
 #if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
-IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, VerifyCdmHostTest) {
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, VerifyCdmHostTest) {
+  // TODO(xhwang): Enable CDM host verification in mojo CDM. See
+  // http://crbug.com/730770
+  if (IsUsingMojoCdm()) {
+    DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
+    return;
+  }
+
   TestNonPlaybackCases(kExternalClearKeyVerifyCdmHostTestKeySystem,
                        kUnitTestSuccess);
 }
diff --git a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
index 5f3c17c5..9e20e63c 100644
--- a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
+++ b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
@@ -46,6 +46,7 @@
 #include "components/ntp_snippets/features.h"
 #include "components/ntp_snippets/ntp_snippets_constants.h"
 #include "components/ntp_snippets/remote/persistent_scheduler.h"
+#include "components/ntp_snippets/remote/prefetched_pages_tracker.h"
 #include "components/ntp_snippets/remote/remote_suggestions_database.h"
 #include "components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h"
 #include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h"
@@ -83,6 +84,7 @@
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
 #include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h"
 #include "components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h"
+#include "components/ntp_snippets/remote/prefetched_pages_tracker_impl.h"
 #include "components/offline_pages/core/background/request_coordinator.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/offline_pages/core/offline_page_model.h"
@@ -105,6 +107,7 @@
 using ntp_snippets::GetPushUpdatesSubscriptionEndpoint;
 using ntp_snippets::GetPushUpdatesUnsubscriptionEndpoint;
 using ntp_snippets::PersistentScheduler;
+using ntp_snippets::PrefetchedPagesTracker;
 using ntp_snippets::RemoteSuggestionsDatabase;
 using ntp_snippets::RemoteSuggestionsFetcherImpl;
 using ntp_snippets::RemoteSuggestionsProviderImpl;
@@ -124,10 +127,11 @@
 #endif  // OS_ANDROID
 
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
+using ntp_snippets::PrefetchedPagesTrackerImpl;
 using ntp_snippets::RecentTabSuggestionsProvider;
 using offline_pages::OfflinePageModel;
-using offline_pages::RequestCoordinator;
 using offline_pages::OfflinePageModelFactory;
+using offline_pages::RequestCoordinator;
 using offline_pages::RequestCoordinatorFactory;
 #endif  // BUILDFLAG(ENABLE_OFFLINE_PAGES)
 
@@ -298,7 +302,8 @@
 void RegisterArticleProviderIfEnabled(ContentSuggestionsService* service,
                                       Profile* profile,
                                       SigninManagerBase* signin_manager,
-                                      UserClassifier* user_classifier) {
+                                      UserClassifier* user_classifier,
+                                      OfflinePageModel* offline_page_model) {
   if (!IsArticleProviderEnabled()) {
     return;
   }
@@ -337,6 +342,11 @@
           chrome::android::kContentSuggestionsSettings)) {
     additional_toggle_pref = prefs::kSearchSuggestEnabled;
   }
+  std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker;
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
+  prefetched_pages_tracker =
+      base::MakeUnique<PrefetchedPagesTrackerImpl>(offline_page_model);
+#endif  // BUILDFLAG(ENABLE_OFFLINE_PAGES)
   auto suggestions_fetcher = base::MakeUnique<RemoteSuggestionsFetcherImpl>(
       signin_manager, token_service, request_context, pref_service,
       language_model, base::Bind(&safe_json::SafeJsonParser::Parse),
@@ -349,7 +359,8 @@
                                          request_context.get()),
       base::MakeUnique<RemoteSuggestionsDatabase>(database_dir, task_runner),
       base::MakeUnique<RemoteSuggestionsStatusService>(
-          signin_manager, pref_service, additional_toggle_pref));
+          signin_manager, pref_service, additional_toggle_pref),
+      std::move(prefetched_pages_tracker));
 
   service->remote_suggestions_scheduler()->SetProvider(provider.get());
   service->set_remote_suggestions_provider(provider.get());
@@ -510,7 +521,7 @@
       std::move(scheduler));
 
   RegisterArticleProviderIfEnabled(service, profile, signin_manager,
-                                   user_classifier_raw);
+                                   user_classifier_raw, offline_page_model);
   RegisterBookmarkProviderIfEnabled(service, profile);
   RegisterForeignSessionsProviderIfEnabled(service, profile);
 
diff --git a/chrome/browser/search/hotword_audio_history_handler.cc b/chrome/browser/search/hotword_audio_history_handler.cc
index 1553fc8..0d418db30 100644
--- a/chrome/browser/search/hotword_audio_history_handler.cc
+++ b/chrome/browser/search/hotword_audio_history_handler.cc
@@ -54,6 +54,8 @@
 
 void HotwordAudioHistoryHandler::GetAudioHistoryEnabled(
     const HotwordAudioHistoryCallback& callback) {
+// Please add network traffic annotation if you want to remove this #if.
+#if defined(OS_CHROMEOS)
   history::WebHistoryService* web_history = GetWebHistory();
   if (web_history) {
     web_history->GetAudioHistoryEnabled(
@@ -66,11 +68,17 @@
     PrefService* prefs = profile_->GetPrefs();
     callback.Run(false, prefs->GetBoolean(prefs::kHotwordAudioLoggingEnabled));
   }
+#else
+  NOTREACHED()
+      << ": This functions is supposed to be called only in Chrome OS.";
+#endif  // defined(OS_CHROMEOS)
 }
 
 void HotwordAudioHistoryHandler::SetAudioHistoryEnabled(
     const bool enabled,
     const HotwordAudioHistoryCallback& callback) {
+// Please add network traffic annotation if you want to remove this #if.
+#if defined(OS_CHROMEOS)
   history::WebHistoryService* web_history = GetWebHistory();
   if (web_history) {
     web_history->SetAudioHistoryEnabled(
@@ -84,6 +92,10 @@
     PrefService* prefs = profile_->GetPrefs();
     callback.Run(false, prefs->GetBoolean(prefs::kHotwordAudioLoggingEnabled));
   }
+#else
+  NOTREACHED()
+      << ": This functions is supposed to be called only in Chrome OS.";
+#endif  // defined(OS_CHROMEOS)
 }
 
 void HotwordAudioHistoryHandler::GetAudioHistoryComplete(
diff --git a/chrome/browser/search/hotword_service.cc b/chrome/browser/search/hotword_service.cc
index 1abfa69b..4407c118 100644
--- a/chrome/browser/search/hotword_service.cc
+++ b/chrome/browser/search/hotword_service.cc
@@ -365,8 +365,14 @@
           &HotwordService::MaybeReinstallHotwordExtension),
                  weak_factory_.GetWeakPtr()));
 
+// This service is actually used only on ChromeOS, and the next function
+// results in a sequence of calls that triggers
+// HotwordAudioHistoryHandler::GetAudioHistoryEnabled which is not supported
+// on other platforms.
+#if defined(OS_CHROMEOS)
   SetAudioHistoryHandler(new HotwordAudioHistoryHandler(
       profile_, base::ThreadTaskRunnerHandle::Get()));
+#endif
 
   if (HotwordServiceFactory::IsAlwaysOnAvailable() &&
       IsHotwordAllowed()) {
diff --git a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
index 34fc79dd..a16b6173 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
@@ -68,6 +68,8 @@
 const base::FilePath::CharType kDocRoot[] =
     FILE_PATH_LITERAL("chrome/test/data");
 
+const std::string kTestCertificateIssuerName = "Test Root CA";
+
 // Inject a script into every frame in the page. Used by tests that check for
 // visible password fields to wait for notifications about these
 // fields. Notifications about visible password fields are queued at the end of
@@ -166,11 +168,14 @@
   ASSERT_EQ(cert_status == VALID_CERTIFICATE ? 2u : 1u,
             secure_explanations.size());
   if (cert_status == VALID_CERTIFICATE) {
+    ASSERT_EQ(kTestCertificateIssuerName,
+              expected_cert->issuer().GetDisplayName());
     EXPECT_EQ(l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE),
               secure_explanations[0].summary);
-    EXPECT_EQ(
-        l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION),
-        secure_explanations[0].description);
+    EXPECT_EQ(l10n_util::GetStringFUTF8(
+                  IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION,
+                  base::UTF8ToUTF16(kTestCertificateIssuerName)),
+              secure_explanations[0].description);
     net::X509Certificate* cert = browser->tab_strip_model()
                                      ->GetActiveWebContents()
                                      ->GetController()
@@ -2120,16 +2125,16 @@
   // Populate description string replacement with values corresponding
   // to test constants.
   std::vector<base::string16> description_replacements;
-  description_replacements.push_back(
-      l10n_util::GetStringUTF16(IDS_SSL_AN_OBSOLETE_PROTOCOL));
   description_replacements.push_back(base::ASCIIToUTF16("TLS 1.1"));
   description_replacements.push_back(
-      l10n_util::GetStringUTF16(IDS_SSL_A_STRONG_KEY_EXCHANGE));
+      l10n_util::GetStringUTF16(IDS_SSL_AN_OBSOLETE_PROTOCOL));
   description_replacements.push_back(base::ASCIIToUTF16("ECDHE_RSA"));
   description_replacements.push_back(
-      l10n_util::GetStringUTF16(IDS_SSL_AN_OBSOLETE_CIPHER));
+      l10n_util::GetStringUTF16(IDS_SSL_A_STRONG_KEY_EXCHANGE));
   description_replacements.push_back(
       base::ASCIIToUTF16("AES_128_CBC with HMAC-SHA1"));
+  description_replacements.push_back(
+      l10n_util::GetStringUTF16(IDS_SSL_AN_OBSOLETE_CIPHER));
   base::string16 obsolete_description = l10n_util::GetStringFUTF16(
       IDS_OBSOLETE_SSL_DESCRIPTION, description_replacements, nullptr);
 
diff --git a/chrome/test/data/nacl/BUILD.gn b/chrome/test/data/nacl/BUILD.gn
index 8258044c..4a46affe 100644
--- a/chrome/test/data/nacl/BUILD.gn
+++ b/chrome/test/data/nacl/BUILD.gn
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/compiler/compiler.gni")
 import("//build/config/features.gni")
 import("//build/config/nacl/config.gni")
-import("//build/toolchain/toolchain.gni")
 import("//ppapi/native_client/nacl_test_data.gni")
 
 group("nacl") {
diff --git a/components/ntp_snippets/remote/prefetched_pages_tracker.h b/components/ntp_snippets/remote/prefetched_pages_tracker.h
index 9766ad3..b81e78c1 100644
--- a/components/ntp_snippets/remote/prefetched_pages_tracker.h
+++ b/components/ntp_snippets/remote/prefetched_pages_tracker.h
@@ -27,7 +27,7 @@
   virtual void AddInitializationCompletedCallback(
       base::OnceCallback<void()> callback) = 0;
 
-  virtual bool PrefetchedOfflinePageExists(const GURL url) const = 0;
+  virtual bool PrefetchedOfflinePageExists(const GURL& url) const = 0;
 };
 
 }  // namespace ntp_snippets
diff --git a/components/ntp_snippets/remote/prefetched_pages_tracker_impl.cc b/components/ntp_snippets/remote/prefetched_pages_tracker_impl.cc
index b00b339..df17693 100644
--- a/components/ntp_snippets/remote/prefetched_pages_tracker_impl.cc
+++ b/components/ntp_snippets/remote/prefetched_pages_tracker_impl.cc
@@ -67,7 +67,7 @@
 }
 
 bool PrefetchedPagesTrackerImpl::PrefetchedOfflinePageExists(
-    const GURL url) const {
+    const GURL& url) const {
   DCHECK(initialized_);
   return prefetched_urls_.count(url) == 1;
 }
diff --git a/components/ntp_snippets/remote/prefetched_pages_tracker_impl.h b/components/ntp_snippets/remote/prefetched_pages_tracker_impl.h
index 3c6b41ac..045c98e 100644
--- a/components/ntp_snippets/remote/prefetched_pages_tracker_impl.h
+++ b/components/ntp_snippets/remote/prefetched_pages_tracker_impl.h
@@ -34,7 +34,7 @@
   bool IsInitialized() const override;
   void AddInitializationCompletedCallback(
       base::OnceCallback<void()> callback) override;
-  bool PrefetchedOfflinePageExists(const GURL url) const override;
+  bool PrefetchedOfflinePageExists(const GURL& url) const override;
 
   // OfflinePageModel::Observer implementation.
   void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override;
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index db8a5ae..ebeb516 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -63,6 +63,14 @@
 const char kOrderNewRemoteCategoriesBasedOnArticlesCategory[] =
     "order_new_remote_categories_based_on_articles_category";
 
+// Not more than this number of prefetched suggestions will be kept longer.
+const int kMaxAdditionalPrefetchedSuggestions = 5;
+
+// Only prefetched suggestions published not later than this are considered to
+// be kept longer.
+const base::TimeDelta kMaxAgeForAdditionalPrefetchedSuggestion =
+    base::TimeDelta::FromHours(36);
+
 bool IsOrderingNewRemoteCategoriesBasedOnArticlesCategoryEnabled() {
   return variations::GetVariationParamByFeatureAsBool(
       ntp_snippets::kArticleSuggestionsFeature,
@@ -98,6 +106,10 @@
   NOTREACHED() << "Articles category was not found.";
 }
 
+bool IsKeepingPrefetchedSuggestionsEnabled() {
+  return base::FeatureList::IsEnabled(kKeepPrefetchedContentSuggestions);
+}
+
 template <typename SuggestionPtrContainer>
 std::unique_ptr<std::vector<std::string>> GetSuggestionIDVector(
     const SuggestionPtrContainer& suggestions) {
@@ -342,7 +354,8 @@
     std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher,
     std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
     std::unique_ptr<RemoteSuggestionsDatabase> database,
-    std::unique_ptr<RemoteSuggestionsStatusService> status_service)
+    std::unique_ptr<RemoteSuggestionsStatusService> status_service,
+    std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker)
     : RemoteSuggestionsProvider(observer),
       state_(State::NOT_INITED),
       pref_service_(pref_service),
@@ -358,7 +371,8 @@
       fetch_when_ready_(false),
       fetch_when_ready_interactive_(false),
       clear_history_dependent_state_when_initialized_(false),
-      clock_(base::MakeUnique<base::DefaultClock>()) {
+      clock_(base::MakeUnique<base::DefaultClock>()),
+      prefetched_pages_tracker_(std::move(prefetched_pages_tracker)) {
   RestoreCategoriesFromPrefs();
   // The articles category always exists. Add it if we didn't get it from prefs.
   // TODO(treib): Rethink this.
@@ -752,6 +766,16 @@
     return;
   }
 
+  if (IsKeepingPrefetchedSuggestionsEnabled() && prefetched_pages_tracker_ &&
+      !prefetched_pages_tracker_->IsInitialized()) {
+    // Wait until the tracker is initialized.
+    prefetched_pages_tracker_->AddInitializationCompletedCallback(
+        base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchFinished,
+                       base::Unretained(this), callback, interactive_request,
+                       status, std::move(fetched_categories)));
+    return;
+  }
+
   // Record the fetch time of a successfull background fetch.
   if (!interactive_request && status.IsSuccess()) {
     pref_service_->SetInt64(prefs::kLastSuccessfulBackgroundFetchTime,
@@ -792,7 +816,8 @@
       content->included_in_last_server_response = true;
       SanitizeReceivedSuggestions(content->dismissed,
                                   &fetched_category.suggestions);
-      IntegrateSuggestions(content, std::move(fetched_category.suggestions));
+      IntegrateSuggestions(fetched_category.category, content,
+                           std::move(fetched_category.suggestions));
     }
 
     // Add new remote categories to the ranker.
@@ -868,6 +893,7 @@
 }
 
 void RemoteSuggestionsProviderImpl::IntegrateSuggestions(
+    Category category,
     CategoryContent* content,
     RemoteSuggestion::PtrVector new_suggestions) {
   DCHECK(ready());
@@ -888,6 +914,51 @@
   // IDs though).
   EraseByPrimaryID(&content->suggestions,
                    *GetSuggestionIDVector(new_suggestions));
+
+  // If enabled, keep some older prefetched article suggestions, otherwise the
+  // user has little time to see them.
+  if (IsKeepingPrefetchedSuggestionsEnabled() &&
+      category == articles_category_ && prefetched_pages_tracker_) {
+    DCHECK(prefetched_pages_tracker_->IsInitialized());
+
+    // Select suggestions to keep.
+    std::sort(content->suggestions.begin(), content->suggestions.end(),
+              [](const std::unique_ptr<RemoteSuggestion>& first,
+                 const std::unique_ptr<RemoteSuggestion>& second) {
+                return first->fetch_date() > second->fetch_date();
+              });
+    std::vector<std::unique_ptr<RemoteSuggestion>>
+        additional_prefetched_suggestions, other_suggestions;
+    for (auto& remote_suggestion : content->suggestions) {
+      const GURL& url = remote_suggestion->amp_url().is_empty()
+                            ? remote_suggestion->url()
+                            : remote_suggestion->amp_url();
+      if (prefetched_pages_tracker_->PrefetchedOfflinePageExists(url) &&
+          clock_->Now() - remote_suggestion->fetch_date() <
+              kMaxAgeForAdditionalPrefetchedSuggestion &&
+          additional_prefetched_suggestions.size() <
+              kMaxAdditionalPrefetchedSuggestions) {
+        additional_prefetched_suggestions.push_back(
+            std::move(remote_suggestion));
+      } else {
+        other_suggestions.push_back(std::move(remote_suggestion));
+      }
+    }
+
+    // Mix them into the new set according to their score.
+    for (auto& remote_suggestion : additional_prefetched_suggestions) {
+      new_suggestions.push_back(std::move(remote_suggestion));
+    }
+    std::sort(new_suggestions.begin(), new_suggestions.end(),
+              [](const std::unique_ptr<RemoteSuggestion>& first,
+                 const std::unique_ptr<RemoteSuggestion>& second) {
+                return first->score() > second->score();
+              });
+
+    // Treat remaining suggestions as usual.
+    content->suggestions = std::move(other_suggestions);
+  }
+
   // Do not delete the thumbnail images as they are still handy on open NTPs.
   database_->DeleteSnippets(GetSuggestionIDVector(content->suggestions));
   // Note, that ArchiveSuggestions will clear |content->suggestions|.
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.h b/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
index a5ac7f57..dac737b 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
@@ -26,6 +26,7 @@
 #include "components/ntp_snippets/content_suggestion.h"
 #include "components/ntp_snippets/content_suggestions_provider.h"
 #include "components/ntp_snippets/remote/json_to_categories.h"
+#include "components/ntp_snippets/remote/prefetched_pages_tracker.h"
 #include "components/ntp_snippets/remote/remote_suggestion.h"
 #include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
 #include "components/ntp_snippets/remote/remote_suggestions_provider.h"
@@ -123,7 +124,8 @@
       std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher,
       std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
       std::unique_ptr<RemoteSuggestionsDatabase> database,
-      std::unique_ptr<RemoteSuggestionsStatusService> status_service);
+      std::unique_ptr<RemoteSuggestionsStatusService> status_service,
+      std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker);
 
   ~RemoteSuggestionsProviderImpl() override;
 
@@ -334,8 +336,9 @@
   void SanitizeReceivedSuggestions(const RemoteSuggestion::PtrVector& dismissed,
                                    RemoteSuggestion::PtrVector* suggestions);
 
-  // Adds newly available suggestions to |content|.
-  void IntegrateSuggestions(CategoryContent* content,
+  // Adds newly available suggestions to |content| corresponding to |category|.
+  void IntegrateSuggestions(Category category,
+                            CategoryContent* content,
                             RemoteSuggestion::PtrVector new_suggestions);
 
   // Dismisses a suggestion within a given category content.
@@ -464,6 +467,9 @@
   // A clock for getting the time. This allows to inject a clock in tests.
   std::unique_ptr<base::Clock> clock_;
 
+  // Prefetched pages tracker to query which urls have been prefetched.
+  std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker_;
+
   DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProviderImpl);
 };
 
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index 8a4a178..d2b95e9 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -88,6 +88,11 @@
 
 namespace {
 
+ACTION_P(MoveFirstArgumentPointeeTo, ptr) {
+  // 0-based indexation.
+  *ptr = std::move(*arg0);
+}
+
 ACTION_P(MoveSecondArgumentPointeeTo, ptr) {
   // 0-based indexation.
   *ptr = std::move(*arg1);
@@ -117,6 +122,11 @@
 
 const int kUnknownRemoteCategoryId = 1234;
 
+const int kMaxAdditionalPrefetchedSuggestions = 5;
+
+const base::TimeDelta kMaxAgeForAdditionalPrefetchedSuggestion =
+    base::TimeDelta::FromHours(36);
+
 base::Time GetDefaultCreationTime() {
   base::Time out_time;
   EXPECT_TRUE(base::Time::FromUTCExploded(kDefaultCreationTime, &out_time));
@@ -219,7 +229,8 @@
     const base::Time& creation_time,
     const base::Time& expiry_time,
     const std::string& publisher,
-    const std::string& amp_url) {
+    const std::string& amp_url,
+    double score) {
   const std::string ids_string = base::JoinString(ids, "\",\n      \"");
   return base::StringPrintf(
       "{\n"
@@ -233,19 +244,29 @@
       "      \"expirationTime\": \"%s\",\n"
       "      \"attribution\": \"%s\",\n"
       "      \"imageUrl\": \"%s\",\n"
-      "      \"ampUrl\": \"%s\"\n"
+      "      \"ampUrl\": \"%s\",\n"
+      "      \"score\": %f\n"
       "    }",
       ids_string.c_str(), kSuggestionTitle, kSuggestionText, url.c_str(),
       FormatTime(creation_time).c_str(), FormatTime(expiry_time).c_str(),
-      publisher.c_str(), kSuggestionSalientImage, amp_url.c_str());
+      publisher.c_str(), kSuggestionSalientImage, amp_url.c_str(), score);
 }
 
 std::string GetSuggestionWithSources(const std::string& source_url,
                                      const std::string& publisher,
                                      const std::string& amp_url) {
   return GetSuggestionWithUrlAndTimesAndSource(
-      {kSuggestionUrl}, source_url, GetDefaultCreationTime(),
-      GetDefaultExpirationTime(), publisher, amp_url);
+      {source_url}, source_url, GetDefaultCreationTime(),
+      GetDefaultExpirationTime(), publisher, amp_url, /*score=*/1);
+}
+
+std::string GetSuggestionWithSourcesAndScore(const std::string& source_url,
+                                             const std::string& publisher,
+                                             const std::string& amp_url,
+                                             double score) {
+  return GetSuggestionWithUrlAndTimesAndSource(
+      {source_url}, source_url, GetDefaultCreationTime(),
+      GetDefaultExpirationTime(), publisher, amp_url, score);
 }
 
 std::string GetSuggestionWithUrlAndTimes(
@@ -254,7 +275,7 @@
     const base::Time& expiry_time) {
   return GetSuggestionWithUrlAndTimesAndSource(
       {url}, url, content_creation_time, expiry_time, kSuggestionPublisherName,
-      kSuggestionAmpUrl);
+      kSuggestionAmpUrl, /*score=*/1);
 }
 
 std::string GetSuggestionWithTimes(const base::Time& content_creation_time,
@@ -428,6 +449,22 @@
   MOCK_CONST_METHOD0(GetFetchUrlForDebugging, const GURL&());
 };
 
+class MockPrefetchedPagesTracker : public PrefetchedPagesTracker {
+ public:
+  MOCK_CONST_METHOD0(IsInitialized, bool());
+
+  // GMock does not support movable-only types (e.g. OnceCallback), therefore,
+  // the call is redirected to a mock method with a pointer to the callback.
+  void AddInitializationCompletedCallback(
+      base::OnceCallback<void()> callback) override {
+    AddInitializationCompletedCallback(&callback);
+  }
+  MOCK_METHOD1(AddInitializationCompletedCallback,
+               void(base::OnceCallback<void()>* callback));
+
+  MOCK_CONST_METHOD1(PrefetchedOfflinePageExists, bool(const GURL& url));
+};
+
 }  // namespace
 
 class RemoteSuggestionsProviderImplTest : public ::testing::Test {
@@ -466,7 +503,8 @@
   std::unique_ptr<RemoteSuggestionsProviderImpl> MakeSuggestionsProvider(
       bool set_empty_response = true) {
     auto provider = MakeSuggestionsProviderWithoutInitialization(
-        /*use_mock_suggestions_fetcher=*/false);
+        /*use_mock_suggestions_fetcher=*/false,
+        /*use_mock_prefetched_pages_tracker=*/false);
     WaitForSuggestionsProviderInitialization(provider.get(),
                                              set_empty_response);
     return provider;
@@ -475,7 +513,8 @@
   // TODO(vitaliii): Rewrite tests and always use mock suggestions fetcher.
   std::unique_ptr<RemoteSuggestionsProviderImpl>
   MakeSuggestionsProviderWithoutInitialization(
-      bool use_mock_suggestions_fetcher) {
+      bool use_mock_suggestions_fetcher,
+      bool use_mock_prefetched_pages_tracker) {
     scoped_refptr<base::SingleThreadTaskRunner> task_runner(
         base::ThreadTaskRunnerHandle::Get());
     scoped_refptr<net::TestURLRequestContextGetter> request_context_getter =
@@ -496,6 +535,13 @@
     }
     suggestions_fetcher_ = suggestions_fetcher.get();
 
+    std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker;
+    if (use_mock_prefetched_pages_tracker) {
+      prefetched_pages_tracker =
+          base::MakeUnique<StrictMock<MockPrefetchedPagesTracker>>();
+    }
+    prefetched_pages_tracker_ = prefetched_pages_tracker.get();
+
     auto image_fetcher = base::MakeUnique<NiceMock<MockImageFetcher>>();
 
     image_fetcher_ = image_fetcher.get();
@@ -512,15 +558,16 @@
         scheduler_.get(), std::move(suggestions_fetcher),
         std::move(image_fetcher), std::move(database),
         base::MakeUnique<RemoteSuggestionsStatusService>(
-            utils_.fake_signin_manager(), utils_.pref_service(),
-            std::string()));
+            utils_.fake_signin_manager(), utils_.pref_service(), std::string()),
+        std::move(prefetched_pages_tracker));
   }
 
   std::unique_ptr<RemoteSuggestionsProviderImpl>
   MakeSuggestionsProviderWithoutInitializationWithStrictScheduler() {
     scheduler_ = base::MakeUnique<StrictMock<MockScheduler>>();
     return MakeSuggestionsProviderWithoutInitialization(
-        /*use_mock_suggestions_fetcher=*/false);
+        /*use_mock_suggestions_fetcher=*/false,
+        /*use_mock_prefetched_pages_tracker=*/false);
   }
 
   void WaitForSuggestionsProviderInitialization(
@@ -579,6 +626,9 @@
   RemoteSuggestionsFetcher* suggestions_fetcher() {
     return suggestions_fetcher_;
   }
+  PrefetchedPagesTracker* prefetched_pages_tracker() {
+    return prefetched_pages_tracker_;
+  }
   // TODO(tschumann): Make this a strict-mock. We want to avoid unneccesary
   // network requests.
   NiceMock<MockImageFetcher>* image_fetcher() { return image_fetcher_; }
@@ -643,6 +693,19 @@
         {kArticleSuggestionsFeature.name});
   }
 
+  void EnableKeepingPrefetchedContentSuggestions() {
+    // params_manager supports only one
+    // |SetVariationParamsWithFeatureAssociations| at a time, so we clear
+    // previous settings first and then set everything we need.
+    params_manager_.ClearAllVariationParams();
+    params_manager_.SetVariationParamsWithFeatureAssociations(
+        kKeepPrefetchedContentSuggestions.name,
+        {{"content_suggestions_backend",
+          kTestContentSuggestionsServerEndpoint}},
+        {kArticleSuggestionsFeature.name,
+         kKeepPrefetchedContentSuggestions.name});
+  }
+
  private:
   variations::testing::VariationParamsManager params_manager_;
   test::RemoteSuggestionsTestUtils utils_;
@@ -655,6 +718,7 @@
   UserClassifier user_classifier_;
   std::unique_ptr<FakeContentSuggestionsProviderObserver> observer_;
   RemoteSuggestionsFetcher* suggestions_fetcher_;
+  PrefetchedPagesTracker* prefetched_pages_tracker_;
   NiceMock<MockImageFetcher>* image_fetcher_;
   FakeImageDecoder image_decoder_;
   std::unique_ptr<MockScheduler> scheduler_;
@@ -1113,7 +1177,8 @@
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldNotChangeSuggestionsInOtherSurfacesWhenFetchingMore) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/true);
+      /*use_mock_suggestions_fetcher=*/true,
+      /*use_mock_prefetched_pages_tracker=*/false);
   WaitForSuggestionsProviderInitialization(provider.get(),
                                            /*set_empty_response=*/true);
 
@@ -1186,7 +1251,8 @@
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldNotAffectFetchMoreInOtherSurfacesWhenFetchingMore) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/true);
+      /*use_mock_suggestions_fetcher=*/true,
+      /*use_mock_prefetched_pages_tracker=*/false);
   WaitForSuggestionsProviderInitialization(provider.get(),
                                            /*set_empty_response=*/true);
   auto* mock_fetcher = static_cast<StrictMock<MockRemoteSuggestionsFetcher>*>(
@@ -1309,7 +1375,8 @@
 
 TEST_F(RemoteSuggestionsProviderImplTest, ReturnFetchRequestEmptyBeforeInit) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/false);
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/false);
   MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
   EXPECT_CALL(loaded, Call(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
                            IsEmpty()));
@@ -1443,7 +1510,8 @@
   EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
       .WillOnce(WithArgs<0, 2>(Invoke(CreateFunctor(cb))));
   image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
-  gfx::Image image = FetchImage(provider.get(), MakeArticleID(kSuggestionUrl));
+  gfx::Image image =
+      FetchImage(provider.get(), MakeArticleID("http://site.com"));
   EXPECT_FALSE(image.IsEmpty());
   EXPECT_EQ(1, image.Width());
 
@@ -1453,7 +1521,7 @@
               SizeIs(1));
 
   // Dismiss the suggestion.
-  provider->DismissSuggestion(MakeArticleID(kSuggestionUrl));
+  provider->DismissSuggestion(MakeArticleID("http://site.com"));
   EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
               IsEmpty());
 
@@ -1461,7 +1529,7 @@
   // might still reference it). This should come from the database -- no network
   // fetch necessary.
   image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
-  image = FetchImage(provider.get(), MakeArticleID(kSuggestionUrl));
+  image = FetchImage(provider.get(), MakeArticleID("http://site.com"));
   EXPECT_FALSE(image.IsEmpty());
   EXPECT_EQ(1, image.Width());
 
@@ -1589,7 +1657,7 @@
               SizeIs(1));
   const RemoteSuggestion& suggestion =
       *provider->GetSuggestionsForTesting(articles_category()).front();
-  EXPECT_EQ(suggestion.id(), kSuggestionUrl);
+  EXPECT_EQ(suggestion.id(), "http://source1.com");
   EXPECT_EQ(suggestion.url(), GURL("http://source1.com"));
   EXPECT_EQ(suggestion.publisher_name(), std::string("Source 1"));
   EXPECT_EQ(suggestion.amp_url(), GURL("http://source1.amp.com"));
@@ -1691,7 +1759,7 @@
   LoadFromJSONString(provider.get(),
                      GetTestJson({GetSuggestionWithUrlAndTimesAndSource(
                          source_urls, source_urls[0], creation, expiry,
-                         publishers[0], amp_urls[0])}));
+                         publishers[0], amp_urls[0], /*score=*/1)}));
   ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
               SizeIs(1));
   // Dismiss the suggestion via the mashable source corpus ID.
@@ -1703,7 +1771,7 @@
   LoadFromJSONString(provider.get(),
                      GetTestJson({GetSuggestionWithUrlAndTimesAndSource(
                          source_urls, source_urls[1], creation, expiry,
-                         publishers[1], amp_urls[1])}));
+                         publishers[1], amp_urls[1], /*score=*/1)}));
   EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
               IsEmpty());
 }
@@ -1870,7 +1938,8 @@
   // is triggered since the suggestions DB is empty. Therefore the provider must
   // not be initialized until the test clock is set.
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/false);
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/false);
 
   auto simple_test_clock = base::MakeUnique<base::SimpleTestClock>();
   base::SimpleTestClock* simple_test_clock_ptr = simple_test_clock.get();
@@ -1980,7 +2049,8 @@
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldExcludeKnownSuggestionsWithoutTruncatingWhenFetchingMore) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/true);
+      /*use_mock_suggestions_fetcher=*/true,
+      /*use_mock_prefetched_pages_tracker=*/false);
   WaitForSuggestionsProviderInitialization(provider.get(),
                                            /*set_empty_response=*/true);
   auto* mock_fetcher = static_cast<StrictMock<MockRemoteSuggestionsFetcher>*>(
@@ -2005,7 +2075,8 @@
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldExcludeDismissedSuggestionsWhenFetchingMore) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/true);
+      /*use_mock_suggestions_fetcher=*/true,
+      /*use_mock_prefetched_pages_tracker=*/false);
   WaitForSuggestionsProviderInitialization(provider.get(),
                                            /*set_empty_response=*/true);
 
@@ -2052,7 +2123,8 @@
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldTruncateExcludedDismissedSuggestionsWhenFetchingMore) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/true);
+      /*use_mock_suggestions_fetcher=*/true,
+      /*use_mock_prefetched_pages_tracker=*/false);
   WaitForSuggestionsProviderInitialization(provider.get(),
                                            /*set_empty_response=*/true);
 
@@ -2105,7 +2177,8 @@
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldPreferLatestExcludedDismissedSuggestionsWhenFetchingMore) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/true);
+      /*use_mock_suggestions_fetcher=*/true,
+      /*use_mock_prefetched_pages_tracker=*/false);
   WaitForSuggestionsProviderInitialization(provider.get(),
                                            /*set_empty_response=*/true);
 
@@ -2164,7 +2237,8 @@
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldExcludeDismissedSuggestionsFromAllCategoriesWhenFetchingMore) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/true);
+      /*use_mock_suggestions_fetcher=*/true,
+      /*use_mock_prefetched_pages_tracker=*/false);
   WaitForSuggestionsProviderInitialization(provider.get(),
                                            /*set_empty_response=*/true);
 
@@ -2233,7 +2307,8 @@
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldPreferTargetCategoryExcludedDismissedSuggestionsWhenFetchingMore) {
   auto provider = MakeSuggestionsProviderWithoutInitialization(
-      /*use_mock_suggestions_fetcher=*/true);
+      /*use_mock_suggestions_fetcher=*/true,
+      /*use_mock_prefetched_pages_tracker=*/false);
   WaitForSuggestionsProviderInitialization(provider.get(),
                                            /*set_empty_response=*/true);
 
@@ -2296,4 +2371,322 @@
                     std::vector<ContentSuggestion> suggestions) {}));
 }
 
+TEST_F(RemoteSuggestionsProviderImplTest,
+       ShouldFetchNormallyWithoutPrefetchedPagesTracker) {
+  EnableKeepingPrefetchedContentSuggestions();
+  auto provider = MakeSuggestionsProvider();
+  LoadFromJSONString(provider.get(), GetTestJson({GetSuggestion()}));
+  EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(1));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+       ShouldKeepPrefetchedSuggestionsAfterFetchWhenEnabled) {
+  EnableKeepingPrefetchedContentSuggestions();
+  auto provider = MakeSuggestionsProviderWithoutInitialization(
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/true);
+  auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+      prefetched_pages_tracker());
+  WaitForSuggestionsProviderInitialization(provider.get(),
+                                           /*set_empty_response=*/false);
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  LoadFromJSONString(
+      provider.get(),
+      GetTestJson({GetSuggestionWithSources("http://prefeched.com", "publisher",
+                                            "http://amp.prefetched.com")}));
+
+  ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(1));
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_tracker,
+              PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com")))
+      .WillOnce(Return(true));
+  LoadFromJSONString(
+      provider.get(),
+      GetTestJson({GetSuggestionWithSources("http://other.com", "publisher",
+                                            "http://amp.other.com")}));
+
+  EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+              UnorderedElementsAre(
+                  Property(&ContentSuggestion::id,
+                           Property(&ContentSuggestion::ID::id_within_category,
+                                    "http://prefeched.com")),
+                  Property(&ContentSuggestion::id,
+                           Property(&ContentSuggestion::ID::id_within_category,
+                                    "http://other.com"))));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+       ShouldIgnoreNotPrefetchedSuggestionsAfterFetchWhenEnabled) {
+  EnableKeepingPrefetchedContentSuggestions();
+  auto provider = MakeSuggestionsProviderWithoutInitialization(
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/true);
+  auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+      prefetched_pages_tracker());
+  WaitForSuggestionsProviderInitialization(provider.get(),
+                                           /*set_empty_response=*/false);
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  LoadFromJSONString(provider.get(),
+                     GetTestJson({GetSuggestionWithSources(
+                         "http://not_prefeched.com", "publisher",
+                         "http://amp.not_prefetched.com")}));
+
+  ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(1));
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_tracker, PrefetchedOfflinePageExists(
+                                 GURL("http://amp.not_prefetched.com")))
+      .WillOnce(Return(false));
+  LoadFromJSONString(
+      provider.get(),
+      GetTestJson({GetSuggestionWithSources("http://other.com", "publisher",
+                                            "http://amp.other.com")}));
+
+  EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+              UnorderedElementsAre(
+                  Property(&ContentSuggestion::id,
+                           Property(&ContentSuggestion::ID::id_within_category,
+                                    "http://other.com"))));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+       ShouldLimitKeptPrefetchedSuggestionsAfterFetchWhenEnabled) {
+  EnableKeepingPrefetchedContentSuggestions();
+  auto provider = MakeSuggestionsProviderWithoutInitialization(
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/true);
+  auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+      prefetched_pages_tracker());
+  WaitForSuggestionsProviderInitialization(provider.get(),
+                                           /*set_empty_response=*/false);
+
+  const int prefetched_suggestions_count =
+      2 * kMaxAdditionalPrefetchedSuggestions + 1;
+  std::vector<std::string> prefetched_suggestions;
+  for (int i = 0; i < prefetched_suggestions_count; ++i) {
+    prefetched_suggestions.push_back(GetSuggestionWithSources(
+        base::StringPrintf("http://prefetched.com/%d", i), "publisher",
+        base::StringPrintf("http://amp.prefetched.com/%d", i)));
+  }
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  LoadFromJSONString(provider.get(), GetTestJson(prefetched_suggestions));
+  ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(prefetched_suggestions_count));
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  for (int i = 0; i < prefetched_suggestions_count; ++i) {
+    EXPECT_CALL(*mock_tracker,
+                PrefetchedOfflinePageExists(GURL(
+                    base::StringPrintf("http://amp.prefetched.com/%d", i))))
+        .WillOnce(Return(true));
+  }
+  LoadFromJSONString(provider.get(),
+                     GetTestJson({GetSuggestionWithSources(
+                         "http://not_prefeched.com", "publisher",
+                         "http://amp.not_prefetched.com")}));
+
+  ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(kMaxAdditionalPrefetchedSuggestions + 1));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+       ShouldMixInPrefetchedSuggestionsByScoreAfterFetchWhenEnabled) {
+  EnableKeepingPrefetchedContentSuggestions();
+  auto provider = MakeSuggestionsProviderWithoutInitialization(
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/true);
+  auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+      prefetched_pages_tracker());
+  WaitForSuggestionsProviderInitialization(provider.get(),
+                                           /*set_empty_response=*/false);
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  LoadFromJSONString(
+      provider.get(),
+      GetTestJson({GetSuggestionWithSourcesAndScore(
+                       "http://prefeched.com/1", "publisher",
+                       "http://amp.prefetched.com/1", /*score=*/1),
+                   GetSuggestionWithSourcesAndScore(
+                       "http://prefeched.com/3", "publisher",
+                       "http://amp.prefetched.com/3", /*score=*/3)}));
+  ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(2));
+
+  EXPECT_CALL(*mock_tracker,
+              PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com/1")))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*mock_tracker,
+              PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com/3")))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  LoadFromJSONString(provider.get(),
+                     GetTestJson({GetSuggestionWithSourcesAndScore(
+                                      "http://new.com/2", "publisher",
+                                      "http://amp.new.com/2", /*score=*/2),
+                                  GetSuggestionWithSourcesAndScore(
+                                      "http://new.com/4", "publisher",
+                                      "http://amp.new.com/4", /*score=*/4)}));
+
+  EXPECT_THAT(
+      observer().SuggestionsForCategory(articles_category()),
+      ElementsAre(
+          Property(&ContentSuggestion::id, MakeArticleID("http://new.com/4")),
+          Property(&ContentSuggestion::id,
+                   Property(&ContentSuggestion::ID::id_within_category,
+                            "http://prefeched.com/3")),
+          Property(&ContentSuggestion::id,
+                   Property(&ContentSuggestion::ID::id_within_category,
+                            "http://new.com/2")),
+          Property(&ContentSuggestion::id,
+                   Property(&ContentSuggestion::ID::id_within_category,
+                            "http://prefeched.com/1"))));
+}
+
+TEST_F(
+    RemoteSuggestionsProviderImplTest,
+    ShouldKeepMostRecentlyFetchedPrefetchedSuggestionsFirstAfterFetchWhenEnabled) {
+  EnableKeepingPrefetchedContentSuggestions();
+  auto provider = MakeSuggestionsProviderWithoutInitialization(
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/true);
+  auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+      prefetched_pages_tracker());
+  WaitForSuggestionsProviderInitialization(provider.get(),
+                                           /*set_empty_response=*/false);
+
+  const int prefetched_suggestions_count =
+      2 * kMaxAdditionalPrefetchedSuggestions + 1;
+
+  for (int i = 0; i < prefetched_suggestions_count; ++i) {
+    EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+    if (i != 0) {
+      EXPECT_CALL(*mock_tracker,
+                  PrefetchedOfflinePageExists(GURL(base::StringPrintf(
+                      "http://amp.prefetched.com/%d", i - 1))))
+          .WillRepeatedly(Return(true));
+    }
+    LoadFromJSONString(
+        provider.get(),
+        GetTestJson({GetSuggestionWithSources(
+            base::StringPrintf("http://prefetched.com/%d", i), "publisher",
+            base::StringPrintf("http://amp.prefetched.com/%d", i))}));
+  }
+
+  const std::vector<ContentSuggestion>& actual_suggestions =
+      observer().SuggestionsForCategory(articles_category());
+
+  ASSERT_THAT(actual_suggestions,
+              SizeIs(kMaxAdditionalPrefetchedSuggestions + 1));
+
+  int matched = 0;
+  for (int i = prefetched_suggestions_count - 1; i >= 0; --i) {
+    EXPECT_THAT(actual_suggestions,
+                Contains(Property(&ContentSuggestion::id,
+                                  MakeArticleID(base::StringPrintf(
+                                      "http://prefetched.com/%d", i)))));
+    ++matched;
+    if (matched == kMaxAdditionalPrefetchedSuggestions + 1) {
+      break;
+    }
+  }
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+       ShouldNotKeepStalePrefetchedSuggestionsAfterFetchWhenEnabled) {
+  EnableKeepingPrefetchedContentSuggestions();
+  auto provider = MakeSuggestionsProviderWithoutInitialization(
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/true);
+  auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+      prefetched_pages_tracker());
+
+  auto* fetcher =
+      static_cast<RemoteSuggestionsFetcherImpl*>(suggestions_fetcher());
+  auto wrapped_fetcher_clock = base::MakeUnique<base::SimpleTestClock>();
+  base::SimpleTestClock* fetcher_clock = wrapped_fetcher_clock.get();
+  fetcher->SetClockForTesting(std::move(wrapped_fetcher_clock));
+
+  auto wrapped_provider_clock = base::MakeUnique<base::SimpleTestClock>();
+  base::SimpleTestClock* provider_clock = wrapped_provider_clock.get();
+  provider->SetClockForTesting(std::move(wrapped_provider_clock));
+
+  provider_clock->SetNow(GetDefaultCreationTime() +
+                         base::TimeDelta::FromHours(10));
+  fetcher_clock->SetNow(provider_clock->Now());
+
+  WaitForSuggestionsProviderInitialization(provider.get(),
+                                           /*set_empty_response=*/false);
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  LoadFromJSONString(
+      provider.get(),
+      GetTestJson({GetSuggestionWithSources("http://prefeched.com", "publisher",
+                                            "http://amp.prefetched.com")}));
+  ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(1));
+
+  provider_clock->Advance(kMaxAgeForAdditionalPrefetchedSuggestion -
+                          base::TimeDelta::FromSeconds(1));
+  fetcher_clock->SetNow(provider_clock->Now());
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_tracker,
+              PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com")))
+      .WillOnce(Return(true));
+  LoadFromJSONString(
+      provider.get(),
+      GetTestJson({GetSuggestionWithSources("http://other.com", "publisher",
+                                            "http://amp.other.com")}));
+  ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(2));
+
+  provider_clock->Advance(base::TimeDelta::FromSeconds(2));
+  fetcher_clock->SetNow(provider_clock->Now());
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_tracker,
+              PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com")))
+      .WillOnce(Return(true));
+  LoadFromJSONString(
+      provider.get(),
+      GetTestJson({GetSuggestionWithSources("http://other.com", "publisher",
+                                            "http://amp.other.com")}));
+  EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+              ElementsAre(Property(&ContentSuggestion::id,
+                                   MakeArticleID("http://other.com"))));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+       ShouldWaitForPrefetchedPagesTrackerInitialization) {
+  EnableKeepingPrefetchedContentSuggestions();
+  auto provider = MakeSuggestionsProviderWithoutInitialization(
+      /*use_mock_suggestions_fetcher=*/false,
+      /*use_mock_prefetched_pages_tracker=*/true);
+  auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+      prefetched_pages_tracker());
+  WaitForSuggestionsProviderInitialization(provider.get(),
+                                           /*set_empty_response=*/false);
+
+  base::OnceCallback<void()> initialization_completed_callback;
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_tracker, AddInitializationCompletedCallback(_))
+      .WillOnce(MoveFirstArgumentPointeeTo(&initialization_completed_callback));
+  LoadFromJSONString(
+      provider.get(),
+      GetTestJson({GetSuggestionWithSources("http://prefeched.com", "publisher",
+                                            "http://amp.prefetched.com")}));
+  EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(0));
+
+  EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+  std::move(initialization_completed_callback).Run();
+  EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+              SizeIs(1));
+}
+
 }  // namespace ntp_snippets
diff --git a/components/security_state/content/content_utils.cc b/components/security_state/content/content_utils.cc
index f1dc115..3432722 100644
--- a/components/security_state/content/content_utils.cc
+++ b/components/security_state/content/content_utils.cc
@@ -112,19 +112,19 @@
   str_id = (status & net::OBSOLETE_SSL_MASK_PROTOCOL)
                ? IDS_SSL_AN_OBSOLETE_PROTOCOL
                : IDS_SSL_A_STRONG_PROTOCOL;
-  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
   description_replacements.push_back(protocol_name);
+  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
 
   str_id = (status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE)
                ? IDS_SSL_AN_OBSOLETE_KEY_EXCHANGE
                : IDS_SSL_A_STRONG_KEY_EXCHANGE;
-  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
   description_replacements.push_back(key_exchange_name);
+  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
 
   str_id = (status & net::OBSOLETE_SSL_MASK_CIPHER) ? IDS_SSL_AN_OBSOLETE_CIPHER
                                                     : IDS_SSL_A_STRONG_CIPHER;
-  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
   description_replacements.push_back(cipher_name);
+  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
 
   security_style_explanations->info_explanations.push_back(
       content::SecurityStyleExplanation(
@@ -297,12 +297,26 @@
   } else {
     // If the certificate does not have errors and is not using SHA1, then add
     // an explanation that the certificate is valid.
+
+    base::string16 issuer_name;
+    if (security_info.certificate) {
+      // This results in the empty string if there is no relevant display name.
+      issuer_name = base::UTF8ToUTF16(
+          security_info.certificate->issuer().GetDisplayName());
+    } else {
+      issuer_name = base::string16();
+    }
+    if (issuer_name.empty()) {
+      issuer_name.assign(
+          l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
+    }
+
     if (!security_info.sha1_in_chain) {
       security_style_explanations->secure_explanations.push_back(
           content::SecurityStyleExplanation(
               l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE),
-              l10n_util::GetStringUTF8(
-                  IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION),
+              l10n_util::GetStringFUTF8(
+                  IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION, issuer_name),
               !!security_info.certificate));
     }
   }
diff --git a/components/security_state/content/content_utils_unittest.cc b/components/security_state/content/content_utils_unittest.cc
index 090a835..2fbf436bb 100644
--- a/components/security_state/content/content_utils_unittest.cc
+++ b/components/security_state/content/content_utils_unittest.cc
@@ -164,7 +164,7 @@
   return false;
 }
 
-// Test that connection explanations are formated as expected. Note the strings
+// Test that connection explanations are formatted as expected. Note the strings
 // are not translated and so will be the same in any locale.
 TEST(SecurityStateContentUtilsTest, ConnectionExplanation) {
   // Test a modern configuration with a key exchange group.
@@ -185,9 +185,9 @@
     ASSERT_TRUE(FindSecurityStyleExplanation(
         explanations.secure_explanations, "Secure connection", &explanation));
     EXPECT_EQ(
-        "The connection to this site is encrypted and authenticated using a "
-        "strong protocol (TLS 1.2), a strong key exchange (ECDHE_RSA with "
-        "X25519), and a strong cipher (CHACHA20_POLY1305).",
+        "The connection to this site is encrypted and authenticated using TLS "
+        "1.2 (a strong protocol), ECDHE_RSA with X25519 (a strong key "
+        "exchange), and CHACHA20_POLY1305 (a strong cipher).",
         explanation.description);
   }
 
@@ -201,9 +201,9 @@
     ASSERT_TRUE(FindSecurityStyleExplanation(
         explanations.secure_explanations, "Secure connection", &explanation));
     EXPECT_EQ(
-        "The connection to this site is encrypted and authenticated using a "
-        "strong protocol (TLS 1.2), a strong key exchange (ECDHE_RSA), and a "
-        "strong cipher (CHACHA20_POLY1305).",
+        "The connection to this site is encrypted and authenticated using TLS "
+        "1.2 (a strong protocol), ECDHE_RSA (a strong key exchange), and "
+        "CHACHA20_POLY1305 (a strong cipher).",
         explanation.description);
   }
 
@@ -220,9 +220,38 @@
     ASSERT_TRUE(FindSecurityStyleExplanation(
         explanations.secure_explanations, "Secure connection", &explanation));
     EXPECT_EQ(
-        "The connection to this site is encrypted and authenticated using a "
-        "strong protocol (TLS 1.3), a strong key exchange (X25519), and a "
-        "strong cipher (AES_128_GCM).",
+        "The connection to this site is encrypted and authenticated using TLS "
+        "1.3 (a strong protocol), X25519 (a strong key exchange), and "
+        "AES_128_GCM (a strong cipher).",
+        explanation.description);
+  }
+}
+
+// Test that obsolete connection explanations are formatted as expected.
+TEST(SecurityStateContentUtilsTest, ObsoleteConnectionExplanation) {
+  security_state::SecurityInfo security_info;
+  security_info.cert_status = net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+  security_info.scheme_is_cryptographic = true;
+  net::SSLConnectionStatusSetCipherSuite(
+      0xc013 /* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA */,
+      &security_info.connection_status);
+  net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_2,
+                                     &security_info.connection_status);
+  security_info.key_exchange_group = 29;  // X25519
+  security_info.obsolete_ssl_status =
+      net::ObsoleteSSLMask::OBSOLETE_SSL_MASK_CIPHER;
+
+  {
+    content::SecurityStyleExplanations explanations;
+    GetSecurityStyle(security_info, &explanations);
+    content::SecurityStyleExplanation explanation;
+    ASSERT_TRUE(FindSecurityStyleExplanation(explanations.info_explanations,
+                                             "Obsolete connection settings",
+                                             &explanation));
+    EXPECT_EQ(
+        "The connection to this site uses TLS 1.2 (a strong protocol), "
+        "ECDHE_RSA with X25519 (a strong key exchange), and AES_128_CBC with "
+        "HMAC-SHA1 (an obsolete cipher).",
         explanation.description);
   }
 }
diff --git a/components/security_state_strings.grdp b/components/security_state_strings.grdp
index 482b6c9..6094c157 100644
--- a/components/security_state_strings.grdp
+++ b/components/security_state_strings.grdp
@@ -38,7 +38,7 @@
     Valid certificate
   </message>
   <message name="IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION" desc="Description of a site that has a valid server certificate." translateable="false">
-    The connection to this site is using a valid, trusted server certificate.
+    The connection to this site is using a valid, trusted server certificate issued by <ph name="ISSUER">$1<ex>Let's Encrypt Authority X3</ex></ph>.
   </message>
   <message name="IDS_STRONG_SSL_SUMMARY" desc="Summary phrase for a site that uses a modern, secure TLS protocol and cipher." translateable="false">
     Secure connection
@@ -50,13 +50,13 @@
     Public-Key-Pinning was bypassed by a local root certificate.
   </message>
   <message name="IDS_STRONG_SSL_DESCRIPTION" desc="Description of a site that uses a modern, secure TLS protocol and cipher." translateable="false">
-    The connection to this site is encrypted and authenticated using a strong protocol (<ph name="PROTOCOL_VERSION">$1<ex>TLS 1.2</ex></ph>), a strong key exchange (<ph name="KEY_EXCHANGE">$2<ex>ECDHE_RSA</ex></ph>), and a strong cipher (<ph name="CIPHER_SUTE">$3<ex>AES_128_GCM</ex></ph>).
+    The connection to this site is encrypted and authenticated using <ph name="PROTOCOL_VERSION">$1<ex>TLS 1.2</ex></ph> (a strong protocol), <ph name="KEY_EXCHANGE">$2<ex>ECDHE_RSA</ex></ph> (a strong key exchange), and <ph name="CIPHER_SUTE">$3<ex>AES_128_GCM</ex></ph> (a strong cipher).
   </message>
   <message name="IDS_OBSOLETE_SSL_SUMMARY" desc="Summary phrase for a site that uses an outdated SSL settings (protocol, key exchange, or cipher)." translateable="false">
     Obsolete connection settings
   </message>
   <message name="IDS_OBSOLETE_SSL_DESCRIPTION" desc="Description of a site that uses an outdated TLS protocol or cipher." translateable="false">
-    The connection to this site uses <ph name="A_PROTOCOL">$1<ex>an obsolete protocol</ex></ph> (<ph name="PROTOCOL">$2<ex>TLS 1.0</ex></ph>), <ph name="A_KEY_EXCHANGE">$3<ex>an obsolete key exchange</ex></ph> (<ph name="KEY_EXCHANGE">$4<ex>ECDHE_RSA</ex></ph>), and <ph name="A_CIPHER">$5<ex>an obsolete cipher</ex></ph> (<ph name="CIPHER">$6<ex>AES_256_CBC with HMAC-SHA1</ex></ph>).
+    The connection to this site uses <ph name="PROTOCOL">$1<ex>TLS 1.0</ex></ph> (<ph name="A_PROTOCOL">$2<ex>an obsolete protocol</ex></ph>), <ph name="KEY_EXCHANGE">$3<ex>ECDHE_RSA</ex></ph> (<ph name="A_KEY_EXCHANGE">$4<ex>an obsolete key exchange</ex></ph>), and <ph name="CIPHER">$5<ex>AES_256_CBC with HMAC-SHA1</ex></ph> (<ph name="A_CIPHER">$6<ex>an obsolete cipher</ex></ph>).
   </message>
   <message name="IDS_CIPHER_WITH_MAC" desc="Description of an SSL cipher that contains a separate (bulk) cipher and MAC." translateable="false">
     <ph name="CIPHER">$1<ex>AES_256_CBC</ex></ph> with <ph name="MAC">$2<ex>HMAC-SHA1</ex></ph>
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
index 1bc06ebf9..d336f4a 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
@@ -193,7 +193,8 @@
             request_context.get()),
         base::MakeUnique<RemoteSuggestionsDatabase>(database_dir, task_runner),
         base::MakeUnique<RemoteSuggestionsStatusService>(signin_manager, prefs,
-                                                         std::string()));
+                                                         std::string()),
+        /*prefetched_pages_tracker=*/nullptr);
 
     service->remote_suggestions_scheduler()->SetProvider(provider.get());
     service->set_remote_suggestions_provider(provider.get());
diff --git a/media/cdm/cdm_adapter_factory.cc b/media/cdm/cdm_adapter_factory.cc
index 6c9c085..b00a09f 100644
--- a/media/cdm/cdm_adapter_factory.cc
+++ b/media/cdm/cdm_adapter_factory.cc
@@ -11,6 +11,7 @@
 #include "base/path_service.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/cdm_factory.h"
+#include "media/base/key_system_names.h"
 #include "media/base/key_systems.h"
 #include "media/cdm/cdm_adapter.h"
 #include "media/cdm/cdm_paths.h"
@@ -49,15 +50,16 @@
     return;
   }
 
+  // TODO(xhwang): We should have the CDM path forwarded from the browser
+  // already. See http://crbug.com/510604
   base::FilePath cdm_path;
 
+// TODO(xhwang): Remove key-system-specific logic. We should have the
+// CDM path forwarded from the browser already. See http://crbug.com/510604
+
 #if defined(WIDEVINE_CDM_AVAILABLE)
-  // TODO(xhwang): Remove key-system-specific logic here. We should have the
-  // CDM path forwarded from the browser already. See http://crbug.com/510604
   if (key_system == kWidevineKeySystem) {
     // Build the library path for Widevine CDM.
-    // TODO(xhwang): We should have the CDM path forwarded from the browser
-    // already. See http://crbug.com/510604
     base::FilePath cdm_base_path;
 
 #if defined(OS_MACOSX)
@@ -75,6 +77,19 @@
   }
 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
 
+// The hardcoded path for ClearKeyCdm does not work on Mac due to bundling.
+// See http://crbug.com/736106
+#if !defined(OS_MACOSX)
+  if (cdm_path.empty() && IsExternalClearKey(key_system)) {
+    base::FilePath cdm_base_path;
+    base::PathService::Get(base::DIR_MODULE, &cdm_base_path);
+    cdm_base_path = cdm_base_path.Append(
+        GetPlatformSpecificDirectory(kClearKeyCdmBaseDirectory));
+    cdm_path = cdm_base_path.AppendASCII(
+        base::GetNativeLibraryName(kClearKeyCdmLibraryName));
+  }
+#endif  // !defined(OS_MACOSX)
+
   if (cdm_path.empty()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
diff --git a/net/data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued/chain.pem b/net/data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued/chain.pem
index 1df6f70..7d2d449 100644
--- a/net/data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 2 intermediates. The first intermediate has a basic
-constraints path length of 0. The second one is self-issued so does not count
-against the path length.
+Certificate chain where the intermediate sets pathlen=0, and is followed by
+a self-issued intermediate.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued/generate-chains.py b/net/data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued/generate-chains.py
index 46b6b84..059620a 100755
--- a/net/data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/basic-constraints-pathlen-0-self-issued/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 2 intermediates. The first intermediate has a basic
-constraints path length of 0. The second one is self-issued so does not count
-against the path length."""
+"""Certificate chain where the intermediate sets pathlen=0, and is followed by
+a self-issued intermediate."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate with pathlen 0
diff --git a/net/data/verify_certificate_chain_unittest/expired-intermediate/chain.pem b/net/data/verify_certificate_chain_unittest/expired-intermediate/chain.pem
index 8b304a0..c1abbafb 100644
--- a/net/data/verify_certificate_chain_unittest/expired-intermediate/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/expired-intermediate/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with a root, intermediate and target. The intermediate has
-a smaller validity range than the other certificates, making it easy to violate
-just its validity.
+Certificate chain where the intermediate has a smaller validity range
+than the other certificates, making it easy to violate just its validity.
 
   Root:          2015/01/01 -> 2016/01/01
   Intermediate:  2015/03/01 -> 2015/09/01
diff --git a/net/data/verify_certificate_chain_unittest/expired-intermediate/generate-chains.py b/net/data/verify_certificate_chain_unittest/expired-intermediate/generate-chains.py
index 1387f9b..fa883e9f 100755
--- a/net/data/verify_certificate_chain_unittest/expired-intermediate/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/expired-intermediate/generate-chains.py
@@ -3,9 +3,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with a root, intermediate and target. The intermediate has
-a smaller validity range than the other certificates, making it easy to violate
-just its validity.
+"""Certificate chain where the intermediate has a smaller validity range
+than the other certificates, making it easy to violate just its validity.
 
   Root:          2015/01/01 -> 2016/01/01
   Intermediate:  2015/03/01 -> 2015/09/01
diff --git a/net/data/verify_certificate_chain_unittest/expired-root/chain.pem b/net/data/verify_certificate_chain_unittest/expired-root/chain.pem
index 45f1b7c..cf1f3b2 100644
--- a/net/data/verify_certificate_chain_unittest/expired-root/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/expired-root/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with a root, intermediate and target. The root has a
-smaller validity range than the other certificates, making it easy to violate
-just its validity.
+Certificate chain where the root has a smaller validity range than the other
+certificates, making it easy to violate just its validity.
 
   Root:          2015/03/01 -> 2015/09/01
   Intermediate:  2015/01/01 -> 2016/01/01
diff --git a/net/data/verify_certificate_chain_unittest/expired-root/generate-chains.py b/net/data/verify_certificate_chain_unittest/expired-root/generate-chains.py
index 93d5bb7..369dbe17 100755
--- a/net/data/verify_certificate_chain_unittest/expired-root/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/expired-root/generate-chains.py
@@ -3,9 +3,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with a root, intermediate and target. The root has a
-smaller validity range than the other certificates, making it easy to violate
-just its validity.
+"""Certificate chain where the root has a smaller validity range than the other
+certificates, making it easy to violate just its validity.
 
   Root:          2015/03/01 -> 2015/09/01
   Intermediate:  2015/01/01 -> 2016/01/01
diff --git a/net/data/verify_certificate_chain_unittest/expired-target/chain.pem b/net/data/verify_certificate_chain_unittest/expired-target/chain.pem
index fced9e6..d538892 100644
--- a/net/data/verify_certificate_chain_unittest/expired-target/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/expired-target/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with a root, intermediate and target. The target has a
-smaller validity range than the other certificates, making it easy to violate
-just its validity.
+Certificate chain where the target certificate has a smaller validity range
+than the other certificates, making it easy to violate just its validity.
 
   Root:          2015/01/01 -> 2016/01/01
   Intermediate:  2015/01/01 -> 2016/01/01
diff --git a/net/data/verify_certificate_chain_unittest/expired-target/generate-chains.py b/net/data/verify_certificate_chain_unittest/expired-target/generate-chains.py
index 8e548be..f75e499 100755
--- a/net/data/verify_certificate_chain_unittest/expired-target/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/expired-target/generate-chains.py
@@ -3,9 +3,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with a root, intermediate and target. The target has a
-smaller validity range than the other certificates, making it easy to violate
-just its validity.
+"""Certificate chain where the target certificate has a smaller validity range
+than the other certificates, making it easy to violate just its validity.
 
   Root:          2015/01/01 -> 2016/01/01
   Intermediate:  2015/01/01 -> 2016/01/01
diff --git a/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/chain.pem b/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/chain.pem
index d363a06..5c47cf6 100644
--- a/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/chain.pem
@@ -1,8 +1,10 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate, but the trust anchor used is
-incorrect (neither subject nor signature matches). Verification is expected to
-fail.
+Certificate chain where the supposed root certificate is wrong:
+
+  * The intermediate's "issuer" does not match the root's "subject"
+  * The intermediate's signature was not generated using the root's key
+
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/generate-chains.py b/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/generate-chains.py
index 3348296..a337dbb6 100755
--- a/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/generate-chains.py
@@ -3,16 +3,18 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate, but the trust anchor used is
-incorrect (neither subject nor signature matches). Verification is expected to
-fail."""
+"""Certificate chain where the supposed root certificate is wrong:
+
+  * The intermediate's "issuer" does not match the root's "subject"
+  * The intermediate's signature was not generated using the root's key
+"""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate, which is NOT saved as the trust anchor.
+# Self-signed root certificate, which actually signed the intermediate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
@@ -21,8 +23,8 @@
 # Target certificate.
 target = common.create_end_entity_certificate('Target', intermediate)
 
-# Self-signed root certificate, not part of chain, which is saved as trust
-# anchor.
+# Self-signed root certificate that has nothing to do with this chain, but will
+# be saved as its root certificate.
 bogus_root = common.create_self_signed_root_certificate('BogusRoot')
 
 chain = [target, intermediate, bogus_root]
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-ca-false/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-ca-false/chain.pem
index 3c33888..6ffe22fa 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-ca-false/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-ca-false/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-has a basic constraints extension that indicates it is NOT a CA. Verification
-is expected to fail.
+Certificate chain where the intermediate has a basic constraints extension
+that indicates it is NOT a CA.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-ca-false/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-ca-false/generate-chains.py
index bd80f25c..5435ab1b 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-ca-false/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-ca-false/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-has a basic constraints extension that indicates it is NOT a CA. Verification
-is expected to fail."""
+"""Certificate chain where the intermediate has a basic constraints extension
+that indicates it is NOT a CA."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate with incorrect basic constraints.
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical/chain.pem
index e4eda0b6..0e1da1c 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical/chain.pem
@@ -1,9 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-has a basic constraints extension but does not mark it as critical.
-Verification is expected to succeed, since although not critical, the
-basicConstraints indicates CA=true as expected.
+Certificate chain where the intermediate's Basic Constraints extension is
+not marked as critical.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical/generate-chains.py
index a0f50e7..c83ecf5 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-basic-constraints-not-critical/generate-chains.py
@@ -3,20 +3,18 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-has a basic constraints extension but does not mark it as critical.
-Verification is expected to succeed, since although not critical, the
-basicConstraints indicates CA=true as expected."""
+"""Certificate chain where the intermediate's Basic Constraints extension is
+not marked as critical."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
-# Intermediate with non-critical basic constarints.
+# Intermediate with non-critical basic constraints.
 intermediate = common.create_intermediate_certificate('Intermediate', root)
 intermediate.get_extensions().set_property('basicConstraints', 'CA:true')
 
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/chain.pem
index 9d67a50..53dcebad 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/chain.pem
@@ -1,9 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-restricts the EKU to clientAuth + any, and the target has serverAuth +
-clientAuth. Verification is expected to succeed because intermediate will match
-the "any".
+Certificate chain where the intermediate restricts the extended key usage to
+clientAuth + any, and the target sets serverAuth + clientAuth.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/generate-chains.py
index 1a1460a9..83a5af09 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/generate-chains.py
@@ -3,17 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-restricts the EKU to clientAuth + any, and the target has serverAuth +
-clientAuth. Verification is expected to succeed because intermediate will match
-the "any"."""
+"""Certificate chain where the intermediate restricts the extended key usage to
+clientAuth + any, and the target sets serverAuth + clientAuth."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem
index f51209a5..ec9cd15 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-restricts the EKU to clientAuth, and the target has serverAuth +
-clientAuth. Verification is expected to fail when requesting serverAuth.
+Certificate chain where the intermediate restricts the extended key usage to
+clientAuth, and the target asserts serverAuth + clientAuth.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-eku-clientauth/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-eku-clientauth/generate-chains.py
index 8f8eae7..5268f39 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-eku-clientauth/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-eku-clientauth/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-restricts the EKU to clientAuth, and the target has serverAuth +
-clientAuth. Verification is expected to fail when requesting serverAuth."""
+"""Certificate chain where the intermediate restricts the extended key usage to
+clientAuth, and the target asserts serverAuth + clientAuth."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints/chain.pem
index 3941ad6..169249d 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-lacks the basic constraints extension, and hence is expected to fail validation
-(RFC 5280 requires v3 signing certificates have a BasicConstaints).
+Certificate chain where the intermediate lacks a basic constraints
+extension (yet is used to issue another certificate).
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints/generate-chains.py
index b1219afa..c258acb2a 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-lacks-basic-constraints/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-lacks the basic constraints extension, and hence is expected to fail validation
-(RFC 5280 requires v3 signing certificates have a BasicConstaints)."""
+"""Certificate chain where the intermediate lacks a basic constraints
+extension (yet is used to issue another certificate)."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate that lacks basic constraints.
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage/chain.pem
index e646cf6..04d5660 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage/chain.pem
@@ -1,8 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-contains a keyUsage extension, HOWEVER it does not contain the keyCertSign bit.
-Hence validation is expected to fail.
+Certificate chain where the intermediate lacks a keyUsage extension.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage/generate-chains.py
index 42cb22f..657beab 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-lacks-signing-key-usage/generate-chains.py
@@ -3,16 +3,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-contains a keyUsage extension, HOWEVER it does not contain the keyCertSign bit.
-Hence validation is expected to fail."""
+"""Certificate chain where the intermediate lacks a keyUsage extension."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate that is missing keyCertSign.
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-signed-with-md5/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-signed-with-md5/chain.pem
index 55aae06..614fc6c 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-signed-with-md5/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-signed-with-md5/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-however is signed using the MD5 hash. Verification is expected to fail because
-MD5 is too weak.
+Certificate chain where the intermediate has a valid signature, however uses
+MD5 in the signature algorithm.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-signed-with-md5/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-signed-with-md5/generate-chains.py
index cfdbb1a5..518d5df 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-signed-with-md5/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-signed-with-md5/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-however is signed using the MD5 hash. Verification is expected to fail because
-MD5 is too weak."""
+"""Certificate chain where the intermediate has a valid signature, however uses
+MD5 in the signature algorithm."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate.
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension/chain.pem
index 36aeef7b..4320fdb 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension/chain.pem
@@ -1,9 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-has an unknown X.509v3 extension (OID=1.2.3.4) that is marked as critical.
-Verifying this certificate chain is expected to fail because there is an
-unrecognized critical extension.
+Certificate chain where the intermediate has an unknown critical
+extension.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension/generate-chains.py
index f7863df..26cb4d4 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-unknown-critical-extension/generate-chains.py
@@ -3,17 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-has an unknown X.509v3 extension (OID=1.2.3.4) that is marked as critical.
-Verifying this certificate chain is expected to fail because there is an
-unrecognized critical extension."""
+"""Certificate chain where the intermediate has an unknown critical
+extension."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate that has an unknown critical extension.
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension/chain.pem b/net/data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension/chain.pem
index 9769849..deb636d 100644
--- a/net/data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension/chain.pem
@@ -1,9 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The intermediate
-has an unknown X.509v3 extension that is marked as non-critical. Verification
-is expected to succeed because although unrecognized, the extension is not
-critical.
+Certificate chain where the intermediate contains an unknown non-critical
+extension.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension/generate-chains.py b/net/data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension/generate-chains.py
index 108c9196..4833c1b 100755
--- a/net/data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/intermediate-unknown-non-critical-extension/generate-chains.py
@@ -3,17 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The intermediate
-has an unknown X.509v3 extension that is marked as non-critical. Verification
-is expected to succeed because although unrecognized, the extension is not
-critical."""
+"""Certificate chain where the intermediate contains an unknown non-critical
+extension."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 intermediate = common.create_intermediate_certificate('Intermediate', root)
 
diff --git a/net/data/verify_certificate_chain_unittest/non-self-signed-root/chain.pem b/net/data/verify_certificate_chain_unittest/non-self-signed-root/chain.pem
index dd522984..55dc965 100644
--- a/net/data/verify_certificate_chain_unittest/non-self-signed-root/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/non-self-signed-root/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The trusted root
-is NOT self signed, however its issuer is not included in the chain or root
-store. Verification is expected to succeed since the root is trusted.
+Certificate chain where the root certificate is not self-signed (or
+self-issued for that matter).
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/non-self-signed-root/generate-chains.py b/net/data/verify_certificate_chain_unittest/non-self-signed-root/generate-chains.py
index 858b18e3..7c6d6d8a 100755
--- a/net/data/verify_certificate_chain_unittest/non-self-signed-root/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/non-self-signed-root/generate-chains.py
@@ -3,9 +3,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The trusted root
-is NOT self signed, however its issuer is not included in the chain or root
-store. Verification is expected to succeed since the root is trusted."""
+"""Certificate chain where the root certificate is not self-signed (or
+self-issued for that matter)."""
 
 import sys
 sys.path += ['..']
@@ -14,7 +13,7 @@
 
 shadow_root = common.create_self_signed_root_certificate('ShadowRoot')
 
-# Non-self-signed root (part of trust store).
+# Non-self-signed root certificate.
 root = common.create_intermediate_certificate('Root', shadow_root)
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/root-basic-constraints-ca-false/chain.pem b/net/data/verify_certificate_chain_unittest/root-basic-constraints-ca-false/chain.pem
index cf378a4f..9700061 100644
--- a/net/data/verify_certificate_chain_unittest/root-basic-constraints-ca-false/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/root-basic-constraints-ca-false/chain.pem
@@ -1,9 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trust anchor. The trust anchor
-has a basic constraints extension that indicates it is NOT a CA. Verification
-is expected to succeed even though the trust anchor enforces constraints, since
-the CA part of basic constraints is not enforced.
+Certificate chain where the root certificate contains a basic constraints
+extension that indicates it is NOT a CA.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/root-basic-constraints-ca-false/generate-chains.py b/net/data/verify_certificate_chain_unittest/root-basic-constraints-ca-false/generate-chains.py
index 4919d35..23a313c 100755
--- a/net/data/verify_certificate_chain_unittest/root-basic-constraints-ca-false/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/root-basic-constraints-ca-false/generate-chains.py
@@ -3,18 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trust anchor. The trust anchor
-has a basic constraints extension that indicates it is NOT a CA. Verification
-is expected to succeed even though the trust anchor enforces constraints, since
-the CA part of basic constraints is not enforced."""
+"""Certificate chain where the root certificate contains a basic constraints
+extension that indicates it is NOT a CA."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor) with non-CA basic
-# constraints.
+# Self-signed root certificate with non-CA basic constraints.
 root = common.create_self_signed_root_certificate('Root')
 root.get_extensions().set_property('basicConstraints', 'critical,CA:false')
 
diff --git a/net/data/verify_certificate_chain_unittest/root-eku-clientauth/chain.pem b/net/data/verify_certificate_chain_unittest/root-eku-clientauth/chain.pem
index 6985faf..ac8fc31 100644
--- a/net/data/verify_certificate_chain_unittest/root-eku-clientauth/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/root-eku-clientauth/chain.pem
@@ -1,9 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trust anchor. The trust anchor
-has an EKU that restricts it to clientAuth. Verification is expected to fail as
-the end-entity is verified for serverAuth, and the trust anchor enforces
-constraints.
+Certificate chain where the root certificate restricts the extended key
+usage to clientAuth.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/root-eku-clientauth/generate-chains.py b/net/data/verify_certificate_chain_unittest/root-eku-clientauth/generate-chains.py
index e58cd0a..e7490fb 100755
--- a/net/data/verify_certificate_chain_unittest/root-eku-clientauth/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/root-eku-clientauth/generate-chains.py
@@ -3,18 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trust anchor. The trust anchor
-has an EKU that restricts it to clientAuth. Verification is expected to fail as
-the end-entity is verified for serverAuth, and the trust anchor enforces
-constraints."""
+"""Certificate chain where the root certificate restricts the extended key
+usage to clientAuth."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor) with non-CA basic
-# constraints.
+# Self-signed root certificate with extended key usage of clientAuth.
 root = common.create_self_signed_root_certificate('Root')
 root.get_extensions().set_property('extendedKeyUsage', 'clientAuth')
 
diff --git a/net/data/verify_certificate_chain_unittest/root-lacks-basic-constraints/chain.pem b/net/data/verify_certificate_chain_unittest/root-lacks-basic-constraints/chain.pem
index 13eb9fc3..1e7b4ff4 100644
--- a/net/data/verify_certificate_chain_unittest/root-lacks-basic-constraints/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/root-lacks-basic-constraints/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trust anchor. The trust anchor
-lacks the basic constraints extension. This is not a problem and verification
-should succeed.
+Certificate chain where the root certificate lacks a basic constraints
+extension.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/root-lacks-basic-constraints/generate-chains.py b/net/data/verify_certificate_chain_unittest/root-lacks-basic-constraints/generate-chains.py
index 268cc1e2..dbd5619 100755
--- a/net/data/verify_certificate_chain_unittest/root-lacks-basic-constraints/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/root-lacks-basic-constraints/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trust anchor. The trust anchor
-lacks the basic constraints extension. This is not a problem and verification
-should succeed."""
+"""Certificate chain where the root certificate lacks a basic constraints
+extension."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 root.get_extensions().remove_property('basicConstraints')
 
diff --git a/net/data/verify_certificate_chain_unittest/target-and-intermediate/chain.pem b/net/data/verify_certificate_chain_unittest/target-and-intermediate/chain.pem
index 975f4fc5..55225b6 100644
--- a/net/data/verify_certificate_chain_unittest/target-and-intermediate/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-and-intermediate/chain.pem
@@ -1,7 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. Verification is
-expected to succeed.
+Simple certificate chain for a serverAuth which is comprised of a root,
+intermediate, and leaf certificate.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-and-intermediate/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-and-intermediate/generate-chains.py
index d118a7c..5cb2b46 100755
--- a/net/data/verify_certificate_chain_unittest/target-and-intermediate/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-and-intermediate/generate-chains.py
@@ -3,15 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. Verification is
-expected to succeed."""
+"""Simple certificate chain for a serverAuth which is comprised of a root,
+intermediate, and leaf certificate."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/target-eku-clientauth/chain.pem b/net/data/verify_certificate_chain_unittest/target-eku-clientauth/chain.pem
index 713555460..ae578583 100644
--- a/net/data/verify_certificate_chain_unittest/target-eku-clientauth/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-eku-clientauth/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The target
-certificate has only clientAuth EKU, so is expected to fail when verifying for
-serverAuth.
+Certificate chain where the target certificate sets the extended key usage
+to clientAuth. Neither the root nor the intermediate have an EKU.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-eku-clientauth/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-eku-clientauth/generate-chains.py
index fa5256c..12d04738 100755
--- a/net/data/verify_certificate_chain_unittest/target-eku-clientauth/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-eku-clientauth/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The target
-certificate has only clientAuth EKU, so is expected to fail when verifying for
-serverAuth."""
+"""Certificate chain where the target certificate sets the extended key usage
+to clientAuth. Neither the root nor the intermediate have an EKU."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/target-eku-none/chain.pem b/net/data/verify_certificate_chain_unittest/target-eku-none/chain.pem
index 3d9fd070..cbfacfc 100644
--- a/net/data/verify_certificate_chain_unittest/target-eku-none/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-eku-none/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The target has no
-Extended Key Usage extension (meaning it is unrestricted). Verification is
-expected to succeed.
+Certificate chain where the leaf certificate lacks an extended key usage
+extension.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-eku-none/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-eku-none/generate-chains.py
index 666b1c3..bed26f3 100755
--- a/net/data/verify_certificate_chain_unittest/target-eku-none/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-eku-none/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The target has no
-Extended Key Usage extension (meaning it is unrestricted). Verification is
-expected to succeed."""
+"""Certificate chain where the leaf certificate lacks an extended key usage
+extension."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/chain.pem b/net/data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/chain.pem
index 6ddc5ff..b168de17 100644
--- a/net/data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/chain.pem
@@ -1,9 +1,8 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate, a trusted root, and a target
-certificate that is not a CA, and yet has the keyCertSign bit set. Verification
-is expected to fail, since keyCertSign should only be asserted when CA is
-true.
+Certificate chain where the leaf certificate asserts the keyCertSign key
+usage, however does not have CA=true in the basic constraints extension to
+indicate it is a CA.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/generate-chains.py
index 01f4509d2..8be0639d 100755
--- a/net/data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/generate-chains.py
@@ -3,17 +3,16 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate, a trusted root, and a target
-certificate that is not a CA, and yet has the keyCertSign bit set. Verification
-is expected to fail, since keyCertSign should only be asserted when CA is
-true."""
+"""Certificate chain where the leaf certificate asserts the keyCertSign key
+usage, however does not have CA=true in the basic constraints extension to
+indicate it is a CA."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/chain.pem b/net/data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/chain.pem
index a9755a0..d30730c 100644
--- a/net/data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate, a trusted root, and a target
-certificate that is not a CA, and yet has a pathlen set. Verification is
-expected to fail, since pathlen should only be set for CAs.
+Certificate chain where the leaf has a basic constraints extension with
+CA=false, however specifies the optional pathlen.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/generate-chains.py
index 44405c9..ee76922 100755
--- a/net/data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate, a trusted root, and a target
-certificate that is not a CA, and yet has a pathlen set. Verification is
-expected to fail, since pathlen should only be set for CAs."""
+"""Certificate chain where the leaf has a basic constraints extension with
+CA=false, however specifies the optional pathlen."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/target-not-end-entity/chain.pem b/net/data/verify_certificate_chain_unittest/target-not-end-entity/chain.pem
index 25b2680e..71b7cdb 100644
--- a/net/data/verify_certificate_chain_unittest/target-not-end-entity/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-not-end-entity/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate, a trusted root, and a target
-certificate that is also a CA. Verification is expected to succeed, as the test
-code accepts any target certificate.
+Certificate chain where the target certificate is a CA rather than an
+end-entity certificate (based on the basic constraints extension).
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-not-end-entity/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-not-end-entity/generate-chains.py
index d1c8b493..2ddb84e 100755
--- a/net/data/verify_certificate_chain_unittest/target-not-end-entity/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-not-end-entity/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate, a trusted root, and a target
-certificate that is also a CA. Verification is expected to succeed, as the test
-code accepts any target certificate."""
+"""Certificate chain where the target certificate is a CA rather than an
+end-entity certificate (based on the basic constraints extension)."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-decipherOnly.pem b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-decipherOnly.pem
index 801b997..bd3a9a8 100644
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-decipherOnly.pem
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-decipherOnly.pem
@@ -1,6 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target uses a EC key and has the single key usage decipherOnly
+Certificate chain where the target certificate uses a EC key and has the single key usage decipherOnly
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-digitalSignature.pem b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-digitalSignature.pem
index 4dcaeaf..fd83e03 100644
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-digitalSignature.pem
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-digitalSignature.pem
@@ -1,6 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target uses a EC key and has the single key usage digitalSignature
+Certificate chain where the target certificate uses a EC key and has the single key usage digitalSignature
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-keyAgreement.pem b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-keyAgreement.pem
index 99c9740..f788b98 100644
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-keyAgreement.pem
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-keyAgreement.pem
@@ -1,6 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target uses a EC key and has the single key usage keyAgreement
+Certificate chain where the target certificate uses a EC key and has the single key usage keyAgreement
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-keyEncipherment.pem b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-keyEncipherment.pem
index 39717eb..cfc277c 100644
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-keyEncipherment.pem
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/ec-keyEncipherment.pem
@@ -1,6 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target uses a EC key and has the single key usage keyEncipherment
+Certificate chain where the target certificate uses a EC key and has the single key usage keyEncipherment
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/generate-chains.py
index 6b81970..ce07591 100755
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/generate-chains.py
@@ -47,6 +47,7 @@
 
     # Write the chain.
     chain = [target, intermediate, root]
-    description = ('Certificate chain where the target uses a %s key and has '
-                   'the single key usage %s') % (key_type.upper(), key_usage)
+    description = ('Certificate chain where the target certificate uses a %s '
+                   'key and has the single key usage %s') % (key_type.upper(),
+                                                             key_usage)
     common.write_chain(description, chain, '%s-%s.pem' % (key_type, key_usage))
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-decipherOnly.pem b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-decipherOnly.pem
index 0b4b507..06e2f1c 100644
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-decipherOnly.pem
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-decipherOnly.pem
@@ -1,6 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target uses a RSA key and has the single key usage decipherOnly
+Certificate chain where the target certificate uses a RSA key and has the single key usage decipherOnly
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-digitalSignature.pem b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-digitalSignature.pem
index 2aa08ea..2be07a26 100644
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-digitalSignature.pem
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-digitalSignature.pem
@@ -1,6 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target uses a RSA key and has the single key usage digitalSignature
+Certificate chain where the target certificate uses a RSA key and has the single key usage digitalSignature
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-keyAgreement.pem b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-keyAgreement.pem
index e32e6d2..190afc5 100644
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-keyAgreement.pem
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-keyAgreement.pem
@@ -1,6 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target uses a RSA key and has the single key usage keyAgreement
+Certificate chain where the target certificate uses a RSA key and has the single key usage keyAgreement
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-keyEncipherment.pem b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-keyEncipherment.pem
index 9ae0468b..b4caac3 100644
--- a/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-keyEncipherment.pem
+++ b/net/data/verify_certificate_chain_unittest/target-serverauth-various-keyusages/rsa-keyEncipherment.pem
@@ -1,6 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target uses a RSA key and has the single key usage keyEncipherment
+Certificate chain where the target certificate uses a RSA key and has the single key usage keyEncipherment
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/chain.pem b/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/chain.pem
index dd509cd..4b702e0 100644
--- a/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The target
-certificate is signed using a weak RSA key (512-bit modulus), and so
-verification is expected to fail.
+Certificate chain where the target certificate is signed using a weak RSA
+key (512-bit modulus).
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/generate-chains.py
index 411a831..066fb60e 100755
--- a/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The target
-certificate is signed using a weak RSA key (512-bit modulus), and so
-verification is expected to fail."""
+"""Certificate chain where the target certificate is signed using a weak RSA
+key (512-bit modulus)."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate with a very weak key size (512-bit RSA).
diff --git a/net/data/verify_certificate_chain_unittest/target-signed-using-ecdsa/chain.pem b/net/data/verify_certificate_chain_unittest/target-signed-using-ecdsa/chain.pem
index 86a472a..e53f710 100644
--- a/net/data/verify_certificate_chain_unittest/target-signed-using-ecdsa/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-signed-using-ecdsa/chain.pem
@@ -1,7 +1,8 @@
 [Created by: generate-chains.py]
 
-Certificate chain with a trusted root using RSA, and intermediate using EC,
-and a target certificate using RSA. Verification is expected to succeed.
+Certificate chain where the root certificate holds an RSA key, intermediate
+certificate holds an EC key, and target certificate holds an RSA key. The
+target certificate has a valid signature using ECDSA.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-signed-using-ecdsa/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-signed-using-ecdsa/generate-chains.py
index b852f3f..ed8fd7ad8 100755
--- a/net/data/verify_certificate_chain_unittest/target-signed-using-ecdsa/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-signed-using-ecdsa/generate-chains.py
@@ -3,15 +3,16 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with a trusted root using RSA, and intermediate using EC,
-and a target certificate using RSA. Verification is expected to succeed."""
+"""Certificate chain where the root certificate holds an RSA key, intermediate
+certificate holds an EC key, and target certificate holds an RSA key. The
+target certificate has a valid signature using ECDSA."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor). using RSA.
+# Self-signed root certificate using an RSA key.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate using an EC key for the P-384 curve.
diff --git a/net/data/verify_certificate_chain_unittest/target-signed-with-md5/chain.pem b/net/data/verify_certificate_chain_unittest/target-signed-with-md5/chain.pem
index 88bbc11..bc74388 100644
--- a/net/data/verify_certificate_chain_unittest/target-signed-with-md5/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-signed-with-md5/chain.pem
@@ -1,7 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with an intermediate that uses MD5 to sign the target
-certificate. This is expected to fail because MD5 is too weak.
+Certificate chain where the intermediate used MD5 to sign the target
+certificate.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-signed-with-md5/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-signed-with-md5/generate-chains.py
index 0a4d747..7ca93f4 100755
--- a/net/data/verify_certificate_chain_unittest/target-signed-with-md5/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-signed-with-md5/generate-chains.py
@@ -3,15 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with an intermediate that uses MD5 to sign the target
-certificate. This is expected to fail because MD5 is too weak."""
+"""Certificate chain where the intermediate used MD5 to sign the target
+certificate."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate.
diff --git a/net/data/verify_certificate_chain_unittest/target-unknown-critical-extension/chain.pem b/net/data/verify_certificate_chain_unittest/target-unknown-critical-extension/chain.pem
index 465b565..15273d3 100644
--- a/net/data/verify_certificate_chain_unittest/target-unknown-critical-extension/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-unknown-critical-extension/chain.pem
@@ -1,9 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 1 intermediate and a trusted root. The target
-certificate has an unknown X.509v3 extension (OID=1.2.3.4) that is marked as
-critical. Verifying this certificate chain is expected to fail because there is
-an unrecognized critical extension.
+Certificate chain where the target certificate contains an unknown X.509v3
+extension (OID=1.2.3.4) that is marked as critical.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-unknown-critical-extension/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-unknown-critical-extension/generate-chains.py
index f1d43a4..ce94161 100755
--- a/net/data/verify_certificate_chain_unittest/target-unknown-critical-extension/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-unknown-critical-extension/generate-chains.py
@@ -3,17 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 1 intermediate and a trusted root. The target
-certificate has an unknown X.509v3 extension (OID=1.2.3.4) that is marked as
-critical. Verifying this certificate chain is expected to fail because there is
-an unrecognized critical extension."""
+"""Certificate chain where the target certificate contains an unknown X.509v3
+extension (OID=1.2.3.4) that is marked as critical."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate.
diff --git a/net/data/verify_certificate_chain_unittest/target-wrong-signature/chain.pem b/net/data/verify_certificate_chain_unittest/target-wrong-signature/chain.pem
index 56a5c00..181dee0 100644
--- a/net/data/verify_certificate_chain_unittest/target-wrong-signature/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/target-wrong-signature/chain.pem
@@ -1,8 +1,6 @@
 [Created by: generate-chains.py]
 
-Certificate chain where the target has an incorrect signature. Everything
-else should check out, however the digital signature contained in the target
-certificate is wrong.
+Certificate chain where the target certificate has an incorrect signature.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/target-wrong-signature/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-wrong-signature/generate-chains.py
index 55c7e16..b29ef964b 100755
--- a/net/data/verify_certificate_chain_unittest/target-wrong-signature/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/target-wrong-signature/generate-chains.py
@@ -3,16 +3,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain where the target has an incorrect signature. Everything
-else should check out, however the digital signature contained in the target
-certificate is wrong."""
+"""Certificate chain where the target certificate has an incorrect signature."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate certificate to include in the certificate chain.
diff --git a/net/data/verify_certificate_chain_unittest/unknown-critical-policy-qualifier/chain.pem b/net/data/verify_certificate_chain_unittest/unknown-critical-policy-qualifier/chain.pem
index 496fafca..8795e03 100644
--- a/net/data/verify_certificate_chain_unittest/unknown-critical-policy-qualifier/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/unknown-critical-policy-qualifier/chain.pem
@@ -1,7 +1,7 @@
 [Created by: generate-chains.py]
 
-The intermediate has a policies extension marked as critical, which contains
-an unknown qualifer (1.2.3.4).
+Certificate chain where the intermediate has a policies extension marked as
+critical, and contains an unknown policy qualifer (1.2.3.4).
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/unknown-critical-policy-qualifier/generate-chains.py b/net/data/verify_certificate_chain_unittest/unknown-critical-policy-qualifier/generate-chains.py
index caad547..0cde86e 100755
--- a/net/data/verify_certificate_chain_unittest/unknown-critical-policy-qualifier/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/unknown-critical-policy-qualifier/generate-chains.py
@@ -3,15 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""The intermediate has a policies extension marked as critical, which contains
-an unknown qualifer (1.2.3.4)."""
+"""Certificate chain where the intermediate has a policies extension marked as
+critical, and contains an unknown policy qualifer (1.2.3.4)."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate that has a critical policies extension containing an unknown
diff --git a/net/data/verify_certificate_chain_unittest/unknown-non-critical-policy-qualifier/chain.pem b/net/data/verify_certificate_chain_unittest/unknown-non-critical-policy-qualifier/chain.pem
index f890d68..a7747b91 100644
--- a/net/data/verify_certificate_chain_unittest/unknown-non-critical-policy-qualifier/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/unknown-non-critical-policy-qualifier/chain.pem
@@ -1,7 +1,7 @@
 [Created by: generate-chains.py]
 
-The intermediate has a policies extension (not marked as critical),
-which contains an unknown qualifer (1.2.3.4).
+Certificate chain where the intermediate has a policies extension (not
+marked as critical) which contains an unknown policy qualifer (1.2.3.4).
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/unknown-non-critical-policy-qualifier/generate-chains.py b/net/data/verify_certificate_chain_unittest/unknown-non-critical-policy-qualifier/generate-chains.py
index 32dd793a..7fe6b22 100755
--- a/net/data/verify_certificate_chain_unittest/unknown-non-critical-policy-qualifier/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/unknown-non-critical-policy-qualifier/generate-chains.py
@@ -3,15 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""The intermediate has a policies extension (not marked as critical),
-which contains an unknown qualifer (1.2.3.4)."""
+"""Certificate chain where the intermediate has a policies extension (not
+marked as critical) which contains an unknown policy qualifer (1.2.3.4)."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate that has a non-critical policies extension containing an unknown
diff --git a/net/data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0/chain.pem b/net/data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0/chain.pem
index 9502729..cf5fa027 100644
--- a/net/data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0/chain.pem
+++ b/net/data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0/chain.pem
@@ -1,8 +1,7 @@
 [Created by: generate-chains.py]
 
-Certificate chain with 2 intermediates. The first intermediate has a basic
-constraints path length of 0, so it is a violation for it to have a subordinate
-intermediate.
+Certificate chain where the intermediate sets pathlen=0, however
+violates this by issuing another (non-self-issued) intermediate.
 
 Certificate:
     Data:
diff --git a/net/data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0/generate-chains.py b/net/data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0/generate-chains.py
index e8092f7a..a13bbdf 100755
--- a/net/data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0/generate-chains.py
+++ b/net/data/verify_certificate_chain_unittest/violates-basic-constraints-pathlen-0/generate-chains.py
@@ -3,16 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Certificate chain with 2 intermediates. The first intermediate has a basic
-constraints path length of 0, so it is a violation for it to have a subordinate
-intermediate."""
+"""Certificate chain where the intermediate sets pathlen=0, however
+violates this by issuing another (non-self-issued) intermediate."""
 
 import sys
 sys.path += ['..']
 
 import common
 
-# Self-signed root certificate (used as trust anchor).
+# Self-signed root certificate.
 root = common.create_self_signed_root_certificate('Root')
 
 # Intermediate with pathlen 0
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 400d73d8..4e30c10 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -797,7 +797,7 @@
 crbug.com/521858 [ Win7 ] http/tests/security/media-element-audio-source-node-same-origin.html [ Failure Pass ]
 crbug.com/521858 [ Win7 ] virtual/mojo-loading/http/tests/security/media-element-audio-source-node-same-origin.html [ Failure Pass ]
 crbug.com/521853 [ Win ] http/tests/inspector/search/sources-search-scope.html [ Failure Pass ]
-crbug.com/520170 [ Win ] fast/dom/timer-throttling-hidden-page.html [ Failure Pass ]
+crbug.com/520170 fast/dom/timer-throttling-hidden-page.html [ Failure Pass ]
 crbug.com/652536 fast/events/mouse-cursor-change-after-image-load.html [ Failure Pass ]
 crbug.com/520188 [ Win ] http/tests/local/fileapi/file-last-modified-after-delete.html [ Failure Pass ]
 crbug.com/520188 [ Win ] virtual/mojo-loading/http/tests/local/fileapi/file-last-modified-after-delete.html [ Failure Pass ]
@@ -2466,7 +2466,10 @@
 crbug.com/595993 external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 crbug.com/595993 virtual/off-main-thread-fetch/external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 
-crbug.com/735883 [ Win10 ] external/wpt/service-workers/service-worker/fetch-canvas-tainting-cache.https.html [ Pass Failure ]
+crbug.com/735883 external/wpt/service-workers/service-worker/fetch-canvas-tainting.https.html [ Pass Failure ]
+crbug.com/735883 virtual/off-main-thread-fetch/external/wpt/service-workers/service-worker/fetch-canvas-tainting.https.html [ Pass Failure ]
+crbug.com/735883 external/wpt/service-workers/service-worker/fetch-canvas-tainting-cache.https.html [ Pass Failure ]
+crbug.com/735883 virtual/off-main-thread-fetch/external/wpt/service-workers/service-worker/fetch-canvas-tainting-cache.https.html [ Pass Failure ]
 
 crbug.com/619427 [ Mac Linux ] fast/overflow/overflow-height-float-not-removed-crash3.html [ Pass Failure ]
 
@@ -2764,3 +2767,67 @@
 
 # Rebaselining a newly-passing test fixed by v8
 crbug.com/v8/6504 fast/js/mozilla/strict/B.1.2.html [ NeedsManualRebaseline ]
+
+# These tests are failing on Mac-10.12 when using an Intel GPU.
+crbug.com/736177 [ Mac ] css2.1/t1202-counter-04-b.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] css2.1/t1202-counters-04-b.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/block/basic/001.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/block/float/intruding-painted-twice.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/borders/inline-mask-overlay-image-outset.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/css-generated-content/012.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/css-generated-content/014.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/css/acid2-pixel.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/css/clip-zooming.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/css/h1-in-section-elements.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/css/rem-calc-dynamic-scaling.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/css/rem-dynamic-scaling.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/dom/HTMLMeterElement/meter-optimums.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/dynamic/012.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/forms/form-element-geometry.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/inline/absolute-positioned-inline-in-centred-block.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/inline/left-right-center-inline-alignment-in-ltr-and-rtl-blocks.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/lists/ordered-list-with-no-ol-tag.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/selectors/166.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/text/color-emoji.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/text/emoticons.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/text/fallback-traits-fixup.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/text/unicode-fallback-font.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] html/details_summary/details-marker-style.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] http/tests/misc/acid2-pixel.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] media/video-zoom-controls.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] paint/spellmarkers/document-markers-zoom-150.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] scrollbars/custom-scrollbar-with-incomplete-style.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/W3C-SVG-1.1/script-handle-02-b.svg [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/W3C-SVG-1.1/script-handle-03-b.svg [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/W3C-SVG-1.1/script-handle-04-b.svg [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/W3C-SVG-1.1/struct-use-05-b.svg [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/W3C-SVG-1.1/text-fonts-01-t.svg [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/as-border-image/svg-as-border-image-2.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/as-border-image/svg-as-border-image.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/batik/text/textLayout.svg [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/custom/focus-ring-text.svg [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/text/surrogate-pair-queries.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/text/text-path-middle-align.svg [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/transforms/svg-css-transforms-clip-path.xhtml [ Failure Pass ]
+crbug.com/736177 [ Mac ] svg/transforms/svg-css-transforms.xhtml [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/bugs/bug18359.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/bugs/bug2479-3.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/bugs/bug2479-4.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/bugs/bug46480-1.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/bugs/bug46480-2.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/bugs/bug8032-1.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/bugs/bug8858.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/other/test3.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/other/test6.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/other/wa_table_thtd_rowspan.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla/other/wa_table_tr_align.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla_expected_failures/bugs/bug1128.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla_expected_failures/bugs/bug2479-5.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] tables/mozilla_expected_failures/bugs/bug91057.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] transforms/svg-vs-css.xhtml [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/disable-spinvalidation/paint/spellmarkers/document-markers-zoom-150.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/layout_ng/fast/block/basic/001.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/mojo-loading/http/tests/misc/acid2-pixel.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/off-main-thread-fetch/http/tests/misc/acid2-pixel.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/prefer_compositing_to_lcd_text/scrollbars/custom-scrollbar-with-incomplete-style.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/rootlayerscrolls/scrollbars/custom-scrollbar-with-incomplete-style.html [ Failure Pass ]
diff --git a/third_party/WebKit/Source/core/editing/EditingUtilities.cpp b/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
index 595aebb..b790cb0 100644
--- a/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
+++ b/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
@@ -655,9 +655,9 @@
       << position << ' ' << highest_root;
   // position falls before highestRoot.
   if (position.CompareTo(PositionTemplate<Strategy>::FirstPositionInNode(
-          &highest_root)) == -1 &&
+          highest_root)) == -1 &&
       HasEditableStyle(highest_root))
-    return PositionTemplate<Strategy>::FirstPositionInNode(&highest_root);
+    return PositionTemplate<Strategy>::FirstPositionInNode(highest_root);
 
   PositionTemplate<Strategy> editable_position = position;
 
@@ -1907,7 +1907,7 @@
   else
     scope = document.documentElement();
 
-  EphemeralRange range(Position::FirstPositionInNode(scope),
+  EphemeralRange range(Position::FirstPositionInNode(*scope),
                        p.ParentAnchoredEquivalent());
 
   return TextIterator::RangeLength(
diff --git a/third_party/WebKit/Source/core/editing/EditingUtilitiesTest.cpp b/third_party/WebKit/Source/core/editing/EditingUtilitiesTest.cpp
index eb00000..aeae254 100644
--- a/third_party/WebKit/Source/core/editing/EditingUtilitiesTest.cpp
+++ b/third_party/WebKit/Source/core/editing/EditingUtilitiesTest.cpp
@@ -55,7 +55,7 @@
                 .DeepEquivalent());
 
   EXPECT_EQ(
-      Position::FirstPositionInNode(host),
+      Position::FirstPositionInNode(*host),
       FirstEditablePositionAfterPositionInRoot(Position(three, 0), *host));
   EXPECT_EQ(
       Position(one->firstChild(), 0),
@@ -184,13 +184,13 @@
                 .DeepEquivalent());
 
   EXPECT_EQ(
-      Position::FirstPositionInNode(host),
+      Position::FirstPositionInNode(*host),
       LastEditablePositionBeforePositionInRoot(Position(three, 0), *host));
   EXPECT_EQ(
       Position(one->firstChild(), 0),
       LastEditableVisiblePositionBeforePositionInRoot(Position(three, 0), *host)
           .DeepEquivalent());
-  EXPECT_EQ(PositionInFlatTree::FirstPositionInNode(host),
+  EXPECT_EQ(PositionInFlatTree::FirstPositionInNode(*host),
             LastEditablePositionBeforePositionInRoot(
                 PositionInFlatTree(three, 0), *host));
   EXPECT_EQ(PositionInFlatTree(two->firstChild(), 0),
diff --git a/third_party/WebKit/Source/core/editing/EphemeralRange.cpp b/third_party/WebKit/Source/core/editing/EphemeralRange.cpp
index 574ddb6..019ac2a 100644
--- a/third_party/WebKit/Source/core/editing/EphemeralRange.cpp
+++ b/third_party/WebKit/Source/core/editing/EphemeralRange.cpp
@@ -143,7 +143,7 @@
 EphemeralRangeTemplate<Strategy>
 EphemeralRangeTemplate<Strategy>::RangeOfContents(const Node& node) {
   return EphemeralRangeTemplate<Strategy>(
-      PositionTemplate<Strategy>::FirstPositionInNode(&const_cast<Node&>(node)),
+      PositionTemplate<Strategy>::FirstPositionInNode(node),
       PositionTemplate<Strategy>::LastPositionInNode(&const_cast<Node&>(node)));
 }
 
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
index 7e42e9c..0c0c9718 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -448,7 +448,7 @@
 
   // Selection has focus if it contains the focused element.
   const PositionInFlatTree& focused_position =
-      PositionInFlatTree::FirstPositionInNode(focused_element);
+      PositionInFlatTree::FirstPositionInNode(*focused_element);
   if (ComputeVisibleSelectionInFlatTree().Start() <= focused_position &&
       ComputeVisibleSelectionInFlatTree().End() >= focused_position)
     return true;
diff --git a/third_party/WebKit/Source/core/editing/Position.cpp b/third_party/WebKit/Source/core/editing/Position.cpp
index 6b58eb4..9647826 100644
--- a/third_party/WebKit/Source/core/editing/Position.cpp
+++ b/third_party/WebKit/Source/core/editing/Position.cpp
@@ -490,10 +490,10 @@
 // static
 template <typename Strategy>
 PositionTemplate<Strategy> PositionTemplate<Strategy>::FirstPositionInNode(
-    Node* anchor_node) {
-  if (anchor_node->IsTextNode())
+    const Node& anchor_node) {
+  if (anchor_node.IsTextNode())
     return PositionTemplate<Strategy>(anchor_node, 0);
-  return PositionTemplate<Strategy>(anchor_node,
+  return PositionTemplate<Strategy>(&anchor_node,
                                     PositionAnchorType::kBeforeChildren);
 }
 
@@ -515,7 +515,7 @@
   if (!node)
     return PositionTemplate<Strategy>();
   return EditingIgnoresContent(*node) ? BeforeNode(*node)
-                                      : FirstPositionInNode(node);
+                                      : FirstPositionInNode(*node);
 }
 
 // static
diff --git a/third_party/WebKit/Source/core/editing/Position.h b/third_party/WebKit/Source/core/editing/Position.h
index 11ed856..0e97aae 100644
--- a/third_party/WebKit/Source/core/editing/Position.h
+++ b/third_party/WebKit/Source/core/editing/Position.h
@@ -193,7 +193,8 @@
   static PositionTemplate<Strategy> InParentBeforeNode(const Node& anchor_node);
   static PositionTemplate<Strategy> InParentAfterNode(const Node& anchor_node);
   static int LastOffsetInNode(Node* anchor_node);
-  static PositionTemplate<Strategy> FirstPositionInNode(Node* anchor_node);
+  static PositionTemplate<Strategy> FirstPositionInNode(
+      const Node& anchor_node);
   static PositionTemplate<Strategy> LastPositionInNode(Node* anchor_node);
   static PositionTemplate<Strategy> FirstPositionInOrBeforeNode(
       Node* anchor_node);
diff --git a/third_party/WebKit/Source/core/editing/PositionIteratorTest.cpp b/third_party/WebKit/Source/core/editing/PositionIteratorTest.cpp
index 615d2ae..b609a02 100644
--- a/third_party/WebKit/Source/core/editing/PositionIteratorTest.cpp
+++ b/third_party/WebKit/Source/core/editing/PositionIteratorTest.cpp
@@ -141,7 +141,7 @@
 
   // Increment until start of "123" from INPUT on DOM tree
   PositionIterator dom_iterator(
-      Position::FirstPositionInNode(GetDocument().body()));
+      Position::FirstPositionInNode(*GetDocument().body()));
   EXPECT_EQ(Position(GetDocument().body(), 0), dom_iterator.ComputePosition());
   dom_iterator.Increment();
   EXPECT_EQ(Position::BeforeNode(*input), dom_iterator.ComputePosition());
@@ -154,7 +154,7 @@
 
   // Increment until start of "123" from INPUT on flat tree
   PositionIteratorInFlatTree flat_iterator(
-      PositionInFlatTree::FirstPositionInNode(GetDocument().body()));
+      PositionInFlatTree::FirstPositionInNode(*GetDocument().body()));
   EXPECT_EQ(PositionInFlatTree(GetDocument().body(), 0),
             flat_iterator.ComputePosition());
   flat_iterator.Increment();
@@ -177,7 +177,7 @@
 
   // Increment until start of "123" from SELECT on DOM tree
   PositionIterator dom_iterator(
-      Position::FirstPositionInNode(GetDocument().body()));
+      Position::FirstPositionInNode(*GetDocument().body()));
   EXPECT_EQ(Position(GetDocument().body(), 0), dom_iterator.ComputePosition());
   dom_iterator.Increment();
   EXPECT_EQ(Position::BeforeNode(*select), dom_iterator.ComputePosition());
@@ -194,7 +194,7 @@
 
   // Increment until start of "123" from SELECT on flat tree
   PositionIteratorInFlatTree flat_iterator(
-      PositionInFlatTree::FirstPositionInNode(GetDocument().body()));
+      PositionInFlatTree::FirstPositionInNode(*GetDocument().body()));
   EXPECT_EQ(PositionInFlatTree(GetDocument().body(), 0),
             flat_iterator.ComputePosition());
   flat_iterator.Increment();
@@ -223,7 +223,7 @@
 
   // Increment until start of "123" from TEXTAREA on DOM tree
   PositionIterator dom_iterator(
-      Position::FirstPositionInNode(GetDocument().body()));
+      Position::FirstPositionInNode(*GetDocument().body()));
   EXPECT_EQ(Position(GetDocument().body(), 0), dom_iterator.ComputePosition());
   dom_iterator.Increment();
   EXPECT_EQ(Position::BeforeNode(*textarea), dom_iterator.ComputePosition());
@@ -236,7 +236,7 @@
 
   // Increment until start of "123" from TEXTAREA on flat tree
   PositionIteratorInFlatTree flat_iterator(
-      PositionInFlatTree::FirstPositionInNode(GetDocument().body()));
+      PositionInFlatTree::FirstPositionInNode(*GetDocument().body()));
   EXPECT_EQ(PositionInFlatTree(GetDocument().body(), 0),
             flat_iterator.ComputePosition());
   // TODO(yosin): We should not traverse inside TEXTAREA
diff --git a/third_party/WebKit/Source/core/editing/PositionTest.cpp b/third_party/WebKit/Source/core/editing/PositionTest.cpp
index 96b11dc..63532a6 100644
--- a/third_party/WebKit/Source/core/editing/PositionTest.cpp
+++ b/third_party/WebKit/Source/core/editing/PositionTest.cpp
@@ -18,7 +18,7 @@
   EXPECT_TRUE(Position(sample, 0).IsEquivalent(Position(sample, 0)));
 
   EXPECT_TRUE(
-      Position(sample, 0).IsEquivalent(Position::FirstPositionInNode(sample)));
+      Position(sample, 0).IsEquivalent(Position::FirstPositionInNode(*sample)));
   EXPECT_TRUE(Position(sample, 0).IsEquivalent(
       Position::BeforeNode(*sample->firstChild())));
   EXPECT_TRUE(Position(sample, 1).IsEquivalent(
diff --git a/third_party/WebKit/Source/core/editing/SelectionEditor.cpp b/third_party/WebKit/Source/core/editing/SelectionEditor.cpp
index fdf7cea..e17c6ee55 100644
--- a/third_party/WebKit/Source/core/editing/SelectionEditor.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionEditor.cpp
@@ -177,7 +177,7 @@
                                                   ContainerNode& container) {
   Node* node = position.ComputeContainerNode();
   if (container.ContainsIncludingHostElements(*node))
-    return Position::FirstPositionInNode(&container);
+    return Position::FirstPositionInNode(container);
   return position;
 }
 
diff --git a/third_party/WebKit/Source/core/editing/SurroundingText.cpp b/third_party/WebKit/Source/core/editing/SurroundingText.cpp
index 68e561e..7e9926d 100644
--- a/third_party/WebKit/Source/core/editing/SurroundingText.cpp
+++ b/third_party/WebKit/Source/core/editing/SurroundingText.cpp
@@ -92,7 +92,7 @@
   // starts at the document's or input element's start and ends at the selection
   // start and will be updated.
   BackwardsCharacterIterator backwards_iterator(
-      Position::FirstPositionInNode(root_element).ParentAnchoredEquivalent(),
+      Position::FirstPositionInNode(*root_element).ParentAnchoredEquivalent(),
       start_position,
       TextIteratorBehavior::Builder().SetStopsOnFormControls(true).Build());
   if (!backwards_iterator.AtEnd())
diff --git a/third_party/WebKit/Source/core/editing/TextFinder.cpp b/third_party/WebKit/Source/core/editing/TextFinder.cpp
index 5bb2ca8..0cc5628 100644
--- a/third_party/WebKit/Source/core/editing/TextFinder.cpp
+++ b/third_party/WebKit/Source/core/editing/TextFinder.cpp
@@ -395,7 +395,7 @@
   }
 
   PositionInFlatTree search_start = PositionInFlatTree::FirstPositionInNode(
-      OwnerFrame().GetFrame()->GetDocument());
+      *OwnerFrame().GetFrame()->GetDocument());
   PositionInFlatTree search_end = PositionInFlatTree::LastPositionInNode(
       OwnerFrame().GetFrame()->GetDocument());
   DCHECK_EQ(search_start.GetDocument(), search_end.GetDocument());
diff --git a/third_party/WebKit/Source/core/editing/VisiblePosition.cpp b/third_party/WebKit/Source/core/editing/VisiblePosition.cpp
index 80b34b7..63daef9 100644
--- a/third_party/WebKit/Source/core/editing/VisiblePosition.cpp
+++ b/third_party/WebKit/Source/core/editing/VisiblePosition.cpp
@@ -118,7 +118,7 @@
 VisiblePositionTemplate<Strategy>
 VisiblePositionTemplate<Strategy>::FirstPositionInNode(Node* node) {
   return Create(PositionWithAffinityTemplate<Strategy>(
-      PositionTemplate<Strategy>::FirstPositionInNode(node)));
+      PositionTemplate<Strategy>::FirstPositionInNode(*node)));
 }
 
 template <typename Strategy>
diff --git a/third_party/WebKit/Source/core/editing/VisibleSelectionTest.cpp b/third_party/WebKit/Source/core/editing/VisibleSelectionTest.cpp
index 868b1b216..1fa4edd 100644
--- a/third_party/WebKit/Source/core/editing/VisibleSelectionTest.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleSelectionTest.cpp
@@ -253,7 +253,7 @@
 
   const VisibleSelection& visible_selectin_in_dom_tree = CreateVisibleSelection(
       SelectionInDOMTree::Builder()
-          .Collapse(Position::FirstPositionInNode(html_element))
+          .Collapse(Position::FirstPositionInNode(*html_element))
           .Extend(Position::LastPositionInNode(html_element))
           .Build());
   EXPECT_EQ(SelectionInDOMTree::Builder()
@@ -265,7 +265,7 @@
   const VisibleSelectionInFlatTree& visible_selectin_in_flat_tree =
       CreateVisibleSelection(
           SelectionInFlatTree::Builder()
-              .Collapse(PositionInFlatTree::FirstPositionInNode(html_element))
+              .Collapse(PositionInFlatTree::FirstPositionInNode(*html_element))
               .Extend(PositionInFlatTree::LastPositionInNode(html_element))
               .Build());
   EXPECT_EQ(SelectionInFlatTree::Builder()
@@ -292,12 +292,12 @@
 
   VisibleSelection selection = CreateVisibleSelection(
       SelectionInDOMTree::Builder()
-          .Collapse(Position::FirstPositionInNode(one))
+          .Collapse(Position::FirstPositionInNode(*one))
           .Extend(Position::LastPositionInNode(shadow_root))
           .Build());
   VisibleSelectionInFlatTree selection_in_flat_tree = CreateVisibleSelection(
       SelectionInFlatTree::Builder()
-          .Collapse(PositionInFlatTree::FirstPositionInNode(one))
+          .Collapse(PositionInFlatTree::FirstPositionInNode(*one))
           .Extend(PositionInFlatTree::LastPositionInNode(host))
           .Build());
 
@@ -327,12 +327,12 @@
 
   VisibleSelection selection =
       CreateVisibleSelection(SelectionInDOMTree::Builder()
-                                 .Collapse(Position::FirstPositionInNode(one))
+                                 .Collapse(Position::FirstPositionInNode(*one))
                                  .Extend(Position::LastPositionInNode(two))
                                  .Build());
   VisibleSelectionInFlatTree selection_in_flat_tree = CreateVisibleSelection(
       SelectionInFlatTree::Builder()
-          .Collapse(PositionInFlatTree::FirstPositionInNode(one))
+          .Collapse(PositionInFlatTree::FirstPositionInNode(*one))
           .Extend(PositionInFlatTree::LastPositionInNode(two))
           .Build());
 
@@ -373,12 +373,12 @@
 
   VisibleSelection selection = CreateVisibleSelection(
       SelectionInDOMTree::Builder()
-          .Collapse(Position::FirstPositionInNode(one))
+          .Collapse(Position::FirstPositionInNode(*one))
           .Extend(Position::LastPositionInNode(shadow_root2))
           .Build());
   VisibleSelectionInFlatTree selection_in_flat_tree = CreateVisibleSelection(
       SelectionInFlatTree::Builder()
-          .Collapse(PositionInFlatTree::FirstPositionInNode(one))
+          .Collapse(PositionInFlatTree::FirstPositionInNode(*one))
           .Extend(PositionInFlatTree::AfterNode(*eight))
           .Build());
 
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
index 8c81961..868deaa 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
@@ -418,7 +418,7 @@
   BackwardsTextBuffer prefix_string;
   if (RequiresContextForWordBoundary(CharacterAfter(c))) {
     SimplifiedBackwardsTextIteratorAlgorithm<Strategy> backwards_iterator(
-        PositionTemplate<Strategy>::FirstPositionInNode(&d), start);
+        PositionTemplate<Strategy>::FirstPositionInNode(d), start);
     while (!backwards_iterator.AtEnd()) {
       backwards_iterator.CopyTextTo(&prefix_string);
       int context_start_index = StartOfLastWordBoundaryContext(
@@ -590,7 +590,7 @@
     return VisiblePositionTemplate<Strategy>();
 
   return CreateVisiblePosition(PositionTemplate<Strategy>::FirstPositionInNode(
-      node->GetDocument().documentElement()));
+      *node->GetDocument().documentElement()));
 }
 
 VisiblePosition StartOfDocument(const VisiblePosition& c) {
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp b/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp
index 2bb61c8..37ed8d25 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp
@@ -359,7 +359,7 @@
     if (!editable_root->contains(
             vis_pos.GetPosition().ComputeContainerNode())) {
       return PositionWithAffinityTemplate<Strategy>(
-          PositionTemplate<Strategy>::FirstPositionInNode(editable_root));
+          PositionTemplate<Strategy>::FirstPositionInNode(*editable_root));
     }
   }
 
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnitsTest.cpp b/third_party/WebKit/Source/core/editing/VisibleUnitsTest.cpp
index a5c92fe5..741a73e8 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnitsTest.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleUnitsTest.cpp
@@ -246,11 +246,11 @@
 
   EXPECT_EQ(Position::BeforeNode(*input),
             CanonicalPositionOf(Position::FirstPositionInNode(
-                GetDocument().documentElement())));
+                *GetDocument().documentElement())));
 
   EXPECT_EQ(PositionInFlatTree::BeforeNode(*input),
             CanonicalPositionOf(PositionInFlatTree::FirstPositionInNode(
-                GetDocument().documentElement())));
+                *GetDocument().documentElement())));
 }
 
 TEST_F(VisibleUnitsTest, characterBefore) {
@@ -1338,13 +1338,13 @@
 
   EXPECT_EQ(Position(GetDocument().body(), 0),
             MostForwardCaretPosition(
-                Position::FirstPositionInNode(GetDocument().body())));
+                Position::FirstPositionInNode(*GetDocument().body())));
   EXPECT_EQ(
       Position(sample, 1),
       MostForwardCaretPosition(Position::BeforeNode(*sample->parentNode())));
   EXPECT_EQ(Position(sample, 1),
             MostForwardCaretPosition(
-                Position::FirstPositionInNode(sample->parentNode())));
+                Position::FirstPositionInNode(*sample->parentNode())));
   EXPECT_EQ(Position(sample, 1), MostForwardCaretPosition(Position(sample, 0)));
   EXPECT_EQ(Position(sample, 1), MostForwardCaretPosition(Position(sample, 1)));
   EXPECT_EQ(Position(sample, 2), MostForwardCaretPosition(Position(sample, 2)));
diff --git a/third_party/WebKit/Source/core/editing/commands/ApplyBlockElementCommand.cpp b/third_party/WebKit/Source/core/editing/commands/ApplyBlockElementCommand.cpp
index a1c3139e..a2d4f00 100644
--- a/third_party/WebKit/Source/core/editing/commands/ApplyBlockElementCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/ApplyBlockElementCommand.cpp
@@ -279,7 +279,7 @@
       SplitTextNode(start_text, start_offset);
       GetDocument().UpdateStyleAndLayoutTree();
 
-      start = Position::FirstPositionInNode(start_text);
+      start = Position::FirstPositionInNode(*start_text);
       if (is_start_and_end_on_same_node) {
         DCHECK_GE(end.OffsetInContainerNode(), start_offset);
         end = Position(start_text, end.OffsetInContainerNode() - start_offset);
@@ -360,7 +360,7 @@
   if (!style->PreserveNewline() ||
       !end_of_next_paragraph_position.OffsetInContainerNode() ||
       !IsNewLineAtPosition(
-          Position::FirstPositionInNode(end_of_next_paragraph_text)))
+          Position::FirstPositionInNode(*end_of_next_paragraph_text)))
     return end_of_next_paragraph;
 
   // \n at the beginning of the text node immediately following the current
diff --git a/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.cpp b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.cpp
index 19b8a96..8682b1f 100644
--- a/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.cpp
@@ -263,10 +263,10 @@
   Node& scope = NodeTraversal::HighestAncestorOrSelf(
       *visible_start.DeepEquivalent().AnchorNode());
   Range* start_range =
-      Range::Create(GetDocument(), Position::FirstPositionInNode(&scope),
+      Range::Create(GetDocument(), Position::FirstPositionInNode(scope),
                     visible_start.DeepEquivalent().ParentAnchoredEquivalent());
   Range* end_range =
-      Range::Create(GetDocument(), Position::FirstPositionInNode(&scope),
+      Range::Create(GetDocument(), Position::FirstPositionInNode(scope),
                     visible_end.DeepEquivalent().ParentAnchoredEquivalent());
 
   const TextIteratorBehavior behavior =
@@ -303,8 +303,8 @@
           block = new_block;
           if (paragraph_start.IsOrphan()) {
             GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
-            paragraph_start =
-                CreateVisiblePosition(Position::FirstPositionInNode(new_block));
+            paragraph_start = CreateVisiblePosition(
+                Position::FirstPositionInNode(*new_block));
           }
         }
         DCHECK(!paragraph_start.IsOrphan()) << paragraph_start;
@@ -1552,7 +1552,7 @@
 
   Text* text = ToText(start.ComputeContainerNode());
   SplitTextNode(text, start.OffsetInContainerNode());
-  UpdateStartEnd(Position::FirstPositionInNode(text), new_end);
+  UpdateStartEnd(Position::FirstPositionInNode(*text), new_end);
 }
 
 void ApplyStyleCommand::SplitTextAtEnd(const Position& start,
diff --git a/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp b/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp
index 253830d2..21d0a7b 100644
--- a/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp
@@ -1504,7 +1504,7 @@
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
 
   destination_index = TextIterator::RangeLength(
-      Position::FirstPositionInNode(GetDocument().documentElement()),
+      Position::FirstPositionInNode(*GetDocument().documentElement()),
       destination.ToParentAnchoredPosition(),
       TextIteratorBehavior::AllVisiblePositionsRangeLengthBehavior());
 
@@ -1678,7 +1678,7 @@
     return false;
 
   SetEndingSelection(SelectionInDOMTree::Builder()
-                         .Collapse(Position::FirstPositionInNode(new_block))
+                         .Collapse(Position::FirstPositionInNode(*new_block))
                          .SetIsDirectional(EndingSelection().IsDirectional())
                          .Build());
 
diff --git a/third_party/WebKit/Source/core/editing/commands/InsertLineBreakCommand.cpp b/third_party/WebKit/Source/core/editing/commands/InsertLineBreakCommand.cpp
index 7ab8a9f6..4d8630f 100644
--- a/third_party/WebKit/Source/core/editing/commands/InsertLineBreakCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/InsertLineBreakCommand.cpp
@@ -169,7 +169,7 @@
     InsertNodeBefore(node_to_insert, text_node, editing_state);
     if (editing_state->IsAborted())
       return;
-    Position ending_position = Position::FirstPositionInNode(text_node);
+    Position ending_position = Position::FirstPositionInNode(*text_node);
 
     // Handle whitespace that occurs after the split
     GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
@@ -190,7 +190,7 @@
         InsertNodeAt(nbsp_node, position_before_text_node, editing_state);
         if (editing_state->IsAborted())
           return;
-        ending_position = Position::FirstPositionInNode(nbsp_node);
+        ending_position = Position::FirstPositionInNode(*nbsp_node);
       }
     }
 
diff --git a/third_party/WebKit/Source/core/editing/commands/InsertListCommand.cpp b/third_party/WebKit/Source/core/editing/commands/InsertListCommand.cpp
index e89026a..3635296f 100644
--- a/third_party/WebKit/Source/core/editing/commands/InsertListCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/InsertListCommand.cpp
@@ -426,7 +426,7 @@
       }
 
       SetEndingSelection(SelectionInDOMTree::Builder()
-                             .Collapse(Position::FirstPositionInNode(new_list))
+                             .Collapse(Position::FirstPositionInNode(*new_list))
                              .Build());
 
       return true;
diff --git a/third_party/WebKit/Source/core/editing/commands/InsertParagraphSeparatorCommand.cpp b/third_party/WebKit/Source/core/editing/commands/InsertParagraphSeparatorCommand.cpp
index 7e2d71e..760d55c 100644
--- a/third_party/WebKit/Source/core/editing/commands/InsertParagraphSeparatorCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/InsertParagraphSeparatorCommand.cpp
@@ -337,7 +337,7 @@
       return;
 
     SetEndingSelection(SelectionInDOMTree::Builder()
-                           .Collapse(Position::FirstPositionInNode(parent))
+                           .Collapse(Position::FirstPositionInNode(*parent))
                            .SetIsDirectional(EndingSelection().IsDirectional())
                            .Build());
     return;
@@ -496,7 +496,7 @@
       SplitTextNode(text_node, text_offset);
       GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
 
-      position_after_split = Position::FirstPositionInNode(text_node);
+      position_after_split = Position::FirstPositionInNode(*text_node);
       insertion_position = Position(text_node->previousSibling(), text_offset);
     }
   }
@@ -590,7 +590,7 @@
 
   SetEndingSelection(
       SelectionInDOMTree::Builder()
-          .Collapse(Position::FirstPositionInNode(block_to_insert))
+          .Collapse(Position::FirstPositionInNode(*block_to_insert))
           .SetIsDirectional(EndingSelection().IsDirectional())
           .Build());
   ApplyStyleAfterInsertion(start_block, editing_state);
diff --git a/third_party/WebKit/Source/core/editing/commands/InsertTextCommand.cpp b/third_party/WebKit/Source/core/editing/commands/InsertTextCommand.cpp
index 0d7f811..cdafbfa8 100644
--- a/third_party/WebKit/Source/core/editing/commands/InsertTextCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/InsertTextCommand.cpp
@@ -58,7 +58,7 @@
     InsertNodeAtTabSpanPosition(text_node, pos, editing_state);
     if (editing_state->IsAborted())
       return Position();
-    return Position::FirstPositionInNode(text_node);
+    return Position::FirstPositionInNode(*text_node);
   }
 
   // Prepare for text input by looking at the specified position.
@@ -68,7 +68,7 @@
     InsertNodeAt(text_node, pos, editing_state);
     if (editing_state->IsAborted())
       return Position();
-    return Position::FirstPositionInNode(text_node);
+    return Position::FirstPositionInNode(*text_node);
   }
 
   return pos;
diff --git a/third_party/WebKit/Source/core/editing/commands/ReplaceSelectionCommand.cpp b/third_party/WebKit/Source/core/editing/commands/ReplaceSelectionCommand.cpp
index f7b3793..e44d9d4f 100644
--- a/third_party/WebKit/Source/core/editing/commands/ReplaceSelectionCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/ReplaceSelectionCommand.cpp
@@ -592,7 +592,7 @@
           !context
               ? ToHTMLQuoteElement(context)
               : ToHTMLQuoteElement(EnclosingNodeOfType(
-                    Position::FirstPositionInNode(context),
+                    Position::FirstPositionInNode(*context),
                     IsMailHTMLBlockquoteElement, kCanCrossEditingBoundary));
 
       // EditingStyle::removeStyleFromRulesAndContext() uses StyleResolver,
@@ -1254,7 +1254,7 @@
       SplitTextNode(ToText(insertion_pos.ComputeContainerNode()),
                     insertion_pos.OffsetInContainerNode());
       insertion_pos =
-          Position::FirstPositionInNode(insertion_pos.ComputeContainerNode());
+          Position::FirstPositionInNode(*insertion_pos.ComputeContainerNode());
     }
 
     if (HTMLElement* element_to_split_to =
@@ -1543,7 +1543,7 @@
             return;
           SetEndingSelection(
               SelectionInDOMTree::Builder()
-                  .Collapse(Position::FirstPositionInNode(new_list_item))
+                  .Collapse(Position::FirstPositionInNode(*new_list_item))
                   .Build());
         } else {
           // Use a default paragraph element (a plain div) for the empty
@@ -1714,7 +1714,7 @@
       InsertNodeBefore(node, start_node, editing_state);
       if (editing_state->IsAborted())
         return;
-      start_of_inserted_content_ = Position::FirstPositionInNode(node);
+      start_of_inserted_content_ = Position::FirstPositionInNode(*node);
     }
   }
 }
diff --git a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
index a43633b..3dbbedfd 100644
--- a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
@@ -685,7 +685,7 @@
   if (editing_state->IsAborted())
     return false;
   SetEndingSelection(SelectionInDOMTree::Builder()
-                         .Collapse(Position::FirstPositionInNode(root))
+                         .Collapse(Position::FirstPositionInNode(*root))
                          .SetIsDirectional(EndingSelection().IsDirectional())
                          .Build());
 
diff --git a/third_party/WebKit/Source/core/editing/iterators/SearchBuffer.cpp b/third_party/WebKit/Source/core/editing/iterators/SearchBuffer.cpp
index 3322d03..47369b2c 100644
--- a/third_party/WebKit/Source/core/editing/iterators/SearchBuffer.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/SearchBuffer.cpp
@@ -332,7 +332,7 @@
   if (buffer.NeedsMoreContext()) {
     for (SimplifiedBackwardsTextIteratorAlgorithm<Strategy> backwards_iterator(
              PositionTemplate<Strategy>::FirstPositionInNode(
-                 it.OwnerDocument()),
+                 *it.OwnerDocument()),
              PositionTemplate<Strategy>(it.CurrentContainer(),
                                         it.StartOffset()));
          !backwards_iterator.AtEnd(); backwards_iterator.Advance()) {
diff --git a/third_party/WebKit/Source/core/editing/serializers/Serialization.cpp b/third_party/WebKit/Source/core/editing/serializers/Serialization.cpp
index 3f628d2..9040767 100644
--- a/third_party/WebKit/Source/core/editing/serializers/Serialization.cpp
+++ b/third_party/WebKit/Source/core/editing/serializers/Serialization.cpp
@@ -209,7 +209,7 @@
   if (check_ancestor->GetLayoutObject()) {
     HTMLElement* new_special_common_ancestor =
         ToHTMLElement(HighestEnclosingNodeOfType(
-            Position::FirstPositionInNode(check_ancestor),
+            Position::FirstPositionInNode(*check_ancestor),
             &IsPresentationalHTMLElement, kCanCrossEditingBoundary,
             constraining_ancestor));
     if (new_special_common_ancestor)
@@ -229,8 +229,8 @@
   if (HTMLAnchorElement* enclosing_anchor =
           toHTMLAnchorElement(EnclosingElementWithTag(
               Position::FirstPositionInNode(special_common_ancestor
-                                                ? special_common_ancestor
-                                                : common_ancestor),
+                                                ? *special_common_ancestor
+                                                : *common_ancestor),
               aTag)))
     special_common_ancestor = enclosing_anchor;
 
diff --git a/third_party/WebKit/Source/core/editing/serializers/StyledMarkupAccumulator.cpp b/third_party/WebKit/Source/core/editing/serializers/StyledMarkupAccumulator.cpp
index 09ba1ab6..a073148 100644
--- a/third_party/WebKit/Source/core/editing/serializers/StyledMarkupAccumulator.cpp
+++ b/third_party/WebKit/Source/core/editing/serializers/StyledMarkupAccumulator.cpp
@@ -115,7 +115,7 @@
     AppendText(text);
   } else {
     const bool use_rendered_text = !EnclosingElementWithTag(
-        Position::FirstPositionInNode(&text), selectTag);
+        Position::FirstPositionInNode(text), selectTag);
     String content =
         use_rendered_text ? RenderedText(text) : StringValueForRange(text);
     StringBuilder buffer;
diff --git a/third_party/WebKit/Source/core/editing/serializers/StyledMarkupSerializer.cpp b/third_party/WebKit/Source/core/editing/serializers/StyledMarkupSerializer.cpp
index 5b47b20..accb117 100644
--- a/third_party/WebKit/Source/core/editing/serializers/StyledMarkupSerializer.cpp
+++ b/third_party/WebKit/Source/core/editing/serializers/StyledMarkupSerializer.cpp
@@ -231,7 +231,7 @@
         *start_.ComputeContainerNode(), *end_.ComputeContainerNode());
     DCHECK(common_ancestor);
     HTMLBodyElement* body = toHTMLBodyElement(EnclosingElementWithTag(
-        Position::FirstPositionInNode(common_ancestor), bodyTag));
+        Position::FirstPositionInNode(*common_ancestor), bodyTag));
     HTMLBodyElement* fully_selected_root = nullptr;
     // FIXME: Do this for all fully selected blocks, not just the body.
     if (body && AreSameRanges(body, start_, end_))
diff --git a/third_party/WebKit/Source/core/editing/serializers/StyledMarkupSerializerTest.cpp b/third_party/WebKit/Source/core/editing/serializers/StyledMarkupSerializerTest.cpp
index 3531094..d1d5a15 100644
--- a/third_party/WebKit/Source/core/editing/serializers/StyledMarkupSerializerTest.cpp
+++ b/third_party/WebKit/Source/core/editing/serializers/StyledMarkupSerializerTest.cpp
@@ -283,10 +283,11 @@
   SetBodyContent(body_content);
   Element* span1 = GetDocument().getElementById("span1");
   Element* span2 = GetDocument().getElementById("span2");
-  Position start_dom = Position::FirstPositionInNode(span1);
+  Position start_dom = Position::FirstPositionInNode(*span1);
   Position end_dom = Position::LastPositionInNode(span2);
   EXPECT_EQ("", SerializePart<EditingStrategy>(start_dom, end_dom));
-  PositionInFlatTree start_ict = PositionInFlatTree::FirstPositionInNode(span1);
+  PositionInFlatTree start_ict =
+      PositionInFlatTree::FirstPositionInNode(*span1);
   PositionInFlatTree end_ict = PositionInFlatTree::LastPositionInNode(span2);
   EXPECT_EQ("", SerializePart<EditingInFlatTreeStrategy>(start_ict, end_ict));
 }
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/ColdModeSpellCheckRequester.cpp b/third_party/WebKit/Source/core/editing/spellcheck/ColdModeSpellCheckRequester.cpp
index f172052..5d5ebb66 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/ColdModeSpellCheckRequester.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/ColdModeSpellCheckRequester.cpp
@@ -26,8 +26,7 @@
   if (!node.IsElementNode())
     return false;
   // TODO(editing-dev): Make |Position| constructors take const parameters.
-  const Position& position =
-      Position::FirstPositionInNode(const_cast<Node*>(&node));
+  const Position& position = Position::FirstPositionInNode(node);
   if (!IsEditablePosition(position))
     return false;
   return SpellChecker::IsSpellCheckingEnabledAt(position);
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
index 4dda54c..5103772 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
@@ -212,7 +212,7 @@
   TextControlElement* enclosing_text_control_element = nullptr;
   if (!IsTextControlElement(*element)) {
     enclosing_text_control_element =
-        EnclosingTextControl(Position::FirstPositionInNode(element));
+        EnclosingTextControl(Position::FirstPositionInNode(*element));
   }
   element =
       enclosing_text_control_element ? enclosing_text_control_element : element;
@@ -537,7 +537,7 @@
   if (!node)
     return;
 
-  EphemeralRange paragraph_range(Position::FirstPositionInNode(node),
+  EphemeralRange paragraph_range(Position::FirstPositionInNode(*node),
                                  Position::LastPositionInNode(node));
   TextCheckingParagraph text_to_check(inserted_range, paragraph_range);
   ChunkAndMarkAllMisspellings(text_to_check);
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index 9a010b9c..edda6f9f 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -69,7 +69,6 @@
 #include "core/layout/LayoutMedia.h"
 #include "core/layout/api/LayoutViewItem.h"
 #include "core/layout/compositing/PaintLayerCompositor.h"
-#include "core/loader/FrameLoader.h"
 #include "core/page/ChromeClient.h"
 #include "platform/Histogram.h"
 #include "platform/LayoutTestSupport.h"
@@ -528,7 +527,7 @@
   LocalFrame* frame = document.GetFrame();
   if (frame) {
     remote_playback_client_ =
-        frame->Loader().Client()->CreateWebRemotePlaybackClient(*this);
+        frame->Client()->CreateWebRemotePlaybackClient(*this);
   }
 
   SetHasCustomStyleCallbacks();
@@ -1252,7 +1251,7 @@
   }
 
   web_media_player_ =
-      frame->Loader().Client()->CreateWebMediaPlayer(*this, source, this);
+      frame->Client()->CreateWebMediaPlayer(*this, source, this);
   if (!web_media_player_) {
     MediaLoadingFailed(WebMediaPlayer::kNetworkStateFormatError,
                        BuildElementErrorMessage(
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.cpp b/third_party/WebKit/Source/core/loader/EmptyClients.cpp
index 4ba46e8..ecb3c5d 100644
--- a/third_party/WebKit/Source/core/loader/EmptyClients.cpp
+++ b/third_party/WebKit/Source/core/loader/EmptyClients.cpp
@@ -71,6 +71,8 @@
 class EmptyFrameScheduler : public WebFrameScheduler {
  public:
   EmptyFrameScheduler() { DCHECK(IsMainThread()); }
+  void AddThrottlingObserver(ObserverType, Observer*) override {}
+  void RemoveThrottlingObserver(ObserverType, Observer*) override {}
   void SetFrameVisible(bool) override {}
   RefPtr<WebTaskRunner> LoadingTaskRunner() override;
   RefPtr<WebTaskRunner> TimerTaskRunner() override;
diff --git a/third_party/WebKit/Source/modules/webaudio/OfflineAudioDestinationNode.cpp b/third_party/WebKit/Source/modules/webaudio/OfflineAudioDestinationNode.cpp
index 32f1f5f2a..e15549c 100644
--- a/third_party/WebKit/Source/modules/webaudio/OfflineAudioDestinationNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/OfflineAudioDestinationNode.cpp
@@ -173,7 +173,23 @@
 void OfflineAudioDestinationHandler::DoOfflineRendering() {
   DCHECK(!IsMainThread());
 
-  unsigned number_of_channels = render_target_->numberOfChannels();
+  unsigned number_of_channels;
+  Vector<float*> destinations;
+  {
+    // Main thread GCs cannot happen while we're reading out channel
+    // data. Detect that condition by trying to take the cross-thread
+    // persistent lock which is held while a GC runs. If the lock is
+    // already held, simply delay rendering until the next quantum.
+    CrossThreadPersistentRegion::LockScope gc_lock(
+        ProcessHeap::GetCrossThreadPersistentRegion(), true);
+    if (!gc_lock.HasLock())
+      return;
+
+    number_of_channels = render_target_->numberOfChannels();
+    destinations.ReserveInitialCapacity(number_of_channels);
+    for (unsigned i = 0; i < number_of_channels; ++i)
+      destinations.push_back(render_target_->getChannelData(i).View()->Data());
+  }
 
   // Reset the suspend flag.
   should_suspend_ = false;
@@ -198,9 +214,7 @@
     for (unsigned channel_index = 0; channel_index < number_of_channels;
          ++channel_index) {
       const float* source = render_bus_->Channel(channel_index)->Data();
-      float* destination =
-          render_target_->getChannelData(channel_index).View()->Data();
-      memcpy(destination + frames_processed_, source,
+      memcpy(destinations[channel_index] + frames_processed_, source,
              sizeof(float) * frames_available_to_copy);
     }
 
diff --git a/third_party/WebKit/Source/platform/WebFrameScheduler.h b/third_party/WebKit/Source/platform/WebFrameScheduler.h
index 83ca660..42ffbda9 100644
--- a/third_party/WebKit/Source/platform/WebFrameScheduler.h
+++ b/third_party/WebKit/Source/platform/WebFrameScheduler.h
@@ -18,6 +18,15 @@
  public:
   virtual ~WebFrameScheduler() {}
 
+  // Observer type that regulates conditions to invoke callbacks.
+  enum class ObserverType { kLoader };
+
+  // Represents throttling state.
+  enum class ThrottlingState {
+    kThrottled,
+    kNotThrottled,
+  };
+
   class ActiveConnectionHandle {
    public:
     ActiveConnectionHandle() {}
@@ -27,6 +36,21 @@
     DISALLOW_COPY_AND_ASSIGN(ActiveConnectionHandle);
   };
 
+  // Observer interface to receive scheduling policy change events.
+  class Observer {
+   public:
+    // Notified when throttling state is changed.
+    virtual void OnThrottlingStateChanged(ThrottlingState) = 0;
+  };
+
+  // Adds an Observer instance to be notified on scheduling policy changed.
+  // When an Observer is added, the initial state will be notified synchronously
+  // through the Observer interface.
+  virtual void AddThrottlingObserver(ObserverType, Observer*) = 0;
+
+  // Removes an Observer instance.
+  virtual void RemoveThrottlingObserver(ObserverType, Observer*) = 0;
+
   // The scheduler may throttle tasks associated with offscreen frames.
   virtual void SetFrameVisible(bool) {}
 
diff --git a/third_party/WebKit/Source/platform/heap/PersistentNode.h b/third_party/WebKit/Source/platform/heap/PersistentNode.h
index 9ed41b0..60d179c6 100644
--- a/third_party/WebKit/Source/platform/heap/PersistentNode.h
+++ b/third_party/WebKit/Source/platform/heap/PersistentNode.h
@@ -202,14 +202,27 @@
     STACK_ALLOCATED();
 
    public:
-    LockScope(CrossThreadPersistentRegion& persistent_region)
-        : persistent_region_(persistent_region) {
-      persistent_region_.lock();
+    LockScope(CrossThreadPersistentRegion& persistent_region,
+              bool try_lock = false)
+        : persistent_region_(persistent_region), locked_(true) {
+      if (try_lock)
+        locked_ = persistent_region_.TryLock();
+      else
+        persistent_region_.lock();
     }
-    ~LockScope() { persistent_region_.unlock(); }
+    ~LockScope() {
+      if (locked_)
+        persistent_region_.unlock();
+    }
+
+    // If the lock scope is set up with |try_lock| set to |true|, caller/user
+    // is responsible for checking whether the GC lock was taken via
+    // |HasLock()|.
+    bool HasLock() const { return locked_; }
 
    private:
     CrossThreadPersistentRegion& persistent_region_;
+    bool locked_;
   };
 
   void TracePersistentNodes(Visitor* visitor) {
@@ -237,6 +250,8 @@
 
   void unlock() { mutex_.unlock(); }
 
+  bool TryLock() { return mutex_.TryLock(); }
+
   // We don't make CrossThreadPersistentRegion inherit from PersistentRegion
   // because we don't want to virtualize performance-sensitive methods
   // such as PersistentRegion::allocate/freePersistentNode.
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.cpp b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
index 2c2b263..a8716456 100644
--- a/third_party/WebKit/Source/platform/heap/ThreadState.cpp
+++ b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
@@ -1453,9 +1453,11 @@
   GCForbiddenScope gc_forbidden_scope(this);
 
   {
-    // Access to the CrossThreadPersistentRegion has to be prevented while in
-    // the marking phase because otherwise other threads may allocate or free
-    // PersistentNodes and we can't handle that.
+    // Access to the CrossThreadPersistentRegion has to be prevented
+    // while in the marking phase because otherwise other threads may
+    // allocate or free PersistentNodes and we can't handle
+    // that. Grabbing this lock also prevents non-attached threads
+    // from accessing any GCed heap while a GC runs.
     CrossThreadPersistentRegion::LockScope persistent_lock(
         ProcessHeap::GetCrossThreadPersistentRegion());
     {
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.cc
index d1100e3..29f57db 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.cc
@@ -106,6 +106,25 @@
                                 timer_task_queue_.get());
 }
 
+void WebFrameSchedulerImpl::AddThrottlingObserver(ObserverType type,
+                                                  Observer* observer) {
+  DCHECK_EQ(ObserverType::kLoader, type);
+  DCHECK(observer);
+  observer->OnThrottlingStateChanged(page_visible_
+                                         ? ThrottlingState::kNotThrottled
+                                         : ThrottlingState::kThrottled);
+  loader_observers_.insert(observer);
+}
+
+void WebFrameSchedulerImpl::RemoveThrottlingObserver(ObserverType type,
+                                                     Observer* observer) {
+  DCHECK_EQ(ObserverType::kLoader, type);
+  DCHECK(observer);
+  const auto found = loader_observers_.find(observer);
+  DCHECK(loader_observers_.end() != found);
+  loader_observers_.erase(found);
+}
+
 void WebFrameSchedulerImpl::SetFrameVisible(bool frame_visible) {
   DCHECK(parent_web_view_scheduler_);
   if (frame_visible_ == frame_visible)
@@ -301,6 +320,12 @@
   bool was_throttled = ShouldThrottleTimers();
   page_visible_ = page_visible;
   UpdateTimerThrottling(was_throttled);
+
+  for (auto observer : loader_observers_) {
+    observer->OnThrottlingStateChanged(page_visible_
+                                           ? ThrottlingState::kNotThrottled
+                                           : ThrottlingState::kThrottled);
+  }
 }
 
 void WebFrameSchedulerImpl::SetSuspended(bool frame_suspended) {
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.h
index f66523a..6f4b58d6 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_RENDERER_WEB_FRAME_SCHEDULER_IMPL_H_
 
 #include <memory>
+#include <set>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -39,6 +40,8 @@
   ~WebFrameSchedulerImpl() override;
 
   // WebFrameScheduler implementation:
+  void AddThrottlingObserver(ObserverType, Observer*) override;
+  void RemoveThrottlingObserver(ObserverType, Observer*) override;
   void SetFrameVisible(bool frame_visible) override;
   void SetPageVisible(bool page_visible) override;
   void SetSuspended(bool frame_suspended) override;
@@ -60,7 +63,6 @@
   void SetDocumentParsingInBackground(bool background_parser_active) override;
   void OnFirstMeaningfulPaint() override;
   std::unique_ptr<ActiveConnectionHandle> OnActiveConnectionCreated() override;
-
   void AsValueInto(base::trace_event::TracedValue* state) const;
 
   bool has_active_connection() const { return active_connection_count_; }
@@ -107,6 +109,7 @@
   RendererSchedulerImpl* renderer_scheduler_;        // NOT OWNED
   WebViewSchedulerImpl* parent_web_view_scheduler_;  // NOT OWNED
   base::trace_event::BlameContext* blame_context_;   // NOT OWNED
+  std::set<Observer*> loader_observers_;             // NOT OWNED
   bool frame_visible_;
   bool page_visible_;
   bool frame_suspended_;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl_unittest.cc
index 1d5a787..b0d55fbd 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl_unittest.cc
@@ -58,6 +58,35 @@
 
 namespace {
 
+class MockThrottlingObserver : public WebFrameScheduler::Observer {
+ public:
+  MockThrottlingObserver() : throttled_count_(0u), not_throttled_count_(0u) {}
+
+  void CheckObserverState(size_t throttled_count_expectation,
+                          size_t not_throttled_count_expectation) {
+    EXPECT_EQ(throttled_count_expectation, throttled_count_);
+    EXPECT_EQ(not_throttled_count_expectation, not_throttled_count_);
+  }
+
+  // WebFrameScheduler::Observer.
+  void OnThrottlingStateChanged(
+      WebFrameScheduler::ThrottlingState state) override {
+    switch (state) {
+      case WebFrameScheduler::ThrottlingState::kThrottled:
+        throttled_count_++;
+        break;
+      case WebFrameScheduler::ThrottlingState::kNotThrottled:
+        not_throttled_count_++;
+        break;
+        // We should not have another state, and compiler checks it.
+    }
+  }
+
+ private:
+  size_t throttled_count_;
+  size_t not_throttled_count_;
+};
+
 void RunRepeatingTask(RefPtr<WebTaskRunner> task_runner, int* run_count);
 
 std::unique_ptr<WTF::Closure> MakeRepeatingTask(
@@ -209,5 +238,46 @@
   EXPECT_EQ(5, counter);
 }
 
+// Tests if throttling observer interfaces work.
+TEST_F(WebFrameSchedulerImplTest, ThrottlingObserver) {
+  std::unique_ptr<MockThrottlingObserver> observer =
+      base::MakeUnique<MockThrottlingObserver>();
+
+  size_t throttled_count = 0u;
+  size_t not_throttled_count = 0u;
+
+  observer->CheckObserverState(throttled_count, not_throttled_count);
+
+  web_frame_scheduler_->AddThrottlingObserver(
+      WebFrameScheduler::ObserverType::kLoader, observer.get());
+
+  // Initial state should be synchronously notified here.
+  // We assume kNotThrottled is notified as an initial state, but it could
+  // depend on implementation details and can be changed.
+  observer->CheckObserverState(throttled_count, ++not_throttled_count);
+
+  // Once the page gets to be invisible, it should notify the observer of
+  // kThrottled synchronously.
+  web_view_scheduler_->SetPageVisible(false);
+  observer->CheckObserverState(++throttled_count, not_throttled_count);
+
+  // Going back to visible state should notify the observer of kNotThrottled
+  // synchronously.
+  web_view_scheduler_->SetPageVisible(true);
+  observer->CheckObserverState(throttled_count, ++not_throttled_count);
+
+  // Remove from the observer list, and see if any other callback should not be
+  // invoked when the condition is changed.
+  web_frame_scheduler_->RemoveThrottlingObserver(
+      WebFrameScheduler::ObserverType::kLoader, observer.get());
+  web_view_scheduler_->SetPageVisible(false);
+
+  // Wait 100 secs virtually and run pending tasks just in case.
+  clock_->Advance(base::TimeDelta::FromSeconds(100));
+  mock_task_runner_->RunUntilIdle();
+
+  observer->CheckObserverState(throttled_count, not_throttled_count);
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/tools/win/DebugVisualizers/BUILD.gn b/tools/win/DebugVisualizers/BUILD.gn
index d858fe2..d85b8d3d 100644
--- a/tools/win/DebugVisualizers/BUILD.gn
+++ b/tools/win/DebugVisualizers/BUILD.gn
@@ -16,7 +16,7 @@
 # Since these only add ldflags, the targets themselves are not rebuilt when the
 # natvis files are updated. To debug, erase the .pdb file and build to re-link.
 
-import("//build/toolchain/toolchain.gni")
+import("//build/config/compiler/compiler.gni")
 
 assert(is_win)